-import copy, time
+#import copy, time
+import time
import mal_types as types
-from mal_types import (MalType, nil, true, false,
- MalInt, MalSym, MalStr, MalList)
+from mal_types import (throw_str,
+ MalType, MalMeta, nil, true, false,
+ MalInt, MalSym, MalStr,
+ MalList, MalVector, MalHashMap,
+ MalAtom, MalFunc)
import mal_readline
import reader
import printer
def nil_Q(args): return wrap_tf(types._nil_Q(args[0]))
def true_Q(args): return wrap_tf(types._true_Q(args[0]))
def false_Q(args): return wrap_tf(types._false_Q(args[0]))
+def string_Q(args): return wrap_tf(types._string_Q(args[0]))
def symbol(args):
a0 = args[0]
if isinstance(a0, MalStr):
elif isinstance(a0, MalSym):
return a0
else:
- types.throw_str("symbol called on non-string/non-symbol")
+ throw_str("symbol called on non-string/non-symbol")
def symbol_Q(args): return wrap_tf(types._symbol_Q(args[0]))
def keyword(args): return types._keyword(args[0])
def keyword_Q(args): return wrap_tf(types._keyword_Q(args[0]))
+def number_Q(args): return wrap_tf(types._int_Q(args[0]))
+def function_Q(args): return wrap_tf(types._function_Q(args[0]) and not args[0].ismacro)
+def macro_Q(args): return wrap_tf(types._function_Q(args[0]) and args[0].ismacro)
# String functions
def do_readline(args):
prompt = args[0]
- assert isinstance(prompt, MalStr)
- return MalStr(unicode(mal_readline.readline(str(prompt.value))))
+ if not isinstance(prompt, MalStr):
+ throw_str("readline prompt is not a string")
+ try:
+ return MalStr(unicode(mal_readline.readline(str(prompt.value))))
+ except EOFError:
+ return nil
def read_str(args):
a0 = args[0]
- assert isinstance(a0, MalStr)
+ if not isinstance(a0, MalStr):
+ throw_str("read-string of non-string")
return reader.read_str(str(a0.value))
def slurp(args):
a0 = args[0]
- assert isinstance(a0, MalStr)
+ if not isinstance(a0, MalStr):
+ throw_str("slurp with non-string filename")
return MalStr(unicode(open(str(a0.value)).read()))
# Number functions
def lt(args):
a, b = args[0], args[1]
- assert isinstance(a, MalInt)
- assert isinstance(b, MalInt)
+ if not isinstance(a, MalInt) or not isinstance(b, MalInt):
+ throw_str("< called on non-integer")
return wrap_tf(a.value < b.value)
def lte(args):
a, b = args[0], args[1]
- assert isinstance(a, MalInt)
- assert isinstance(b, MalInt)
+ if not isinstance(a, MalInt) or not isinstance(b, MalInt):
+ throw_str("<= called on non-integer")
return wrap_tf(a.value <= b.value)
def gt(args):
a, b = args[0], args[1]
- assert isinstance(a, MalInt)
- assert isinstance(b, MalInt)
+ if not isinstance(a, MalInt) or not isinstance(b, MalInt):
+ throw_str("> called on non-integer")
return wrap_tf(a.value > b.value)
def gte(args):
a, b = args[0], args[1]
- assert isinstance(a, MalInt)
- assert isinstance(b, MalInt)
+ if not isinstance(a, MalInt) or not isinstance(b, MalInt):
+ throw_str(">= called on non-integer")
return wrap_tf(a.value >= b.value)
def plus(args):
a, b = args[0], args[1]
- assert isinstance(a, MalInt)
- assert isinstance(b, MalInt)
+ if not isinstance(a, MalInt) or not isinstance(b, MalInt):
+ throw_str("+ called on non-integer")
return MalInt(a.value+b.value)
def minus(args):
a, b = args[0], args[1]
- assert isinstance(a, MalInt)
- assert isinstance(b, MalInt)
+ if not isinstance(a, MalInt) or not isinstance(b, MalInt):
+ throw_str("- called on non-integer")
return MalInt(a.value-b.value)
def multiply(args):
a, b = args[0], args[1]
- assert isinstance(a, MalInt)
- assert isinstance(b, MalInt)
+ if not isinstance(a, MalInt) or not isinstance(b, MalInt):
+ throw_str("* called on non-integer")
return MalInt(a.value*b.value)
def divide(args):
a, b = args[0], args[1]
- assert isinstance(a, MalInt)
- assert isinstance(b, MalInt)
+ if not isinstance(a, MalInt) or not isinstance(b, MalInt):
+ throw_str("/ called on non-integer")
+ if b.value == 0:
+ throw_str("divide by zero")
return MalInt(int(a.value/b.value))
+def time_ms(args):
+ return MalInt(int(time.time() * 1000))
+
+
+# Hash map functions
+def do_hash_map(ml):
+ return types._hash_mapl(ml.values)
+
+def hash_map_Q(args):
+ return wrap_tf(types._hash_map_Q(args[0]))
+
+def assoc(args):
+ src_hm, key_vals = args[0], args.rest()
+ new_dct = src_hm.dct.copy()
+ for i in range(0,len(key_vals),2):
+ k = key_vals[i]
+ if not isinstance(k, MalStr):
+ throw_str("assoc called with non-string/non-keyword key")
+ new_dct[k.value] = key_vals[i+1]
+ return MalHashMap(new_dct)
+
+def dissoc(args):
+ src_hm, keys = args[0], args.rest()
+ new_dct = src_hm.dct.copy()
+ for k in keys.values:
+ if not isinstance(k, MalStr):
+ throw_str("dissoc called with non-string/non-keyword key")
+ if k.value in new_dct:
+ del new_dct[k.value]
+ return MalHashMap(new_dct)
+
+def get(args):
+ obj, key = args[0], args[1]
+ if obj is nil:
+ return nil
+ elif isinstance(obj, MalHashMap):
+ if not isinstance(key, MalStr):
+ throw_str("get called on hash-map with non-string/non-keyword key")
+ if obj and key.value in obj.dct:
+ return obj.dct[key.value]
+ else:
+ return nil
+ elif isinstance(obj, MalList):
+ if not isinstance(key, MalInt):
+ throw_str("get called on list/vector with non-string/non-keyword key")
+ return obj.values[key.value]
+ else:
+ throw_str("get called on invalid type")
+
+def contains_Q(args):
+ hm, key = args[0], args[1]
+ if not isinstance(key, MalStr):
+ throw_str("contains? called on hash-map with non-string/non-keyword key")
+ return wrap_tf(key.value in hm.dct)
+
+def keys(args):
+ hm = args[0]
+ keys = []
+ for k in hm.dct.keys(): keys.append(MalStr(k))
+ return MalList(keys)
+
+def vals(args):
+ hm = args[0]
+ return MalList(hm.dct.values())
-## Hash map functions
-#def assoc(src_hm, *key_vals):
-# hm = copy.copy(src_hm)
-# for i in range(0,len(key_vals),2): hm[key_vals[i]] = key_vals[i+1]
-# return hm
-#
-#def dissoc(src_hm, *keys):
-# hm = copy.copy(src_hm)
-# for key in keys:
-# if key in hm: del hm[key]
-# return hm
-#
-#def get(hm, key):
-# if hm and key in hm:
-# return hm[key]
-# else:
-# return None
-#
-#def contains_Q(hm, key): return key in hm
-#
-#def keys(hm): return types._list(*hm.keys())
-#
-#def vals(hm): return types._list(*hm.values())
-#
# Sequence functions
def do_list(ml):
- assert isinstance(ml, MalList)
return ml
def list_Q(args):
- return wrap_tf(isinstance(args[0], MalList))
+ return wrap_tf(types._list_Q(args[0]))
+
+def do_vector(ml):
+ return MalVector(ml.values)
+
+def vector_Q(args):
+ return wrap_tf(types._vector_Q(args[0]))
def empty_Q(args):
- assert isinstance(args, MalType)
seq = args[0]
if isinstance(seq, MalList):
return wrap_tf(len(seq) == 0)
elif seq is nil:
return true
else:
- types.throw_str("empty? called on non-sequence")
+ throw_str("empty? called on non-sequence")
def count(args):
- assert isinstance(args, MalType)
seq = args[0]
if isinstance(seq, MalList):
return MalInt(len(seq))
elif seq is nil:
return MalInt(0)
else:
- types.throw_str("count called on non-sequence")
+ throw_str("count called on non-sequence")
+
+def sequential_Q(args):
+ return wrap_tf(types._sequential_Q(args[0]))
-#def coll_Q(coll): return sequential_Q(coll) or hash_map_Q(coll)
-#
def cons(args):
x, seq = args[0], args[1]
- assert isinstance(seq, MalList)
+ if not isinstance(seq, MalList):
+ throw_str("cons called with non-list/non-vector")
return MalList([x] + seq.values)
def concat(args):
new_lst = []
for l in args.values:
- assert isinstance(l, MalList)
+ if not isinstance(l, MalList):
+ throw_str("concat called with non-list/non-vector")
new_lst = new_lst + l.values
return MalList(new_lst)
def nth(args):
lst, idx = args[0], args[1]
- assert isinstance(lst, MalList)
- assert isinstance(idx, MalInt)
+ if not isinstance(lst, MalList):
+ throw_str("nth called with non-list/non-vector")
+ if not isinstance(idx, MalInt):
+ throw_str("nth called with non-int index")
if idx.value < len(lst): return lst[idx.value]
- else: types.throw_str("nth: index out of range")
+ else: throw_str("nth: index out of range")
def first(args):
a0 = args[0]
- assert isinstance(a0, MalList)
+ if a0 is nil:
+ return nil
+ elif not isinstance(a0, MalList):
+ throw_str("first called with non-list/non-vector")
if len(a0) == 0: return nil
else: return a0[0]
def rest(args):
a0 = args[0]
- assert isinstance(a0, MalList)
+ if a0 is nil:
+ return MalList([])
+ elif not isinstance(a0, MalList):
+ throw_str("rest called with non-list/non-vector")
if len(a0) == 0: return MalList([])
else: return a0.rest()
-## retains metadata
-#def conj(lst, *args):
-# if types._list_Q(lst):
-# new_lst = List(list(reversed(list(args))) + lst)
-# else:
-# new_lst = Vector(lst + list(args))
-# if hasattr(lst, "__meta__"):
-# new_lst.__meta__ = lst.__meta__
-# return new_lst
-
def apply(args):
f, fargs = args[0], args.rest()
last_arg = fargs.values[-1]
- assert isinstance(last_arg, MalList)
+ if not isinstance(last_arg, MalList):
+ throw_str("map called with non-list")
all_args = fargs.values[0:-1] + last_arg.values
return f.apply(MalList(all_args))
def mapf(args):
f, lst = args[0], args[1]
- assert isinstance(lst, MalList)
+ if not isinstance(lst, MalList):
+ throw_str("map called with non-list")
res = []
for a in lst.values:
res.append(f.apply(MalList([a])))
return MalList(res)
+# retains metadata
+def conj(args):
+ lst, args = args[0], args.rest()
+ new_lst = None
+ if types._list_Q(lst):
+ vals = args.values[:]
+ vals.reverse()
+ new_lst = MalList(vals + lst.values)
+ elif types._vector_Q(lst):
+ new_lst = MalVector(lst.values + list(args.values))
+ else:
+ throw_str("conj on non-list/non-vector")
+ new_lst.meta = lst.meta
+ return new_lst
+
+def seq(args):
+ a0 = args[0]
+ if isinstance(a0, MalVector):
+ if len(a0) == 0: return nil
+ return MalList(a0.values)
+ elif isinstance(a0, MalList):
+ if len(a0) == 0: return nil
+ return a0
+ elif types._string_Q(a0):
+ assert isinstance(a0, MalStr)
+ if len(a0) == 0: return nil
+ return MalList([MalStr(unicode(c)) for c in a0.value])
+ elif a0 is nil:
+ return nil
+ else:
+ throw_str("seq: called on non-sequence")
+
+# Metadata functions
+def with_meta(args):
+ obj, meta = args[0], args[1]
+ if isinstance(obj, MalMeta):
+ new_obj = types._clone(obj)
+ new_obj.meta = meta
+ return new_obj
+ else:
+ throw_str("with-meta not supported on type")
-## Metadata functions
-#def with_meta(obj, meta):
-# new_obj = types._clone(obj)
-# new_obj.__meta__ = meta
-# return new_obj
-#
-#def meta(obj):
-# if hasattr(obj, "__meta__"): return obj.__meta__
-# else: return None
-#
-#
-## Atoms functions
-#def deref(atm): return atm.val
-#def reset_BANG(atm,val):
-# atm.val = val
-# return atm.val
-#def swap_BANG(atm,f,*args):
-# atm.val = f(atm.val,*args)
-# return atm.val
-
-
-ns = {
+def meta(args):
+ obj = args[0]
+ if isinstance(obj, MalMeta):
+ return obj.meta
+ else:
+ throw_str("meta not supported on type")
+
+
+# Atoms functions
+def do_atom(args):
+ return MalAtom(args[0])
+def atom_Q(args):
+ return wrap_tf(types._atom_Q(args[0]))
+def deref(args):
+ atm = args[0]
+ if not isinstance(atm, MalAtom):
+ throw_str("deref called on non-atom")
+ return atm.value
+def reset_BANG(args):
+ atm, val = args[0], args[1]
+ if not isinstance(atm, MalAtom):
+ throw_str("reset! called on non-atom")
+ atm.value = val
+ return atm.value
+def swap_BANG(args):
+ atm, f, fargs = args[0], args[1], args.slice(2)
+ if not isinstance(atm, MalAtom):
+ throw_str("swap! called on non-atom")
+ if not isinstance(f, MalFunc):
+ throw_str("swap! called with non-function")
+ all_args = [atm.value] + fargs.values
+ atm.value = f.apply(MalList(all_args))
+ return atm.value
+
+
+ns = {
'=': do_equal,
'throw': throw,
'nil?': nil_Q,
'true?': true_Q,
'false?': false_Q,
+ 'string?': string_Q,
'symbol': symbol,
'symbol?': symbol_Q,
'keyword': keyword,
'keyword?': keyword_Q,
+ 'number?': number_Q,
+ 'fn?': function_Q,
+ 'macro?': macro_Q,
'pr-str': pr_str,
'str': do_str,
'-': minus,
'*': multiply,
'/': divide,
-# 'time-ms': lambda : int(time.time() * 1000),
-#
+ 'time-ms': time_ms,
+
'list': do_list,
'list?': list_Q,
-# 'vector': types._vector,
-# 'vector?': types._vector_Q,
-# 'hash-map': types._hash_map,
-# 'map?': types._hash_map_Q,
-# 'assoc': assoc,
-# 'dissoc': dissoc,
-# 'get': get,
-# 'contains?': contains_Q,
-# 'keys': keys,
-# 'vals': vals,
-#
-# 'sequential?': types._sequential_Q,
+ 'vector': do_vector,
+ 'vector?': vector_Q,
+ 'hash-map': do_hash_map,
+ 'map?': hash_map_Q,
+ 'assoc': assoc,
+ 'dissoc': dissoc,
+ 'get': get,
+ 'contains?': contains_Q,
+ 'keys': keys,
+ 'vals': vals,
+
+ 'sequential?': sequential_Q,
'cons': cons,
'concat': concat,
'nth': nth,
'rest': rest,
'empty?': empty_Q,
'count': count,
-# 'conj': conj,
'apply': apply,
'map': mapf,
-#
-# 'with-meta': with_meta,
-# 'meta': meta,
-# 'atom': types._atom,
-# 'atom?': types._atom_Q,
-# 'deref': deref,
-# 'reset!': reset_BANG,
-# 'swap!': swap_BANG
+
+ 'conj': conj,
+ 'seq': seq,
+
+ 'with-meta': with_meta,
+ 'meta': meta,
+ 'atom': do_atom,
+ 'atom?': atom_Q,
+ 'deref': deref,
+ 'reset!': reset_BANG,
+ 'swap!': swap_BANG
}