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
21 // @import types/MalSubr.ck
22 // @import types/subr/*.ck
25 fun MalObject READ(string input)
27 return Reader.read_str(input);
30 fun int isPair(MalObject m)
32 if( (m.type == "list" || m.type == "vector") &&
33 Util.sequenceToMalObjectArray(m).size() > 0 )
43 fun MalObject quasiquote(MalObject ast)
47 return MalList.create([MalSymbol.create("quote"), ast]);
50 Util.sequenceToMalObjectArray(ast) @=> MalObject a[];
51 a[0] @=> MalObject a0;
53 if( a0.type == "symbol" && (a0$MalSymbol).value() == "unquote" )
60 Util.sequenceToMalObjectArray(a0) @=> MalObject a0_[];
61 a0_[0] @=> MalObject a0_0;
63 if( a0_0.type == "symbol" && (a0_0$MalSymbol).value() == "splice-unquote" )
65 return MalList.create(
66 [MalSymbol.create("concat"), a0_[1],
67 quasiquote(MalList.create(MalObject.slice(a, 1)))]);
71 return MalList.create(
72 [MalSymbol.create("cons"), quasiquote(a[0]),
73 quasiquote(MalList.create(MalObject.slice(a, 1)))]);
76 fun int isMacroCall(MalObject ast, Env env)
78 if( ast.type == "list" )
80 (ast$MalList).value() @=> MalObject a[];
82 if( a[0].type == "symbol" )
84 (a[0]$MalSymbol).value() => string name;
85 env.find(name) @=> MalObject value;
87 if( value != null && value.type == "func" && (value$Func).isMacro )
97 fun MalObject macroexpand(MalObject ast, Env env)
99 while( isMacroCall(ast, env) )
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[];
106 if( macro.type == "subr" )
108 (macro$MalSubr).call(args) @=> ast;
110 else // macro.type == "func"
112 macro$Func @=> Func func;
113 Env.create(func.env, func.args, args) @=> Env eval_env;
114 EVAL(func.ast, eval_env) @=> ast;;
121 fun MalObject EVAL(MalObject m, Env env)
125 if( m.type != "list" )
127 return eval_ast(m, env);
130 if( (m$MalList).value().size() == 0 )
135 macroexpand(m, env) @=> m;
137 if( m.type != "list" )
139 return eval_ast(m, env);
142 (m$MalList).value() @=> MalObject ast[];
144 if( ast[0].type == "symbol" )
146 (ast[0]$MalSymbol).value() => string a0;
150 (ast[1]$MalSymbol).value() => string a1;
152 EVAL(ast[2], env) @=> MalObject value;
153 if( value.type == "error" )
161 else if( a0 == "let*" )
163 Env.create(env) @=> Env let_env;
164 Util.sequenceToMalObjectArray(ast[1]) @=> MalObject bindings[];
166 for( 0 => int i; i < bindings.size(); 2 +=> i)
168 (bindings[i]$MalSymbol).value() => string symbol;
169 EVAL(bindings[i+1], let_env) @=> MalObject value;
171 if( value.type == "error" )
176 let_env.set(symbol, value);
183 else if( a0 == "quote" )
187 else if( a0 == "quasiquote" )
189 quasiquote(ast[1]) @=> m;
192 else if( a0 == "defmacro!" )
194 (ast[1]$MalSymbol).value() => string a1;
196 EVAL(ast[2], env) @=> MalObject value;
197 if( value.type == "error" )
202 true => (value$Func).isMacro;
207 else if( a0 == "macroexpand" )
209 return macroexpand(ast[1], env);
211 else if( a0 == "do" )
213 MalObject.slice(ast, 1, ast.size()-1) @=> MalObject forms[];
214 eval_ast(MalList.create(forms), env) @=> MalObject value;
216 if( value.type == "error" )
221 // HACK: this assumes do gets at least one argument...
222 ast[ast.size()-1] @=> m;
225 else if( a0 == "if" )
227 EVAL(ast[1], env) @=> MalObject condition;
229 if( condition.type == "error" )
234 if( !(condition.type == "nil") && !(condition.type == "false") )
243 return Constants.NIL;
252 else if( a0 == "fn*" )
254 (ast[1]$MalList).value() @=> MalObject arg_values[];
255 string args[arg_values.size()];
257 for( 0 => int i; i < arg_values.size(); i++ )
259 (arg_values[i]$MalSymbol).value() => args[i];
262 ast[2] @=> MalObject _ast;
264 return Func.create(env, args, _ast);
268 eval_ast(m, env) @=> MalObject result;
269 if( result.type == "error" )
274 (result$MalList).value() @=> MalObject values[];
275 values[0].type => string type;
276 MalObject.slice(values, 1) @=> MalObject args[];
280 values[0]$MalSubr @=> MalSubr subr;
281 return subr.call(args);
283 else // type == "func"
285 values[0]$Func @=> Func func;
286 Env.create(func.env, func.args, args) @=> Env eval_env;
294 fun MalObject eval_ast(MalObject m, Env env)
296 m.type => string type;
298 if( type == "symbol" )
300 (m$MalSymbol).value() => string symbol;
301 return env.get(symbol);
303 else if( type == "list" || type == "vector" || type == "hashmap" )
305 (m$MalList).value() @=> MalObject values[];
306 MalObject results[values.size()];
308 if( type != "hashmap" )
310 for( 0 => int i; i < values.size(); i++ )
312 EVAL(values[i], env) @=> MalObject result;
314 if( result.type == "error" )
319 result @=> results[i];
324 for( 0 => int i; i < values.size(); i++ )
328 values[i] @=> results[i];
332 EVAL(values[i], env) @=> results[i];
339 return MalList.create(results);
341 else if( type == "vector" )
343 return MalVector.create(results);
345 else if( type == "hashmap" )
347 return MalHashMap.create(results);
356 fun string PRINT(MalObject m)
358 return Printer.pr_str(m, true);
361 Env.create(null) @=> Env repl_env;
362 for( 0 => int i; i < Core.names.size(); i++ )
364 Core.names[i] => string name;
365 repl_env.set(name, Core.ns[name]);
369 class MalEval extends MalSubr
371 fun MalObject call(MalObject args[])
373 args[0] @=> MalObject m;
374 return EVAL(args[0], repl_env);
377 fun MalObject apply(MalObject f, MalObject args[])
379 if( f.type == "subr" )
381 return (f$MalSubr).call(args);
383 else // f.type == "func"
385 f$Func @=> Func func;
386 Env.create(func.env, func.args, args) @=> Env eval_env;
387 return EVAL(func.ast, eval_env);
392 new MalEval @=> MalEval eval;
393 repl_env.set("eval", new MalEval);
394 eval @=> (repl_env.get("swap!")$MalSubr).eval;
396 fun MalObject[] MalArgv(string args[])
400 for( 1 => int i; i < args.size(); i++ )
402 values << MalString.create(args[i]);
408 // NOTE: normally I'd use \0, but strings are null-terminated...
409 String.split(Std.getenv("CHUCK_ARGS"), "\a") @=> string args[];
410 repl_env.set("*ARGV*", MalList.create(MalArgv(args)));
412 fun string errorMessage(MalObject m)
414 (m$MalError).value() @=> MalObject value;
415 return "exception: " + Printer.pr_str(value, true);
418 fun string rep(string input)
420 READ(input) @=> MalObject m;
422 if( m.type == "error" )
424 return errorMessage(m);
427 EVAL(m, repl_env) @=> MalObject result;
428 if( result.type == "error" )
430 return errorMessage(result);
433 return PRINT(result);
436 rep("(def! not (fn* (a) (if a false true)))");
437 rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))");
438 rep("(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)))))))");
446 Readline.readline("user> ") => string input;
450 rep(input) => string output;
452 if( output == "empty input" )
454 // proceed immediately with prompt
458 Util.println(output);
468 if( args.size() > 0 )
470 args[0] => string filename;
471 rep("(load-file \"" + filename + "\")");