1
1
mirror of https://github.com/kanaka/mal.git synced 2024-09-20 01:57:09 +03:00
mal/rpython/mal_types.py

274 lines
7.6 KiB
Python
Raw Normal View History

2015-06-03 07:58:23 +03:00
import sys, copy, types as pytypes
IS_RPYTHON = sys.argv[0].endswith('rpython')
if IS_RPYTHON:
from rpython.rlib.listsort import TimSort
else:
import re
2015-06-03 07:58:23 +03:00
2015-06-09 05:58:37 +03:00
# General functions
class StringSort(TimSort):
def lt(self, a, b):
assert isinstance(a, unicode)
assert isinstance(b, unicode)
return a < b
2015-06-09 05:58:37 +03:00
def _equal_Q(a, b):
assert isinstance(a, MalType) and isinstance(b, MalType)
ota, otb = a.__class__, b.__class__
if not (ota is otb or (_sequential_Q(a) and _sequential_Q(b))):
return False
if isinstance(a, MalSym) and isinstance(b, MalSym):
2015-06-09 05:58:37 +03:00
return a.value == b.value
elif isinstance(a, MalStr) and isinstance(b, MalStr):
2015-06-09 05:58:37 +03:00
return a.value == b.value
elif isinstance(a, MalInt) and isinstance(b, MalInt):
2015-06-09 05:58:37 +03:00
return a.value == b.value
elif _list_Q(a) or _vector_Q(a):
2015-06-09 05:58:37 +03:00
if len(a) != len(b): return False
for i in range(len(a)):
if not _equal_Q(a[i], b[i]): return False
return True
elif _hash_map_Q(a):
assert isinstance(a, MalHashMap)
assert isinstance(b, MalHashMap)
akeys = a.dct.keys()
bkeys = b.dct.keys()
if len(akeys) != len(bkeys): return False
StringSort(akeys).sort()
StringSort(bkeys).sort()
for i in range(len(akeys)):
ak, bk = akeys[i], bkeys[i]
assert isinstance(ak, unicode)
assert isinstance(bk, unicode)
if ak != bk: return False
av, bv = a.dct[ak], b.dct[bk]
if not _equal_Q(av, bv): return False
return True
2015-06-09 05:58:37 +03:00
elif a is b:
return True
else:
2015-06-10 07:07:03 +03:00
throw_str("no = op defined for %s" % a.__class__.__name__)
2015-06-09 05:58:37 +03:00
def _sequential_Q(seq): return _list_Q(seq) or _vector_Q(seq)
2015-06-09 05:58:37 +03:00
2015-06-11 07:25:55 +03:00
def _clone(obj):
if isinstance(obj, MalFunc):
return MalFunc(obj.fn, obj.ast, obj.env, obj.params,
obj.EvalFunc, obj.ismacro)
elif isinstance(obj, MalList):
return obj.__class__(obj.values)
elif isinstance(obj, MalHashMap):
return MalHashMap(obj.dct)
elif isinstance(obj, MalAtom):
return MalAtom(obj.value)
else:
2015-06-11 07:25:55 +03:00
raise Exception("_clone on invalid type")
2015-06-03 07:58:23 +03:00
def _replace(match, sub, old_str):
new_str = u""
2015-06-03 07:58:23 +03:00
idx = 0
while idx < len(old_str):
midx = old_str.find(match, idx)
if midx < 0: break
assert midx >= 0 and midx < len(old_str)
new_str = new_str + old_str[idx:midx]
new_str = new_str + sub
idx = midx + len(match)
new_str = new_str + old_str[idx:]
return new_str
#
# Mal Types
#
2015-06-10 07:07:03 +03:00
class MalException(Exception):
def __init__(self, object):
self.object = object
def throw_str(s):
raise MalException(MalStr(unicode(s)))
2015-06-11 07:25:55 +03:00
### Parent types
2015-06-03 07:58:23 +03:00
class MalType(): pass
class MalMeta(MalType): pass
2015-06-03 07:58:23 +03:00
### Scalars
class MalNil(MalType): pass
nil = MalNil()
def _nil_Q(exp):
assert isinstance(exp, MalType)
return exp is nil
class MalTrue(MalType): pass
true = MalTrue()
def _true_Q(exp):
assert isinstance(exp, MalType)
return exp is true
class MalFalse(MalType): pass
false = MalFalse()
def _false_Q(exp):
assert isinstance(exp, MalType)
return exp is false
# Numbers
class MalInt(MalType):
def __init__(self, value):
assert isinstance(value, int)
self.value = value
def _int_Q(exp):
assert isinstance(exp, MalType)
return exp.__class__ is MalInt
# String
class MalStr(MalType):
def __init__(self, value):
assert isinstance(value, unicode)
2015-06-03 07:58:23 +03:00
self.value = value
def __len__(self):
return len(self.value)
def _string_Q(exp):
assert isinstance(exp, MalType)
return exp.__class__ is MalStr and not _keyword_Q(exp)
2015-06-03 07:58:23 +03:00
# Keywords
# A specially prefixed string
def _keyword(mstr):
assert isinstance(mstr, MalType)
if isinstance(mstr, MalStr):
val = mstr.value
if val[0] == u"\u029e": return mstr
else: return MalStr(u"\u029e" + val)
else:
2015-06-10 07:07:03 +03:00
throw_str("_keyword called on non-string")
# Create keyword from unicode string
def _keywordu(strn):
assert isinstance(strn, unicode)
return MalStr(u"\u029e" + strn)
def _keyword_Q(exp):
if isinstance(exp, MalStr) and len(exp.value) > 0:
2015-06-10 07:07:03 +03:00
return exp.value[0] == u"\u029e"
else:
return False
# Symbols
class MalSym(MalMeta):
def __init__(self, value):
assert isinstance(value, unicode)
self.value = value
self.meta = nil
def _symbol(strn):
assert isinstance(strn, unicode)
return MalSym(strn)
def _symbol_Q(exp):
assert isinstance(exp, MalType)
return exp.__class__ is MalSym
2015-06-03 07:58:23 +03:00
# lists
class MalList(MalMeta):
2015-06-03 07:58:23 +03:00
def __init__(self, vals):
assert isinstance(vals, list)
self.values = vals
self.meta = nil
2015-06-03 07:58:23 +03:00
def append(self, val):
self.values.append(val)
2015-06-09 05:58:37 +03:00
def rest(self):
2015-06-04 07:16:37 +03:00
return MalList(self.values[1:])
2015-06-04 07:01:16 +03:00
def __len__(self):
return len(self.values)
2015-06-04 07:16:37 +03:00
def __getitem__(self, i):
assert isinstance(i, int)
return self.values[i]
2015-06-09 06:14:37 +03:00
def slice(self, start):
return MalList(self.values[start:len(self.values)])
def slice2(self, start, end):
assert end >= 0
2015-06-09 05:58:37 +03:00
return MalList(self.values[start:end])
2015-06-03 07:58:23 +03:00
def _list(*vals): return MalList(list(vals))
def _listl(lst): return MalList(lst)
2015-06-03 07:58:23 +03:00
def _list_Q(exp):
assert isinstance(exp, MalType)
return exp.__class__ is MalList
### vectors
class MalVector(MalList):
pass
def _vector(*vals): return MalVector(list(vals))
def _vectorl(lst): return MalVector(lst)
def _vector_Q(exp):
assert isinstance(exp, MalType)
return exp.__class__ is MalVector
### hash maps
class MalHashMap(MalMeta):
def __init__(self, dct):
self.dct = dct
self.meta = nil
def append(self, val):
self.dct.append(val)
def __getitem__(self, k):
assert isinstance(k, unicode)
if not isinstance(k, unicode):
throw_str("hash-map lookup by non-string/non-keyword")
return self.dct[k]
def __setitem__(self, k, v):
if not isinstance(k, unicode):
throw_str("hash-map key must be string or keyword")
assert isinstance(v, MalType)
self.dct[k] = v
return v
def _hash_mapl(kvs):
dct = {}
for i in range(0, len(kvs), 2):
k = kvs[i]
if not isinstance(k, MalStr):
throw_str("hash-map key must be string or keyword")
v = kvs[i+1]
dct[k.value] = v
return MalHashMap(dct)
def _hash_map_Q(exp):
assert isinstance(exp, MalType)
return exp.__class__ is MalHashMap
2015-06-09 05:58:37 +03:00
# Functions
# env import must happen after MalSym and MalList definitions to allow
# circular dependency
from env import Env
class MalFunc(MalMeta):
2015-06-09 05:58:37 +03:00
def __init__(self, fn, ast=None, env=None, params=None,
2015-06-10 05:21:12 +03:00
EvalFunc=None, ismacro=False):
2015-06-09 05:58:37 +03:00
if fn is None and EvalFunc is None:
2015-06-10 07:07:03 +03:00
throw_str("MalFunc requires either fn or EvalFunc")
2015-06-09 05:58:37 +03:00
self.fn = fn
self.ast = ast
self.env = env
self.params = params
self.EvalFunc = EvalFunc
2015-06-10 05:21:12 +03:00
self.ismacro = ismacro
2015-06-11 07:25:55 +03:00
self.meta = nil
2015-06-09 05:58:37 +03:00
def apply(self, args):
if self.EvalFunc:
return self.EvalFunc(self.ast, self.gen_env(args))
else:
return self.fn(args)
def gen_env(self, args):
return Env(self.env, self.params, args)
def _function_Q(exp):
assert isinstance(exp, MalType)
return exp.__class__ is MalFunc
2015-06-11 07:25:55 +03:00
# atoms
class MalAtom(MalMeta):
def __init__(self, value):
self.value = value
self.meta = nil
def get_value(self):
return self.value
2015-06-11 07:25:55 +03:00
def _atom(val): return MalAtom(val)
def _atom_Q(exp): return exp.__class__ is MalAtom