2 const tokens List<string>
6 if position >= tokens.count {
9 return tokens[position]
19 def tokenize(str string) List<string> {
20 var re = RegExp.new("[\\s,]*(~@|[\\[\\]{}()'`~^@]|\"(?:\\\\.|[^\\\\\"])*\"?|;.*|[^\\s\\[\\]{}('\"`,;)]*)", "g")
21 var tokens List<string> = []
23 while (match = re.exec(str)[1]) != "" {
32 def unescape(s string) string {
33 return s.replaceAll("\\\\", "\x01").replaceAll("\\\"", "\"").replaceAll("\\n", "\n").replaceAll("\x01", "\\")
36 def read_atom(rdr Reader) MalVal {
37 const token = rdr.peek
51 case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' { return MalNumber.new(stringToInt(rdr.next)) }
53 if token.count <= 1 { return MalSymbol.new(rdr.next) }
55 case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' { return MalNumber.new(stringToInt(rdr.next)) }
56 default { return MalSymbol.new(rdr.next) }
61 if s[s.count - 1] == '"' {
62 return MalString.new(unescape(s.slice(1, s.count - 1)))
64 throw MalError.new("expected '\"', got EOF")
67 case ':' { return MalKeyword.new(rdr.next.slice(1)) }
68 default { return MalSymbol.new(rdr.next) }
72 def read_sequence(rdr Reader, open string, close string) List<MalVal> {
74 throw MalError.new("expected '" + open + "'")
77 var items List<MalVal> = []
78 while (token = rdr.peek) != close {
80 throw MalError.new("expected '" + close + "', got EOF")
82 items.append(read_form(rdr))
84 rdr.next # consume the close paren/bracket/brace
88 def read_list(rdr Reader) MalList {
89 return MalList.new(read_sequence(rdr, "(", ")"))
92 def read_vector(rdr Reader) MalVector {
93 return MalVector.new(read_sequence(rdr, "[", "]"))
96 def read_hash_map(rdr Reader) MalHashMap {
97 return MalHashMap.fromList(read_sequence(rdr, "{", "}"))
100 def reader_macro(rdr Reader, symbol_name string) MalVal {
102 return MalList.new([MalSymbol.new(symbol_name), read_form(rdr)])
105 def read_form(rdr Reader) MalVal {
107 case '\'' { return reader_macro(rdr, "quote") }
108 case '`' { return reader_macro(rdr, "quasiquote") }
110 if rdr.peek == "~" { return reader_macro(rdr, "unquote") }
111 else if rdr.peek == "~@" { return reader_macro(rdr, "splice-unquote") }
112 else { return read_atom(rdr) }
116 const meta = read_form(rdr)
117 return MalList.new([MalSymbol.new("with-meta"), read_form(rdr), meta])
119 case '@' { return reader_macro(rdr, "deref") }
120 case ')' { throw MalError.new("unexpected ')'") }
121 case '(' { return read_list(rdr) }
122 case ']' { throw MalError.new("unexpected ']'") }
123 case '[' { return read_vector(rdr) }
124 case '}' { throw MalError.new("unexpected '}'") }
125 case '{' { return read_hash_map(rdr) }
126 default { return read_atom(rdr) }
130 def read_str(str string) MalVal {
131 const tokens = tokenize(str)
132 if tokens.isEmpty { return null }
133 var rdr = Reader.new(tokens)
134 return read_form(rdr)