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