1 import sys
, copy
, types
as pytypes
2 IS_RPYTHON
= sys
.argv
[0].endswith('rpython')
5 from rpython
.rlib
.listsort
import TimSort
11 class StringSort(TimSort
):
13 assert isinstance(a
, unicode)
14 assert isinstance(b
, unicode)
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
))):
22 if isinstance(a
, MalSym
) and isinstance(b
, MalSym
):
23 return a
.value
== b
.value
24 elif isinstance(a
, MalStr
) and isinstance(b
, MalStr
):
25 return a
.value
== b
.value
26 elif isinstance(a
, MalInt
) and isinstance(b
, MalInt
):
27 return a
.value
== b
.value
28 elif _list_Q(a
) or _vector_Q(a
):
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
34 assert isinstance(a
, MalHashMap
)
35 assert isinstance(b
, MalHashMap
)
38 if len(akeys
) != len(bkeys
): return False
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
53 throw_str("no = op defined for %s" % a
.__class
__.__name
__)
55 def _sequential_Q(seq
): return _list_Q(seq
) or _vector_Q(seq
)
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
):
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
)
68 raise Exception("_clone on invalid type")
70 def _replace(match
, sub
, old_str
):
73 while idx
< len(old_str
):
74 midx
= old_str
.find(match
, idx
)
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
:]
87 class MalException(Exception):
88 def __init__(self
, object):
92 raise MalException(MalStr(unicode(s
)))
97 class MalMeta(MalType
): pass
100 class MalNil(MalType
): pass
103 assert isinstance(exp
, MalType
)
106 class MalTrue(MalType
): pass
109 assert isinstance(exp
, MalType
)
112 class MalFalse(MalType
): pass
115 assert isinstance(exp
, MalType
)
119 class MalInt(MalType
):
120 def __init__(self
, value
):
121 assert isinstance(value
, int)
124 assert isinstance(exp
, MalType
)
125 return exp
.__class
__ is MalInt
128 class MalStr(MalType
):
129 def __init__(self
, value
):
130 assert isinstance(value
, unicode)
133 return len(self
.value
)
135 assert isinstance(exp
, MalType
)
136 return exp
.__class
__ is MalStr
and not _keyword_Q(exp
)
139 # A specially prefixed string
141 assert isinstance(mstr
, MalType
)
142 if isinstance(mstr
, MalStr
):
144 if val
[0] == u
"\u029e": return mstr
145 else: return MalStr(u
"\u029e" + val
)
147 throw_str("_keyword called on non-string")
148 # Create keyword from unicode string
150 assert isinstance(strn
, unicode)
151 return MalStr(u
"\u029e" + strn
)
153 if isinstance(exp
, MalStr
) and len(exp
.value
) > 0:
154 return exp
.value
[0] == u
"\u029e"
159 class MalSym(MalMeta
):
160 def __init__(self
, value
):
161 assert isinstance(value
, unicode)
165 assert isinstance(strn
, unicode)
168 assert isinstance(exp
, MalType
)
169 return exp
.__class
__ is MalSym
172 class MalList(MalMeta
):
173 def __init__(self
, vals
):
174 assert isinstance(vals
, list)
177 def append(self
, val
):
178 self
.values
.append(val
)
180 return MalList(self
.values
[1:])
182 return len(self
.values
)
183 def __getitem__(self
, i
):
184 assert isinstance(i
, int)
185 return self
.values
[i
]
186 def slice(self
, start
):
187 return MalList(self
.values
[start
:len(self
.values
)])
188 def slice2(self
, start
, end
):
190 return MalList(self
.values
[start
:end
])
191 def _list(*vals
): return MalList(list(vals
))
192 def _listl(lst
): return MalList(lst
)
194 assert isinstance(exp
, MalType
)
195 return exp
.__class
__ is MalList
198 class MalVector(MalList
):
200 def _vector(*vals
): return MalVector(list(vals
))
201 def _vectorl(lst
): return MalVector(lst
)
203 assert isinstance(exp
, MalType
)
204 return exp
.__class
__ is MalVector
207 class MalHashMap(MalMeta
):
208 def __init__(self
, dct
):
211 def append(self
, val
):
213 def __getitem__(self
, k
):
214 assert isinstance(k
, unicode)
215 if not isinstance(k
, unicode):
216 throw_str("hash-map lookup by non-string/non-keyword")
218 def __setitem__(self
, k
, v
):
219 if not isinstance(k
, unicode):
220 throw_str("hash-map key must be string or keyword")
221 assert isinstance(v
, MalType
)
226 for i
in range(0, len(kvs
), 2):
228 if not isinstance(k
, MalStr
):
229 throw_str("hash-map key must be string or keyword")
232 return MalHashMap(dct
)
233 def _hash_map_Q(exp
):
234 assert isinstance(exp
, MalType
)
235 return exp
.__class
__ is MalHashMap
238 # env import must happen after MalSym and MalList definitions to allow
239 # circular dependency
241 class MalFunc(MalMeta
):
242 def __init__(self
, fn
, ast
=None, env
=None, params
=None,
243 EvalFunc
=None, ismacro
=False):
244 if fn
is None and EvalFunc
is None:
245 throw_str("MalFunc requires either fn or EvalFunc")
250 self
.EvalFunc
= EvalFunc
251 self
.ismacro
= ismacro
253 def apply(self
, args
):
255 return self
.EvalFunc(self
.ast
, self
.gen_env(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
266 class MalAtom(MalMeta
):
267 def __init__(self
, value
):
272 def _atom(val
): return MalAtom(val
)
273 def _atom_Q(exp
): return exp
.__class
__ is MalAtom