All: fix read/print of \\, and \n
[jackhill/mal.git] / rpython / mal_types.py
CommitLineData
80320efc
JM
1import sys, copy, types as pytypes
2
b0a9121d
JM
3# General functions
4
5def _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 36def _sequential_Q(seq): return _list_Q(seq) or _vector_Q(seq)
b0a9121d 37
7f714804 38def _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
51def _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
68class MalException(Exception):
69 def __init__(self, object):
70 self.object = object
71
72def throw_str(s):
73 raise MalException(MalStr(unicode(s)))
74
75
7f714804 76### Parent types
80320efc 77class MalType(): pass
ab02c5bb 78class MalMeta(MalType): pass
80320efc
JM
79
80### Scalars
81class MalNil(MalType): pass
82nil = MalNil()
83def _nil_Q(exp):
84 assert isinstance(exp, MalType)
85 return exp is nil
86
87class MalTrue(MalType): pass
88true = MalTrue()
89def _true_Q(exp):
90 assert isinstance(exp, MalType)
91 return exp is true
92
93class MalFalse(MalType): pass
94false = MalFalse()
95def _false_Q(exp):
96 assert isinstance(exp, MalType)
97 return exp is false
98
99# Numbers
100class MalInt(MalType):
101 def __init__(self, value):
102 assert isinstance(value, int)
103 self.value = value
104def _int_Q(exp):
105 assert isinstance(exp, MalType)
106 return exp.__class__ is MalInt
107
108# String
109class 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)
115def _string_Q(exp):
116 assert isinstance(exp, MalType)
117 return exp.__class__ is MalStr
118
f0cd1318
JM
119# Keywords
120# A specially prefixed string
121def _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
130def _keywordu(strn):
131 assert isinstance(strn, unicode)
132 return MalStr(u"\u029e" + strn)
133def _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
140class MalSym(MalMeta):
141 def __init__(self, value):
142 assert isinstance(value, unicode)
143 self.value = value
144 self.meta = nil
145def _symbol(strn):
146 assert isinstance(strn, unicode)
147 return MalSym(strn)
148def _symbol_Q(exp):
149 assert isinstance(exp, MalType)
150 return exp.__class__ is MalSym
151
80320efc 152# lists
ab02c5bb 153class 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 172def _list(*vals): return MalList(list(vals))
8855a05a 173def _listl(lst): return MalList(lst)
80320efc
JM
174def _list_Q(exp):
175 assert isinstance(exp, MalType)
176 return exp.__class__ is MalList
177
80320efc 178### vectors
8855a05a
JM
179class MalVector(MalList):
180 pass
181def _vector(*vals): return MalVector(list(vals))
182def _vectorl(lst): return MalVector(lst)
183def _vector_Q(exp):
184 assert isinstance(exp, MalType)
185 return exp.__class__ is MalVector
186
187### hash maps
ab02c5bb 188class 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
205def _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
214def _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
221from env import Env
ab02c5bb 222class 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)
241def _function_Q(exp):
242 assert isinstance(exp, MalType)
243 return exp.__class__ is MalFunc
244
7f714804
JM
245
246# atoms
ab02c5bb
JM
247class 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
253def _atom(val): return MalAtom(val)
254def _atom_Q(exp): return exp.__class__ is MalAtom