diff options
-rw-r--r-- | read.lua | 73 |
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 = {} | |||
4 | local type = require "type" | 4 | local type = require "type" |
5 | local utf8 = require "utf8" | 5 | local utf8 = require "utf8" |
6 | local util = require "util" | 6 | local util = require "util" |
7 | local pop = util.pop | ||
7 | local unpack = table.unpack or unpack | 8 | local unpack = table.unpack or unpack |
8 | 9 | ||
9 | function program_characters (program) | 10 | function 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 |
31 | end | 32 | end |
@@ -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 | ||
85 | local function consume_token (chars) | 86 | local 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 |
91 | end | 92 | end |
@@ -100,14 +101,14 @@ local function consume_number (chars) | |||
100 | end | 101 | end |
101 | 102 | ||
102 | local function consume_whitespace (chars) | 103 | local 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 |
105 | end | 106 | end |
106 | 107 | ||
107 | local function consume_comment (chars) | 108 | local 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 |
113 | end | 114 | end |
@@ -115,14 +116,14 @@ end | |||
115 | --- API | 116 | --- API |
116 | 117 | ||
117 | read.readtable = { | 118 | read.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 | ||
128 | function read.scan (chars) | 129 | function read.scan (chars) |
@@ -157,18 +158,40 @@ function read.tokenize (program) | |||
157 | return tokens | 158 | return tokens |
158 | end | 159 | end |
159 | 160 | ||
161 | read.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 | |||
160 | function read.parse (tokens) | 190 | function 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 |