clojure, groovy, rpython, scala: fix macro result evaluation
[jackhill/mal.git] / rpython / core.py
CommitLineData
8855a05a
JM
1#import copy, time
2import time
b0a9121d
JM
3
4import mal_types as types
23fa1b11
JM
5from 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
10import mal_readline
11import reader
12import printer
13
14# General functions
9be6d5a6
JM
15def wrap_tf(tf):
16 if tf: return true
17 else: return false
b0a9121d 18
9be6d5a6
JM
19def do_equal(args): return wrap_tf(types._equal_Q(args[0], args[1]))
20
21# Errors/Exceptions
22def throw(args):
23 raise types.MalException(args[0])
24
25# Scalar functions
26def nil_Q(args): return wrap_tf(types._nil_Q(args[0]))
27def true_Q(args): return wrap_tf(types._true_Q(args[0]))
28def false_Q(args): return wrap_tf(types._false_Q(args[0]))
29def 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
37def symbol_Q(args): return wrap_tf(types._symbol_Q(args[0]))
38def keyword(args): return types._keyword(args[0])
39def keyword_Q(args): return wrap_tf(types._keyword_Q(args[0]))
c9fe67a8 40
b0a9121d
JM
41
42# String functions
43def 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
48def 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
53def 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
59def 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
65def 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
74def 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
80def 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
87def 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
92def 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
97def 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
102def 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
108def 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)
113def 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)
118def 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)
123def 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
131def time_ms(args):
132 return MalInt(int(time.time() * 1000))
133
b0a9121d 134
8855a05a
JM
135# Hash map functions
136def do_hash_map(ml):
8855a05a
JM
137 return types._hash_mapl(ml.values)
138
139def hash_map_Q(args):
140 return wrap_tf(types._hash_map_Q(args[0]))
141
142def 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
152def 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
162def 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
180def 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
186def keys(args):
187 hm = args[0]
188 keys = []
189 for k in hm.dct.keys(): keys.append(MalStr(k))
190 return MalList(keys)
191
192def vals(args):
193 hm = args[0]
194 return MalList(hm.dct.values())
195
b0a9121d
JM
196
197# Sequence functions
198def do_list(ml):
b0a9121d
JM
199 return ml
200
201def list_Q(args):
8855a05a
JM
202 return wrap_tf(types._list_Q(args[0]))
203
204def do_vector(ml):
8855a05a
JM
205 return MalVector(ml.values)
206
207def vector_Q(args):
208 return wrap_tf(types._vector_Q(args[0]))
b0a9121d
JM
209
210def 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
219def 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
228def sequential_Q(args):
229 return wrap_tf(types._sequential_Q(args[0]))
230
0096b107
JM
231def 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
237def 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
245def 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
254def 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
261def 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
269def 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
283def 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
291def 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
302def 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
311def 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
320def do_atom(args):
321 return MalAtom(args[0])
322def atom_Q(args):
323 return wrap_tf(types._atom_Q(args[0]))
324def 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
329def 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
335def 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 346ns = {
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