Commit | Line | Data |
---|---|---|
8855a05a JM |
1 | #import copy, time |
2 | import time | |
b0a9121d JM |
3 | |
4 | import mal_types as types | |
23fa1b11 JM |
5 | from mal_types import (throw_str, |
6 | MalType, MalMeta, nil, true, false, | |
8855a05a | 7 | MalInt, MalSym, MalStr, |
7f714804 JM |
8 | MalList, MalVector, MalHashMap, |
9 | MalAtom, MalFunc) | |
b0a9121d JM |
10 | import mal_readline |
11 | import reader | |
12 | import printer | |
13 | ||
14 | # General functions | |
9be6d5a6 JM |
15 | def wrap_tf(tf): |
16 | if tf: return true | |
17 | else: return false | |
b0a9121d | 18 | |
9be6d5a6 JM |
19 | def do_equal(args): return wrap_tf(types._equal_Q(args[0], args[1])) |
20 | ||
21 | # Errors/Exceptions | |
22 | def throw(args): | |
23 | raise types.MalException(args[0]) | |
24 | ||
25 | # Scalar functions | |
26 | def nil_Q(args): return wrap_tf(types._nil_Q(args[0])) | |
27 | def true_Q(args): return wrap_tf(types._true_Q(args[0])) | |
28 | def false_Q(args): return wrap_tf(types._false_Q(args[0])) | |
6791e640 | 29 | def string_Q(args): return wrap_tf(types._string_Q(args[0])) |
9be6d5a6 JM |
30 | def symbol(args): |
31 | a0 = args[0] | |
32 | if isinstance(a0, MalStr): | |
33 | return types._symbol(a0.value) | |
34 | elif isinstance(a0, MalSym): | |
35 | return a0 | |
36 | else: | |
23fa1b11 | 37 | throw_str("symbol called on non-string/non-symbol") |
9be6d5a6 JM |
38 | def symbol_Q(args): return wrap_tf(types._symbol_Q(args[0])) |
39 | def keyword(args): return types._keyword(args[0]) | |
40 | def keyword_Q(args): return wrap_tf(types._keyword_Q(args[0])) | |
2df92e06 JM |
41 | def number_Q(args): return wrap_tf(types._int_Q(args[0])) |
42 | def function_Q(args): return wrap_tf(types._function_Q(args[0]) and not args[0].ismacro) | |
43 | def macro_Q(args): return wrap_tf(types._function_Q(args[0]) and args[0].ismacro) | |
c9fe67a8 | 44 | |
b0a9121d JM |
45 | |
46 | # String functions | |
47 | def pr_str(args): | |
48 | parts = [] | |
49 | for exp in args.values: parts.append(printer._pr_str(exp, True)) | |
50 | return MalStr(u" ".join(parts)) | |
51 | ||
52 | def do_str(args): | |
53 | parts = [] | |
54 | for exp in args.values: parts.append(printer._pr_str(exp, False)) | |
55 | return MalStr(u"".join(parts)) | |
56 | ||
57 | def prn(args): | |
58 | parts = [] | |
59 | for exp in args.values: parts.append(printer._pr_str(exp, True)) | |
60 | print(u" ".join(parts)) | |
61 | return nil | |
62 | ||
63 | def println(args): | |
64 | parts = [] | |
65 | for exp in args.values: parts.append(printer._pr_str(exp, False)) | |
66 | print(u" ".join(parts)) | |
67 | return nil | |
68 | ||
11b4be99 JM |
69 | def do_readline(args): |
70 | prompt = args[0] | |
23fa1b11 JM |
71 | if not isinstance(prompt, MalStr): |
72 | throw_str("readline prompt is not a string") | |
7f714804 JM |
73 | try: |
74 | return MalStr(unicode(mal_readline.readline(str(prompt.value)))) | |
75 | except EOFError: | |
76 | return nil | |
11b4be99 | 77 | |
219f5239 JM |
78 | def read_str(args): |
79 | a0 = args[0] | |
23fa1b11 JM |
80 | if not isinstance(a0, MalStr): |
81 | throw_str("read-string of non-string") | |
219f5239 JM |
82 | return reader.read_str(str(a0.value)) |
83 | ||
84 | def slurp(args): | |
85 | a0 = args[0] | |
23fa1b11 JM |
86 | if not isinstance(a0, MalStr): |
87 | throw_str("slurp with non-string filename") | |
219f5239 JM |
88 | return MalStr(unicode(open(str(a0.value)).read())) |
89 | ||
b0a9121d JM |
90 | # Number functions |
91 | def lt(args): | |
92 | a, b = args[0], args[1] | |
23fa1b11 JM |
93 | if not isinstance(a, MalInt) or not isinstance(b, MalInt): |
94 | throw_str("< called on non-integer") | |
9be6d5a6 | 95 | return wrap_tf(a.value < b.value) |
b0a9121d JM |
96 | def lte(args): |
97 | a, b = args[0], args[1] | |
23fa1b11 JM |
98 | if not isinstance(a, MalInt) or not isinstance(b, MalInt): |
99 | throw_str("<= called on non-integer") | |
9be6d5a6 | 100 | return wrap_tf(a.value <= b.value) |
b0a9121d JM |
101 | def gt(args): |
102 | a, b = args[0], args[1] | |
23fa1b11 JM |
103 | if not isinstance(a, MalInt) or not isinstance(b, MalInt): |
104 | throw_str("> called on non-integer") | |
9be6d5a6 | 105 | return wrap_tf(a.value > b.value) |
b0a9121d JM |
106 | def gte(args): |
107 | a, b = args[0], args[1] | |
23fa1b11 JM |
108 | if not isinstance(a, MalInt) or not isinstance(b, MalInt): |
109 | throw_str(">= called on non-integer") | |
9be6d5a6 | 110 | return wrap_tf(a.value >= b.value) |
b0a9121d JM |
111 | |
112 | def plus(args): | |
113 | a, b = args[0], args[1] | |
23fa1b11 JM |
114 | if not isinstance(a, MalInt) or not isinstance(b, MalInt): |
115 | throw_str("+ called on non-integer") | |
b0a9121d JM |
116 | return MalInt(a.value+b.value) |
117 | def minus(args): | |
118 | a, b = args[0], args[1] | |
23fa1b11 JM |
119 | if not isinstance(a, MalInt) or not isinstance(b, MalInt): |
120 | throw_str("- called on non-integer") | |
b0a9121d JM |
121 | return MalInt(a.value-b.value) |
122 | def multiply(args): | |
123 | a, b = args[0], args[1] | |
23fa1b11 JM |
124 | if not isinstance(a, MalInt) or not isinstance(b, MalInt): |
125 | throw_str("* called on non-integer") | |
b0a9121d JM |
126 | return MalInt(a.value*b.value) |
127 | def divide(args): | |
128 | a, b = args[0], args[1] | |
23fa1b11 JM |
129 | if not isinstance(a, MalInt) or not isinstance(b, MalInt): |
130 | throw_str("/ called on non-integer") | |
131 | if b.value == 0: | |
132 | throw_str("divide by zero") | |
b0a9121d JM |
133 | return MalInt(int(a.value/b.value)) |
134 | ||
7f714804 JM |
135 | def time_ms(args): |
136 | return MalInt(int(time.time() * 1000)) | |
137 | ||
b0a9121d | 138 | |
8855a05a JM |
139 | # Hash map functions |
140 | def do_hash_map(ml): | |
8855a05a JM |
141 | return types._hash_mapl(ml.values) |
142 | ||
143 | def hash_map_Q(args): | |
144 | return wrap_tf(types._hash_map_Q(args[0])) | |
145 | ||
146 | def assoc(args): | |
147 | src_hm, key_vals = args[0], args.rest() | |
8855a05a JM |
148 | new_dct = src_hm.dct.copy() |
149 | for i in range(0,len(key_vals),2): | |
150 | k = key_vals[i] | |
23fa1b11 JM |
151 | if not isinstance(k, MalStr): |
152 | throw_str("assoc called with non-string/non-keyword key") | |
8855a05a JM |
153 | new_dct[k.value] = key_vals[i+1] |
154 | return MalHashMap(new_dct) | |
155 | ||
156 | def dissoc(args): | |
157 | src_hm, keys = args[0], args.rest() | |
8855a05a JM |
158 | new_dct = src_hm.dct.copy() |
159 | for k in keys.values: | |
23fa1b11 JM |
160 | if not isinstance(k, MalStr): |
161 | throw_str("dissoc called with non-string/non-keyword key") | |
8855a05a JM |
162 | if k.value in new_dct: |
163 | del new_dct[k.value] | |
164 | return MalHashMap(new_dct) | |
165 | ||
166 | def get(args): | |
167 | obj, key = args[0], args[1] | |
168 | if obj is nil: | |
169 | return nil | |
170 | elif isinstance(obj, MalHashMap): | |
23fa1b11 JM |
171 | if not isinstance(key, MalStr): |
172 | throw_str("get called on hash-map with non-string/non-keyword key") | |
8855a05a JM |
173 | if obj and key.value in obj.dct: |
174 | return obj.dct[key.value] | |
175 | else: | |
176 | return nil | |
177 | elif isinstance(obj, MalList): | |
23fa1b11 JM |
178 | if not isinstance(key, MalInt): |
179 | throw_str("get called on list/vector with non-string/non-keyword key") | |
8855a05a JM |
180 | return obj.values[key.value] |
181 | else: | |
23fa1b11 | 182 | throw_str("get called on invalid type") |
8855a05a JM |
183 | |
184 | def contains_Q(args): | |
185 | hm, key = args[0], args[1] | |
23fa1b11 JM |
186 | if not isinstance(key, MalStr): |
187 | throw_str("contains? called on hash-map with non-string/non-keyword key") | |
8855a05a JM |
188 | return wrap_tf(key.value in hm.dct) |
189 | ||
190 | def keys(args): | |
191 | hm = args[0] | |
192 | keys = [] | |
193 | for k in hm.dct.keys(): keys.append(MalStr(k)) | |
194 | return MalList(keys) | |
195 | ||
196 | def vals(args): | |
197 | hm = args[0] | |
198 | return MalList(hm.dct.values()) | |
199 | ||
b0a9121d JM |
200 | |
201 | # Sequence functions | |
202 | def do_list(ml): | |
b0a9121d JM |
203 | return ml |
204 | ||
205 | def list_Q(args): | |
8855a05a JM |
206 | return wrap_tf(types._list_Q(args[0])) |
207 | ||
208 | def do_vector(ml): | |
8855a05a JM |
209 | return MalVector(ml.values) |
210 | ||
211 | def vector_Q(args): | |
212 | return wrap_tf(types._vector_Q(args[0])) | |
b0a9121d JM |
213 | |
214 | def empty_Q(args): | |
b0a9121d JM |
215 | seq = args[0] |
216 | if isinstance(seq, MalList): | |
9be6d5a6 | 217 | return wrap_tf(len(seq) == 0) |
b0a9121d JM |
218 | elif seq is nil: |
219 | return true | |
220 | else: | |
23fa1b11 | 221 | throw_str("empty? called on non-sequence") |
b0a9121d JM |
222 | |
223 | def count(args): | |
b0a9121d JM |
224 | seq = args[0] |
225 | if isinstance(seq, MalList): | |
226 | return MalInt(len(seq)) | |
227 | elif seq is nil: | |
228 | return MalInt(0) | |
229 | else: | |
23fa1b11 | 230 | throw_str("count called on non-sequence") |
b0a9121d | 231 | |
8855a05a JM |
232 | def sequential_Q(args): |
233 | return wrap_tf(types._sequential_Q(args[0])) | |
234 | ||
0096b107 JM |
235 | def cons(args): |
236 | x, seq = args[0], args[1] | |
23fa1b11 JM |
237 | if not isinstance(seq, MalList): |
238 | throw_str("cons called with non-list/non-vector") | |
0096b107 JM |
239 | return MalList([x] + seq.values) |
240 | ||
241 | def concat(args): | |
242 | new_lst = [] | |
243 | for l in args.values: | |
23fa1b11 JM |
244 | if not isinstance(l, MalList): |
245 | throw_str("concat called with non-list/non-vector") | |
0096b107 JM |
246 | new_lst = new_lst + l.values |
247 | return MalList(new_lst) | |
248 | ||
c9fe67a8 JM |
249 | def nth(args): |
250 | lst, idx = args[0], args[1] | |
23fa1b11 JM |
251 | if not isinstance(lst, MalList): |
252 | throw_str("nth called with non-list/non-vector") | |
253 | if not isinstance(idx, MalInt): | |
254 | throw_str("nth called with non-int index") | |
c9fe67a8 | 255 | if idx.value < len(lst): return lst[idx.value] |
23fa1b11 | 256 | else: throw_str("nth: index out of range") |
c9fe67a8 JM |
257 | |
258 | def first(args): | |
259 | a0 = args[0] | |
d5a3eb94 JM |
260 | if a0 is nil: |
261 | return nil | |
262 | elif not isinstance(a0, MalList): | |
23fa1b11 | 263 | throw_str("first called with non-list/non-vector") |
c9fe67a8 JM |
264 | if len(a0) == 0: return nil |
265 | else: return a0[0] | |
266 | ||
267 | def rest(args): | |
268 | a0 = args[0] | |
d5a3eb94 JM |
269 | if a0 is nil: |
270 | return MalList([]) | |
271 | elif not isinstance(a0, MalList): | |
23fa1b11 | 272 | throw_str("rest called with non-list/non-vector") |
c9fe67a8 JM |
273 | if len(a0) == 0: return MalList([]) |
274 | else: return a0.rest() | |
275 | ||
9be6d5a6 JM |
276 | def apply(args): |
277 | f, fargs = args[0], args.rest() | |
278 | last_arg = fargs.values[-1] | |
23fa1b11 JM |
279 | if not isinstance(last_arg, MalList): |
280 | throw_str("map called with non-list") | |
9be6d5a6 JM |
281 | all_args = fargs.values[0:-1] + last_arg.values |
282 | return f.apply(MalList(all_args)) | |
283 | ||
284 | def mapf(args): | |
285 | f, lst = args[0], args[1] | |
23fa1b11 JM |
286 | if not isinstance(lst, MalList): |
287 | throw_str("map called with non-list") | |
9be6d5a6 JM |
288 | res = [] |
289 | for a in lst.values: | |
290 | res.append(f.apply(MalList([a]))) | |
291 | return MalList(res) | |
292 | ||
6791e640 JM |
293 | # retains metadata |
294 | def conj(args): | |
295 | lst, args = args[0], args.rest() | |
296 | new_lst = None | |
297 | if types._list_Q(lst): | |
298 | vals = args.values[:] | |
299 | vals.reverse() | |
300 | new_lst = MalList(vals + lst.values) | |
301 | elif types._vector_Q(lst): | |
302 | new_lst = MalVector(lst.values + list(args.values)) | |
303 | else: | |
304 | throw_str("conj on non-list/non-vector") | |
305 | new_lst.meta = lst.meta | |
306 | return new_lst | |
307 | ||
308 | def seq(args): | |
309 | a0 = args[0] | |
310 | if isinstance(a0, MalVector): | |
311 | if len(a0) == 0: return nil | |
312 | return MalList(a0.values) | |
313 | elif isinstance(a0, MalList): | |
314 | if len(a0) == 0: return nil | |
315 | return a0 | |
316 | elif types._string_Q(a0): | |
317 | assert isinstance(a0, MalStr) | |
318 | if len(a0) == 0: return nil | |
319 | return MalList([MalStr(unicode(c)) for c in a0.value]) | |
320 | elif a0 is nil: | |
321 | return nil | |
322 | else: | |
323 | throw_str("seq: called on non-sequence") | |
9be6d5a6 | 324 | |
7f714804 JM |
325 | # Metadata functions |
326 | def with_meta(args): | |
327 | obj, meta = args[0], args[1] | |
ab02c5bb | 328 | if isinstance(obj, MalMeta): |
7f714804 JM |
329 | new_obj = types._clone(obj) |
330 | new_obj.meta = meta | |
331 | return new_obj | |
332 | else: | |
23fa1b11 | 333 | throw_str("with-meta not supported on type") |
7f714804 JM |
334 | |
335 | def meta(args): | |
336 | obj = args[0] | |
ab02c5bb | 337 | if isinstance(obj, MalMeta): |
7f714804 JM |
338 | return obj.meta |
339 | else: | |
23fa1b11 | 340 | throw_str("meta not supported on type") |
7f714804 JM |
341 | |
342 | ||
343 | # Atoms functions | |
344 | def do_atom(args): | |
345 | return MalAtom(args[0]) | |
346 | def atom_Q(args): | |
347 | return wrap_tf(types._atom_Q(args[0])) | |
348 | def deref(args): | |
349 | atm = args[0] | |
23fa1b11 JM |
350 | if not isinstance(atm, MalAtom): |
351 | throw_str("deref called on non-atom") | |
ab02c5bb | 352 | return atm.value |
7f714804 JM |
353 | def reset_BANG(args): |
354 | atm, val = args[0], args[1] | |
23fa1b11 JM |
355 | if not isinstance(atm, MalAtom): |
356 | throw_str("reset! called on non-atom") | |
ab02c5bb JM |
357 | atm.value = val |
358 | return atm.value | |
7f714804 JM |
359 | def swap_BANG(args): |
360 | atm, f, fargs = args[0], args[1], args.slice(2) | |
23fa1b11 JM |
361 | if not isinstance(atm, MalAtom): |
362 | throw_str("swap! called on non-atom") | |
363 | if not isinstance(f, MalFunc): | |
364 | throw_str("swap! called with non-function") | |
ab02c5bb JM |
365 | all_args = [atm.value] + fargs.values |
366 | atm.value = f.apply(MalList(all_args)) | |
367 | return atm.value | |
b0a9121d JM |
368 | |
369 | ||
ab02c5bb | 370 | ns = { |
b0a9121d | 371 | '=': do_equal, |
9be6d5a6 JM |
372 | 'throw': throw, |
373 | 'nil?': nil_Q, | |
374 | 'true?': true_Q, | |
375 | 'false?': false_Q, | |
6791e640 | 376 | 'string?': string_Q, |
9be6d5a6 JM |
377 | 'symbol': symbol, |
378 | 'symbol?': symbol_Q, | |
379 | 'keyword': keyword, | |
380 | 'keyword?': keyword_Q, | |
2df92e06 JM |
381 | 'number?': number_Q, |
382 | 'fn?': function_Q, | |
383 | 'macro?': macro_Q, | |
9be6d5a6 | 384 | |
b0a9121d JM |
385 | 'pr-str': pr_str, |
386 | 'str': do_str, | |
387 | 'prn': prn, | |
388 | 'println': println, | |
11b4be99 | 389 | 'readline': do_readline, |
219f5239 JM |
390 | 'read-string': read_str, |
391 | 'slurp': slurp, | |
b0a9121d JM |
392 | '<': lt, |
393 | '<=': lte, | |
394 | '>': gt, | |
395 | '>=': gte, | |
396 | '+': plus, | |
397 | '-': minus, | |
398 | '*': multiply, | |
399 | '/': divide, | |
7f714804 JM |
400 | 'time-ms': time_ms, |
401 | ||
b0a9121d JM |
402 | 'list': do_list, |
403 | 'list?': list_Q, | |
8855a05a JM |
404 | 'vector': do_vector, |
405 | 'vector?': vector_Q, | |
406 | 'hash-map': do_hash_map, | |
407 | 'map?': hash_map_Q, | |
408 | 'assoc': assoc, | |
409 | 'dissoc': dissoc, | |
410 | 'get': get, | |
411 | 'contains?': contains_Q, | |
412 | 'keys': keys, | |
413 | 'vals': vals, | |
414 | ||
415 | 'sequential?': sequential_Q, | |
0096b107 JM |
416 | 'cons': cons, |
417 | 'concat': concat, | |
c9fe67a8 JM |
418 | 'nth': nth, |
419 | 'first': first, | |
420 | 'rest': rest, | |
b0a9121d JM |
421 | 'empty?': empty_Q, |
422 | 'count': count, | |
9be6d5a6 JM |
423 | 'apply': apply, |
424 | 'map': mapf, | |
7f714804 | 425 | |
6791e640 JM |
426 | 'conj': conj, |
427 | 'seq': seq, | |
428 | ||
7f714804 JM |
429 | 'with-meta': with_meta, |
430 | 'meta': meta, | |
431 | 'atom': do_atom, | |
432 | 'atom?': atom_Q, | |
433 | 'deref': deref, | |
434 | 'reset!': reset_BANG, | |
435 | 'swap!': swap_BANG | |
b0a9121d JM |
436 | } |
437 |