Embedded programming language for Zig
1
fork

Configure Feed

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

Add runtime-defined macros

IamPyu 7fa0b2bf 10c6f7d9

+114 -15
+38
src/corelib.zig
··· 23 23 .{ "while", try Value.initNativeMacro(scope, @"while") }, 24 24 .{ "let", try Value.initNativeMacro(scope, let) }, 25 25 .{ "lambda", try Value.initNativeMacro(scope, lambda) }, 26 + .{ "macro", try Value.initNativeMacro(scope, macro) }, 26 27 .{ "dolist", try Value.initNativeMacro(scope, dolist) }, 27 28 28 29 .{ "eval", try Value.initNativeFunc(scope, eval) }, ··· 171 172 defer root.allocator.free(sl); 172 173 const func = try Value.Function.create(root, sl, @constCast(body)); 173 174 return root.createAst(&.{try Value.initFunction(root, func)}); 175 + } 176 + 177 + pub fn macro(scope: *Scope, args: []const *Value) RuntimeError!AST { 178 + var sb = try scope.stringBuilder(); 179 + defer sb.deinit(); 180 + if (args.len == 0) { 181 + try util.unexpectedArguments(&sb, .{ .expected = 1, .got = 0, .mode = .at_least }); 182 + try scope.setException(sb.written()); 183 + return RuntimeError.InvalidArguments; 184 + } 185 + const root = scope.getRoot(); 186 + 187 + const arglist = args[0]; 188 + const body = args[1..]; 189 + 190 + if (arglist.getType() != .cons) { 191 + try util.invalidType(&sb, .cons, args[0].getType()); 192 + try scope.setException(sb.written()); 193 + return RuntimeError.InvalidArguments; 194 + } 195 + 196 + var arga = try std.ArrayList([]const u8).initCapacity(root.allocator, 5); 197 + var iter = Value.Iter.init(arglist); 198 + 199 + while (iter.next()) |current| { 200 + const sym = current; 201 + if (sym.getType() != .sym) { 202 + continue; 203 + } 204 + const symstr = sym.expr.sym; 205 + try arga.append(root.allocator, symstr); 206 + } 207 + 208 + const sl = try arga.toOwnedSlice(root.allocator); 209 + defer root.allocator.free(sl); 210 + const func = try Value.Macro.create(root, sl, @constCast(body)); 211 + return root.createAst(&.{try Value.initMacro(root, func)}); 174 212 } 175 213 176 214 pub fn dolist(scope: *Scope, args: []const *Value) RuntimeError!AST {
+68 -15
src/lang.zig
··· 46 46 str, 47 47 quote, 48 48 function, 49 + macro, 49 50 native_func, 50 51 native_macro, 51 52 userdata, ··· 57 58 arglist: [][]const u8, 58 59 body: AST, 59 60 60 - pub fn create(scope: *Scope, args: [][]const u8, ast: AST) !Function { 61 + pub fn create(scope: *Scope, args: [][]const u8, ast: AST) RuntimeError!Function { 61 62 const root = scope.getRoot(); 62 63 const a = try root.arena.allocator().alloc([]const u8, args.len); 63 64 for (args, 0..) |s, i| { ··· 71 72 } 72 73 }; 73 74 75 + /// A runtime-defined macro 76 + pub const Macro = struct { 77 + arglist: [][]const u8, 78 + body: AST, 79 + 80 + pub fn create(scope: *Scope, args: [][]const u8, ast: AST) RuntimeError!Macro { 81 + const root = scope.getRoot(); 82 + const a = try root.arena.allocator().alloc([]const u8, args.len); 83 + for (args, 0..) |s, i| { 84 + a[i] = try root.createString(s); 85 + } 86 + const st = try root.createAst(ast); 87 + return Macro{ 88 + .arglist = a, 89 + .body = st, 90 + }; 91 + } 92 + }; 93 + 74 94 /// A `Value`'s underlying data 75 95 pub const Expr = union(ExprTag) { 76 96 nil, ··· 80 100 str: []const u8, 81 101 quote: *Self, 82 102 function: Function, 103 + macro: Macro, 83 104 native_func: NativeFunc, 84 105 native_macro: NativeMacro, 85 106 userdata: *anyopaque, ··· 181 202 }); 182 203 } 183 204 205 + /// Create a macro 206 + pub fn initMacro(scope: *Scope, macro: Macro) RuntimeError!*Self { 207 + return Self.initFrom(scope, .{ 208 + .expr = .{ .macro = macro }, 209 + }); 210 + } 211 + 184 212 /// Create a native function 185 213 pub fn initNativeFunc(scope: *Scope, func: NativeFunc) RuntimeError!*Self { 186 214 return Self.initFrom(scope, .{ ··· 244 272 }, 245 273 }), 246 274 .function => return Value.initFunction(scope, self.expr.function), 275 + .macro => return Value.initMacro(scope, self.expr.macro), 247 276 .native_func => |f| return try Value.initNativeFunc(scope, f), 248 277 .native_macro => |m| return try Value.initNativeMacro(scope, m), 249 278 .userdata => |u| return try Value.initUserData(scope, u), ··· 328 357 .sym => std.mem.eql(u8, self.expr.sym, other.expr.sym), 329 358 .quote => self.expr.quote.isEqual(other.expr.quote), 330 359 .function => false, 360 + .macro => false, 331 361 .native_func => self.expr.native_func == other.expr.native_func, 332 362 .native_macro => self.expr.native_macro == other.expr.native_macro, 333 363 .userdata => self.expr.userdata == other.expr.userdata, ··· 348 378 .str => |s| try writer.print("{s}", .{s}), 349 379 .sym => |s| try writer.print("'{s}", .{s}), 350 380 .quote => |v| try writer.print("{f}", .{v}), 351 - .function, .native_func, .native_macro, .userdata => try writer.print( 381 + .function, .macro, .native_func, .native_macro, .userdata => try writer.print( 352 382 "{*}", 353 383 .{self}, 354 384 ), ··· 456 486 457 487 /// Call this value as a function with `args` 458 488 pub fn call(self: *Self, scope: *Scope, args: []const *Value) RuntimeError!*Value { 459 - var arglist = try std.ArrayList(*Value).initCapacity( 460 - scope.allocator, 461 - args.len, 462 - ); 463 - defer arglist.deinit(scope.allocator); 464 - try arglist.appendSlice(scope.allocator, args); 489 + // TODO: implement comma w/ backtick quoting 490 + 491 + const arglist = try scope.allocator.alloc(*Value, args.len); 492 + @memcpy(arglist, args); 493 + defer scope.allocator.free(arglist); 494 + 495 + // var arglist = try std.ArrayList(*Value).initCapacity( 496 + // scope.allocator, 497 + // args.len, 498 + // ); 499 + // defer arglist.deinit(scope.allocator); 500 + // try arglist.appendSlice(scope.allocator, args); 465 501 466 502 switch (self.getType()) { 467 503 .function, .native_func => { 468 - for (arglist.items) |*v| { 504 + for (arglist) |*v| { 469 505 v.* = try scope.evalExpr(v.*); 470 506 } 471 507 }, 472 - .native_macro => {}, 508 + .macro, .native_macro => {}, 473 509 else => { 474 510 var sb = try scope.stringBuilder(); 475 511 defer sb.deinit(); ··· 485 521 .function => { 486 522 const f = self.expr.function; 487 523 488 - if (f.arglist.len != arglist.items.len) { 524 + if (f.arglist.len != arglist.len) { 489 525 try scope.setException("Function called with incorrect number of arguments"); 490 526 491 527 return RuntimeError.InvalidArguments; ··· 493 529 494 530 if (f.arglist.len != 0) { 495 531 for (f.arglist, 0..) |s, i| { 496 - try scope.insert(s, arglist.items[i]); 532 + try scope.insert(s, arglist[i]); 497 533 } 498 534 } 499 535 500 536 break :blk scope.evalAst(f.body); 501 537 }, 538 + .macro => { 539 + const m = self.expr.macro; 540 + 541 + if (m.arglist.len != arglist.len) { 542 + try scope.setException("Macro called with incorrect number of arguments"); 543 + 544 + return RuntimeError.InvalidArguments; 545 + } 546 + 547 + if (m.arglist.len != 0) { 548 + for (m.arglist, 0..) |s, i| { 549 + try scope.insert(s, arglist[i]); 550 + } 551 + } 552 + 553 + break :blk scope.evalAst(m.body); 554 + }, 502 555 .native_func => { 503 556 const result = self.expr.native_func( 504 557 scope, 505 - arglist.items, 558 + arglist, 506 559 ); 507 560 break :blk result; 508 561 }, 509 562 .native_macro => { 510 563 const ast = self.expr.native_macro( 511 564 scope, 512 - arglist.items, 565 + arglist, 513 566 ) catch |e| return e; 514 567 break :blk scope.evalAst(ast); 515 568 }, ··· 722 775 .str => |s| return try Value.initString(self, s), 723 776 .sym => |s| return self.get(s) orelse try Value.initNil(self), 724 777 .quote => |v| return v, 725 - .function, .native_func, .native_macro, .userdata => return expr, 778 + .function, .macro, .native_func, .native_macro, .userdata => return expr, 726 779 .cons => { 727 780 const uneval_func = expr.getCar().?; 728 781 _ = self.getCallStack().append(uneval_func) catch return RuntimeError.External;
+8
test/macros.zexa
··· 1 + (define 'quote (macro (x) x)) 2 + (print (quote ferjifoer)) 3 + 4 + (define 'my-macro (macro (a) (print a))) 5 + (my-macro sym) ; should print 'sym instead of evaluating sym 6 + (my-macro (1 2 3)) 7 + (my-macro (print "Hello World")) 8 + (my-macro (eval my-macro))