8 return tokens[position];
13 return tokens[position++];
16 fun static string[] tokenizer(string input)
18 "^[ \n,]*(~@|[][{}()'`~^@]|\"(\\\\.|[^\\\"])*\"|;[^\n]*|[^][ \n{}()'`~@,;\"]*)" => string tokenRe;
19 "^([ \n,]*|;[^\n]*)$" => string blankRe;
26 RegEx.match(tokenRe, input, matches);
27 matches[1] => string token;
29 if( token.length() == 0 && !RegEx.match(blankRe, input) )
35 if( !RegEx.match(blankRe, token) )
40 matches[0].length() => int tokenStart;
41 String.slice(input, tokenStart) => input;
43 if( input.length() == 0 )
52 fun static MalObject read_str(string input)
55 tokenizer(input) @=> reader.tokens;
57 if( reader.tokens.size() == 0 )
59 return MalError.create(MalString.create("empty input"));
63 return read_form(reader);
67 fun static MalObject read_form(Reader reader)
69 reader.peek() => string token;
72 return read_list(reader, "(", ")");
74 else if( token == "[" )
76 return read_list(reader, "[", "]");
78 else if( token == "{" )
80 return read_list(reader, "{", "}");
82 else if( token == ")" || token == "]" || token == "}" )
84 return MalError.create(MalString.create("unexpected '" + token + "'"));
86 else if( token == "'" )
88 return read_simple_reader_macro(reader, "quote");
90 else if( token == "`" )
92 return read_simple_reader_macro(reader, "quasiquote");
94 else if( token == "~" )
96 return read_simple_reader_macro(reader, "unquote");
98 else if( token == "~@" )
100 return read_simple_reader_macro(reader, "splice-unquote");
102 else if( token == "@" )
104 return read_simple_reader_macro(reader, "deref");
106 else if( token == "^" )
108 return read_meta_reader_macro(reader);
112 return read_atom(reader);
116 fun static MalObject read_list(Reader reader, string start, string end)
120 reader.next(); // discard list start token
124 // HACK: avoid checking for reader.peek() returning null
125 // (as doing that directly isn't possible and too
126 // bothersome to do indirectly)
127 if( reader.position == reader.tokens.size() )
129 return MalError.create(MalString.create("expected '" + end + "', got EOF"));
132 if( reader.peek() == end )
137 read_form(reader) @=> MalObject item;
139 if( item.type == "error" )
149 reader.next(); // discard list end token
153 return MalList.create(items);
155 else if( start == "[" )
157 return MalVector.create(items);
159 else if( start == "{" )
161 return MalHashMap.create(items);
165 fun static MalObject read_atom(Reader reader)
167 "^[+-]?[0-9]+$" => string intRe;
168 "^\"(\\\\.|[^\\\"])*\"$" => string stringRe;
170 reader.next() => string token;
172 if( token == "true" )
174 return Constants.TRUE;
176 else if( token == "false" )
178 return Constants.FALSE;
180 else if( token == "nil" )
182 return Constants.NIL;
184 else if( RegEx.match(intRe, token) )
186 return MalInt.create(Std.atoi(token));
188 else if( token.substring(0, 1) == "\"" )
190 if( RegEx.match(stringRe, token) )
192 return MalString.create(String.parse(token));
196 return MalError.create(MalString.create("expected '\"', got EOF"));
199 else if( token.substring(0, 1) == ":" )
201 return MalKeyword.create(String.slice(token, 1));
205 return MalSymbol.create(token);
209 fun static MalObject read_simple_reader_macro(Reader reader, string symbol)
211 reader.next(); // discard reader macro token
213 read_form(reader) @=> MalObject form;
214 if( form.type == "error" )
219 return MalList.create([MalSymbol.create(symbol), form]);
222 fun static MalObject read_meta_reader_macro(Reader reader)
224 reader.next(); // discard reader macro token
226 read_form(reader) @=> MalObject meta;
227 if( meta.type == "error" )
232 read_form(reader) @=> MalObject form;
233 if( form.type == "error" )
238 return MalList.create([MalSymbol.create("with-meta"), form, meta]);