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