Embedded programming language for Zig
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Documentation and cleaning up the library

IamPyu 9b9c6b4c 4c9364f1

+170 -121
+2 -2
README.md
··· 2 2 3 3 Pyu's Extension Language 4 4 5 - ## TODO 5 + ## TO-DO 6 6 7 - - Improve core library 7 + - Add closures
+2 -1
bin/main.zig
··· 35 35 try state.loadString(contents); 36 36 37 37 _ = state.exec() catch {}; 38 - // if (try state.exec()) |_| {} 38 + } else { 39 + // TODO: REPL 39 40 } 40 41 }
+7 -8
src/env.zig
··· 1 1 const std = @import("std"); 2 2 const Allocator = std.mem.Allocator; 3 3 const ArrayList = std.ArrayList; 4 - const SliceIterator = @import("mitochondria").SliceIterator; 5 4 6 5 const zexa = @import("./root.zig"); 7 - const Scope = zexa.types.Scope; 8 - const Value = zexa.types.Value; 6 + const Scope = zexa.lang.Scope; 7 + const Value = zexa.lang.Value; 9 8 10 9 pub const Environment = struct { 11 10 const Self = @This(); ··· 31 30 var env = try Environment.init(std.testing.allocator); 32 31 defer env.deinit(); 33 32 34 - const v = try Value.initFrom(&env.primary_scope, .{ .atom = .{ .num = 34 } }); 33 + const v = try Value.initFrom(&env.primary_scope, .{ .expr = .{ .num = 34 } }); 35 34 const val = try env.primary_scope.evalExpr(v); 36 35 37 - try std.testing.expectEqual(Value{ .atom = .{ .num = 34 } }, val.*); 36 + try std.testing.expectEqual(Value{ .expr = .{ .num = 34 } }, val.*); 38 37 } 39 38 40 39 test "variable" { ··· 47 46 try env.primary_scope.insert(key, val); 48 47 49 48 const sym = try Value.initFrom(&env.primary_scope, .{ 50 - .atom = .{ .sym = key }, 49 + .expr = .{ .sym = key }, 51 50 }); 52 51 53 52 const fetched = try env.primary_scope.evalExpr(sym); 54 - try std.testing.expectEqualStrings("hello world!", fetched.*.atom.str); 53 + try std.testing.expectEqualStrings("hello world!", fetched.*.expr.str); 55 54 } 56 55 57 56 test "ast" { ··· 72 71 }; 73 72 74 73 const final = try env.primary_scope.evalAst(&ast); 75 - try std.testing.expectEqual(8, final.*.atom.num); 74 + try std.testing.expectEqual(8, final.*.expr.num); 76 75 } 77 76 78 77 test "cons" {
+110 -65
src/lang.zig
··· 3 3 const Allocator = std.mem.Allocator; 4 4 const Arena = std.heap.ArenaAllocator; 5 5 6 - const SliceIterator = @import("mitochondria").SliceIterator; 6 + const SliceIterator = @import("./util.zig").SliceIterator; 7 7 8 8 pub const RuntimeError = error{ 9 9 InvalidFunction, ··· 17 17 18 18 pub const Symbol = []const u8; 19 19 pub const Number = f64; 20 + 21 + /// A function defined in Zig code 20 22 pub const NativeFunc = *const fn (scope: *Scope, args: []const *Value) RuntimeError!*Value; 23 + 24 + /// A macro defined in Zig code 21 25 pub const NativeMacro = *const fn (scope: *Scope, args: []const *Value) RuntimeError!AST; 22 26 27 + /// A function that runs on a scope to define new values 23 28 pub const Library = *const fn (scope: *Scope) RuntimeError!void; 24 29 25 - pub const AST = []*Value; 30 + /// Abstract syntax tree of a program 31 + pub const AST = []const *Value; 26 32 27 - pub const ValueType = Value.AtomTag; 33 + pub const ValueType = Value.ExprTag; 28 34 29 35 /// A value that lives in a `Scope` 30 36 /// ··· 32 38 pub const Value = struct { 33 39 const Self = @This(); 34 40 35 - pub const AtomTag = enum { 41 + /// Enum of the possible types of values 42 + pub const ExprTag = enum { 36 43 nil, 37 44 bool, 38 45 sym, ··· 46 53 cons, 47 54 }; 48 55 56 + /// A runtime-defined function 49 57 pub const Function = struct { 50 58 arglist: [][]const u8, 51 59 body: AST, ··· 64 72 } 65 73 }; 66 74 67 - pub const Atom = union(AtomTag) { 75 + /// A `Value`'s underlying data 76 + pub const Expr = union(ExprTag) { 68 77 nil, 69 78 bool: bool, 70 79 sym: Symbol, ··· 77 86 userdata: *anyopaque, 78 87 cons: Cons, 79 88 }; 89 + 90 + /// A pair of values 80 91 pub const Cons = struct { car: *Value, cdr: *Value }; 81 92 93 + /// An iterator of values 82 94 pub const Iter = struct { 83 95 current: ?*Value, 84 96 97 + /// Initialize the iterator from `value` 85 98 pub fn init(value: ?*Value) Iter { 86 99 return Iter{ .current = value }; 87 100 } 88 101 102 + /// Return the next item 89 103 pub fn next(self: *Iter) ?*Value { 90 104 const cur = self.current orelse return null; 91 105 self.current = cur.getCdr(); ··· 93 107 } 94 108 }; 95 109 96 - atom: Atom, 110 + expr: Expr, 97 111 98 112 /// Create a `Value` independent of a `Scope` 99 113 pub fn init(allocator: Allocator, raw: Self) RuntimeError!*Self { ··· 105 119 /// Initialize as nil 106 120 pub fn initNil(scope: *Scope) RuntimeError!*Self { 107 121 const atom = try scope.createValue(); 108 - atom.* = Self{ .atom = .nil }; 122 + atom.* = Self{ .expr = .nil }; 109 123 110 124 return atom; 111 125 } ··· 121 135 return switch (@TypeOf(value)) { 122 136 void => Self.initNil(scope), 123 137 comptime_int, comptime_float, f32, f64 => Self.initFrom(scope, .{ 124 - .atom = .{ .num = @as(f64, value) }, 138 + .expr = .{ .num = @as(f64, value) }, 125 139 }), 126 140 i32, i64, u32, u64 => Self.initFrom(scope, .{ 127 - .atom = .{ .num = @floatFromInt(value) }, 141 + .expr = .{ .num = @floatFromInt(value) }, 128 142 }), 129 143 bool => Self.initFrom(scope, .{ 130 - .atom = .{ .bool = value }, 144 + .expr = .{ .bool = value }, 131 145 }), 132 146 *Value => Self.initFrom(scope, .{ 133 - .atom = .{ .quote = value }, 147 + .expr = .{ .quote = value }, 134 148 }), 135 149 else => RuntimeError.InvalidType, 136 150 }; ··· 140 154 pub fn initString(scope: *Scope, str: []const u8) RuntimeError!*Self { 141 155 const atom = try Self.initNil(scope); 142 156 const nstr = try scope.createString(str); 143 - atom.atom = .{ .str = nstr }; 157 + atom.expr = .{ .str = nstr }; 144 158 return atom; 145 159 } 146 160 ··· 148 162 pub fn initSymbol(scope: *Scope, sym: []const u8) RuntimeError!*Self { 149 163 const atom = try Self.initNil(scope); 150 164 const nsym = try scope.createString(sym); 151 - atom.atom = .{ .sym = nsym }; 165 + atom.expr = .{ .sym = nsym }; 152 166 return atom; 153 167 } 154 168 155 169 /// Create a cons pair 156 170 pub fn initCons(scope: *Scope, car: *Value, cdr: *Value) RuntimeError!*Self { 157 171 const cons = try Self.initNil(scope); 158 - cons.atom = .{ .cons = .{ .car = car, .cdr = cdr } }; 172 + cons.expr = .{ .cons = .{ .car = car, .cdr = cdr } }; 159 173 return cons; 160 174 } 161 175 162 176 /// Create a function 163 177 pub fn initFunction(scope: *Scope, func: Function) RuntimeError!*Self { 164 178 return Self.initFrom(scope, .{ 165 - .atom = .{ .function = func }, 179 + .expr = .{ .function = func }, 166 180 }); 167 181 } 168 182 169 183 /// Create a native function 170 184 pub fn initNativeFunc(scope: *Scope, func: NativeFunc) RuntimeError!*Self { 171 185 return Self.initFrom(scope, .{ 172 - .atom = .{ .native_func = func }, 186 + .expr = .{ .native_func = func }, 173 187 }); 174 188 } 175 189 176 190 /// Create a native macro 177 191 pub fn initNativeMacro(scope: *Scope, func: NativeMacro) RuntimeError!*Self { 178 192 return Self.initFrom(scope, .{ 179 - .atom = .{ .native_macro = func }, 193 + .expr = .{ .native_macro = func }, 180 194 }); 181 195 } 182 196 183 197 /// Initialize from userdata 184 198 pub fn initUserData(scope: *Scope, userdata: *anyopaque) RuntimeError!*Self { 185 199 return Self.initFrom(scope, .{ 186 - .atom = .{ .userdata = userdata }, 200 + .expr = .{ .userdata = userdata }, 187 201 }); 188 202 } 189 203 ··· 194 208 if (values.len == 0) { 195 209 return cons; 196 210 } 197 - cons.atom.cons.car = values[0]; 211 + cons.expr.cons.car = values[0]; 198 212 199 213 const ptr = cons; 200 214 var rest = values[1..]; 201 215 while (rest.len != 0) { 202 216 const ncons = try Value.initCons(scope, rest[0], try Value.initNil(scope)); 203 217 204 - cons.atom.cons.cdr = ncons; 218 + cons.expr.cons.cdr = ncons; 205 219 cons = ncons; 206 220 207 221 rest = rest[1..]; ··· 210 224 return ptr; 211 225 } 212 226 227 + /// Get the type of this value 228 + pub fn getType(self: *Self) ValueType { 229 + return std.meta.activeTag(self.expr); 230 + } 231 + 232 + /// Clone this value using `scope` 213 233 pub fn clone(self: *const Self, scope: *Scope) RuntimeError!*Value { 214 - switch (self.atom) { 234 + switch (self.expr) { 215 235 .nil => return try Value.initNil(scope), 216 236 .bool => |b| return try Value.initFromAny(scope, b), 217 237 .num => |n| return try Value.initFromAny(scope, n), 218 238 .str => |s| return try Value.initString(scope, s), 219 239 .sym => |s| return try Value.initSymbol(scope, s), 220 240 .quote => |v| return try Value.initFrom(scope, Value{ 221 - .atom = .{ 241 + .expr = .{ 222 242 .quote = v, 223 243 }, 224 244 }), 225 - // TODO: clone functions 226 - .function => return Value.initFunction(scope, self.atom.function), 245 + .function => return Value.initFunction(scope, self.expr.function), 227 246 .native_func => |f| return try Value.initNativeFunc(scope, f), 228 247 .native_macro => |m| return try Value.initNativeMacro(scope, m), 229 248 .userdata => |u| return try Value.initUserData(scope, u), ··· 237 256 } 238 257 } 239 258 259 + /// If this value is a cons pair, append `other` to it. 240 260 pub fn append(self: *Self, scope: *Scope, other: *Self) RuntimeError!void { 241 - switch (self.atom) { 261 + switch (self.expr) { 242 262 .cons => |*cons| { 243 263 if (cons.cdr.isNil()) { 244 264 cons.cdr = try Value.initCons( ··· 256 276 257 277 /// If this value is a cons set return the CAR of the set 258 278 pub fn getCar(self: *const Self) ?*Value { 259 - return switch (self.atom) { 279 + return switch (self.expr) { 260 280 .cons => |cons| cons.car, 261 281 // .nil => @constCast(self), 262 282 else => null, ··· 265 285 266 286 /// If this value is a cons set return the CDR of the set 267 287 pub fn getCdr(self: *const Self) ?*Value { 268 - return switch (self.atom) { 288 + return switch (self.expr) { 269 289 .cons => |cons| cons.cdr, 270 290 // .nil => @constCast(self), 271 291 else => null, 272 292 }; 273 293 } 274 294 295 + /// Returns whether this value is `nil` 275 296 pub fn isNil(self: *const Self) bool { 276 - return switch (self.atom) { 297 + return switch (self.expr) { 277 298 .nil => true, 278 - // .cons => |cons| cons.car.isNil() and cons.cdr.isNil(), 279 299 else => false, 280 300 }; 281 301 } 282 302 303 + /// Return whether this value is a cons pair or `nil` 283 304 pub fn isConsOrNil(self: *const Self) bool { 284 - return switch (self.atom) { 305 + return switch (self.expr) { 285 306 .nil => true, 286 307 .cons => true, 287 308 else => false, 288 309 }; 289 310 } 290 311 312 + /// Return whether this value is truthy 291 313 pub fn isTruthy(self: *const Self) bool { 292 - return switch (self.atom) { 314 + return switch (self.expr) { 293 315 .nil => false, 294 316 .bool => |b| b, 295 317 .num => |n| n != 0, ··· 300 322 }; 301 323 } 302 324 325 + ///Check equality with another value 303 326 pub fn isEqual(self: *Self, other: *Self) bool { 304 327 if (self.getType() != other.getType()) { 305 328 return false; ··· 307 330 308 331 return switch (self.getType()) { 309 332 .nil => true, 310 - .bool => self.atom.bool == other.atom.bool, 311 - .num => self.atom.num == other.atom.num, 312 - .str => std.mem.eql(u8, self.atom.str, other.atom.str), 313 - .sym => std.mem.eql(u8, self.atom.sym, other.atom.sym), 314 - .quote => self.atom.quote.isEqual(other.atom.quote), 333 + .bool => self.expr.bool == other.expr.bool, 334 + .num => self.expr.num == other.expr.num, 335 + .str => std.mem.eql(u8, self.expr.str, other.expr.str), 336 + .sym => std.mem.eql(u8, self.expr.sym, other.expr.sym), 337 + .quote => self.expr.quote.isEqual(other.expr.quote), 315 338 .function => false, 316 - .native_func => self.atom.native_func == other.atom.native_func, 317 - .native_macro => self.atom.native_macro == other.atom.native_macro, 318 - .userdata => self.atom.userdata == other.atom.userdata, 339 + .native_func => self.expr.native_func == other.expr.native_func, 340 + .native_macro => self.expr.native_macro == other.expr.native_macro, 341 + .userdata => self.expr.userdata == other.expr.userdata, 319 342 .cons => blk: { 320 - const c1 = self.atom.cons.car.isEqual(other.atom.cons.car); 321 - const c2 = self.atom.cons.cdr.isEqual(other.atom.cons.cdr); 343 + const c1 = self.expr.cons.car.isEqual(other.expr.cons.car); 344 + const c2 = self.expr.cons.cdr.isEqual(other.expr.cons.cdr); 322 345 break :blk c1 and c2; 323 346 }, 324 347 }; 325 348 } 326 349 327 - pub fn getType(self: *Self) ValueType { 328 - return std.meta.activeTag(self.atom); 329 - } 330 - 350 + /// Convert this value to a slice 331 351 pub fn toSlice(self: *Self, scope: *Scope) RuntimeError![]*Value { 332 352 var list = try std.ArrayList(*Value).initCapacity(scope.allocator, 10); 333 353 try self.toSliceInner(scope, self, &list); ··· 350 370 return; 351 371 } 352 372 353 - switch (self.atom) { 373 + switch (self.expr) { 354 374 .nil => {}, 355 375 .cons => |cons| { 356 376 try list.append(scope.allocator, cons.car); ··· 360 380 } 361 381 } 362 382 383 + /// Format the value to a writer 363 384 pub fn format(self: *Self, writer: *std.Io.Writer) !void { 364 - switch (self.atom) { 385 + switch (self.expr) { 365 386 .nil => _ = try writer.write("nil"), 366 387 .bool => |b| try writer.print("{any}", .{b}), 367 388 .num => |n| try writer.print("{d}", .{n}), ··· 388 409 } 389 410 } 390 411 412 + /// Add `other` to this value 391 413 pub fn add(self: *Self, scope: *Scope, other: *Self) RuntimeError!*Value { 392 414 if (self.getType() != other.getType()) { 393 415 return Value.initNil(scope); ··· 395 417 396 418 const cloned = try self.clone(scope); 397 419 398 - switch (cloned.atom) { 420 + switch (cloned.expr) { 399 421 .num => |n| return try Value.initFromAny( 400 422 scope, 401 - n + other.atom.num, 423 + n + other.expr.num, 402 424 ), 403 425 .str => |s| { 404 426 const str = try std.mem.concat(scope.allocator, u8, &.{ 405 427 s, 406 - other.atom.str, 428 + other.expr.str, 407 429 }); 408 430 defer scope.allocator.free(str); 409 431 ··· 412 434 .sym => |s| { 413 435 const str = try std.mem.concat(scope.allocator, u8, &.{ 414 436 s, 415 - other.atom.sym, 437 + other.expr.sym, 416 438 }); 417 439 defer scope.allocator.free(str); 418 440 ··· 428 450 return cloned; 429 451 } 430 452 453 + /// Subtract `other` from this value 431 454 pub fn sub(self: *Self, scope: *Scope, other: *Value) RuntimeError!*Value { 432 455 if (self.getType() != other.getType()) { 433 456 return Value.initNil(scope); 434 457 } 435 458 436 459 const cloned = try self.clone(scope); 437 - switch (cloned.atom) { 438 - .num => |n| return try Value.initFromAny(scope, n - other.atom.num), 460 + switch (cloned.expr) { 461 + .num => |n| return try Value.initFromAny(scope, n - other.expr.num), 439 462 else => {}, 440 463 } 441 464 442 465 return cloned; 443 466 } 444 467 468 + /// Multiply this value by `other` 445 469 pub fn mul(self: *Self, scope: *Scope, other: *Value) RuntimeError!*Value { 446 470 if (self.getType() != other.getType()) { 447 471 return Value.initNil(scope); 448 472 } 449 473 450 474 const cloned = try self.clone(scope); 451 - switch (cloned.atom) { 452 - .num => |n| return try Value.initFromAny(scope, n * other.atom.num), 475 + switch (cloned.expr) { 476 + .num => |n| return try Value.initFromAny(scope, n * other.expr.num), 453 477 else => {}, 454 478 } 455 479 456 480 return cloned; 457 481 } 458 482 483 + /// Divide this value by `other` 459 484 pub fn div(self: *Self, scope: *Scope, other: *Value) RuntimeError!*Value { 460 485 if (self.getType() != other.getType()) { 461 486 return Value.initNil(scope); 462 487 } 463 488 464 489 const cloned = try self.clone(scope); 465 - switch (cloned.atom) { 466 - .num => |n| return try Value.initFromAny(scope, n / other.atom.num), 490 + switch (cloned.expr) { 491 + .num => |n| return try Value.initFromAny(scope, n / other.expr.num), 467 492 else => {}, 468 493 } 469 494 470 495 return cloned; 471 496 } 472 497 498 + /// Call this value as a function with `args` 473 499 pub fn call(self: *Self, scope: *Scope, args: []const *Value) RuntimeError!*Value { 474 500 var arglist = try std.ArrayList(*Value).initCapacity( 475 501 scope.allocator, ··· 491 517 const value = blk: { 492 518 switch (self.getType()) { 493 519 .function => { 494 - const f = self.atom.function; 520 + const f = self.expr.function; 495 521 496 522 if (f.arglist.len != arglist.items.len) { 497 523 try scope.setException("Function called with incorrect number of arguments"); ··· 508 534 break :blk scope.evalAst(f.body); 509 535 }, 510 536 .native_func => { 511 - const result = self.atom.native_func( 537 + const result = self.expr.native_func( 512 538 scope, 513 539 arglist.items, 514 540 ); 515 541 break :blk result; 516 542 }, 517 543 .native_macro => { 518 - const ast = self.atom.native_macro( 544 + const ast = self.expr.native_macro( 519 545 scope, 520 546 arglist.items, 521 547 ) catch |e| return e; ··· 587 613 call_stack: CallStack, 588 614 exception: ?[]u8 = null, 589 615 616 + /// Initialize a new scope 590 617 pub fn init(allocator: Allocator) !Self { 591 618 return Self{ 592 619 .allocator = allocator, ··· 597 624 }; 598 625 } 599 626 627 + /// Deinitialize this scope 600 628 pub fn deinit(self: *Self) void { 601 629 self.variables.deinit(); 602 630 for (self.tracked_values.items) |ptr| { ··· 608 636 self.arena.deinit(); 609 637 } 610 638 639 + /// Create a new scope with `self` as the parent 611 640 pub fn branch(self: *Self) !Scope { 612 641 var s = try Scope.init(self.allocator); 613 642 s.setParent(self); ··· 615 644 return s; 616 645 } 617 646 647 + /// Returns the top-most scope 618 648 pub fn getRoot(self: *Self) *Scope { 619 649 if (self.parent) |p| { 620 650 return p.getRoot(); ··· 623 653 } 624 654 } 625 655 626 - pub fn passTo(self: *Self, other: *Self) void { 627 - other.arena = other.arena.state.promote(self.arena.child_allocator); 628 - } 656 + // pub fn passTo(self: *Self, other: *Self) void { 657 + // other.arena = other.arena.state.promote(self.arena.child_allocator); 658 + // } 629 659 660 + /// Set the parent of this scope 630 661 pub fn setParent(self: *Self, parent: *Self) void { 631 662 self.parent = parent; 632 663 } 633 664 665 + /// Create a new `Value` bounded to the scope 634 666 pub fn createValue(self: *Self) !*Value { 635 667 const out = try self.allocator.create(Value); 636 668 try self.tracked_values.append(self.allocator, out); 637 669 return out; 638 670 } 639 671 672 + /// Allocate a string bounded to the scope 640 673 pub fn createString(self: *Self, str: []const u8) ![]const u8 { 641 674 const out = try self.arena.allocator().alloc(u8, str.len); 642 675 @memcpy(out, str); 643 676 return out; 644 677 } 645 678 646 - /// Copies `msg` to use as the current exception message 679 + /// Copies `msg` to use as the current exception message of the root scope 647 680 pub fn setException(self: *Self, msg: []const u8) !void { 648 681 const s = self.getRoot(); 649 682 s.exception = try s.allocator.alloc(u8, msg.len); 650 683 @memcpy(s.exception.?, msg); 651 684 } 652 685 686 + /// Clear the current exception message of the root scoep, if any 653 687 pub fn clearException(self: *Self) void { 654 688 const s = self.getRoot(); 655 689 if (s.exception) |p| { ··· 658 692 } 659 693 } 660 694 695 + /// Get the current exception message from the root scope 661 696 pub fn getException(self: *Self) ?[]const u8 { 662 697 return self.getRoot().exception; 663 698 } 664 699 700 + /// Get the call stack used by the root scope, other call stacks shouldn't be used. 665 701 pub fn getCallStack(self: *Self) *CallStack { 666 702 return &self.getRoot().call_stack; 667 703 } 668 704 705 + /// Insert a variable into the scope 669 706 pub fn insert(self: *Self, key: []const u8, value: *Value) !void { 670 707 try self.variables.put(key, value); 671 708 } 672 709 710 + /// Insert many variables (a registry) into the scope 673 711 pub fn insertRegistry(self: *Self, registry: []const struct { []const u8, *Value }) !void { 674 712 for (registry) |registee| { 675 713 try self.insert(registee[0], registee[1]); 676 714 } 677 715 } 678 716 717 + /// Get a variable from the scope, or its parents if not found 679 718 pub fn get(self: *Self, key: []const u8) ?*Value { 680 719 if (self.variables.get(key)) |v| { 681 720 return v; ··· 686 725 return null; 687 726 } 688 727 728 + /// Check if the `get` function succeeded 689 729 pub fn hasKey(self: *Self, key: []const u8) bool { 690 730 return self.get(key) != null; 691 731 } 692 732 733 + /// Create an abstract syntax tree from a slice 693 734 pub fn createAst(self: *Self, ast: []const *Value) RuntimeError!AST { 694 735 const root = self.getRoot(); 695 736 const out = try root.arena.allocator().alloc(*Value, ast.len); ··· 700 741 return out; 701 742 } 702 743 744 + /// Evaluate an expression 703 745 pub fn evalExpr(self: *Self, expr: *Value) RuntimeError!*Value { 704 - switch (expr.atom) { 746 + switch (expr.expr) { 705 747 .nil => return Value.initNil(self), 706 748 .bool => |b| return Value.initFromAny(self, b), 707 749 .num => |n| return Value.initFromAny(self, n), ··· 734 776 } 735 777 } 736 778 779 + /// Evaluate the AST of a program 737 780 pub fn evalAst(self: *Self, ast: []const *Value) RuntimeError!*Value { 738 781 var iter = SliceIterator(*Value).init(ast); 739 782 var value: ?*Value = null; ··· 741 784 return value orelse try Value.initNil(self); 742 785 } 743 786 787 + /// Dump the call stack and return `e` 744 788 pub fn dumpStackAndError(self: *Self, e: RuntimeError) RuntimeError { 745 789 std.debug.print("error occured in scope {*}: {}\n", .{ self, e }); 746 790 std.debug.print("unwinding callstack {*}:\n", .{&self.getCallStack()}); ··· 748 792 return e; 749 793 } 750 794 795 + /// Load a library into the scope 751 796 pub fn loadLibrary(self: *Self, lib: Library) RuntimeError!void { 752 797 try lib(self); 753 798 }
+3 -3
src/lexer.zig
··· 1 1 const std = @import("std"); 2 2 const Allocator = std.mem.Allocator; 3 3 4 - const mitochondria = @import("mitochondria"); 5 - const SliceIterator = mitochondria.SliceIterator; 6 - const StringBuilder = mitochondria.StringBuilder; 4 + const util = @import("./util.zig"); 5 + const SliceIterator = util.SliceIterator; 6 + const StringBuilder = util.StringBuilder; 7 7 8 8 pub const TokenValue = union(enum) { 9 9 lparen,
+9 -8
src/parser.zig
··· 2 2 const Allocator = std.mem.Allocator; 3 3 4 4 const zexa = @import("./root.zig"); 5 - const Value = zexa.types.Value; 6 - const Scope = zexa.types.Scope; 7 - const Number = zexa.types.Number; 5 + const Value = zexa.lang.Value; 6 + const Scope = zexa.lang.Scope; 7 + const Number = zexa.lang.Number; 8 8 const Environment = zexa.env.Environment; 9 - const lexer = zexa.lexer; 9 + 10 + const lexer = @import("./lexer.zig"); 10 11 const Tokenizer = lexer.Tokenizer; 11 12 const Token = lexer.Token; 12 13 const TokenValue = lexer.TokenValue; 13 14 const LineInfo = lexer.LineInfo; 14 15 15 - const mitochondria = @import("mitochondria"); 16 - const SliceIterator = mitochondria.SliceIterator; 17 - const StringBuilder = mitochondria.StringBuilder; 16 + const util = @import("./util.zig"); 17 + const SliceIterator = util.SliceIterator; 18 + const StringBuilder = util.StringBuilder; 18 19 19 20 pub const Parser = struct { 20 21 const Self = @This(); ··· 99 100 else => { 100 101 const val = try self.parseExpr(ntok, tokens); 101 102 if (first) { 102 - list.atom.cons.car = val; 103 + list.expr.cons.car = val; 103 104 first = false; 104 105 } else { 105 106 try list.append(scope, val);
+14 -15
src/root.zig
··· 1 1 pub const lang = @import("./lang.zig"); 2 - pub const types = lang; 3 2 pub const env = @import("./env.zig"); 4 3 pub const stdlib = @import("./stdlib.zig"); 5 - pub const lexer = @import("./lexer.zig"); 6 - pub const parser = @import("./parser.zig"); 7 4 pub const util = @import("./util.zig"); 5 + 6 + const lexer = @import("./lexer.zig"); 7 + const parser = @import("./parser.zig"); 8 8 9 9 const std = @import("std"); 10 10 const Allocator = std.mem.Allocator; ··· 12 12 pub const State = struct { 13 13 const Self = @This(); 14 14 15 - pub const Frontend = struct { 15 + const Frontend = struct { 16 16 allocator: Allocator, 17 17 tokenizer: lexer.Tokenizer, 18 18 env: *env.Environment, ··· 59 59 } 60 60 61 61 /// Returns the primary scope 62 - pub fn getScope(self: *Self) *types.Scope { 62 + pub fn getScope(self: *Self) *lang.Scope { 63 63 return &self.env.primary_scope; 64 64 } 65 65 66 - /// Load `buf` into the frontend 66 + /// Load `buf` into the frontend for execution 67 67 pub fn loadString(self: *Self, buf: []const u8) !void { 68 68 self.frontend = try Frontend.init(self.allocator, &self.env, buf); 69 69 } 70 70 71 - /// Tokenize, parser and evaluate the code in the frontend 72 - pub fn exec(self: *Self) !?*types.Value { 71 + /// Load the library `lib` 72 + pub fn loadLibrary(self: *Self, lib: lang.Library) !void { 73 + try self.getScope().loadLibrary(lib); 74 + } 75 + 76 + /// Tokenize, parse and evaluate the code in the frontend 77 + pub fn exec(self: *Self) !?*lang.Value { 73 78 if (self.frontend) |*f| { 74 79 defer { 75 80 f.deinit(); ··· 88 93 }; 89 94 90 95 test { 91 - _ = types; 96 + _ = lang; 92 97 _ = env; 93 98 _ = stdlib; 94 99 _ = lexer; 95 100 _ = parser; 96 101 } 97 - 98 - test "test2" { 99 - // var state = try State.init(zigstd.testing.allocator); 100 - // try state.loadString(@embedFile("test/test2.zexa")); 101 - // _ = try state.exec(); 102 - }
+2 -4
src/stdlib.zig
··· 1 1 const std = @import("std"); 2 2 const zexa = @import("./root.zig"); 3 - const Scope = zexa.types.Scope; 4 - const Value = zexa.types.Value; 3 + const Scope = zexa.lang.Scope; 5 4 6 5 pub const corelib = @import("./stdlib/core.zig"); 7 6 8 - pub const StdConfiguration = struct {}; 9 - 7 + /// Load the standard library for `scope` 10 8 pub fn loadStd(scope: *Scope) !void { 11 9 try scope.loadLibrary(corelib.load); 12 10 }
+14 -14
src/stdlib/core.zig
··· 2 2 3 3 const Io = std.Io; 4 4 5 - const SliceIterator = @import("mitochondria").SliceIterator; 6 - const StringBuilder = @import("mitochondria").StringBuilder; 7 - 8 5 const zexa = @import("../root.zig"); 9 - const Scope = zexa.types.Scope; 10 - const Value = zexa.types.Value; 11 - const AST = zexa.types.AST; 12 - const RuntimeError = zexa.types.RuntimeError; 6 + const Scope = zexa.lang.Scope; 7 + const Value = zexa.lang.Value; 8 + const AST = zexa.lang.AST; 9 + const RuntimeError = zexa.lang.RuntimeError; 13 10 14 11 const util = @import("../util.zig"); 12 + const SliceIterator = util.SliceIterator; 13 + const StringBuilder = util.StringBuilder; 15 14 16 15 const EXCEPTION_SIZE = 30; 17 16 17 + /// Entry point of the core library 18 18 pub fn load(scope: *Scope) RuntimeError!void { 19 19 try scope.insertRegistry(&.{ 20 20 .{ "#t", try Value.initFromAny(scope, true) }, ··· 110 110 if (sym.getType() != .sym) { 111 111 continue; 112 112 } 113 - const symstr = sym.atom.sym; 113 + const symstr = sym.expr.sym; 114 114 115 115 if (def.getCdr().?.getCar()) |val| { 116 116 try scope.insert(symstr, val); ··· 142 142 if (sym.getType() != .sym) { 143 143 continue; 144 144 } 145 - const symstr = sym.atom.sym; 145 + const symstr = sym.expr.sym; 146 146 try arga.append(root.allocator, symstr); 147 147 } 148 148 ··· 183 183 return RuntimeError.InvalidArguments; 184 184 } 185 185 186 - const sym = switch (args[0].atom) { 186 + const sym = switch (args[0].expr) { 187 187 .sym => |s| s, 188 188 else => return RuntimeError.InvalidArguments, 189 189 }; ··· 200 200 while (iter.next()) |sym| { 201 201 const val = iter.next() orelse break; 202 202 203 - switch (sym.atom) { 203 + switch (sym.expr) { 204 204 .sym => |s| { 205 205 const p = scope.parent.?; 206 206 if (p.hasKey(s)) { ··· 215 215 }, 216 216 else => { 217 217 sb.clear(); 218 - try util.invalidType(&sb, Value.AtomTag.sym, sym.getType()); 218 + try util.invalidType(&sb, Value.ExprTag.sym, sym.getType()); 219 219 try scope.setException(sb.written()); 220 220 221 221 return RuntimeError.InvalidArguments; ··· 248 248 return Value.initNil(scope); 249 249 } 250 250 251 - const fmt = args[0].*.atom.str; 251 + const fmt = args[0].*.expr.str; 252 252 253 253 var buf: []u8 = try scope.allocator.alloc(u8, fmt.len); 254 254 defer scope.allocator.free(buf); ··· 319 319 320 320 for (args) |i| { 321 321 if (first) { 322 - v.atom.cons.car = i; 322 + v.expr.cons.car = i; 323 323 first = false; 324 324 } else { 325 325 try v.append(scope, i);
+5 -1
src/test/map.zexa
··· 1 + (define 'double (lambda (x) (mul x 2))) 2 + (define 'triple (lambda (x) (mul x 3))) 3 + 1 4 (define 'my-list (list 1 2 3 4)) 2 - (define 'mapped-list (map (lambda (x) (mul x 2)) my-list)) 5 + (define 'mapped-list (map double my-list)) 3 6 4 7 (print my-list) 5 8 (print mapped-list) 9 + (print (map triple my-list))
+2
src/util.zig
··· 3 3 const ValueType = zexa.lang.ValueType; 4 4 5 5 const mitochondria = @import("mitochondria"); 6 + 7 + pub const SliceIterator = mitochondria.SliceIterator; 6 8 pub const StringBuilder = mitochondria.StringBuilder; 7 9 8 10 pub fn undefinedVariable(sb: *StringBuilder, name: []const u8) !void {