| 1 | const std = @import("std"); |
| 2 | const warn = @import("std").debug.warn; |
| 3 | |
| 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 | |
| 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 | |
| 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); |
| 750 | return (try reader.read_form(&read)) orelse return MalType.new_nil(Allocator); |
| 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, |
| 774 | Fn3, |
| 775 | Fn4, |
| 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, |
| 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, |
| 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} }, |
| 820 | CorePair { .name = "vec", .func = CorePairData {.Fn1 = &vec} }, |
| 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 | }; |