RPython: misc cleanup, step sync, stats target.
[jackhill/mal.git] / rpython / mal_types.py
1 import sys, copy, types as pytypes
2
3 # General functions
4
5 def _equal_Q(a, b):
6 assert isinstance(a, MalType) and isinstance(b, MalType)
7 ota, otb = a.__class__, b.__class__
8 if not (ota is otb or (_sequential_Q(a) and _sequential_Q(b))):
9 return False
10 if _symbol_Q(a):
11 assert isinstance(a, MalSym) and isinstance(b, MalSym)
12 return a.value == b.value
13 elif _string_Q(a):
14 assert isinstance(a, MalStr) and isinstance(b, MalStr)
15 return a.value == b.value
16 elif _int_Q(a):
17 assert isinstance(a, MalInt) and isinstance(b, MalInt)
18 return a.value == b.value
19 elif _list_Q(a) or _vector_Q(a):
20 if len(a) != len(b): return False
21 for i in range(len(a)):
22 if not _equal_Q(a[i], b[i]): return False
23 return True
24 ## elif _hash_map_Q(a):
25 ## akeys = a.keys()
26 ## akeys.sort()
27 ## bkeys = b.keys()
28 ## bkeys.sort()
29 ## if len(akeys) != len(bkeys): return False
30 ## for i in range(len(akeys)):
31 ## if akeys[i] != bkeys[i]: return False
32 ## if not equal_Q(a[akeys[i]], b[bkeys[i]]): return False
33 ## return True
34 elif a is b:
35 #elif ((a is nil and a is nil) or (a is true and b is true) or (a
36 # is false and b is false)):
37 return True
38 else:
39 throw_str("no = op defined for %s" % a.__class__.__name__)
40
41 def _sequential_Q(seq): return _list_Q(seq) or _vector_Q(seq)
42
43 def _clone(obj):
44 if isinstance(obj, MalFunc):
45 return MalFunc(obj.fn, obj.ast, obj.env, obj.params,
46 obj.EvalFunc, obj.ismacro)
47 elif isinstance(obj, MalList):
48 return obj.__class__(obj.values)
49 elif isinstance(obj, MalHashMap):
50 return MalHashMap(obj.dct)
51 elif isinstance(obj, MalAtom):
52 return MalAtom(obj.value)
53 else:
54 raise Exception("_clone on invalid type")
55
56 def _replace(match, sub, old_str):
57 new_str = u""
58 idx = 0
59 while idx < len(old_str):
60 midx = old_str.find(match, idx)
61 if midx < 0: break
62 assert midx >= 0 and midx < len(old_str)
63 new_str = new_str + old_str[idx:midx]
64 new_str = new_str + sub
65 idx = midx + len(match)
66 new_str = new_str + old_str[idx:]
67 return new_str
68
69 #
70 # Mal Types
71 #
72
73 class MalException(Exception):
74 def __init__(self, object):
75 self.object = object
76
77 def throw_str(s):
78 raise MalException(MalStr(unicode(s)))
79
80
81 ### Parent types
82 class MalType(): pass
83 class MalMeta(MalType): pass
84
85 ### Scalars
86 class MalNil(MalType): pass
87 nil = MalNil()
88 def _nil_Q(exp):
89 assert isinstance(exp, MalType)
90 return exp is nil
91
92 class MalTrue(MalType): pass
93 true = MalTrue()
94 def _true_Q(exp):
95 assert isinstance(exp, MalType)
96 return exp is true
97
98 class MalFalse(MalType): pass
99 false = MalFalse()
100 def _false_Q(exp):
101 assert isinstance(exp, MalType)
102 return exp is false
103
104 # Numbers
105 class MalInt(MalType):
106 def __init__(self, value):
107 assert isinstance(value, int)
108 self.value = value
109 def _int_Q(exp):
110 assert isinstance(exp, MalType)
111 return exp.__class__ is MalInt
112
113 # String
114 class MalStr(MalType):
115 def __init__(self, value):
116 assert isinstance(value, unicode)
117 self.value = value
118 def __len__(self):
119 return len(self.value)
120 def _string_Q(exp):
121 assert isinstance(exp, MalType)
122 return exp.__class__ is MalStr
123
124 # Keywords
125 # A specially prefixed string
126 def _keyword(mstr):
127 assert isinstance(mstr, MalType)
128 if isinstance(mstr, MalStr):
129 val = mstr.value
130 if val[0] == u"\u029e": return mstr
131 else: return MalStr(u"\u029e" + val)
132 else:
133 throw_str("_keyword called on non-string")
134 # Create keyword from unicode string
135 def _keywordu(strn):
136 assert isinstance(strn, unicode)
137 return MalStr(u"\u029e" + strn)
138 def _keyword_Q(exp):
139 if isinstance(exp, MalStr):
140 return exp.value[0] == u"\u029e"
141 else:
142 return False
143
144 # Symbols
145 class MalSym(MalMeta):
146 def __init__(self, value):
147 assert isinstance(value, unicode)
148 self.value = value
149 self.meta = nil
150 def _symbol(strn):
151 assert isinstance(strn, unicode)
152 return MalSym(strn)
153 def _symbol_Q(exp):
154 assert isinstance(exp, MalType)
155 return exp.__class__ is MalSym
156
157 # lists
158 class MalList(MalMeta):
159 def __init__(self, vals):
160 assert isinstance(vals, list)
161 self.values = vals
162 self.meta = nil
163 def append(self, val):
164 self.values.append(val)
165 def rest(self):
166 return MalList(self.values[1:])
167 def __len__(self):
168 return len(self.values)
169 def __getitem__(self, i):
170 assert isinstance(i, int)
171 return self.values[i]
172 def slice(self, start):
173 return MalList(self.values[start:len(self.values)])
174 def slice2(self, start, end):
175 assert end >= 0
176 return MalList(self.values[start:end])
177 def _list(*vals): return MalList(list(vals))
178 def _listl(lst): return MalList(lst)
179 def _list_Q(exp):
180 assert isinstance(exp, MalType)
181 return exp.__class__ is MalList
182
183 ### vectors
184 class MalVector(MalList):
185 pass
186 def _vector(*vals): return MalVector(list(vals))
187 def _vectorl(lst): return MalVector(lst)
188 def _vector_Q(exp):
189 assert isinstance(exp, MalType)
190 return exp.__class__ is MalVector
191
192 ### hash maps
193 class MalHashMap(MalMeta):
194 def __init__(self, dct):
195 #assert isinstance(dct, {}.__class__)
196 self.dct = dct
197 self.meta = nil
198 def append(self, val):
199 self.dct.append(val)
200 def __getitem__(self, k):
201 assert isinstance(k, unicode)
202 return self.dct[k]
203 def __setitem__(self, k, v):
204 assert isinstance(k, unicode)
205 assert isinstance(v, MalType)
206 self.dct[k] = v
207 return v
208 def _hash_mapl(kvs):
209 dct = {}
210 for i in range(0, len(kvs), 2):
211 k = kvs[i]
212 assert isinstance(k, MalStr)
213 v = kvs[i+1]
214 dct[k.value] = v
215 return MalHashMap(dct)
216 def _hash_map_Q(exp):
217 assert isinstance(exp, MalType)
218 return exp.__class__ is MalHashMap
219
220 # Functions
221 # env import must happen after MalSym and MalList definitions to allow
222 # circular dependency
223 from env import Env
224 class MalFunc(MalMeta):
225 def __init__(self, fn, ast=None, env=None, params=None,
226 EvalFunc=None, ismacro=False):
227 if fn is None and EvalFunc is None:
228 throw_str("MalFunc requires either fn or EvalFunc")
229 self.fn = fn
230 self.ast = ast
231 self.env = env
232 self.params = params
233 self.EvalFunc = EvalFunc
234 self.ismacro = ismacro
235 self.meta = nil
236 def apply(self, args):
237 if self.EvalFunc:
238 return self.EvalFunc(self.ast, self.gen_env(args))
239 else:
240 return self.fn(args)
241 def gen_env(self, args):
242 return Env(self.env, self.params, args)
243 def _function_Q(exp):
244 assert isinstance(exp, MalType)
245 return exp.__class__ is MalFunc
246
247
248 # atoms
249 class MalAtom(MalMeta):
250 def __init__(self, value):
251 self.value = value
252 self.meta = nil
253 def _atom(val): return MalAtom(val)
254 def _atom_Q(exp): return exp.__class__ is MalAtom