Merge pull request #361 from asarhaddon/exercise-native-implementations
[jackhill/mal.git] / d / types.d
1 import std.algorithm;
2 import std.array;
3 import std.conv;
4 import std.functional;
5 import std.range;
6 import env;
7
8 abstract class MalType
9 {
10 string print(bool readable) const;
11 bool is_truthy() const { return true; }
12 }
13
14 interface MalMeta
15 {
16 MalType meta();
17 MalType with_meta(MalType new_meta);
18 }
19
20 interface HasSeq
21 {
22 MalType seq();
23 }
24
25 class MalNil : MalType, HasSeq
26 {
27 override string print(bool readable) const { return "nil"; }
28 override bool is_truthy() const { return false; }
29 override bool opEquals(Object o) { return (cast(MalNil)(o) !is null); }
30 override MalType seq() { return this; }
31 }
32
33 class MalFalse : MalType
34 {
35 override string print(bool readable) const { return "false"; }
36 override bool is_truthy() const { return false; }
37 override bool opEquals(Object o) { return (cast(MalFalse)(o) !is null); }
38 }
39
40 class MalTrue : MalType
41 {
42 override string print(bool readable) const { return "true"; }
43 override bool opEquals(Object o) { return (cast(MalTrue)(o) !is null); }
44 }
45
46 MalNil mal_nil;
47 MalFalse mal_false;
48 MalTrue mal_true;
49
50 static this()
51 {
52 mal_nil = new MalNil;
53 mal_false = new MalFalse;
54 mal_true = new MalTrue;
55 }
56
57 MalType bool_to_mal(in bool b)
58 {
59 return b ? mal_true : mal_false;
60 }
61
62 class MalSymbol : MalType
63 {
64 const string name;
65 this(in string token) { name = token; }
66 override string print(bool readable) const { return name; }
67
68 override size_t toHash()
69 {
70 return typeid(name).getHash(&name);
71 }
72
73 override int opCmp(Object other)
74 {
75 MalSymbol o = cast(MalSymbol) other;
76 return cmp(name, o.name);
77 }
78
79 override bool opEquals(Object other)
80 {
81 auto o = cast(MalSymbol) other;
82 return (o !is null && name == o.name);
83 }
84 }
85
86 class MalInteger : MalType
87 {
88 const long val;
89 this(string token) { val = to!long(token); }
90 this(long v) { val = v; }
91 override string print(bool readable) const { return to!string(val); }
92
93 override bool opEquals(Object o)
94 {
95 auto oint = cast(MalInteger)(o);
96 return (oint !is null && val == oint.val);
97 }
98 }
99
100 class MalString : MalType, HasSeq
101 {
102 const string val;
103 this(in string token) { val = token; }
104 override string print(bool readable) const
105 {
106 if (is_keyword()) return ":" ~ val[2..$];
107 if (readable)
108 {
109 string escaped = val.replace("\\", "\\\\")
110 .replace("\"", "\\\"")
111 .replace("\n", "\\n");
112 return "\"" ~ escaped ~ "\"";
113 }
114 else
115 {
116 return val;
117 }
118 }
119
120 bool is_keyword() const
121 {
122 return val.length > 1 && val[0..2] == "\u029e";
123 }
124
125 override bool opEquals(Object o)
126 {
127 auto ostr = cast(MalString)(o);
128 return (ostr !is null && val == ostr.val);
129 }
130
131 override MalType seq() {
132 if (is_keyword() || val.length == 0) return mal_nil;
133 auto chars = val.map!(c => cast(MalType)(new MalString(to!string(c))));
134 return new MalList(array(chars));
135 }
136 }
137
138 abstract class MalSequential : MalType, HasSeq, MalMeta
139 {
140 MalType[] elements;
141 MalType meta_val;
142
143 this(MalType[] lst) {
144 elements = lst;
145 meta_val = mal_nil;
146 }
147
148 override bool opEquals(Object o)
149 {
150 auto oseq = cast(MalSequential)(o);
151 return (oseq !is null && elements == oseq.elements);
152 }
153
154 MalSequential conj(MalType element);
155
156 MalType seq() {
157 if (elements.length == 0) return mal_nil;
158 return new MalList(elements);
159 }
160 }
161
162 class MalList : MalSequential, MalMeta
163 {
164 this(MalType[] lst) { super(lst); }
165 this(MalList that, MalType new_meta)
166 {
167 super(that.elements);
168 meta_val = new_meta;
169 }
170
171 override string print(bool readable) const
172 {
173 auto items_strs = elements.map!(e => e.print(readable));
174 return "(" ~ array(items_strs).join(" ") ~ ")";
175 }
176
177 override MalSequential conj(MalType element)
178 {
179 return new MalList([element] ~ elements);
180 }
181
182 override MalType meta() { return meta_val; }
183 override MalType with_meta(MalType new_meta)
184 {
185 return new MalList(this, new_meta);
186 }
187 }
188
189 class MalVector : MalSequential, MalMeta
190 {
191 this(MalType[] lst) { super(lst); }
192 this(MalVector that, MalType new_meta)
193 {
194 super(that.elements);
195 meta_val = new_meta;
196 }
197
198 override string print(bool readable) const
199 {
200 auto items_strs = elements.map!(e => e.print(readable));
201 return "[" ~ array(items_strs).join(" ") ~ "]";
202 }
203
204 override MalSequential conj(MalType element)
205 {
206 return new MalVector(elements ~ [element]);
207 }
208
209 override MalType meta() { return meta_val; }
210 override MalType with_meta(MalType new_meta)
211 {
212 return new MalVector(this, new_meta);
213 }
214 }
215
216 class MalHashmap : MalType, MalMeta
217 {
218 MalType[string] data;
219 MalType meta_val;
220
221 this(MalType[string] map)
222 {
223 data = map;
224 meta_val = mal_nil;
225 }
226 this(MalType[] lst)
227 {
228 put_kv_list(lst);
229 meta_val = mal_nil;
230 }
231 this(MalHashmap that, MalType new_meta)
232 {
233 data = that.data;
234 meta_val = new_meta;
235 }
236
237 bool contains(in MalType key)
238 {
239 auto valp = (make_hash_key(key) in data);
240 return valp !is null;
241 }
242
243 MalType get(in MalType key)
244 {
245 auto valp = (make_hash_key(key) in data);
246 return valp is null ? mal_nil : *valp;
247 }
248
249 void remove(in MalType key)
250 {
251 data.remove(make_hash_key(key));
252 }
253
254 void put(in MalType key, MalType val)
255 {
256 data[make_hash_key(key)] = val;
257 }
258
259 void put_kv_list(MalType[] lst)
260 {
261 foreach (kv; chunks(lst, 2))
262 {
263 if (kv.length < 2) throw new Exception("requires even number of elements");
264 put(kv[0], kv[1]);
265 }
266 }
267
268 private string make_hash_key(in MalType key)
269 {
270 return verify_cast!MalString(key).val;
271 }
272
273 override string print(bool readable) const
274 {
275 string[] parts;
276 foreach (k, v; data)
277 {
278 parts ~= (new MalString(k)).print(readable);
279 parts ~= v.print(readable);
280 }
281 return "{" ~ parts.join(" ") ~ "}";
282 }
283
284 override bool opEquals(Object o)
285 {
286 auto ohm = cast(MalHashmap)(o);
287 return (ohm !is null && data == ohm.data);
288 }
289
290 override MalType meta() { return meta_val; }
291 override MalType with_meta(MalType new_meta)
292 {
293 return new MalHashmap(this, new_meta);
294 }
295 }
296
297 alias BuiltinStaticFuncType = MalType function(MalType[] a ...);
298 alias BuiltinFuncType = MalType delegate(MalType[] a ...);
299
300 class MalBuiltinFunc : MalType, MalMeta
301 {
302 const BuiltinFuncType fn;
303 const string name;
304 MalType meta_val;
305
306 this(in BuiltinFuncType fn_v, in string name_v)
307 {
308 fn = fn_v;
309 name = name_v;
310 meta_val = mal_nil;
311 }
312
313 this(in BuiltinStaticFuncType static_fn_v, in string name_v)
314 {
315 fn = toDelegate(static_fn_v);
316 name = name_v;
317 meta_val = mal_nil;
318 }
319
320 this(MalBuiltinFunc that, MalType new_meta)
321 {
322 fn = that.fn;
323 name = that.name;
324 meta_val = new_meta;
325 }
326
327 override string print(bool readable) const
328 {
329 return "<BuiltinFunc:" ~ name ~ ">";
330 }
331
332 override MalType meta() { return meta_val; }
333
334 override MalType with_meta(MalType new_meta)
335 {
336 return new MalBuiltinFunc(this, new_meta);
337 }
338 }
339
340 class MalFunc : MalType, MalMeta
341 {
342 MalType[] arg_names;
343 MalType func_body;
344 Env def_env;
345 bool is_macro;
346 MalType meta_val;
347
348 this(MalType[] arg_names_v, MalType func_body_v, Env def_env_v)
349 {
350 arg_names = arg_names_v;
351 func_body = func_body_v;
352 def_env = def_env_v;
353 is_macro = false;
354 meta_val = mal_nil;
355 }
356
357 this(MalFunc that, MalType new_meta)
358 {
359 arg_names = that.arg_names;
360 func_body = that.func_body;
361 def_env = that.def_env;
362 is_macro = that.is_macro;
363 meta_val = new_meta;
364 }
365
366 override string print(bool readable) const
367 {
368 return "<Function:args=" ~ array(arg_names.map!(e => e.print(true))).join(",") ~ ">";
369 }
370
371 override MalType meta() { return meta_val; }
372
373 override MalType with_meta(MalType new_meta)
374 {
375 return new MalFunc(this, new_meta);
376 }
377 }
378
379 class MalAtom : MalType, MalMeta
380 {
381 MalType val;
382 MalType meta_val;
383
384 this(MalType v)
385 {
386 val = v;
387 meta_val = mal_nil;
388 }
389
390 this(MalAtom that, MalType new_meta)
391 {
392 val = that.val;
393 meta_val = new_meta;
394 }
395
396 override string print(bool readable) const
397 {
398 return "(atom " ~ val.print(readable) ~ ")";
399 }
400
401 override bool opEquals(Object other)
402 {
403 auto o = cast(MalAtom) other;
404 return (o !is null && val == o.val);
405 }
406
407 override MalType meta() { return meta_val; }
408
409 override MalType with_meta(MalType new_meta)
410 {
411 return new MalAtom(this, new_meta);
412 }
413 }
414
415 class MalException : Exception
416 {
417 MalType data;
418
419 this(MalType val)
420 {
421 super("MalException");
422 data = val;
423 }
424 }
425
426 T verify_cast(T)(in MalType v)
427 {
428 if (T res = cast(T) v) return res;
429 throw new Exception("Expected " ~ typeid(T).name);
430 }
431
432 MalType mal_type_q(T)(in MalType[] a)
433 {
434 verify_args_count(a, 1);
435 T res = cast(T) a[0];
436 return bool_to_mal(res !is null);
437 }
438
439 inout(MalType[]) verify_args_count(inout MalType[] args, in int expected_length)
440 {
441 if (args.length != expected_length)
442 {
443 throw new Exception("Expected " ~ to!string(expected_length) ~ " arguments");
444 }
445 return args;
446 }
447
448 void verify_min_args_count(in MalType[] args, in int min_expected_length)
449 {
450 if (args.length < min_expected_length)
451 {
452 throw new Exception("Expected at least " ~ to!string(min_expected_length) ~ " arguments");
453 }
454 }