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