Implement step 6
[jackhill/mal.git] / chuck / step5_tco.ck
1 // @import types/boxed/*.ck
2 // @import types/MalObject.ck
3 // @import types/mal/*.ck
4 // @import util/*.ck
5 // @import reader.ck
6 // @import printer.ck
7 // @import env.ck
8 // @import types/MalSubr.ck
9 // @import types/subr/*.ck
10 // @import core.ck
11 // @import func.ck
12
13 fun MalObject READ(string input)
14 {
15 return Reader.read_str(input);
16 }
17
18 fun MalObject EVAL(MalObject m, Env env)
19 {
20 while( true )
21 {
22 if( m.type != "list" )
23 {
24 return eval_ast(m, env);
25 }
26
27 if( (m$MalList).value().size() == 0 )
28 {
29 return m;
30 }
31
32 (m$MalList).value() @=> MalObject ast[];
33
34 if( ast[0].type == "symbol" )
35 {
36 (ast[0]$MalSymbol).value() => string a0;
37
38 if( a0 == "def!" )
39 {
40 (ast[1]$MalSymbol).value() => string a1;
41
42 EVAL(ast[2], env) @=> MalObject value;
43 if( value.type == "error" )
44 {
45 return value;
46 }
47
48 env.set(a1, value);
49 return value;
50 }
51 else if( a0 == "let*" )
52 {
53 Env.create(env) @=> Env let_env;
54 Util.sequenceToMalObjectArray(ast[1]) @=> MalObject bindings[];
55
56 for( 0 => int i; i < bindings.size(); 2 +=> i)
57 {
58 (bindings[i]$MalSymbol).value() => string symbol;
59 EVAL(bindings[i+1], let_env) @=> MalObject value;
60
61 if( value.type == "error" )
62 {
63 return value;
64 }
65
66 let_env.set(symbol, value);
67 }
68
69 let_env @=> env;
70 ast[2] @=> m;
71 continue; // TCO
72 }
73 else if( a0 == "do" )
74 {
75 MalObject.slice(ast, 1, ast.size()) @=> MalObject forms[];
76 eval_ast(MalList.create(forms), env) @=> MalObject value;
77
78 if( value.type == "error" )
79 {
80 return value;
81 }
82
83 // HACK: this assumes do gets at least one argument...
84 ast[ast.size()-1] @=> m;
85 continue; // TCO
86 }
87 else if( a0 == "if" )
88 {
89 EVAL(ast[1], env) @=> MalObject condition;
90
91 if( condition.type == "error" )
92 {
93 return condition;
94 }
95
96 if( !(condition.type == "nil") && !(condition.type == "false") )
97 {
98 ast[2] @=> m;
99 continue; // TCO
100 }
101 else
102 {
103 if( ast.size() < 4 )
104 {
105 return Constants.NIL;
106 }
107 else
108 {
109 ast[3] @=> m;
110 continue; // TCO
111 }
112 }
113 }
114 else if( a0 == "fn*" )
115 {
116 (ast[1]$MalList).value() @=> MalObject arg_values[];
117 string args[arg_values.size()];
118
119 for( 0 => int i; i < arg_values.size(); i++ )
120 {
121 (arg_values[i]$MalSymbol).value() => args[i];
122 }
123
124 ast[2] @=> MalObject _ast;
125
126 return Func.create(env, args, _ast);
127 }
128 }
129
130 eval_ast(m, env) @=> MalObject result;
131 if( result.type == "error" )
132 {
133 return result;
134 }
135
136 (result$MalList).value() @=> MalObject values[];
137 values[0].type => string type;
138 MalObject.slice(values, 1) @=> MalObject args[];
139
140 if( type == "subr" )
141 {
142 values[0]$MalSubr @=> MalSubr subr;
143 return subr.call(args);
144 }
145 else // type == "func"
146 {
147 values[0]$Func @=> Func func;
148 Env.create(func.env, func.args, args) @=> Env eval_env;
149 eval_env @=> env;
150 func.ast @=> m;
151 continue; // TCO
152 }
153 }
154 }
155
156 fun MalObject eval_ast(MalObject m, Env env)
157 {
158 m.type => string type;
159
160 if( type == "symbol" )
161 {
162 (m$MalSymbol).value() => string symbol;
163 return env.get(symbol);
164 }
165 else if( type == "list" || type == "vector" || type == "hashmap" )
166 {
167 (m$MalList).value() @=> MalObject values[];
168 MalObject results[values.size()];
169
170 if( type != "hashmap" )
171 {
172 for( 0 => int i; i < values.size(); i++ )
173 {
174 EVAL(values[i], env) @=> MalObject result;
175
176 if( result.type == "error" )
177 {
178 return result;
179 }
180
181 result @=> results[i];
182 }
183 }
184 else
185 {
186 for( 0 => int i; i < values.size(); i++ )
187 {
188 if( i % 2 == 0 )
189 {
190 values[i] @=> results[i];
191 }
192 else
193 {
194 EVAL(values[i], env) @=> results[i];
195 }
196 }
197 }
198
199 if( type == "list" )
200 {
201 return MalList.create(results);
202 }
203 else if( type == "vector" )
204 {
205 return MalVector.create(results);
206 }
207 else if( type == "hashmap" )
208 {
209 return MalHashMap.create(results);
210 }
211 }
212 else
213 {
214 return m;
215 }
216 }
217
218 fun string PRINT(MalObject m)
219 {
220 return Printer.pr_str(m, true);
221 }
222
223 Env.create(null) @=> Env repl_env;
224 for( 0 => int i; i < Core.names.size(); i++ )
225 {
226 Core.names[i] => string name;
227 repl_env.set(name, Core.ns[name]);
228 }
229
230 fun string rep(string input)
231 {
232 READ(input) @=> MalObject m;
233
234 if( m.type == "error" )
235 {
236 return Status.toMessage(m$MalError);
237 }
238
239 EVAL(m, repl_env) @=> MalObject result;
240 if( result.type == "error" )
241 {
242 return Status.toMessage(result$MalError);
243 }
244
245 return PRINT(result);
246 }
247
248 rep("(def! not (fn* (a) (if a false true)))");
249
250 fun void main()
251 {
252 ConsoleInput stdin;
253 string input;
254
255 while( true )
256 {
257 stdin.prompt("user>") => now;
258 stdin.getLine() => input;
259 rep(input) => string output;
260
261 if( output == "empty input" )
262 {
263 // proceed immediately with prompt
264 }
265 else
266 {
267 Util.println(output);
268 }
269 }
270 }
271
272 main();