Commit | Line | Data |
---|---|---|
14b163d2 JT |
1 | const std = @import("std"); |
2 | const 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 | ||
7 | const AllocatorType = @import("std").mem.Allocator; | |
8 | var Allocator: *AllocatorType = undefined; | |
9 | ||
10 | pub fn set_allocator(alloc: *AllocatorType) void { | |
11 | Allocator = alloc; | |
12 | } | |
13 | ||
14 | const Env = @import("env.zig").Env; | |
15 | const MalData = @import("types.zig").MalData; | |
16 | const MalType = @import("types.zig").MalType; | |
17 | const MalTypeValue = @import("types.zig").MalTypeValue; | |
18 | const printer = @import("printer.zig"); | |
19 | const reader = @import("reader.zig"); | |
20 | const getline_prompt = @import("readline.zig").getline_prompt; | |
21 | const string_eql = @import("utils.zig").string_eql; | |
22 | const string_copy = @import("utils.zig").string_copy; | |
23 | ||
24 | const MalError = @import("error.zig").MalError; | |
25 | ||
26 | const hmap = @import("hmap.zig"); | |
27 | ||
28 | const MalLinkedList = @import("linked_list.zig").MalLinkedList; | |
29 | const MalHashMap = @import("hmap.zig").MalHashMap; | |
30 | const linked_list = @import("linked_list.zig"); | |
31 | const apply_function = @import("types.zig").apply_function; | |
32 | ||
33 | const safeAdd = @import("std").math.add; | |
34 | const safeSub = @import("std").math.sub; | |
35 | const safeMul = @import("std").math.mul; | |
36 | const safeDivFloor = @import("std").math.divFloor; | |
37 | ||
38 | fn 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 | ||
45 | fn 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 | ||
52 | fn 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 | ||
59 | fn 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 | ||
69 | fn int_lt(a1: *MalType, a2: *MalType) MalError!*MalType { | |
70 | return MalType.new_bool(Allocator, (try a1.as_int()) < (try a2.as_int())); | |
71 | } | |
72 | ||
73 | fn int_leq(a1: *MalType, a2: *MalType) MalError!*MalType { | |
74 | return MalType.new_bool(Allocator, (try a1.as_int()) <= (try a2.as_int())); | |
75 | } | |
76 | ||
77 | fn int_gt(a1: *MalType, a2: *MalType) MalError!*MalType { | |
78 | return MalType.new_bool(Allocator, (try a1.as_int()) > (try a2.as_int())); | |
79 | } | |
80 | ||
81 | fn int_geq(a1: *MalType, a2: *MalType) MalError!*MalType { | |
82 | return MalType.new_bool(Allocator, (try a1.as_int()) >= (try a2.as_int())); | |
83 | } | |
84 | ||
85 | fn _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 | ||
104 | fn _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 | |
130 | fn 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 | ||
182 | fn 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 | ||
188 | fn 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 | ||
194 | fn 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 | ||
216 | fn is_list(a1: *MalType) MalError!*MalType { | |
217 | return MalType.new_bool(Allocator, MalTypeValue(a1.data) == MalTypeValue.List); | |
218 | } | |
219 | ||
220 | fn is_vector(a1: *MalType) MalError!*MalType { | |
221 | return MalType.new_bool(Allocator, MalTypeValue(a1.data) == MalTypeValue.Vector); | |
222 | } | |
223 | ||
224 | pub fn is_string(a1: *MalType) MalError!*MalType { | |
225 | return MalType.new_bool(Allocator, MalTypeValue(a1.data) == MalTypeValue.String); | |
226 | } | |
227 | ||
228 | pub fn is_number(a1: *MalType) MalError!*MalType { | |
229 | return MalType.new_bool(Allocator, MalTypeValue(a1.data) == MalTypeValue.Int); | |
230 | } | |
231 | ||
232 | pub 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 | ||
246 | pub 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 | ||
254 | fn 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 | ||
262 | fn 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 | ||
272 | fn 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 | ||
282 | fn 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 | ||
291 | fn 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 | ||
300 | fn 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 | ||
315 | fn atom(a1: *MalType) MalError!*MalType { | |
316 | return MalType.new_atom(Allocator, a1); | |
317 | } | |
318 | ||
319 | fn is_atom(a1: *MalType) MalError!*MalType { | |
320 | return MalType.new_bool(Allocator, MalTypeValue(a1.data) == MalTypeValue.Atom); | |
321 | } | |
322 | ||
323 | fn 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 | ||
330 | fn 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 | ||
342 | fn 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 | ||
361 | pub 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 | ||
374 | pub 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 | ||
398 | pub 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 | ||
417 | pub 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 | ||
426 | pub 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 | ||
433 | pub 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 | ||
444 | fn 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 | ||
450 | pub fn is_nil(a1: *const MalType) MalError!*MalType { | |
451 | return check_type(a1, MalTypeValue.Nil); | |
452 | } | |
453 | ||
454 | pub fn is_true(a1: *const MalType) MalError!*MalType { | |
455 | return check_type(a1, MalTypeValue.True); | |
456 | } | |
457 | ||
458 | pub fn is_false(a1: *const MalType) MalError!*MalType { | |
459 | return check_type(a1, MalTypeValue.False); | |
460 | } | |
461 | ||
462 | pub fn is_symbol(a1: *const MalType) MalError!*MalType { | |
463 | return check_type(a1, MalTypeValue.Generic); | |
464 | } | |
465 | ||
466 | pub fn is_keyword(a1: *const MalType) MalError!*MalType { | |
467 | return check_type(a1, MalTypeValue.Keyword); | |
468 | } | |
469 | ||
470 | pub fn is_map(a1: *const MalType) MalError!*MalType { | |
471 | return check_type(a1, MalTypeValue.HashMap); | |
472 | } | |
473 | ||
474 | pub 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 | ||
480 | pub 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 | ||
488 | pub 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 | ||
509 | pub 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 | ||
538 | pub 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 | ||
563 | pub 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 | ||
576 | pub 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 | ||
586 | pub 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 | ||
614 | pub 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 | ||
635 | pub 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 | ||
646 | pub 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 | ||
655 | pub 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 | ||
666 | pub fn time_ms() MalError!*MalType { | |
667 | const itime: i64 = @intCast(i64, std.time.milliTimestamp()); | |
668 | return MalType.new_int(Allocator, itime); | |
669 | } | |
670 | ||
671 | pub 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 | ||
678 | pub 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 | ||
687 | pub 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 | ||
719 | pub 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 | ||
740 | fn 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 | ||
746 | pub 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 | ||
763 | pub const CorePairType = enum { | |
764 | Fn0, | |
765 | Fn1, | |
766 | Fn2, | |
767 | //Fn3, | |
768 | //Fn4, | |
769 | //Fn5, | |
770 | //Fn6, | |
771 | FVar, | |
772 | }; | |
773 | ||
774 | pub 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 | ||
785 | pub const CorePair = struct { | |
786 | name: []const u8, | |
787 | func: CorePairData, | |
788 | }; | |
789 | ||
790 | pub 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 | }; |