about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--read.lua73
1 files changed, 48 insertions, 25 deletions
diff --git a/read.lua b/read.lua index bba4ffa..c5dacc0 100644 --- a/read.lua +++ b/read.lua
@@ -4,6 +4,7 @@ local read = {}
4local type = require "type" 4local type = require "type"
5local utf8 = require "utf8" 5local utf8 = require "utf8"
6local util = require "util" 6local util = require "util"
7local pop = util.pop
7local unpack = table.unpack or unpack 8local unpack = table.unpack or unpack
8 9
9function program_characters (program) 10function program_characters (program)
@@ -18,14 +19,14 @@ local function consume_string_whitespace (chars)
18 -- \<intraline ws>*<line ending> <intraline ws>* : nothing 19 -- \<intraline ws>*<line ending> <intraline ws>* : nothing
19 local s = {"\\"} 20 local s = {"\\"}
20 while chars[1]:match("[ \t]") do 21 while chars[1]:match("[ \t]") do
21 table.insert(s, util.pop(chars)) 22 table.insert(s, pop(chars))
22 end 23 end
23 if chars[1] ~= "\n" then 24 if chars[1] ~= "\n" then
24 table.insert(s, chars[1]) 25 table.insert(s, chars[1])
25 return table.concat(s), chars 26 return table.concat(s), chars
26 end 27 end
27 while chars[1]:match("%s") do 28 while chars[1]:match("%s") do
28 util.pop(chars) 29 pop(chars)
29 end 30 end
30 return chars[1], chars 31 return chars[1], chars
31end 32end
@@ -34,7 +35,7 @@ local function consume_string_hexvalue (chars)
34 -- \x<hex scalar value>; : specified character 35 -- \x<hex scalar value>; : specified character
35 local u8ch = {} 36 local u8ch = {}
36 repeat 37 repeat
37 local c = util.pop(chars) 38 local c = pop(chars)
38 table.insert(u8ch, c) 39 table.insert(u8ch, c)
39 until c == ";" 40 until c == ";"
40 table.remove(u8ch) -- remove semicolon 41 table.remove(u8ch) -- remove semicolon
@@ -57,9 +58,9 @@ local function consume_string (chars)
57 ["\n"] = consume_string_whitespace, 58 ["\n"] = consume_string_whitespace,
58 x = consume_string_hexvalue, 59 x = consume_string_hexvalue,
59 } 60 }
60 util.pop(chars) -- throw initial " away 61 pop(chars) -- throw initial " away
61 repeat 62 repeat
62 local c = util.pop(chars) 63 local c = pop(chars)
63 if c == [[\]] then 64 if c == [[\]] then
64 c = chars[1] 65 c = chars[1]
65 if backlash[c] then 66 if backlash[c] then
@@ -72,7 +73,7 @@ local function consume_string (chars)
72 else 73 else
73 table.insert(str, "\\"..c) 74 table.insert(str, "\\"..c)
74 end 75 end
75 util.pop(chars) 76 pop(chars)
76 elseif c == [["]] then 77 elseif c == [["]] then
77 break 78 break
78 else 79 else
@@ -84,8 +85,8 @@ end
84 85
85local function consume_token (chars) 86local function consume_token (chars)
86 local tok = {} 87 local tok = {}
87 while chars[1]:match("[^%s()\"#'`,@;]") do 88 while #chars>0 and chars[1]:match("[^%s()\"#'`,@;]") do
88 table.insert(tok, util.pop(chars)) 89 table.insert(tok, pop(chars))
89 end 90 end
90 return table.concat(tok), chars 91 return table.concat(tok), chars
91end 92end
@@ -100,14 +101,14 @@ local function consume_number (chars)
100end 101end
101 102
102local function consume_whitespace (chars) 103local function consume_whitespace (chars)
103 while chars[1]:match("%s") do util.pop(chars) end 104 while #chars>0 and chars[1]:match("%s") do pop(chars) end
104 return chars 105 return chars
105end 106end
106 107
107local function consume_comment (chars) 108local function consume_comment (chars)
108 local comment = {} 109 local comment = {}
109 repeat 110 repeat
110 table.insert(comment, util.pop(chars)) 111 table.insert(comment, pop(chars))
111 until #chars == 0 or chars[1]:match("\n") 112 until #chars == 0 or chars[1]:match("\n")
112 return table.concat(comment), "comment", chars 113 return table.concat(comment), "comment", chars
113end 114end
@@ -115,14 +116,14 @@ end
115--- API 116--- API
116 117
117read.readtable = { 118read.readtable = {
118 ["("] = function(chars) return util.pop(chars), "begin_list", chars end, 119 ["("] = function(chars) return pop(chars), "open", chars end,
119 [")"] = function(chars) return util.pop(chars), "end_list", chars end, 120 [")"] = function(chars) return pop(chars), "close", chars end,
121 ["'"] = function(chars) return pop(chars), "quote", chars end,
122 ["`"] = function(chars) return pop(chars), "quote", chars end,
123 [","] = function(chars) return pop(chars), "quote", chars end,
120 ["\""] = consume_string, 124 ["\""] = consume_string,
121 [";"] = consume_comment, 125 [";"] = consume_comment,
122 -- ["#"] = 126 -- ["#"] =
123 -- ["'"] =
124 -- ["`"] =
125 -- [","] =
126} 127}
127 128
128function read.scan (chars) 129function read.scan (chars)
@@ -157,18 +158,40 @@ function read.tokenize (program)
157 return tokens 158 return tokens
158end 159end
159 160
161read.readmacros = {
162 open =
163 function (token, tokens)
164 local L = {}
165 while tokens[1].type ~= "close" do
166 table.insert(L, read.parse(tokens))
167 end
168 pop(tokens) -- remove final ")"
169 return type.List(L)
170 end,
171 close =
172 function (token, tokens)
173 error ("Unexpected '" .. token.value .. "'")
174 end,
175 quote =
176 function (token, tokens)
177 local Q
178 if token.value == "'" then
179 Q = {"quote"}
180 elseif token.value == "`" then
181 Q = {"quasiquote"}
182 elseif token.value == "," then
183 Q = {"unquote"}
184 end
185 table.insert(Q, read.parse(tokens))
186 return type.List(Q)
187 end,
188}
189
160function read.parse (tokens) 190function read.parse (tokens)
161 if not next(tokens) then return nil end 191 if not next(tokens) then return nil end
162 local token = util.pop(tokens) 192 local token = pop(tokens)
163 if token.value == "(" then 193 if read.readmacros[token.type] then
164 local L = {} 194 return read.readmacros[token.type](token, tokens)
165 while tokens[1].value ~= ")" do
166 table.insert(L, read.parse(tokens))
167 end
168 util.pop(tokens) -- remove the final ")"
169 return type.List(L)
170 elseif token.value == ")" then
171 error("Unexpected ')'")
172 else 195 else
173 return token.value 196 return token.value
174 end 197 end