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