Merge pull request #406 from chr15m/lib-alias-hacks
[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]))
6791e640 29def string_Q(args): return wrap_tf(types._string_Q(args[0]))
9be6d5a6
JM
30def 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
38def symbol_Q(args): return wrap_tf(types._symbol_Q(args[0]))
39def keyword(args): return types._keyword(args[0])
40def keyword_Q(args): return wrap_tf(types._keyword_Q(args[0]))
2df92e06
JM
41def number_Q(args): return wrap_tf(types._int_Q(args[0]))
42def function_Q(args): return wrap_tf(types._function_Q(args[0]) and not args[0].ismacro)
43def macro_Q(args): return wrap_tf(types._function_Q(args[0]) and args[0].ismacro)
c9fe67a8 44
b0a9121d
JM
45
46# String functions
47def 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
52def 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
57def 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
63def 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
69def 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
78def 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
84def 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
91def 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
96def 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
101def 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
106def 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
112def 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)
117def 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)
122def 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)
127def 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
135def time_ms(args):
136 return MalInt(int(time.time() * 1000))
137
b0a9121d 138
8855a05a
JM
139# Hash map functions
140def do_hash_map(ml):
8855a05a
JM
141 return types._hash_mapl(ml.values)
142
143def hash_map_Q(args):
144 return wrap_tf(types._hash_map_Q(args[0]))
145
146def 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
156def 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
166def 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
184def 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
190def keys(args):
191 hm = args[0]
192 keys = []
193 for k in hm.dct.keys(): keys.append(MalStr(k))
194 return MalList(keys)
195
196def vals(args):
197 hm = args[0]
198 return MalList(hm.dct.values())
199
b0a9121d
JM
200
201# Sequence functions
202def do_list(ml):
b0a9121d
JM
203 return ml
204
205def list_Q(args):
8855a05a
JM
206 return wrap_tf(types._list_Q(args[0]))
207
208def do_vector(ml):
8855a05a
JM
209 return MalVector(ml.values)
210
211def vector_Q(args):
212 return wrap_tf(types._vector_Q(args[0]))
b0a9121d
JM
213
214def 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
223def 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
232def sequential_Q(args):
233 return wrap_tf(types._sequential_Q(args[0]))
234
0096b107
JM
235def 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
241def 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
249def 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
258def 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
267def 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
276def 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
284def 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
294def 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
308def 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
326def 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
335def 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
344def do_atom(args):
345 return MalAtom(args[0])
346def atom_Q(args):
347 return wrap_tf(types._atom_Q(args[0]))
348def 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
353def 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
359def 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 370ns = {
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