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