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