Change quasiquote algorithm
[jackhill/mal.git] / impls / zig / core.zig
CommitLineData
14b163d2
JT
1const std = @import("std");
2const warn = @import("std").debug.warn;
3
14b163d2
JT
4const AllocatorType = @import("std").mem.Allocator;
5var Allocator: *AllocatorType = undefined;
6
7pub fn set_allocator(alloc: *AllocatorType) void {
8 Allocator = alloc;
9}
10
11const Env = @import("env.zig").Env;
12const MalData = @import("types.zig").MalData;
13const MalType = @import("types.zig").MalType;
14const MalTypeValue = @import("types.zig").MalTypeValue;
15const printer = @import("printer.zig");
16const reader = @import("reader.zig");
17const getline_prompt = @import("readline.zig").getline_prompt;
18const string_eql = @import("utils.zig").string_eql;
19const string_copy = @import("utils.zig").string_copy;
20
21const MalError = @import("error.zig").MalError;
22
23const hmap = @import("hmap.zig");
24
25const MalLinkedList = @import("linked_list.zig").MalLinkedList;
26const MalHashMap = @import("hmap.zig").MalHashMap;
27const linked_list = @import("linked_list.zig");
28const apply_function = @import("types.zig").apply_function;
29
30const safeAdd = @import("std").math.add;
31const safeSub = @import("std").math.sub;
32const safeMul = @import("std").math.mul;
33const safeDivFloor = @import("std").math.divFloor;
34
35fn int_plus(a1: *MalType, a2: *MalType) MalError!*MalType {
36 const x = try a1.as_int();
37 const y = try a2.as_int();
38 const res = safeAdd(i64, x, y) catch return MalError.Overflow;
39 return MalType.new_int(Allocator, res);
40}
41
42fn int_minus(a1: *MalType, a2: *MalType) MalError!*MalType {
43 const x = try a1.as_int();
44 const y = try a2.as_int();
45 const res = safeSub(i64, x, y) catch return MalError.Overflow;
46 return MalType.new_int(Allocator, res);
47}
48
49fn int_mult(a1: *MalType, a2: *MalType) MalError!*MalType {
50 const x = try a1.as_int();
51 const y = try a2.as_int();
52 const res = safeMul(i64, x, y) catch return MalError.Overflow;
53 return MalType.new_int(Allocator, res);
54}
55
56fn int_div(a1: *MalType, a2: *MalType) MalError!*MalType {
57 const x = try a1.as_int();
58 const y = try a2.as_int();
59 const res = safeDivFloor(i64, x, y) catch |err| switch(err) {
60 error.DivisionByZero => return MalError.DivisionByZero,
61 else => return MalError.Overflow,
62 };
63 return MalType.new_int(Allocator, res);
64}
65
66fn int_lt(a1: *MalType, a2: *MalType) MalError!*MalType {
67 return MalType.new_bool(Allocator, (try a1.as_int()) < (try a2.as_int()));
68}
69
70fn int_leq(a1: *MalType, a2: *MalType) MalError!*MalType {
71 return MalType.new_bool(Allocator, (try a1.as_int()) <= (try a2.as_int()));
72}
73
74fn int_gt(a1: *MalType, a2: *MalType) MalError!*MalType {
75 return MalType.new_bool(Allocator, (try a1.as_int()) > (try a2.as_int()));
76}
77
78fn int_geq(a1: *MalType, a2: *MalType) MalError!*MalType {
79 return MalType.new_bool(Allocator, (try a1.as_int()) >= (try a2.as_int()));
80}
81
82fn _linked_list_equality(l1: MalLinkedList, l2: MalLinkedList) MalError!bool {
83 if(l1.count() != l2.count()) {
84 return false;
85 }
86 var it1 = l1.iterator();
87 var it2 = l2.iterator();
88 while(true) {
89 const m1 = it1.next() orelse return (it2.next() == null);
90 const m2 = it2.next() orelse return false;
91 const el_cmp = try equality(m1, m2);
92 if(MalTypeValue(el_cmp.data) == MalTypeValue.False) {
93 el_cmp.delete(Allocator);
94 return false;
95 }
96 el_cmp.delete(Allocator);
97 }
98 return true;
99}
100
101fn _hashmap_equality(h1: MalHashMap, h2: MalHashMap) MalError!bool {
102 if(h1.count() != h2.count()) {
103 return false;
104 }
105
106 var iterator = h1.iterator();
107 var optional_pair = iterator.next();
108 while(optional_pair) |pair| {
109 const optional_val = h2.getValue(pair.key);
110 if(optional_val) |val| {
111 const el_cmp = try equality(pair.value, val);
112 if(MalTypeValue(el_cmp.data) == MalTypeValue.False) {
113 el_cmp.delete(Allocator);
114 return false;
115 }
116 el_cmp.delete(Allocator);
117 }
118 else {
119 return false;
120 }
121 optional_pair = iterator.next();
122 }
123 return true;
124}
125
126// TODO: make _equality -> bool
127fn equality(a1: *MalType, a2: *MalType) MalError!*MalType {
128 const a1_is_sequential = (MalTypeValue(a1.data) == MalTypeValue.List) or
129 (MalTypeValue(a1.data) == MalTypeValue.Vector);
130 const a2_is_sequential = (MalTypeValue(a2.data) == MalTypeValue.List) or
131 (MalTypeValue(a2.data) == MalTypeValue.Vector);
132
133 if(a1_is_sequential and a2_is_sequential) {
134 const l1 = (try a1.sequence_linked_list()).*;
135 const l2 = (try a2.sequence_linked_list()).*;
136 return MalType.new_bool(Allocator, try _linked_list_equality(l1, l2));
137 }
138
139 if(MalTypeValue(a1.data) != MalTypeValue(a2.data)) {
140 return MalType.new_bool(Allocator, false);
141 }
142
143 switch(a1.data) {
144 .True, .False, .Nil => {
145 return MalType.new_bool(Allocator, true);
146 },
147 .Int => |v1| {
148 return MalType.new_bool(Allocator, v1 == a2.data.Int);
149 },
150 .List => |l1| {
151 const l2 = a2.data.List;
152 return MalType.new_bool(Allocator, try _linked_list_equality(l1, l2));
153 },
154 .Vector => |v1| {
155 const v2 = a2.data.Vector;
156 return MalType.new_bool(Allocator, try _linked_list_equality(v1, v2));
157 },
158 .String => |s1| {
159 const s2 = a2.data.String;
160 return MalType.new_bool(Allocator, string_eql(s1, s2));
161 },
162 .Generic => |v1| {
163 const v2 = a2.data.Generic;
164 return MalType.new_bool(Allocator, string_eql(v1, v2));
165 },
166 .Keyword => |k1| {
167 const k2 = a2.data.Keyword;
168 return MalType.new_bool(Allocator, string_eql(k1, k2));
169 },
170 .HashMap => |h1| {
171 const h2 = a2.data.HashMap;
172 return MalType.new_bool(Allocator, try _hashmap_equality(h1,h2));
173 },
174 // TODO: implement more types
175 else => return MalType.new_bool(Allocator, false),
176 }
177}
178
179fn list(args: MalLinkedList) MalError!*MalType {
180 var new_mal = try MalType.new_list_empty(Allocator);
181 new_mal.data = MalData{.List = try linked_list.deepcopy(Allocator, args)};
182 return new_mal;
183}
184
185fn vector(args: MalLinkedList) MalError!*MalType {
186 var new_mal = try MalType.new_list_empty(Allocator);
187 new_mal.data = MalData{.Vector = try linked_list.deepcopy(Allocator, args)};
188 return new_mal;
189}
190
191fn map(args: MalLinkedList) MalError!*MalType {
192 if(args.count() < 2) return MalError.ArgError;
193 const func_mal = args.at(0);
194 var args_mal = args.at(1);
195 var new_ll = MalLinkedList.init(Allocator);
196 var to_map_ll = try args_mal.sequence_linked_list();
197
198 var iterator = to_map_ll.iterator();
199 while(iterator.next()) |mal| {
200 var args_ll = MalLinkedList.init(Allocator);
201 // TODO: can be more efficient than this
202 try linked_list.append_mal(Allocator, &args_ll, try func_mal.copy(Allocator));
203 try linked_list.append_mal(Allocator, &args_ll, try mal.copy(Allocator));
204 const new_mal = try apply_function(Allocator, args_ll);
205 linked_list.destroy(Allocator, &args_ll, false);
206 try linked_list.append_mal(Allocator, &new_ll, new_mal);
207 }
208 const new_list = try MalType.new_nil(Allocator);
209 new_list.data = MalData{.List = new_ll};
210 return new_list;
211}
212
213fn is_list(a1: *MalType) MalError!*MalType {
214 return MalType.new_bool(Allocator, MalTypeValue(a1.data) == MalTypeValue.List);
215}
216
217fn is_vector(a1: *MalType) MalError!*MalType {
218 return MalType.new_bool(Allocator, MalTypeValue(a1.data) == MalTypeValue.Vector);
219}
220
221pub fn is_string(a1: *MalType) MalError!*MalType {
222 return MalType.new_bool(Allocator, MalTypeValue(a1.data) == MalTypeValue.String);
223}
224
225pub fn is_number(a1: *MalType) MalError!*MalType {
226 return MalType.new_bool(Allocator, MalTypeValue(a1.data) == MalTypeValue.Int);
227}
228
229pub fn is_fn(a1: *MalType) MalError!*MalType {
230 const is_function = switch(a1.data) {
231 .Fn0 => true,
232 .Fn1 => true,
233 .Fn2 => true,
234 .Fn3 => true,
235 .Fn4 => true,
236 .FVar => true,
237 .Func => |func_data| !func_data.is_macro,
238 else => false,
239 };
240 return MalType.new_bool(Allocator, is_function);
241}
242
243pub fn is_macro(a1: *MalType) MalError!*MalType {
244 const is_func_and_macro = switch(a1.data) {
245 .Func => |data| data.is_macro,
246 else => false,
247 };
248 return MalType.new_bool(Allocator, is_func_and_macro);
249}
250
251fn empty(a1: *MalType) MalError!*MalType {
252 return switch(a1.data) {
253 .List => |l| MalType.new_bool(Allocator, l.len == 0),
254 .Vector => |v| MalType.new_bool(Allocator, v.len == 0),
255 else => MalType.new_bool(Allocator, false),
256 };
257}
258
259fn prn(args: MalLinkedList) MalError!*MalType {
260 const s = try printer.print_mal_to_string(args, true, true);
261 const stdout_file = std.io.getStdOut() catch return MalError.SystemError;
262 stdout_file.write(s) catch return MalError.SystemError;
263 stdout_file.write("\n") catch return MalError.SystemError;
264 Allocator.free(s);
265 const mal = try MalType.new_nil(Allocator);
266 return mal;
267}
268
269fn println(args: MalLinkedList) MalError!*MalType {
270 const s = try printer.print_mal_to_string(args, false, true);
271 const stdout_file = std.io.getStdOut() catch return MalError.SystemError;
272 stdout_file.write(s) catch return MalError.SystemError;
273 stdout_file.write("\n") catch return MalError.SystemError;
274 Allocator.free(s);
275 const mal = try MalType.new_nil(Allocator);
276 return mal;
277}
278
279fn str(args: MalLinkedList) MalError!*MalType {
280 if(args.count() == 0) {
281 const s: []u8 = "";
282 return MalType.new_string(Allocator, s);
283 }
284 const s = try printer.print_mal_to_string(args, false, false);
285 return MalType.new_string(Allocator, s);
286}
287
288fn pr_str(args: MalLinkedList) MalError!*MalType {
289 if(args.count() == 0) {
290 const s: []u8 = "";
291 return MalType.new_string(Allocator, s);
292 }
293 const s = try printer.print_mal_to_string(args, true, true);
294 return MalType.new_string(Allocator, s);
295}
296
297fn slurp(a1: *MalType) MalError!*MalType {
298 switch(a1.data) {
299 .String => |path| {
300 const file_contents = std.io.readFileAlloc(Allocator, path)
301 catch |err| return MalError.SystemError; // TODO: change this error
302 defer Allocator.free(file_contents);
303 return MalType.new_string(Allocator, file_contents);
304 },
305 else => {
306 return MalError.TypeError;
307 },
308 }
309 return unreachable;
310}
311
312fn atom(a1: *MalType) MalError!*MalType {
313 return MalType.new_atom(Allocator, a1);
314}
315
316fn is_atom(a1: *MalType) MalError!*MalType {
317 return MalType.new_bool(Allocator, MalTypeValue(a1.data) == MalTypeValue.Atom);
318}
319
320fn deref(a1: *MalType) MalError!*MalType {
321 return switch(a1.data) {
322 .Atom => |atom_val| atom_val.*.copy(Allocator),
323 else => MalError.TypeError,
324 };
325}
326
327fn atom_reset(a1: *MalType, a2: *MalType) MalError!*MalType {
328 switch(a1.data) {
329 .Atom => |*atom_val| {
330 var new_target = try a2.copy(Allocator);
331 atom_val.*.*.delete(Allocator);
332 atom_val.*.* = new_target;
333 return new_target.copy(Allocator);
334 },
335 else => return MalError.TypeError,
336 }
337}
338
339fn atom_swap(args: MalLinkedList) MalError!*MalType {
340 const args_arr = args.toSlice();
341 const n = args.len;
342 if(n < 2) return MalError.ArgError;
343 var new_args = MalLinkedList.init(Allocator);
344 defer linked_list.destroy(Allocator, &new_args, false);
345 try linked_list.append_mal(Allocator, &new_args, try args_arr[1].copy(Allocator));
346 try linked_list.append_mal(Allocator, &new_args, try deref(args_arr[0]));
347 var i: usize = 2;
348 while(i < n) {
349 try linked_list.append_mal(Allocator, &new_args, try args_arr[i].copy(Allocator));
350 i += 1;
351 }
352 const return_mal = try apply_function(Allocator, new_args);
353 const new_mal = atom_reset(args_arr[0], return_mal);
354 return_mal.delete(Allocator);
355 return new_mal;
356}
357
fbfe6784
NB
358pub fn vec(a1: *const MalType) MalError!*MalType {
359 const ll = switch(a1.data) {
360 .List => |l| l,
361 .Vector => |v| v,
362 else => return MalError.TypeError,
363 };
364 const copy = try linked_list.deepcopy(Allocator, ll);
365 return MalType.new_vector(Allocator, copy);
366}
367
14b163d2
JT
368pub fn cons(a1: *const MalType, a2: *const MalType) MalError!*MalType {
369 // TODO: do we need this for vectors?
370 const old_ll = try a2.const_sequence_linked_list();
371 var new_ll = try linked_list.deepcopy(Allocator, old_ll);
372 var new_list = try MalType.new_nil(Allocator);
373 new_list.data = MalData{.List = new_ll};
374 errdefer new_list.delete(Allocator);
375 var new_mal = try a1.copy(Allocator);
376 errdefer new_mal.delete(Allocator);
377 try linked_list.prepend_mal(Allocator, &new_list.data.List, new_mal);
378 return new_list;
379}
380
381pub fn concat(args: MalLinkedList) MalError!*MalType {
382 // First we make a new array with shallow copies
383 var new_ll = MalLinkedList.init(Allocator);
384 errdefer linked_list.destroy(Allocator, &new_ll, false);
385 var iterator = args.iterator();
386 while(iterator.next()) |mal| {
387 const mal_seq = try mal.sequence_linked_list();
388 new_ll.appendSlice(mal_seq.toSlice()) catch return MalError.SystemError;
389 }
390
391 // Now we turn the shallow copies into deep copies
392 const new_arr = new_ll.toSlice();
393 var i: usize = 0;
394 while(i < new_arr.len) {
395 new_arr[i] = try new_arr[i].copy(Allocator);
396 i += 1;
397 }
398
399 // Wrap the list in a MalType, return
400 var new_mal = try MalType.new_nil(Allocator);
401 new_mal.data = MalData{.List = new_ll};
402 return new_mal;
403}
404
405pub fn rest(a1: *const MalType) MalError!*MalType {
406 var old_list = switch(a1.data) {
407 .List => |l| l,
408 .Vector => |v| v,
409 .Nil => return MalType.new_list_empty(Allocator),
410 else => return MalError.TypeError,
411 };
412 var new_list = try linked_list.deepcopy(Allocator, old_list);
413 errdefer linked_list.destroy(Allocator, &new_list, false);
414
415 if(new_list.count() > 0) {
416 const mal = try linked_list.pop_first(Allocator, &new_list);
417 mal.delete(Allocator);
418 }
419 var new_mal = try MalType.new_nil(Allocator);
420 new_mal.data = MalData{.List = new_list};
421 return new_mal;
422}
423
424pub fn _nth(mal_list: *const MalType, pos: i64) MalError!*MalType {
425 // TODO: vectors?
426 const l = try mal_list.const_sequence_linked_list();
427 if(pos < 0 or pos >= @intCast(i64,l.count())) {
428 return MalError.OutOfBounds;
429 }
430 return l.at(@intCast(usize,pos));
431}
432
433pub fn nth(a1: *const MalType, a2: *const MalType) MalError!*MalType {
434 return switch(a2.data) {
435 .Int => |pos| (try _nth(a1, pos)).copy(Allocator),
436 else => MalError.TypeError,
437 };
438}
439
440pub fn first(a1: *const MalType) MalError!*MalType {
441 var l = switch(a1.data) {
442 .List => |l| l,
443 .Vector => |v| v,
444 .Nil => return MalType.new_nil(Allocator),
445 else => return MalError.TypeError,
446 };
447 if(l.count() == 0) return MalType.new_nil(Allocator);
448 return l.at(0).copy(Allocator);
449}
450
451fn check_type(mal: *const MalType, value_type: MalTypeValue) MalError!*MalType {
452 // TODO: use this everywhere
453 // TODO: do this more generically
454 return MalType.new_bool(Allocator, MalTypeValue(mal.data) == value_type);
455}
456
457pub fn is_nil(a1: *const MalType) MalError!*MalType {
458 return check_type(a1, MalTypeValue.Nil);
459}
460
461pub fn is_true(a1: *const MalType) MalError!*MalType {
462 return check_type(a1, MalTypeValue.True);
463}
464
465pub fn is_false(a1: *const MalType) MalError!*MalType {
466 return check_type(a1, MalTypeValue.False);
467}
468
469pub fn is_symbol(a1: *const MalType) MalError!*MalType {
470 return check_type(a1, MalTypeValue.Generic);
471}
472
473pub fn is_keyword(a1: *const MalType) MalError!*MalType {
474 return check_type(a1, MalTypeValue.Keyword);
475}
476
477pub fn is_map(a1: *const MalType) MalError!*MalType {
478 return check_type(a1, MalTypeValue.HashMap);
479}
480
481pub fn is_sequential(a1: *const MalType) MalError!*MalType {
482 const res = (MalTypeValue(a1.data) == MalTypeValue.Vector) or
483 (MalTypeValue(a1.data) == MalTypeValue.List);
484 return MalType.new_bool(Allocator, res);
485}
486
487pub fn symbol(a1: *const MalType) MalError!*MalType {
488 const string = switch(a1.data) {
489 .String => |s| s,
490 else => return MalError.TypeError,
491 };
492 return MalType.new_generic(Allocator, string);
493}
494
495pub fn hash_map(args: MalLinkedList) MalError!*MalType {
496 const new_mal = try MalType.new_hashmap(Allocator);
497 const args_arr = args.toSlice();
498 const n = args_arr.len;
499 if((n%2) != 0) return MalError.ArgError;
500 var i: usize = 0;
501
502 while(2*i+1 < n) {
503 const this_key = switch(args_arr[2*i].data) {
504 .String => |s| s,
505 .Keyword => |kwd| kwd,
506 else => return MalError.ArgError,
507 };
508 const this_key_cpy = string_copy(Allocator, this_key) catch return MalError.SystemError;
509 const this_val_cpy = try args_arr[2*i+1].copy(Allocator);
510 try new_mal.hashmap_insert(this_key_cpy, this_val_cpy);
511 i += 1;
512 }
513 return new_mal;
514}
515
516pub fn hash_map_assoc(args: MalLinkedList) MalError!*MalType {
517 const args_arr = args.toSlice();
518 if(args_arr.len < 1) return MalError.ArgError;
519 const new_mal = try MalType.new_nil(Allocator);
520 errdefer new_mal.delete(Allocator);
521 const base_hmap = switch(args_arr[0].data) {
522 .HashMap => |hm| hm,
523 else => return MalError.TypeError,
524 };
525 const hmap_cpy = hmap.deepcopy(Allocator, base_hmap) catch return MalError.SystemError;
526 new_mal.data = MalData {.HashMap = hmap_cpy};
527
528 const assoc_arr = args_arr[1..args_arr.len];
529 if((assoc_arr.len % 2) != 0) return MalError.ArgError;
530 var i: usize = 0;
531 while(2*i+1 < assoc_arr.len) {
532 const this_key = switch(assoc_arr[2*i].data) {
533 .String => |s| s,
534 .Keyword => |kwd| kwd,
535 else => return MalError.ArgError,
536 };
537 const this_key_cpy = string_copy(Allocator, this_key) catch return MalError.SystemError;
538 const this_val_cpy = try assoc_arr[2*i+1].copy(Allocator);
539 try new_mal.hashmap_insert(this_key_cpy, this_val_cpy);
540 i += 1;
541 }
542 return new_mal;
543}
544
545pub fn hash_map_dissoc(args: MalLinkedList) MalError!*MalType {
546 const args_arr = args.toSlice();
547 if(args_arr.len < 1) return MalError.ArgError;
548 const new_mal = try MalType.new_nil(Allocator);
549 errdefer new_mal.delete(Allocator);
550 const base_hmap = switch(args_arr[0].data) {
551 .HashMap => |hm| hm,
552 else => return MalError.TypeError,
553 };
554 const hmap_cpy = hmap.deepcopy(Allocator, base_hmap) catch return MalError.SystemError;
555 new_mal.data = MalData {.HashMap = hmap_cpy};
556
557 var i: usize = 1;
558 while(i < args_arr.len) {
559 const this_key = switch(args_arr[i].data) {
560 .String => |s| s,
561 .Keyword => |kwd| kwd,
562 else => return MalError.ArgError,
563 };
564 try new_mal.hashmap_remove(this_key);
565 i += 1;
566 }
567 return new_mal;
568}
569
570pub fn hash_map_get(a1: *MalType, a2: *MalType) MalError!*MalType {
571 const key = switch(a2.data) {
572 .String => |s| s,
573 .Keyword => |kwd| kwd,
574 else => return MalError.TypeError,
575 };
576 const optional_val = try a1.hashmap_get(key);
577 if(optional_val) |val| {
578 return val.copy(Allocator);
579 }
580 else return MalType.new_nil(Allocator);
581}
582
583pub fn hash_map_contains(a1: *MalType, a2: *MalType) MalError!*MalType {
584 const key = switch(a2.data) {
585 .String => |s| s,
586 .Keyword => |kwd| kwd,
587 else => return MalError.TypeError,
588 };
589 const contains_bool = try a1.hashmap_contains(key);
590 return MalType.new_bool(Allocator, contains_bool);
591}
592
593pub fn hash_map_keys(a1: *MalType) MalError!*MalType {
594 const hm = switch(a1.data) {
595 .HashMap => |h| h,
596 else => return MalError.TypeError,
597 };
598 var new_ll = MalLinkedList.init(Allocator);
599 errdefer linked_list.destroy(Allocator, &new_ll, false);
600 var iterator = hm.iterator();
601 var optional_pair = iterator.next();
602
603 while(true) {
604 const pair = optional_pair orelse break;
605 const key = string_copy(Allocator, pair.key) catch return MalError.SystemError;
606
607 var key_mal: *MalType = undefined;
608 if(key.len > 1 and key[0] == 255) {
609 key_mal = try MalType.new_keyword(Allocator, key[1..key.len]);
610 } else {
611 key_mal = try MalType.new_string(Allocator, key);
612 }
613 try linked_list.append_mal(Allocator, &new_ll, key_mal);
614 optional_pair = iterator.next();
615 }
616 var new_mal = try MalType.new_nil(Allocator);
617 new_mal.data = MalData{.List = new_ll};
618 return new_mal;
619}
620
621pub fn hash_map_vals(a1: *MalType) MalError!*MalType {
622 const hm = switch(a1.data) {
623 .HashMap => |h| h,
624 else => return MalError.TypeError,
625 };
626 var new_ll = MalLinkedList.init(Allocator);
627 errdefer linked_list.destroy(Allocator, &new_ll, false);
628 var iterator = hm.iterator();
629 var optional_pair = iterator.next();
630
631 while(true) {
632 const pair = optional_pair orelse break;
633 const val = try pair.value.copy(Allocator);
634 try linked_list.append_mal(Allocator, &new_ll, val);
635 optional_pair = iterator.next();
636 }
637 var new_mal = try MalType.new_nil(Allocator);
638 new_mal.data = MalData{.List = new_ll};
639 return new_mal;
640}
641
642pub fn sequence_length(a1: *MalType) MalError!*MalType {
643 const len = switch(a1.data) {
644 .List => |l| l.count(),
645 .Vector => |v| v.count(),
646 .String => |s| s.len,
647 .Nil => 0,
648 else => return MalError.TypeError,
649 };
650 return MalType.new_int(Allocator, @intCast(i64,len));
651}
652
653pub fn keyword(a1: *MalType) MalError!*MalType {
654 const kwd = switch(a1.data) {
655 .String => |s| s,
656 .Keyword => |k| return a1.copy(Allocator),
657 else => return MalError.TypeError,
658 };
659 return MalType.new_keyword(Allocator, kwd);
660}
661
662pub fn readline(a1: *MalType) MalError!*MalType {
663 const prompt = try a1.as_string();
664 const optional_read_line = getline_prompt(Allocator, prompt)
665 catch return MalError.SystemError;
666 if(optional_read_line) |read_line| {
667 return MalType.new_string(Allocator, read_line);
668 }
669 const mal = try MalType.new_nil(Allocator);
670 return MalType.new_nil(Allocator);
671}
672
673pub fn time_ms() MalError!*MalType {
674 const itime: i64 = @intCast(i64, std.time.milliTimestamp());
675 return MalType.new_int(Allocator, itime);
676}
677
678pub fn meta(a1: *MalType) MalError!*MalType {
679 if(a1.meta) |mal_meta| {
680 return mal_meta.copy(Allocator);
681 }
682 return MalType.new_nil(Allocator);
683}
684
685pub fn with_meta(a1: *MalType, a2: *MalType) MalError!*MalType {
686 var new_mal = try a1.copy(Allocator);
687 if(new_mal.meta) |mal_meta| {
688 mal_meta.delete(Allocator);
689 }
690 new_mal.meta = try a2.copy(Allocator);
691 return new_mal;
692}
693
694pub fn seq(a1: *MalType) MalError!*MalType {
695 switch(a1.data) {
696 .List => |l| {
697 if(l.count() == 0) return MalType.new_nil(Allocator);
698 return a1.copy(Allocator);
699 },
700 .Vector => |v| {
701 if(v.count() == 0) return MalType.new_nil(Allocator);
702 const mal_copy = try a1.copy(Allocator);
703 const ll = mal_copy.data.Vector;
704 mal_copy.data = MalData{.List = ll};
705 return mal_copy;
706 },
707 .String => |s| {
708 if(s.len == 0) return MalType.new_nil(Allocator);
709 const new_list = try MalType.new_list_empty(Allocator);
710 for(s) |letter| {
711 const new_char = try MalType.new_string(Allocator, [_]u8 {letter});
712 try new_list.sequence_append(Allocator, new_char);
713 }
714 return new_list;
715 },
716 .Nil => {
717 return MalType.new_nil(Allocator);
718 },
719 else => {
720 return MalError.TypeError;
721 }
722 }
723 return MalType.new_nil(Allocator);
724}
725
726pub fn conj(args: MalLinkedList) MalError!*MalType {
727 var iterator = args.iterator();
728 const container = iterator.next() orelse return MalError.ArgError;
729 const append = switch(container.data) {
730 .List => false,
731 .Vector => true,
732 else => return MalError.ArgError,
733 };
734
735 var return_mal = try container.copy(Allocator);
736 while(iterator.next()) |mal| {
737 const mal_copy = try mal.copy(Allocator);
738 if(append) {
739 try return_mal.sequence_append(Allocator, mal_copy);
740 } else {
741 try return_mal.sequence_prepend(Allocator, mal_copy);
742 }
743 }
744 return return_mal;
745}
746
747fn read_string(a1: *MalType) MalError!*MalType {
748 const str_to_eval = try a1.as_string();
749 var read = try reader.read_str(str_to_eval);
817e1f0d 750 return (try reader.read_form(&read)) orelse return MalType.new_nil(Allocator);
14b163d2
JT
751}
752
753pub fn do_apply(args: MalLinkedList) MalError!*MalType {
754 // TODO: not always safe to delete new_ll here
755 if(args.count() == 0) return MalError.ArgError;
756 var args_copy = args;
757 const list_node = args_copy.pop();
758 const list_ll = try list_node.sequence_linked_list();
759 var new_ll = try linked_list.deepcopy(Allocator, list_ll.*);
760 defer linked_list.destroy(Allocator, &new_ll, false);
761 var optional_node = args_copy.popOrNull();
762 while(optional_node) |node| {
763 try linked_list.prepend_mal(Allocator, &new_ll, try node.copy(Allocator));
764 optional_node = args_copy.popOrNull();
765 }
766 var return_mal = apply_function(Allocator, new_ll);
767 return return_mal;
768}
769
770pub const CorePairType = enum {
771 Fn0,
772 Fn1,
773 Fn2,
817e1f0d
JT
774 Fn3,
775 Fn4,
14b163d2
JT
776 FVar,
777};
778
779pub const CorePairData = union(CorePairType) {
780 Fn0: *const fn() MalError!*MalType,
781 Fn1: *const fn(a1: *MalType) MalError!*MalType,
782 Fn2: *const fn(a1: *MalType, a2: *MalType) MalError!*MalType,
817e1f0d
JT
783 Fn3: *const fn(a1: *MalType, a2: *MalType, a3: *MalType) MalError!*MalType,
784 Fn4: *const fn(a1: *MalType, a2: *MalType, a3: *MalType, a4: *MalType) MalError!*MalType,
14b163d2
JT
785 FVar: *const fn(args: MalLinkedList) MalError!*MalType,
786};
787
788pub const CorePair = struct {
789 name: []const u8,
790 func: CorePairData,
791};
792
793pub const core_namespace = [_] CorePair {
794 CorePair { .name = "+", .func = CorePairData {.Fn2 = &int_plus} },
795 CorePair { .name = "-", .func = CorePairData {.Fn2 = &int_minus} },
796 CorePair { .name = "*", .func = CorePairData {.Fn2 = &int_mult} },
797 CorePair { .name = "/", .func = CorePairData {.Fn2 = &int_div} },
798 CorePair { .name = "<", .func = CorePairData {.Fn2 = &int_lt} },
799 CorePair { .name = "<=", .func = CorePairData {.Fn2 = &int_leq} },
800 CorePair { .name = ">", .func = CorePairData {.Fn2 = &int_gt} },
801 CorePair { .name = ">=", .func = CorePairData {.Fn2 = &int_geq} },
802 CorePair { .name = "=", .func = CorePairData {.Fn2 = &equality} },
803 CorePair { .name = "list?", .func = CorePairData {.Fn1 = &is_list} },
804 CorePair { .name = "vector?", .func = CorePairData {.Fn1 = &is_vector} },
805 CorePair { .name = "count", .func = CorePairData {.Fn1 = &sequence_length} },
806 CorePair { .name = "list", .func = CorePairData {.FVar = &list} },
807 CorePair { .name = "vector", .func = CorePairData {.FVar = &vector} },
808 CorePair { .name = "map", .func = CorePairData {.FVar = &map} },
809 CorePair { .name = "empty?", .func = CorePairData {.Fn1 = &empty} },
810 CorePair { .name = "prn", .func = CorePairData {.FVar = &prn} },
811 CorePair { .name = "println", .func = CorePairData {.FVar = &println} },
812 CorePair { .name = "pr-str", .func = CorePairData {.FVar = &pr_str} },
813 CorePair { .name = "str", .func = CorePairData {.FVar = &str} },
814 CorePair { .name = "slurp", .func = CorePairData {.Fn1 = &slurp} },
815 CorePair { .name = "atom", .func = CorePairData {.Fn1 = &atom} },
816 CorePair { .name = "atom?", .func = CorePairData {.Fn1 = &is_atom} },
817 CorePair { .name = "deref", .func = CorePairData {.Fn1 = &deref} },
818 CorePair { .name = "reset!", .func = CorePairData {.Fn2 = &atom_reset} },
819 CorePair { .name = "swap!", .func = CorePairData {.FVar = &atom_swap} },
fbfe6784 820 CorePair { .name = "vec", .func = CorePairData {.Fn1 = &vec} },
14b163d2
JT
821 CorePair { .name = "cons", .func = CorePairData {.Fn2 = &cons} },
822 CorePair { .name = "concat", .func = CorePairData {.FVar = &concat} },
823 CorePair { .name = "rest", .func = CorePairData {.Fn1 = &rest } },
824 CorePair { .name = "nth", .func = CorePairData {.Fn2 = &nth } },
825 CorePair { .name = "first", .func = CorePairData {.Fn1 = &first } },
826 CorePair { .name = "nil?", .func = CorePairData {.Fn1 = &is_nil } },
827 CorePair { .name = "true?", .func = CorePairData {.Fn1 = &is_true } },
828 CorePair { .name = "false?", .func = CorePairData {.Fn1 = &is_false } },
829 CorePair { .name = "symbol", .func = CorePairData {.Fn1 = &symbol } },
830 CorePair { .name = "symbol?", .func = CorePairData {.Fn1 = &is_symbol } },
831 CorePair { .name = "keyword?", .func = CorePairData {.Fn1 = &is_keyword } },
832 CorePair { .name = "map?", .func = CorePairData {.Fn1 = &is_map } },
833 CorePair { .name = "sequential?", .func = CorePairData {.Fn1 = &is_sequential } },
834 CorePair { .name = "apply", .func = CorePairData {.FVar = &do_apply } },
835 CorePair { .name = "hash-map", .func = CorePairData {.FVar = &hash_map } },
836 CorePair { .name = "assoc", .func = CorePairData {.FVar = &hash_map_assoc } },
837 CorePair { .name = "dissoc", .func = CorePairData {.FVar = &hash_map_dissoc } },
838 CorePair { .name = "get", .func = CorePairData {.Fn2 = &hash_map_get } },
839 CorePair { .name = "contains?", .func = CorePairData {.Fn2 = &hash_map_contains } },
840 CorePair { .name = "keys", .func = CorePairData {.Fn1 = &hash_map_keys } },
841 CorePair { .name = "vals", .func = CorePairData {.Fn1 = &hash_map_vals } },
842 CorePair { .name = "keyword", .func = CorePairData {.Fn1 = &keyword } },
843 CorePair { .name = "read-string", .func = CorePairData {.Fn1 = &read_string } },
844 CorePair { .name = "readline", .func = CorePairData {.Fn1 = &readline } },
845 CorePair { .name = "time-ms", .func = CorePairData {.Fn0 = &time_ms } },
846 CorePair { .name = "meta", .func = CorePairData {.Fn1 = &meta } },
847 CorePair { .name = "with-meta", .func = CorePairData {.Fn2 = &with_meta } },
848 CorePair { .name = "fn?", .func = CorePairData {.Fn1 = &is_fn } },
849 CorePair { .name = "string?", .func = CorePairData {.Fn1 = &is_string } },
850 CorePair { .name = "number?", .func = CorePairData {.Fn1 = &is_number } },
851 CorePair { .name = "macro?", .func = CorePairData {.Fn1 = &is_macro } },
852 CorePair { .name = "seq", .func = CorePairData {.Fn1 = &seq } },
853 CorePair { .name = "conj", .func = CorePairData {.FVar = &conj } },
854};