Merge pull request #158 from dubek/first-rest-nil
[jackhill/mal.git] / haxe / core / Core.hx
1 package core;
2
3 import Compat;
4 import types.Types.MalType;
5 import types.Types.*;
6 import types.MalException;
7 import printer.Printer;
8 import reader.Reader;
9 import haxe.Timer;
10
11 class Core {
12 static function BoolFn(v) {
13 if (v) { return MalTrue; }
14 else { return MalFalse; }
15 }
16
17 static function BoolOp(op) {
18 return function(args:Array<MalType>) {
19 return switch (args) {
20 case [MalInt(a), MalInt(b)]: BoolFn(op(a,b));
21 case _: throw "Invalid boolean op call";
22 }
23
24 };
25 }
26
27 static function NumOp(op) {
28 return function(args:Array<MalType>) {
29 return switch (args) {
30 case [MalInt(a), MalInt(b)]: MalInt(op(a,b));
31 case _: throw "Invalid numeric op call";
32 }
33
34 };
35 }
36
37 static var start = Timer.stamp();
38 static function time_ms(args) {
39 return MalInt(Std.int(1000 * (Timer.stamp()-start)));
40 }
41
42 static function equal_Q(args) {
43 return BoolFn(_equal_Q(args[0],args[1]));
44 }
45
46 static function pr_str(args) {
47 return MalString(
48 args.map(function(s) { return Printer.pr_str(s,true); }).join(" ")
49 );
50 }
51 static function str(args) {
52 return MalString(
53 args.map(function(s) { return Printer.pr_str(s,false); }).join("")
54 );
55 }
56 static function prn(args) {
57 Compat.println(args.map(function(s) { return Printer.pr_str(s,true); }).join(" "));
58 return nil;
59 }
60 static function println(args) {
61 Compat.println(args.map(function(s) { return Printer.pr_str(s,false); }).join(" "));
62 return nil;
63 }
64
65 static function symbol(args) {
66 return switch (args[0]) {
67 case MalString(s): MalSymbol(s);
68 case MalSymbol(_): args[0];
69 case _: throw "Invalid symbol call";
70 }
71 }
72
73 static function keyword(args) {
74 return switch (args[0]) {
75 case MalString(s):
76 if (keyword_Q(args[0])) {
77 args[0];
78 } else {
79 MalString("\x7f" + s);
80 }
81 case _: throw "Invalid keyword call";
82 }
83 }
84
85 static function read_string(args) {
86 return switch (args[0]) {
87 case MalString(s): Reader.read_str(s);
88 case _: throw "invalid read_str call";
89 }
90 }
91
92 static function readline(args) {
93 return switch (args[0]) {
94 case MalString(prompt):
95 try {
96 MalString(Compat.readline(prompt));
97 } catch (exc:haxe.io.Eof) {
98 nil;
99 }
100 case _: throw "invalid readline call";
101 }
102 }
103
104 static function slurp(args) {
105 return switch (args[0]) {
106 case MalString(s):
107 MalString(Compat.slurp(s));
108 case _: throw "invalid slurp call";
109 }
110 }
111
112 // sequential functions
113 static function sequential_Q(args) {
114 return BoolFn(list_Q(args[0]) || vector_Q(args[0]));
115 }
116
117 static function cons(args) {
118 return switch [args[0], args[1]] {
119 case [a, MalList(l)] |
120 [a, MalVector(l)]:
121 MalList([a].concat(l));
122 case [a, MalNil]:
123 MalList([a]);
124 case _: throw "Invalid cons call";
125 }
126 }
127
128 static function do_concat(args:Array<MalType>) {
129 var res:Array<MalType> = [];
130 for (a in args) {
131 switch (a) {
132 case MalList(l) | MalVector(l):
133 res = res.concat(l);
134 case MalNil:
135 continue;
136 case _:
137 throw "concat called with non-sequence";
138 }
139 }
140 return MalList(res);
141 }
142
143 static function nth(args) {
144 return switch [args[0], args[1]] {
145 case [seq, MalInt(idx)]:
146 _nth(seq, idx);
147 case _: throw "Invalid nth call";
148 }
149 }
150
151 static function empty_Q(args) {
152 return switch (args[0]) {
153 case MalList(l) | MalVector(l):
154 if (l.length == 0) { MalTrue; }
155 else { MalFalse; }
156 case MalNil: MalTrue;
157 case _: MalFalse;
158 }
159 }
160
161 static function count(args) {
162 return switch (args[0]) {
163 case MalList(l) | MalVector(l): MalInt(l.length);
164 case MalNil: MalInt(0);
165 case _: throw "count called on non-sequence";
166 }
167 }
168
169 static function apply(args) {
170 return switch [args[0], args[args.length-1]] {
171 case [MalFunc(f,_,_,_,_), MalList(l)] |
172 [MalFunc(f,_,_,_,_), MalVector(l)]:
173 var fargs = args.slice(1,args.length-1).concat(l);
174 return f(fargs);
175 case _: throw "Invalid apply call";
176 }
177 }
178
179 static function do_map(args) {
180 return switch [args[0], args[1]] {
181 case [MalFunc(f,_,_,_,_), MalList(l)] |
182 [MalFunc(f,_,_,_,_), MalVector(l)]:
183 return MalList(l.map(function(x) { return f([x]); }));
184 case _: throw "Invalid map call";
185 }
186 }
187
188 static function conj(args) {
189 return switch (args[0]) {
190 case MalList(l):
191 var elems = args.slice(1);
192 elems.reverse();
193 MalList(elems.concat(l));
194 case MalVector(l):
195 MalVector(l.concat(args.slice(1)));
196 case _: throw "Invalid conj call";
197 }
198 }
199
200
201 // hash-map functions
202
203 public static function get(hm:MalType, key:MalType) {
204 return switch [hm, key] {
205 case [MalHashMap(m), MalString(k)]:
206 if (m.exists(k)) {
207 m[k];
208 } else {
209 nil;
210 }
211 case [nil, MalString(k)]:
212 nil;
213 case _: throw "invalid get call";
214 }
215 }
216
217 public static function assoc(args) {
218 return switch (args[0]) {
219 case MalHashMap(m):
220 var new_m = _clone(args[0]);
221 MalHashMap(assoc_BANG(new_m, args.slice(1)));
222 case _: throw "invalid assoc call";
223 }
224 }
225
226 public static function dissoc(args) {
227 return switch (args[0]) {
228 case MalHashMap(m):
229 var new_m = _clone(args[0]);
230 MalHashMap(dissoc_BANG(new_m, args.slice(1)));
231 case _: throw "invalid dissoc call";
232 }
233 }
234
235 public static function contains_Q(hm:MalType, key:MalType) {
236 return switch [hm, key] {
237 case [MalHashMap(m), MalString(k)]:
238 m.exists(k);
239 case _: throw "invalid contains? call";
240 }
241 }
242
243 public static function keys(hm:MalType) {
244 return switch (hm) {
245 case MalHashMap(m):
246 MalList([for (k in m.keys()) MalString(k)]);
247 case _: throw "invalid keys call";
248 }
249 }
250
251 public static function vals(hm:MalType) {
252 return switch (hm) {
253 case MalHashMap(m):
254 MalList([for (k in m.keys()) m[k]]);
255 case _: throw "invalid vals call";
256 }
257 }
258
259 // metadata functions
260 static function meta(args) {
261 return switch (args[0]) {
262 case MalFunc(f,_,_,_,_,meta): meta;
263 case _: throw "meta called on non-function";
264 }
265 }
266
267 static function with_meta(args) {
268 return switch (args[0]) {
269 case MalFunc(f,a,e,p,mac,_):
270 MalFunc(f,a,e,p,mac,args[1]);
271 case _: throw "with_meta called on non-function";
272 }
273 }
274
275
276
277 // atom functions
278
279 static function deref(args) {
280 return switch (args[0]) {
281 case MalAtom(v): v.val;
282 case _: throw "deref called on non-atom";
283 }
284 }
285
286 static function reset_BANG(args) {
287 return switch (args[0]) {
288 case MalAtom(v): v.val = args[1];
289 case _: throw "reset! called on non-atom";
290 }
291 }
292
293 static function swap_BANG(args) {
294 return switch [args[0], args[1]] {
295 case [MalAtom(v), MalFunc(f,_,_,_,_)]:
296 var fargs = [v.val].concat(args.slice(2));
297 v.val = f(fargs);
298 v.val;
299 case _: throw "swap! called on non-atom";
300 }
301 }
302
303
304 public static var ns:Map<String,Array<MalType> -> MalType> = [
305 "=" => function(a) { return BoolFn(_equal_Q(a[0],a[1])); },
306 "throw" => function(a) { throw new MalException(a[0]); },
307
308 "nil?" => function(a) { return BoolFn(nil_Q(a[0])); },
309 "true?" => function(a) { return BoolFn(true_Q(a[0])); },
310 "false?" => function(a) { return BoolFn(false_Q(a[0])); },
311 "symbol" => symbol,
312 "symbol?" => function(a) { return BoolFn(symbol_Q(a[0])); },
313 "keyword" => keyword,
314 "keyword?" => function(a) { return BoolFn(keyword_Q(a[0])); },
315
316 "pr-str" => pr_str,
317 "str" => str,
318 "prn" => prn,
319 "println" => println,
320 "read-string" => read_string,
321 "readline" => readline,
322 "slurp" => slurp,
323
324 "<" => BoolOp(function(a,b) {return a<b;}),
325 "<=" => BoolOp(function(a,b) {return a<=b;}),
326 ">" => BoolOp(function(a,b) {return a>b;}),
327 ">=" => BoolOp(function(a,b) {return a>=b;}),
328 "+" => NumOp(function(a,b) {return a+b;}),
329 "-" => NumOp(function(a,b) {return a-b;}),
330 "*" => NumOp(function(a,b) {return a*b;}),
331 "/" => NumOp(function(a,b) {return Std.int(a/b);}),
332 "time-ms" => time_ms,
333
334 "list" => function(a) { return MalList(a); },
335 "list?" => function(a) { return BoolFn(list_Q(a[0])); },
336 "vector" => function(a) { return MalVector(a); },
337 "vector?" => function(a) { return BoolFn(vector_Q(a[0])); },
338 "hash-map" => hash_map,
339 "map?" => function(a) { return BoolFn(hash_map_Q(a[0])); },
340 "assoc" => assoc,
341 "dissoc" => dissoc,
342 "get" => function(a) { return get(a[0],a[1]); },
343 "contains?" => function(a) { return BoolFn(contains_Q(a[0], a[1])); },
344 "keys" => function(a) { return keys(a[0]); } ,
345 "vals" => function(a) { return vals(a[0]); } ,
346
347 "sequential?" => sequential_Q,
348 "cons" => cons,
349 "concat" => do_concat,
350 "nth" => nth,
351 "first" => function(a) { return first(a[0]); },
352 "rest" => function(a) { return rest(a[0]); },
353 "empty?" => empty_Q,
354 "count" => count,
355 "apply" => apply,
356 "map" => do_map,
357
358 "conj" => conj,
359
360 "meta" => meta,
361 "with-meta" => with_meta,
362 "atom" => function(a) { return MalAtom({val:a[0]}); },
363 "atom?" => function(a) { return BoolFn(atom_Q(a[0])); },
364 "deref" => deref,
365 "reset!" => reset_BANG,
366 "swap!" => swap_BANG
367 ];
368 }