Commit | Line | Data |
---|---|---|
14b163d2 JT |
1 | const std = @import("std"); |
2 | const warn = @import("std").debug.warn; | |
3 | ||
14b163d2 JT |
4 | const AllocatorType = @import("std").mem.Allocator; |
5 | var Allocator: *AllocatorType = undefined; | |
6 | ||
7 | pub fn set_allocator(alloc: *AllocatorType) void { | |
8 | Allocator = alloc; | |
9 | } | |
10 | ||
11 | const Env = @import("env.zig").Env; | |
12 | const MalData = @import("types.zig").MalData; | |
13 | const MalType = @import("types.zig").MalType; | |
14 | const MalTypeValue = @import("types.zig").MalTypeValue; | |
15 | const printer = @import("printer.zig"); | |
16 | const reader = @import("reader.zig"); | |
17 | const getline_prompt = @import("readline.zig").getline_prompt; | |
18 | const string_eql = @import("utils.zig").string_eql; | |
19 | const string_copy = @import("utils.zig").string_copy; | |
20 | ||
21 | const MalError = @import("error.zig").MalError; | |
22 | ||
23 | const hmap = @import("hmap.zig"); | |
24 | ||
25 | const MalLinkedList = @import("linked_list.zig").MalLinkedList; | |
26 | const MalHashMap = @import("hmap.zig").MalHashMap; | |
27 | const linked_list = @import("linked_list.zig"); | |
28 | const apply_function = @import("types.zig").apply_function; | |
29 | ||
30 | const safeAdd = @import("std").math.add; | |
31 | const safeSub = @import("std").math.sub; | |
32 | const safeMul = @import("std").math.mul; | |
33 | const safeDivFloor = @import("std").math.divFloor; | |
34 | ||
35 | fn 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 | ||
42 | fn 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 | ||
49 | fn 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 | ||
56 | fn 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 | ||
66 | fn int_lt(a1: *MalType, a2: *MalType) MalError!*MalType { | |
67 | return MalType.new_bool(Allocator, (try a1.as_int()) < (try a2.as_int())); | |
68 | } | |
69 | ||
70 | fn int_leq(a1: *MalType, a2: *MalType) MalError!*MalType { | |
71 | return MalType.new_bool(Allocator, (try a1.as_int()) <= (try a2.as_int())); | |
72 | } | |
73 | ||
74 | fn int_gt(a1: *MalType, a2: *MalType) MalError!*MalType { | |
75 | return MalType.new_bool(Allocator, (try a1.as_int()) > (try a2.as_int())); | |
76 | } | |
77 | ||
78 | fn int_geq(a1: *MalType, a2: *MalType) MalError!*MalType { | |
79 | return MalType.new_bool(Allocator, (try a1.as_int()) >= (try a2.as_int())); | |
80 | } | |
81 | ||
82 | fn _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 | ||
101 | fn _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 | |
127 | fn 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 | ||
179 | fn 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 | ||
185 | fn 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 | ||
191 | fn 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 | ||
213 | fn is_list(a1: *MalType) MalError!*MalType { | |
214 | return MalType.new_bool(Allocator, MalTypeValue(a1.data) == MalTypeValue.List); | |
215 | } | |
216 | ||
217 | fn is_vector(a1: *MalType) MalError!*MalType { | |
218 | return MalType.new_bool(Allocator, MalTypeValue(a1.data) == MalTypeValue.Vector); | |
219 | } | |
220 | ||
221 | pub fn is_string(a1: *MalType) MalError!*MalType { | |
222 | return MalType.new_bool(Allocator, MalTypeValue(a1.data) == MalTypeValue.String); | |
223 | } | |
224 | ||
225 | pub fn is_number(a1: *MalType) MalError!*MalType { | |
226 | return MalType.new_bool(Allocator, MalTypeValue(a1.data) == MalTypeValue.Int); | |
227 | } | |
228 | ||
229 | pub 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 | ||
243 | pub 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 | ||
251 | fn 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 | ||
259 | fn 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 | ||
269 | fn 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 | ||
279 | fn 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 | ||
288 | fn 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 | ||
297 | fn 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 | ||
312 | fn atom(a1: *MalType) MalError!*MalType { | |
313 | return MalType.new_atom(Allocator, a1); | |
314 | } | |
315 | ||
316 | fn is_atom(a1: *MalType) MalError!*MalType { | |
317 | return MalType.new_bool(Allocator, MalTypeValue(a1.data) == MalTypeValue.Atom); | |
318 | } | |
319 | ||
320 | fn 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 | ||
327 | fn 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 | ||
339 | fn 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 |
358 | pub 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 |
368 | pub 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 | ||
381 | pub 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 | ||
405 | pub 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 | ||
424 | pub 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 | ||
433 | pub 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 | ||
440 | pub 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 | ||
451 | fn 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 | ||
457 | pub fn is_nil(a1: *const MalType) MalError!*MalType { | |
458 | return check_type(a1, MalTypeValue.Nil); | |
459 | } | |
460 | ||
461 | pub fn is_true(a1: *const MalType) MalError!*MalType { | |
462 | return check_type(a1, MalTypeValue.True); | |
463 | } | |
464 | ||
465 | pub fn is_false(a1: *const MalType) MalError!*MalType { | |
466 | return check_type(a1, MalTypeValue.False); | |
467 | } | |
468 | ||
469 | pub fn is_symbol(a1: *const MalType) MalError!*MalType { | |
470 | return check_type(a1, MalTypeValue.Generic); | |
471 | } | |
472 | ||
473 | pub fn is_keyword(a1: *const MalType) MalError!*MalType { | |
474 | return check_type(a1, MalTypeValue.Keyword); | |
475 | } | |
476 | ||
477 | pub fn is_map(a1: *const MalType) MalError!*MalType { | |
478 | return check_type(a1, MalTypeValue.HashMap); | |
479 | } | |
480 | ||
481 | pub 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 | ||
487 | pub 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 | ||
495 | pub 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 | ||
516 | pub 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 | ||
545 | pub 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 | ||
570 | pub 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 | ||
583 | pub 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 | ||
593 | pub 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 | ||
621 | pub 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 | ||
642 | pub 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 | ||
653 | pub 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 | ||
662 | pub 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 | ||
673 | pub fn time_ms() MalError!*MalType { | |
674 | const itime: i64 = @intCast(i64, std.time.milliTimestamp()); | |
675 | return MalType.new_int(Allocator, itime); | |
676 | } | |
677 | ||
678 | pub 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 | ||
685 | pub 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 | ||
694 | pub 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 | ||
726 | pub 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 | ||
747 | fn 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 | ||
753 | pub 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 | ||
770 | pub const CorePairType = enum { | |
771 | Fn0, | |
772 | Fn1, | |
773 | Fn2, | |
817e1f0d JT |
774 | Fn3, |
775 | Fn4, | |
14b163d2 JT |
776 | FVar, |
777 | }; | |
778 | ||
779 | pub 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 | ||
788 | pub const CorePair = struct { | |
789 | name: []const u8, | |
790 | func: CorePairData, | |
791 | }; | |
792 | ||
793 | pub 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 | }; |