1 local rex
= require('rex_pcre')
2 local string = require('string')
3 local table = require('table')
4 local types
= require('types')
5 local throw
, Nil
, Symbol
, List
= types
.throw
, types
.Nil
,
6 types
.Symbol
, types
.List
11 function Reader
:new(tokens
)
12 local newObj
= {tokens
= tokens
, position
= 1}
14 return setmetatable(newObj
, self
)
16 function Reader
:next()
17 self
.position
= self
.position
+ 1
18 return self
.tokens
[self
.position
-1]
20 function Reader
:peek()
21 return self
.tokens
[self
.position
]
24 function M
.tokenize(str
)
27 local re
= rex
.new("[\\s,]*(~@|[\\[\\]{}()'`~^@]|\"(?:\\\\.|[^\\\\\"])*\"?|;[^\n]*|[^\\s\\[\\]{}('\"`,;)]*)", rex
.flags().EXTENDED
)
29 local s
, e
, t
= re
:exec(str
, re_pos
)
30 if not s
or s
> e
then break end
32 local val
= string.sub(str
,t
[1],t
[2])
33 if string.sub(val
,1,1) ~= ";" then
34 table.insert(results
, val
)
40 function M
.read_atom(rdr
)
41 local int_re
= rex
.new("^-?[0-9]+$")
42 local float_re
= rex
.new("^-?[0-9][0-9.]*$")
43 local string_re
= rex
.new("^\"(?:\\\\.|[^\\\\\"])*\"$")
44 local token
= rdr
:next()
45 if int_re
:exec(token
) then return tonumber(token
)
46 elseif float_re
:exec(token
) then return tonumber(token
)
47 elseif string_re
:exec(token
) then
48 local sval
= string.sub(token
,2,string.len(token
)-1)
49 sval
= string.gsub(sval
, '\\\\', '\177')
50 sval
= string.gsub(sval
, '\\"', '"')
51 sval
= string.gsub(sval
, '\\n', '\n')
52 sval
= string.gsub(sval
, '\177', '\\')
54 elseif string.sub(token
,1,1) == '"' then
55 throw("expected '\"', got EOF")
56 elseif string.sub(token
,1,1) == ':' then
57 return "\177" .. string.sub(token
,2)
58 elseif token
== "nil" then return Nil
59 elseif token
== "true" then return true
60 elseif token
== "false" then return false
61 else return Symbol
:new(token
)
65 function M
.read_sequence(rdr
, start
, last
)
67 local token
= rdr
:next()
68 if token
~= start
then throw("expected '"..start
.."'") end
71 while token
~= last
do
72 if not token
then throw("expected '"..last
.."', got EOF") end
73 table.insert(ast
, M
.read_form(rdr
))
80 function M
.read_list(rdr
)
81 return types
.List
:new(M
.read_sequence(rdr
, '(', ')'))
84 function M
.read_vector(rdr
)
85 return types
.Vector
:new(M
.read_sequence(rdr
, '[', ']'))
88 function M
.read_hash_map(rdr
)
89 local seq
= M
.read_sequence(rdr
, '{', '}')
90 return types
._assoc_BANG(types
.HashMap
:new(), table.unpack(seq
))
93 function M
.read_form(rdr
)
94 local token
= rdr
:peek()
98 return List
:new({Symbol
:new('quote'), M
.read_form(rdr
)})
99 elseif '`' == token
then
101 return List
:new({Symbol
:new('quasiquote'), M
.read_form(rdr
)})
102 elseif '~' == token
then
104 return List
:new({Symbol
:new('unquote'), M
.read_form(rdr
)})
105 elseif '~@' == token
then
107 return List
:new({Symbol
:new('splice-unquote'), M
.read_form(rdr
)})
108 elseif '^' == token
then
110 local meta
= M
.read_form(rdr
)
111 return List
:new({Symbol
:new('with-meta'), M
.read_form(rdr
), meta
})
112 elseif '@' == token
then
114 return List
:new({Symbol
:new('deref'), M
.read_form(rdr
)})
116 elseif ')' == token
then throw("unexpected ')'")
117 elseif '(' == token
then return M
.read_list(rdr
)
118 elseif ']' == token
then throw("unexpected ']'")
119 elseif '[' == token
then return M
.read_vector(rdr
)
120 elseif '}' == token
then throw("unexpected '}'")
121 elseif '{' == token
then return M
.read_hash_map(rdr
)
122 else return M
.read_atom(rdr
)
126 function M
.read_str(str
)
127 local tokens
= M
.tokenize(str
)
128 if #tokens
== 0 then error(nil) end
129 return M
.read_form(Reader
:new(tokens
))