DISABLE FDs (REMOVE ME).
[jackhill/mal.git] / chuck / step9_try.ck
CommitLineData
2e3d457e 1// @import readline.ck
98c1ecf2
VS
2// @import types/boxed/*.ck
3// @import types/MalObject.ck
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
16// @import util/*.ck
17// @import reader.ck
18// @import printer.ck
19// @import env.ck
7cabea4f 20// @import func.ck
98c1ecf2
VS
21// @import types/MalSubr.ck
22// @import types/subr/*.ck
23// @import core.ck
98c1ecf2
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 int isMacroCall(MalObject ast, Env env)
77{
78 if( ast.type == "list" )
79 {
80 (ast$MalList).value() @=> MalObject a[];
81
82 if( a[0].type == "symbol" )
83 {
84 (a[0]$MalSymbol).value() => string name;
85 env.find(name) @=> MalObject value;
86
87 if( value != null && value.type == "func" && (value$Func).isMacro )
88 {
89 return true;
90 }
91 }
92 }
93
94 return false;
95}
96
97fun MalObject macroexpand(MalObject ast, Env env)
98{
99 while( isMacroCall(ast, env) )
100 {
101 Util.sequenceToMalObjectArray(ast) @=> MalObject list[];
102 (list[0]$MalSymbol).value() => string name;
103 env.get(name) @=> MalObject macro;
104 MalObject.slice(list, 1) @=> MalObject args[];
105
106 if( macro.type == "subr" )
107 {
108 (macro$MalSubr).call(args) @=> ast;
109 }
110 else // macro.type == "func"
111 {
112 macro$Func @=> Func func;
113 Env.create(func.env, func.args, args) @=> Env eval_env;
114 EVAL(func.ast, eval_env) @=> ast;
115 }
116 }
117
118 return ast;
119}
120
121fun MalObject EVAL(MalObject m, Env env)
122{
123 while( true )
124 {
125 if( m.type != "list" )
126 {
127 return eval_ast(m, env);
128 }
129
130 if( (m$MalList).value().size() == 0 )
131 {
132 return m;
133 }
134
135 macroexpand(m, env) @=> m;
136
137 if( m.type != "list" )
138 {
139 return eval_ast(m, env);
140 }
141
142 (m$MalList).value() @=> MalObject ast[];
143
144 if( ast[0].type == "symbol" )
145 {
146 (ast[0]$MalSymbol).value() => string a0;
147
148 if( a0 == "def!" )
149 {
150 (ast[1]$MalSymbol).value() => string a1;
151
152 EVAL(ast[2], env) @=> MalObject value;
153 if( value.type == "error" )
154 {
155 return value;
156 }
157
158 env.set(a1, value);
159 return value;
160 }
161 else if( a0 == "let*" )
162 {
163 Env.create(env) @=> Env let_env;
164 Util.sequenceToMalObjectArray(ast[1]) @=> MalObject bindings[];
165
166 for( 0 => int i; i < bindings.size(); 2 +=> i)
167 {
168 (bindings[i]$MalSymbol).value() => string symbol;
169 EVAL(bindings[i+1], let_env) @=> MalObject value;
170
171 if( value.type == "error" )
172 {
173 return value;
174 }
175
176 let_env.set(symbol, value);
177 }
178
179 let_env @=> env;
180 ast[2] @=> m;
181 continue; // TCO
182 }
183 else if( a0 == "quote" )
184 {
185 return ast[1];
186 }
187 else if( a0 == "quasiquote" )
188 {
189 quasiquote(ast[1]) @=> m;
190 continue; // TCO
191 }
192 else if( a0 == "defmacro!" )
193 {
194 (ast[1]$MalSymbol).value() => string a1;
195
196 EVAL(ast[2], env) @=> MalObject value;
197 if( value.type == "error" )
198 {
199 return value;
200 }
201
202 true => (value$Func).isMacro;
203
204 env.set(a1, value);
205 return value;
206 }
207 else if( a0 == "macroexpand" )
208 {
209 return macroexpand(ast[1], env);
210 }
211 else if( a0 == "try*" )
212 {
213 EVAL(ast[1], env) @=> MalObject value;
214
dd7a4f55 215 if( (value.type != "error") || (ast.size() < 3) )
98c1ecf2
VS
216 {
217 return value;
218 }
219
220 (ast[2]$MalList).value() @=> MalObject form[];
221 (form[1]$MalSymbol).value() => string name;
222
223 Env.create(env, [name], [(value$MalError).value()]) @=> Env error_env;
224 return EVAL(form[2], error_env);
225 }
226 else if( a0 == "do" )
227 {
228 MalObject.slice(ast, 1, ast.size()-1) @=> MalObject forms[];
229 eval_ast(MalList.create(forms), env) @=> MalObject value;
230
231 if( value.type == "error" )
232 {
233 return value;
234 }
235
236 // HACK: this assumes do gets at least one argument...
237 ast[ast.size()-1] @=> m;
238 continue; // TCO
239 }
240 else if( a0 == "if" )
241 {
242 EVAL(ast[1], env) @=> MalObject condition;
243
244 if( condition.type == "error" )
245 {
246 return condition;
247 }
248
249 if( !(condition.type == "nil") && !(condition.type == "false") )
250 {
251 ast[2] @=> m;
252 continue; // TCO
253 }
254 else
255 {
256 if( ast.size() < 4 )
257 {
258 return Constants.NIL;
259 }
260 else
261 {
262 ast[3] @=> m;
263 continue; // TCO
264 }
265 }
266 }
267 else if( a0 == "fn*" )
268 {
269 (ast[1]$MalList).value() @=> MalObject arg_values[];
270 string args[arg_values.size()];
271
272 for( 0 => int i; i < arg_values.size(); i++ )
273 {
274 (arg_values[i]$MalSymbol).value() => args[i];
275 }
276
277 ast[2] @=> MalObject _ast;
278
279 return Func.create(env, args, _ast);
280 }
281 }
282
283 eval_ast(m, env) @=> MalObject result;
284 if( result.type == "error" )
285 {
286 return result;
287 }
288
289 (result$MalList).value() @=> MalObject values[];
290 values[0].type => string type;
291 MalObject.slice(values, 1) @=> MalObject args[];
292
293 if( type == "subr" )
294 {
295 values[0]$MalSubr @=> MalSubr subr;
296 return subr.call(args);
297 }
298 else // type == "func"
299 {
300 values[0]$Func @=> Func func;
301 Env.create(func.env, func.args, args) @=> Env eval_env;
302 eval_env @=> env;
303 func.ast @=> m;
304 continue; // TCO
305 }
306 }
307}
308
309fun MalObject eval_ast(MalObject m, Env env)
310{
311 m.type => string type;
312
313 if( type == "symbol" )
314 {
315 (m$MalSymbol).value() => string symbol;
316 return env.get(symbol);
317 }
318 else if( type == "list" || type == "vector" || type == "hashmap" )
319 {
320 (m$MalList).value() @=> MalObject values[];
321 MalObject results[values.size()];
322
323 if( type != "hashmap" )
324 {
325 for( 0 => int i; i < values.size(); i++ )
326 {
327 EVAL(values[i], env) @=> MalObject result;
328
329 if( result.type == "error" )
330 {
331 return result;
332 }
333
334 result @=> results[i];
335 }
336 }
337 else
338 {
339 for( 0 => int i; i < values.size(); i++ )
340 {
341 if( i % 2 == 0 )
342 {
343 values[i] @=> results[i];
344 }
345 else
346 {
347 EVAL(values[i], env) @=> results[i];
348 }
349 }
350 }
351
352 if( type == "list" )
353 {
354 return MalList.create(results);
355 }
356 else if( type == "vector" )
357 {
358 return MalVector.create(results);
359 }
360 else if( type == "hashmap" )
361 {
362 return MalHashMap.create(results);
363 }
364 }
365 else
366 {
367 return m;
368 }
369}
370
371fun string PRINT(MalObject m)
372{
373 return Printer.pr_str(m, true);
374}
375
376Env.create(null) @=> Env repl_env;
377for( 0 => int i; i < Core.names.size(); i++ )
378{
379 Core.names[i] => string name;
380 repl_env.set(name, Core.ns[name]);
381}
382
383// HACK, HACK, HACK
384class MalEval extends MalSubr
385{
386 fun MalObject call(MalObject args[])
387 {
388 args[0] @=> MalObject m;
389 return EVAL(args[0], repl_env);
390 }
391
392 fun MalObject apply(MalObject f, MalObject args[])
393 {
394 if( f.type == "subr" )
395 {
396 return (f$MalSubr).call(args);
397 }
398 else // f.type == "func"
399 {
400 f$Func @=> Func func;
401 Env.create(func.env, func.args, args) @=> Env eval_env;
402 return EVAL(func.ast, eval_env);
403 }
404 }
405}
406
407new MalEval @=> MalEval eval;
408repl_env.set("eval", new MalEval);
409eval @=> (repl_env.get("swap!")$MalSubr).eval;
410eval @=> (repl_env.get("apply")$MalSubr).eval;
411eval @=> (repl_env.get("map")$MalSubr).eval;
412
413fun MalObject[] MalArgv(string args[])
414{
1b349ccb 415 MalObject values[0];
98c1ecf2
VS
416
417 for( 1 => int i; i < args.size(); i++ )
418 {
1b349ccb 419 values << MalString.create(args[i]);
98c1ecf2
VS
420 }
421
422 return values;
423}
424
425// NOTE: normally I'd use \0, but strings are null-terminated...
426String.split(Std.getenv("CHUCK_ARGS"), "\a") @=> string args[];
427repl_env.set("*ARGV*", MalList.create(MalArgv(args)));
428
429fun string errorMessage(MalObject m)
430{
431 (m$MalError).value() @=> MalObject value;
dd7a4f55 432 return "exception: " + Printer.pr_str(value, true);
98c1ecf2
VS
433}
434
435fun string rep(string input)
436{
437 READ(input) @=> MalObject m;
438
439 if( m.type == "error" )
440 {
441 return errorMessage(m);
442 }
443
444 EVAL(m, repl_env) @=> MalObject result;
445 if( result.type == "error" )
446 {
447 return errorMessage(result);
448 }
449
450 return PRINT(result);
451}
452
453rep("(def! not (fn* (a) (if a false true)))");
e6d41de4 454rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))");
98c1ecf2 455rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))");
98c1ecf2
VS
456
457fun void main()
458{
2e3d457e 459 int done;
98c1ecf2 460
2e3d457e 461 while( !done )
98c1ecf2 462 {
2e3d457e 463 Readline.readline("user> ") => string input;
98c1ecf2 464
2e3d457e 465 if( input != null )
98c1ecf2 466 {
2e3d457e
VS
467 rep(input) => string output;
468
469 if( output == "empty input" )
470 {
471 // proceed immediately with prompt
472 }
473 else
474 {
475 Util.println(output);
476 }
98c1ecf2
VS
477 }
478 else
479 {
2e3d457e 480 true => done;
98c1ecf2
VS
481 }
482 }
483}
484
35163c1b 485if( args.size() > 0 )
98c1ecf2 486{
35163c1b 487 args[0] => string filename;
98c1ecf2
VS
488 rep("(load-file \"" + filename + "\")");
489}
490else
491{
492 main();
493}