DISABLE FDs (REMOVE ME).
[jackhill/mal.git] / chuck / step7_quote.ck
CommitLineData
2e3d457e 1// @import readline.ck
34f7b3db
VS
2// @import types/boxed/*.ck
3// @import types/MalObject.ck
98c1ecf2
VS
4// @import types/mal/MalAtom.ck
5// @import types/mal/MalError.ck
6// @import types/mal/MalNil.ck
7// @import types/mal/MalFalse.ck
8// @import types/mal/MalTrue.ck
9// @import types/mal/MalInt.ck
10// @import types/mal/MalString.ck
11// @import types/mal/MalSymbol.ck
12// @import types/mal/MalKeyword.ck
13// @import types/mal/MalList.ck
14// @import types/mal/MalVector.ck
15// @import types/mal/MalHashMap.ck
34f7b3db
VS
16// @import util/*.ck
17// @import reader.ck
18// @import printer.ck
19// @import env.ck
7cabea4f 20// @import func.ck
34f7b3db
VS
21// @import types/MalSubr.ck
22// @import types/subr/*.ck
23// @import core.ck
34f7b3db
VS
24
25fun MalObject READ(string input)
26{
27 return Reader.read_str(input);
28}
29
30fun int isPair(MalObject m)
31{
32 if( (m.type == "list" || m.type == "vector") &&
33 Util.sequenceToMalObjectArray(m).size() > 0 )
34 {
35 return true;
36 }
37 else
38 {
39 return false;
40 }
41}
42
43fun MalObject quasiquote(MalObject ast)
44{
45 if( !isPair(ast) )
46 {
47 return MalList.create([MalSymbol.create("quote"), ast]);
48 }
49
50 Util.sequenceToMalObjectArray(ast) @=> MalObject a[];
51 a[0] @=> MalObject a0;
52
53 if( a0.type == "symbol" && (a0$MalSymbol).value() == "unquote" )
54 {
55 return a[1];
56 }
57
58 if( isPair(a0) )
59 {
60 Util.sequenceToMalObjectArray(a0) @=> MalObject a0_[];
61 a0_[0] @=> MalObject a0_0;
62
63 if( a0_0.type == "symbol" && (a0_0$MalSymbol).value() == "splice-unquote" )
64 {
65 return MalList.create(
66 [MalSymbol.create("concat"), a0_[1],
67 quasiquote(MalList.create(MalObject.slice(a, 1)))]);
68 }
69 }
70
71 return MalList.create(
72 [MalSymbol.create("cons"), quasiquote(a[0]),
73 quasiquote(MalList.create(MalObject.slice(a, 1)))]);
74}
75
76fun MalObject EVAL(MalObject m, Env env)
77{
78 while( true )
79 {
80 if( m.type != "list" )
81 {
82 return eval_ast(m, env);
83 }
84
85 if( (m$MalList).value().size() == 0 )
86 {
87 return m;
88 }
89
90 (m$MalList).value() @=> MalObject ast[];
91
92 if( ast[0].type == "symbol" )
93 {
94 (ast[0]$MalSymbol).value() => string a0;
95
96 if( a0 == "def!" )
97 {
98 (ast[1]$MalSymbol).value() => string a1;
99
100 EVAL(ast[2], env) @=> MalObject value;
101 if( value.type == "error" )
102 {
103 return value;
104 }
105
106 env.set(a1, value);
107 return value;
108 }
109 else if( a0 == "let*" )
110 {
111 Env.create(env) @=> Env let_env;
112 Util.sequenceToMalObjectArray(ast[1]) @=> MalObject bindings[];
113
114 for( 0 => int i; i < bindings.size(); 2 +=> i)
115 {
116 (bindings[i]$MalSymbol).value() => string symbol;
117 EVAL(bindings[i+1], let_env) @=> MalObject value;
118
119 if( value.type == "error" )
120 {
121 return value;
122 }
123
124 let_env.set(symbol, value);
125 }
126
127 let_env @=> env;
128 ast[2] @=> m;
129 continue; // TCO
130 }
131 else if( a0 == "quote" )
132 {
133 return ast[1];
134 }
135 else if( a0 == "quasiquote" )
136 {
137 quasiquote(ast[1]) @=> m;
138 continue; // TCO
139 }
140 else if( a0 == "do" )
141 {
98c1ecf2 142 MalObject.slice(ast, 1, ast.size()-1) @=> MalObject forms[];
34f7b3db
VS
143 eval_ast(MalList.create(forms), env) @=> MalObject value;
144
145 if( value.type == "error" )
146 {
147 return value;
148 }
149
150 // HACK: this assumes do gets at least one argument...
151 ast[ast.size()-1] @=> m;
152 continue; // TCO
153 }
154 else if( a0 == "if" )
155 {
156 EVAL(ast[1], env) @=> MalObject condition;
157
158 if( condition.type == "error" )
159 {
160 return condition;
161 }
162
163 if( !(condition.type == "nil") && !(condition.type == "false") )
164 {
165 ast[2] @=> m;
166 continue; // TCO
167 }
168 else
169 {
170 if( ast.size() < 4 )
171 {
172 return Constants.NIL;
173 }
174 else
175 {
176 ast[3] @=> m;
177 continue; // TCO
178 }
179 }
180 }
181 else if( a0 == "fn*" )
182 {
183 (ast[1]$MalList).value() @=> MalObject arg_values[];
184 string args[arg_values.size()];
185
186 for( 0 => int i; i < arg_values.size(); i++ )
187 {
188 (arg_values[i]$MalSymbol).value() => args[i];
189 }
190
191 ast[2] @=> MalObject _ast;
192
193 return Func.create(env, args, _ast);
194 }
195 }
196
197 eval_ast(m, env) @=> MalObject result;
198 if( result.type == "error" )
199 {
200 return result;
201 }
202
203 (result$MalList).value() @=> MalObject values[];
204 values[0].type => string type;
205 MalObject.slice(values, 1) @=> MalObject args[];
206
207 if( type == "subr" )
208 {
209 values[0]$MalSubr @=> MalSubr subr;
98c1ecf2 210 return subr.call(args);
34f7b3db
VS
211 }
212 else // type == "func"
213 {
214 values[0]$Func @=> Func func;
215 Env.create(func.env, func.args, args) @=> Env eval_env;
216 eval_env @=> env;
217 func.ast @=> m;
218 continue; // TCO
219 }
220 }
221}
222
223fun MalObject eval_ast(MalObject m, Env env)
224{
225 m.type => string type;
226
227 if( type == "symbol" )
228 {
229 (m$MalSymbol).value() => string symbol;
230 return env.get(symbol);
231 }
232 else if( type == "list" || type == "vector" || type == "hashmap" )
233 {
234 (m$MalList).value() @=> MalObject values[];
235 MalObject results[values.size()];
236
237 if( type != "hashmap" )
238 {
239 for( 0 => int i; i < values.size(); i++ )
240 {
241 EVAL(values[i], env) @=> MalObject result;
242
243 if( result.type == "error" )
244 {
245 return result;
246 }
247
248 result @=> results[i];
249 }
250 }
251 else
252 {
253 for( 0 => int i; i < values.size(); i++ )
254 {
255 if( i % 2 == 0 )
256 {
257 values[i] @=> results[i];
258 }
259 else
260 {
261 EVAL(values[i], env) @=> results[i];
262 }
263 }
264 }
265
266 if( type == "list" )
267 {
268 return MalList.create(results);
269 }
270 else if( type == "vector" )
271 {
272 return MalVector.create(results);
273 }
274 else if( type == "hashmap" )
275 {
276 return MalHashMap.create(results);
277 }
278 }
279 else
280 {
281 return m;
282 }
283}
284
285fun string PRINT(MalObject m)
286{
287 return Printer.pr_str(m, true);
288}
289
290Env.create(null) @=> Env repl_env;
291for( 0 => int i; i < Core.names.size(); i++ )
292{
293 Core.names[i] => string name;
294 repl_env.set(name, Core.ns[name]);
295}
296
98c1ecf2
VS
297// HACK, HACK, HACK
298class MalEval extends MalSubr
299{
300 fun MalObject call(MalObject args[])
301 {
302 args[0] @=> MalObject m;
303 return EVAL(args[0], repl_env);
304 }
305
306 fun MalObject apply(MalObject f, MalObject args[])
307 {
308 if( f.type == "subr" )
309 {
310 return (f$MalSubr).call(args);
311 }
312 else // f.type == "func"
313 {
314 f$Func @=> Func func;
315 Env.create(func.env, func.args, args) @=> Env eval_env;
316 return EVAL(func.ast, eval_env);
317 }
318 }
319}
320
321new MalEval @=> MalEval eval;
322repl_env.set("eval", new MalEval);
323eval @=> (repl_env.get("swap!")$MalSubr).eval;
34f7b3db
VS
324
325fun MalObject[] MalArgv(string args[])
326{
1b349ccb 327 MalObject values[0];
34f7b3db
VS
328
329 for( 1 => int i; i < args.size(); i++ )
330 {
1b349ccb 331 values << MalString.create(args[i]);
34f7b3db
VS
332 }
333
334 return values;
335}
336
337// NOTE: normally I'd use \0, but strings are null-terminated...
338String.split(Std.getenv("CHUCK_ARGS"), "\a") @=> string args[];
339repl_env.set("*ARGV*", MalList.create(MalArgv(args)));
340
98c1ecf2
VS
341fun string errorMessage(MalObject m)
342{
343 (m$MalError).value() @=> MalObject value;
dd7a4f55 344 return "exception: " + Printer.pr_str(value, true);
98c1ecf2
VS
345}
346
34f7b3db
VS
347fun string rep(string input)
348{
349 READ(input) @=> MalObject m;
350
351 if( m.type == "error" )
352 {
98c1ecf2 353 return errorMessage(m);
34f7b3db
VS
354 }
355
356 EVAL(m, repl_env) @=> MalObject result;
357 if( result.type == "error" )
358 {
98c1ecf2 359 return errorMessage(result);
34f7b3db
VS
360 }
361
362 return PRINT(result);
363}
364
365rep("(def! not (fn* (a) (if a false true)))");
e6d41de4 366rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))");
34f7b3db
VS
367
368fun void main()
369{
2e3d457e 370 int done;
34f7b3db 371
2e3d457e 372 while( !done )
34f7b3db 373 {
2e3d457e 374 Readline.readline("user> ") => string input;
34f7b3db 375
2e3d457e 376 if( input != null )
34f7b3db 377 {
2e3d457e
VS
378 rep(input) => string output;
379
380 if( output == "empty input" )
381 {
382 // proceed immediately with prompt
383 }
384 else
385 {
386 Util.println(output);
387 }
34f7b3db
VS
388 }
389 else
390 {
2e3d457e 391 true => done;
34f7b3db
VS
392 }
393 }
394}
395
35163c1b 396if( args.size() > 0 )
34f7b3db 397{
35163c1b 398 args[0] => string filename;
34f7b3db
VS
399 rep("(load-file \"" + filename + "\")");
400}
401else
402{
403 main();
404}