2016-02-18 18:24:26 +03:00
|
|
|
MalReader := Object clone do (
|
|
|
|
|
|
|
|
Reader := Object clone do (
|
|
|
|
pos ::= 0
|
|
|
|
tokens ::= list()
|
|
|
|
|
|
|
|
with := method(theTokens,
|
|
|
|
self clone setTokens(theTokens)
|
|
|
|
)
|
|
|
|
|
|
|
|
peek := method(tokens at(pos))
|
|
|
|
|
|
|
|
next := method(
|
|
|
|
pos = pos + 1
|
|
|
|
tokens at(pos - 1)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
tokenizerRegex := Regex with("[\\s ,]*(~@|[\\[\\]{}()'`~@]|\"(?:[\\\\].|[^\\\\\"])*\"|;.*|[^\\s \\[\\]{}()'\"`~@,;]*)")
|
|
|
|
|
|
|
|
tokenize := method(str,
|
|
|
|
tokenizerRegex matchesIn(str) \
|
|
|
|
map(m, m at(1) asMutable strip) \
|
|
|
|
select(t, t size > 0) \
|
|
|
|
select(t, t exSlice(0, 1) != ";")
|
|
|
|
)
|
|
|
|
|
|
|
|
numberRegex := Regex with("^-?[0-9]+$")
|
|
|
|
|
|
|
|
read_string := method(token,
|
2017-09-27 09:43:15 +03:00
|
|
|
placeholder := 127 asCharacter
|
|
|
|
token exSlice(1, -1) replaceSeq("\\\\", placeholder) replaceSeq("\\\"", "\"") replaceSeq("\\n", "\n") replaceSeq(placeholder, "\\")
|
2016-02-18 18:24:26 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
read_atom := method(rdr,
|
|
|
|
token := rdr next
|
|
|
|
(token hasMatchOfRegex(numberRegex)) ifTrue(return(token asNumber))
|
2016-03-03 23:03:06 +03:00
|
|
|
(token == "true") ifTrue(return(true))
|
|
|
|
(token == "false") ifTrue(return(false))
|
|
|
|
(token == "nil") ifTrue(return(nil))
|
2016-02-18 18:24:26 +03:00
|
|
|
(token beginsWithSeq(":")) ifTrue(return(MalKeyword with(token exSlice(1))))
|
2016-03-03 23:03:06 +03:00
|
|
|
(token beginsWithSeq("\"")) ifTrue(return(read_string(token)))
|
2016-02-18 18:24:26 +03:00
|
|
|
MalSymbol with(token)
|
|
|
|
)
|
|
|
|
|
|
|
|
read_list := method(rdr, start, end,
|
|
|
|
token := rdr next
|
|
|
|
if(token != start, Exception raise("expected '" .. start .. "'"))
|
|
|
|
ast := list()
|
|
|
|
token = rdr peek
|
|
|
|
while(token != end,
|
|
|
|
if(token isNil, Exception raise("expected '" .. end .. "', got EOF"))
|
|
|
|
ast push(read_form(rdr))
|
|
|
|
token = rdr peek
|
|
|
|
)
|
|
|
|
rdr next
|
|
|
|
ast
|
|
|
|
)
|
|
|
|
|
|
|
|
reader_macro := method(symbol, rdr,
|
|
|
|
rdr next
|
|
|
|
MalList with(list(MalSymbol with(symbol), read_form(rdr)))
|
|
|
|
)
|
|
|
|
|
|
|
|
read_form := method(rdr,
|
|
|
|
token := rdr peek
|
2016-03-03 23:03:06 +03:00
|
|
|
(token == "'") ifTrue(return(reader_macro("quote", rdr)))
|
|
|
|
(token == "`") ifTrue(return(reader_macro("quasiquote", rdr)))
|
|
|
|
(token == "~") ifTrue(return(reader_macro("unquote", rdr)))
|
|
|
|
(token == "~@") ifTrue(return(reader_macro("splice-unquote", rdr)))
|
2016-02-18 18:24:26 +03:00
|
|
|
(token == "^") ifTrue(
|
|
|
|
rdr next
|
|
|
|
meta := read_form(rdr)
|
2016-03-03 23:03:06 +03:00
|
|
|
return(MalList with(list(MalSymbol with("with-meta"), read_form(rdr), meta)))
|
2016-02-18 18:24:26 +03:00
|
|
|
)
|
2016-03-03 23:03:06 +03:00
|
|
|
(token == "@") ifTrue(return(reader_macro("deref", rdr)))
|
|
|
|
(token == "(") ifTrue(return(MalList with(read_list(rdr, "(", ")"))))
|
2016-02-18 18:24:26 +03:00
|
|
|
(token == ")") ifTrue(Exception raise("unexepcted ')'"))
|
2016-03-03 23:03:06 +03:00
|
|
|
(token == "[") ifTrue(return(MalVector with(read_list(rdr, "[", "]"))))
|
2016-02-18 18:24:26 +03:00
|
|
|
(token == "]") ifTrue(Exception raise("unexepcted ']'"))
|
2016-03-03 23:03:06 +03:00
|
|
|
(token == "{") ifTrue(return(MalMap withList(read_list(rdr, "{", "}"))))
|
2016-02-18 18:24:26 +03:00
|
|
|
(token == "}") ifTrue(Exception raise("unexepcted '}'"))
|
|
|
|
read_atom(rdr)
|
|
|
|
)
|
|
|
|
|
|
|
|
read_str := method(str,
|
|
|
|
tokens := tokenize(str)
|
|
|
|
if(tokens isEmpty, nil, read_form(Reader with(tokens)))
|
|
|
|
)
|
|
|
|
)
|