Merge pull request #378 from asarhaddon/test-macro-not-changing-function
[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 static function seq(args) {
201 return switch (args[0]) {
202 case MalList(l):
203 l.length > 0 ? args[0] : nil;
204 case MalVector(l):
205 l.length > 0 ? MalList(l.slice(0)) : nil;
206 case MalString(s):
207 if (s.length == 0) { return nil; }
208 MalList(s.split("").map(function(c) { return MalString(c); }));
209 case MalNil:
210 nil;
211 case _: throw "seq: called on non-sequence";
212 }
213 }
214
215
216 // hash-map functions
217
218 public static function get(hm:MalType, key:MalType) {
219 return switch [hm, key] {
220 case [MalHashMap(m), MalString(k)]:
221 if (m.exists(k)) {
222 m[k];
223 } else {
224 nil;
225 }
226 case [nil, MalString(k)]:
227 nil;
228 case _: throw "invalid get call";
229 }
230 }
231
232 public static function assoc(args) {
233 return switch (args[0]) {
234 case MalHashMap(m):
235 var new_m = _clone(args[0]);
236 MalHashMap(assoc_BANG(new_m, args.slice(1)));
237 case _: throw "invalid assoc call";
238 }
239 }
240
241 public static function dissoc(args) {
242 return switch (args[0]) {
243 case MalHashMap(m):
244 var new_m = _clone(args[0]);
245 MalHashMap(dissoc_BANG(new_m, args.slice(1)));
246 case _: throw "invalid dissoc call";
247 }
248 }
249
250 public static function contains_Q(hm:MalType, key:MalType) {
251 return switch [hm, key] {
252 case [MalHashMap(m), MalString(k)]:
253 m.exists(k);
254 case _: throw "invalid contains? call";
255 }
256 }
257
258 public static function keys(hm:MalType) {
259 return switch (hm) {
260 case MalHashMap(m):
261 MalList([for (k in m.keys()) MalString(k)]);
262 case _: throw "invalid keys call";
263 }
264 }
265
266 public static function vals(hm:MalType) {
267 return switch (hm) {
268 case MalHashMap(m):
269 MalList([for (k in m.keys()) m[k]]);
270 case _: throw "invalid vals call";
271 }
272 }
273
274 // metadata functions
275 static function meta(args) {
276 return switch (args[0]) {
277 case MalFunc(f,_,_,_,_,meta): meta;
278 case _: throw "meta called on non-function";
279 }
280 }
281
282 static function with_meta(args) {
283 return switch (args[0]) {
284 case MalFunc(f,a,e,p,mac,_):
285 MalFunc(f,a,e,p,mac,args[1]);
286 case _: throw "with_meta called on non-function";
287 }
288 }
289
290
291
292 // atom functions
293
294 static function deref(args) {
295 return switch (args[0]) {
296 case MalAtom(v): v.val;
297 case _: throw "deref called on non-atom";
298 }
299 }
300
301 static function reset_BANG(args) {
302 return switch (args[0]) {
303 case MalAtom(v): v.val = args[1];
304 case _: throw "reset! called on non-atom";
305 }
306 }
307
308 static function swap_BANG(args) {
309 return switch [args[0], args[1]] {
310 case [MalAtom(v), MalFunc(f,_,_,_,_)]:
311 var fargs = [v.val].concat(args.slice(2));
312 v.val = f(fargs);
313 v.val;
314 case _: throw "swap! called on non-atom";
315 }
316 }
317
318
319 public static var ns:Map<String,Array<MalType> -> MalType> = [
320 "=" => function(a) { return BoolFn(_equal_Q(a[0],a[1])); },
321 "throw" => function(a) { throw new MalException(a[0]); },
322
323 "nil?" => function(a) { return BoolFn(nil_Q(a[0])); },
324 "true?" => function(a) { return BoolFn(true_Q(a[0])); },
325 "false?" => function(a) { return BoolFn(false_Q(a[0])); },
326 "string?" => function(a) { return BoolFn(string_Q(a[0])); },
327 "symbol" => symbol,
328 "symbol?" => function(a) { return BoolFn(symbol_Q(a[0])); },
329 "keyword" => keyword,
330 "keyword?" => function(a) { return BoolFn(keyword_Q(a[0])); },
331 "number?" => function(a) { return BoolFn(number_Q(a[0])); },
332 "fn?" => function(a) { return BoolFn(_fn_Q(a[0])); },
333 "macro?" => function(a) { return BoolFn(_macro_Q(a[0])); },
334
335 "pr-str" => pr_str,
336 "str" => str,
337 "prn" => prn,
338 "println" => println,
339 "read-string" => read_string,
340 "readline" => readline,
341 "slurp" => slurp,
342
343 "<" => BoolOp(function(a,b) {return a<b;}),
344 "<=" => BoolOp(function(a,b) {return a<=b;}),
345 ">" => BoolOp(function(a,b) {return a>b;}),
346 ">=" => BoolOp(function(a,b) {return a>=b;}),
347 "+" => NumOp(function(a,b) {return a+b;}),
348 "-" => NumOp(function(a,b) {return a-b;}),
349 "*" => NumOp(function(a,b) {return a*b;}),
350 "/" => NumOp(function(a,b) {return Std.int(a/b);}),
351 "time-ms" => time_ms,
352
353 "list" => function(a) { return MalList(a); },
354 "list?" => function(a) { return BoolFn(list_Q(a[0])); },
355 "vector" => function(a) { return MalVector(a); },
356 "vector?" => function(a) { return BoolFn(vector_Q(a[0])); },
357 "hash-map" => hash_map,
358 "map?" => function(a) { return BoolFn(hash_map_Q(a[0])); },
359 "assoc" => assoc,
360 "dissoc" => dissoc,
361 "get" => function(a) { return get(a[0],a[1]); },
362 "contains?" => function(a) { return BoolFn(contains_Q(a[0], a[1])); },
363 "keys" => function(a) { return keys(a[0]); } ,
364 "vals" => function(a) { return vals(a[0]); } ,
365
366 "sequential?" => sequential_Q,
367 "cons" => cons,
368 "concat" => do_concat,
369 "nth" => nth,
370 "first" => function(a) { return first(a[0]); },
371 "rest" => function(a) { return rest(a[0]); },
372 "empty?" => empty_Q,
373 "count" => count,
374 "apply" => apply,
375 "map" => do_map,
376
377 "conj" => conj,
378 "seq" => seq,
379
380 "meta" => meta,
381 "with-meta" => with_meta,
382 "atom" => function(a) { return MalAtom({val:a[0]}); },
383 "atom?" => function(a) { return BoolFn(atom_Q(a[0])); },
384 "deref" => deref,
385 "reset!" => reset_BANG,
386 "swap!" => swap_BANG
387 ];
388 }