From eb935a2c3bafd26627d9836894590b39647fbc2c Mon Sep 17 00:00:00 2001 From: David Rubin Date: Thu, 29 Feb 2024 02:57:35 -0800 Subject: [PATCH 1/5] basic function calling --- demo/test.py | 6 ++++-- src/vm/Object.zig | 19 +++++++++++++++++ src/vm/Vm.zig | 53 ++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 73 insertions(+), 5 deletions(-) diff --git a/demo/test.py b/demo/test.py index ab3cf4d..f46cef0 100644 --- a/demo/test.py +++ b/demo/test.py @@ -1,2 +1,4 @@ -a = 1 -print(a) \ No newline at end of file +def test(): + print(1) + +test() \ No newline at end of file diff --git a/src/vm/Object.zig b/src/vm/Object.zig index f089d46..64ef102 100644 --- a/src/vm/Object.zig +++ b/src/vm/Object.zig @@ -1,6 +1,7 @@ const std = @import("std"); const Vm = @import("Vm.zig"); const builtins = @import("../builtins.zig"); +const Co = @import("../compiler/CodeObject.zig"); const BigIntConst = std.math.big.int.Const; const BigIntMutable = std.math.big.int.Mutable; @@ -43,6 +44,9 @@ pub const Tag = enum(usize) { /// A builtin Zig defined function. zig_function, + codeobject, + function, + pub fn PayloadType(comptime t: Tag) type { assert(@intFromEnum(t) >= Tag.first_payload); @@ -57,6 +61,8 @@ pub const Tag = enum(usize) { .list => Payload.List, .zig_function => Payload.ZigFunc, + .codeobject => Payload.CodeObject, + .function => Payload.PythonFunction, .none => unreachable, else => @compileError("TODO: PayloadType " ++ @tagName(t)), @@ -111,6 +117,8 @@ pub const Payload = union(enum) { zig_func: ZigFunc, tuple: Tuple, list: List, + codeobject: CodeObject, + function: PythonFunction, pub const Value = union(enum) { int: BigIntManaged, @@ -119,7 +127,9 @@ pub const Payload = union(enum) { }; pub const ZigFunc = *const builtins.func_proto; + pub const Tuple = []const Object; + pub const List = struct { list: std.ArrayListUnmanaged(Object), @@ -140,6 +150,15 @@ pub const Payload = union(enum) { try vm.stack.append(vm.allocator, return_val); } }; + + pub const CodeObject = struct { + co: *Co, + }; + + pub const PythonFunction = struct { + name: []const u8, + co: *Co, + }; }; pub fn format( diff --git a/src/vm/Vm.zig b/src/vm/Vm.zig index 7c32294..29b74fd 100644 --- a/src/vm/Vm.zig +++ b/src/vm/Vm.zig @@ -105,6 +105,7 @@ fn exec(vm: *Vm, inst: Instruction) !void { .LOAD_CONST => try vm.execLoadConst(inst), .LOAD_NAME => try vm.execLoadName(inst), .LOAD_METHOD => try vm.execLoadMethod(inst), + .LOAD_GLOBAL => try vm.execLoadGlobal(inst), .BUILD_LIST => try vm.execBuildList(inst), .STORE_NAME => try vm.execStoreName(inst), @@ -126,6 +127,8 @@ fn exec(vm: *Vm, inst: Instruction) !void { .CALL_FUNCTION_KW => try vm.execCallFunctionKW(inst), .CALL_METHOD => try vm.execCallMethod(inst), + .MAKE_FUNCTION => try vm.execMakeFunction(inst), + else => std.debug.panic("TODO: {s}", .{@tagName(inst.op)}), } } @@ -158,6 +161,13 @@ fn execLoadMethod(vm: *Vm, inst: Instruction) !void { try vm.stack.append(vm.allocator, tos); } +fn execLoadGlobal(vm: *Vm, inst: Instruction) !void { + const name = vm.current_co.getName(inst.extra); + const val = vm.scope.get(name) orelse + std.debug.panic("couldn't find '{s}' on the global scope", .{name}); + try vm.stack.append(vm.allocator, val); +} + fn execStoreName(vm: *Vm, inst: Instruction) !void { const name = vm.current_co.getName(inst.extra); const tos = vm.stack.pop(); @@ -183,10 +193,22 @@ fn execBuildList(vm: *Vm, inst: Instruction) !void { fn execCallFunction(vm: *Vm, inst: Instruction) !void { const args = try vm.popNObjects(inst.extra); - const func = vm.stack.pop(); - const func_ptr = func.get(.zig_function); + const func_object = vm.stack.pop(); + + if (func_object.tag == .zig_function) { + const func_ptr = func_object.get(.zig_function); + + try @call(.auto, func_ptr.*, .{ vm, args, null }); + return; + } - try @call(.auto, func_ptr.*, .{ vm, args, null }); + if (func_object.tag == .function) { + const func = func_object.get(.function); + try func.co.process(vm.allocator); + + // TODO: questionable pass by value. doesn't work if it isn't, but it shouldn't be. + vm.current_co.* = func.co.*; + } } fn execCallFunctionKW(vm: *Vm, inst: Instruction) !void { @@ -331,6 +353,28 @@ fn execPopJump(vm: *Vm, inst: Instruction, case: bool) !void { } } +fn execMakeFunction(vm: *Vm, inst: Instruction) !void { + if (inst.extra != 0x00) @panic("Don't support function flags yet"); + + const name_object = vm.stack.pop(); + const co_object = vm.stack.pop(); + + // std.debug.print("Name Tag: {}\n", .{name_object.get(.int).int}); + + assert(name_object.tag == .string); + assert(co_object.tag == .codeobject); + + const name = name_object.get(.string).string; + const co = co_object.get(.codeobject).co; + + const function = try Object.create(.function, vm.allocator, .{ + .name = name, + .co = co, + }); + + try vm.stack.append(vm.allocator, function); +} + // Helpers /// Pops `n` items off the stack in reverse order and returns them. @@ -366,6 +410,9 @@ fn loadConst(vm: *Vm, inst: Marshal.Result) !Object { } return Object.create(.tuple, vm.allocator, items); }, + .CodeObject => |co| { + return Object.create(.codeobject, vm.allocator, .{ .co = co }); + }, else => std.debug.panic("TODO: loadConst {s}", .{@tagName(inst)}), } } From 72edbf6d01675f1b31e63fe0c700ffc5064d266d Mon Sep 17 00:00:00 2001 From: David Rubin Date: Thu, 29 Feb 2024 03:03:29 -0800 Subject: [PATCH 2/5] make a seperate `global_scope` --- src/vm/Vm.zig | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/vm/Vm.zig b/src/vm/Vm.zig index 29b74fd..e9a11c6 100644 --- a/src/vm/Vm.zig +++ b/src/vm/Vm.zig @@ -29,7 +29,8 @@ current_co: *CodeObject, is_running: bool, stack: std.ArrayListUnmanaged(Object) = .{}, -scope: std.StringArrayHashMapUnmanaged(Object) = .{}, +scope: std.StringHashMapUnmanaged(Object) = .{}, +global_scope: std.StringHashMapUnmanaged(Object) = .{}, pub fn init() !Vm { const t = tracer.trace(@src(), "", .{}); @@ -41,6 +42,7 @@ pub fn init() !Vm { .current_co = undefined, .stack = .{}, .scope = .{}, + .global_scope = .{}, }; } @@ -60,6 +62,7 @@ pub fn run( // Setup vm.scope = .{}; + vm.global_scope = .{}; vm.stack = .{}; // Generate instruction wrapper. @@ -74,7 +77,7 @@ pub fn run( vm.allocator, fn_ptr, ); - try vm.scope.put(vm.allocator, name, func_val); + try vm.global_scope.put(vm.allocator, name, func_val); } vm.is_running = true; @@ -143,7 +146,8 @@ fn execLoadConst(vm: *Vm, inst: Instruction) !void { fn execLoadName(vm: *Vm, inst: Instruction) !void { const name = vm.current_co.getName(inst.extra); const val = vm.scope.get(name) orelse - std.debug.panic("couldn't find '{s}' on the scope", .{name}); + vm.global_scope.get(name) orelse + std.debug.panic("couldn't find '{s}' on the scope or global scope", .{name}); try vm.stack.append(vm.allocator, val); } @@ -163,7 +167,7 @@ fn execLoadMethod(vm: *Vm, inst: Instruction) !void { fn execLoadGlobal(vm: *Vm, inst: Instruction) !void { const name = vm.current_co.getName(inst.extra); - const val = vm.scope.get(name) orelse + const val = vm.global_scope.get(name) orelse std.debug.panic("couldn't find '{s}' on the global scope", .{name}); try vm.stack.append(vm.allocator, val); } @@ -173,7 +177,6 @@ fn execStoreName(vm: *Vm, inst: Instruction) !void { const tos = vm.stack.pop(); // TODO: don't want to clobber here, make it more controlled. - // i know i will forget why variables are being overwritten correctly. try vm.scope.put(vm.allocator, name, tos); } From 1a6cca0279235eff9a626f12416b6d8e11948826 Mon Sep 17 00:00:00 2001 From: David Rubin Date: Thu, 29 Feb 2024 03:14:11 -0800 Subject: [PATCH 3/5] basic returning and scoping --- demo/test.py | 11 +++++++++-- src/vm/Vm.zig | 35 ++++++++++++++++++++++++++++++----- tests/behaviour/functions.py | 11 +++++++++++ 3 files changed, 50 insertions(+), 7 deletions(-) create mode 100644 tests/behaviour/functions.py diff --git a/demo/test.py b/demo/test.py index f46cef0..6768193 100644 --- a/demo/test.py +++ b/demo/test.py @@ -1,4 +1,11 @@ -def test(): +def a(): print(1) -test() \ No newline at end of file +def b(): + c() + +def c(): + print(2) + +# a() +b() \ No newline at end of file diff --git a/src/vm/Vm.zig b/src/vm/Vm.zig index e9a11c6..81fcd4d 100644 --- a/src/vm/Vm.zig +++ b/src/vm/Vm.zig @@ -25,6 +25,16 @@ allocator: Allocator, current_co: *CodeObject, +/// When we enter into a deeper scope, we push the previous code object +/// onto here. Then when we leave it, we restore this one. +/// +/// TODO: Remove deeper scope variables +co_stack: std.ArrayListUnmanaged(CodeObject), + +/// When at `depth` 0, this is considered the global scope. loads will +/// be targeted at the `global_scope`. +depth: u32 = 0, + /// VM State is_running: bool, @@ -42,6 +52,7 @@ pub fn init() !Vm { .current_co = undefined, .stack = .{}, .scope = .{}, + .co_stack = .{}, .global_scope = .{}, }; } @@ -64,6 +75,7 @@ pub fn run( vm.scope = .{}; vm.global_scope = .{}; vm.stack = .{}; + vm.co_stack = .{}; // Generate instruction wrapper. try co.process(vm.allocator); @@ -85,13 +97,14 @@ pub fn run( while (vm.is_running) { const instruction = co.instructions[vm.current_co.index]; log.debug( - "Executing Instruction: {} (stacksize={}, pc={}/{}, mem={s})", + "Executing Instruction: {} (stack_size={}, pc={}/{}, mem={s}, depth={})", .{ instruction.op, vm.stack.items.len, co.index, co.instructions.len, std.fmt.fmtIntSizeDec(arena.state.end_index), + vm.depth, }, ); @@ -176,13 +189,21 @@ fn execStoreName(vm: *Vm, inst: Instruction) !void { const name = vm.current_co.getName(inst.extra); const tos = vm.stack.pop(); - // TODO: don't want to clobber here, make it more controlled. - try vm.scope.put(vm.allocator, name, tos); + if (vm.depth == 0) { + try vm.global_scope.put(vm.allocator, name, tos); + } else { + try vm.scope.put(vm.allocator, name, tos); + } } fn execReturnValue(vm: *Vm) !void { - // TODO: More logic here - vm.is_running = false; + if (vm.depth == 0) { + vm.is_running = false; + return; + } + + vm.current_co.* = vm.co_stack.pop(); + vm.depth -= 1; } fn execBuildList(vm: *Vm, inst: Instruction) !void { @@ -209,8 +230,12 @@ fn execCallFunction(vm: *Vm, inst: Instruction) !void { const func = func_object.get(.function); try func.co.process(vm.allocator); + try vm.co_stack.append(vm.allocator, vm.current_co.*); + // TODO: questionable pass by value. doesn't work if it isn't, but it shouldn't be. vm.current_co.* = func.co.*; + + vm.depth += 1; } } diff --git a/tests/behaviour/functions.py b/tests/behaviour/functions.py new file mode 100644 index 0000000..18ca65a --- /dev/null +++ b/tests/behaviour/functions.py @@ -0,0 +1,11 @@ +def a(): + print(1) + +def b(): + c() + +def c(): + print(2) + +a() +b() \ No newline at end of file From 7583f78724fef7caa5e1f6b98562b3b0f47b7d53 Mon Sep 17 00:00:00 2001 From: David Rubin Date: Thu, 29 Feb 2024 04:34:59 -0800 Subject: [PATCH 4/5] more scoping! --- demo/test.py | 14 ++--- src/compiler/CodeObject.zig | 3 + src/compiler/Marshal.zig | 9 +++ src/vm/Vm.zig | 111 +++++++++++++++++++++++++----------- 4 files changed, 96 insertions(+), 41 deletions(-) diff --git a/demo/test.py b/demo/test.py index 6768193..b117674 100644 --- a/demo/test.py +++ b/demo/test.py @@ -1,11 +1,9 @@ -def a(): - print(1) - +a = 1 def b(): - c() + c = 20 + print(c) -def c(): - print(2) -# a() -b() \ No newline at end of file +b() +print(c) + \ No newline at end of file diff --git a/src/compiler/CodeObject.zig b/src/compiler/CodeObject.zig index 8cd3bb4..7d1762a 100644 --- a/src/compiler/CodeObject.zig +++ b/src/compiler/CodeObject.zig @@ -4,6 +4,7 @@ const std = @import("std"); const Marshal = @import("Marshal.zig"); const Instruction = @import("Instruction.zig"); const OpCode = @import("opcodes.zig").OpCode; +const Object = @import("../vm/Object.zig"); const Result = Marshal.Result; const Reference = Marshal.Reference; const FlagRef = Marshal.FlagRef; @@ -31,6 +32,8 @@ stack_size: u32, /// ByteCode code: []const u8, +varnames: []Object, + // Interal reference table. flag_refs: []const ?FlagRef, diff --git a/src/compiler/Marshal.zig b/src/compiler/Marshal.zig index 4ffdd9b..dd8a8f7 100644 --- a/src/compiler/Marshal.zig +++ b/src/compiler/Marshal.zig @@ -3,6 +3,8 @@ const std = @import("std"); const ObjType = @import("objtype.zig").ObjType; const CodeObject = @import("CodeObject.zig"); +const Object = @import("../vm/Object.zig"); +const Vm = @import("../vm/Vm.zig"); const tracer = @import("tracer"); const Marshal = @This(); @@ -198,6 +200,13 @@ fn read_codeobject(marshal: *Marshal) Result { const filename = dict.get("filename").?; co.filename = filename.String; + const varname_tuple = dict.get("varnames").?.Tuple; + const varnames = marshal.allocator.alloc(Object, varname_tuple.len) catch @panic("OOM"); + for (varname_tuple, 0..) |elem, i| { + varnames[i] = Vm.loadConst(marshal.allocator, elem) catch @panic("OOM"); + } + co.varnames = varnames; + co.consts = dict.get("consts").?.Tuple; co.stack_size = @intCast(dict.get("stacksize").?.Int); co.code = dict.get("code").?.String; diff --git a/src/vm/Vm.zig b/src/vm/Vm.zig index 81fcd4d..fa63039 100644 --- a/src/vm/Vm.zig +++ b/src/vm/Vm.zig @@ -27,9 +27,7 @@ current_co: *CodeObject, /// When we enter into a deeper scope, we push the previous code object /// onto here. Then when we leave it, we restore this one. -/// -/// TODO: Remove deeper scope variables -co_stack: std.ArrayListUnmanaged(CodeObject), +co_stack: std.ArrayListUnmanaged(CodeObject) = .{}, /// When at `depth` 0, this is considered the global scope. loads will /// be targeted at the `global_scope`. @@ -39,8 +37,7 @@ depth: u32 = 0, is_running: bool, stack: std.ArrayListUnmanaged(Object) = .{}, -scope: std.StringHashMapUnmanaged(Object) = .{}, -global_scope: std.StringHashMapUnmanaged(Object) = .{}, +scopes: std.ArrayListUnmanaged(std.StringHashMapUnmanaged(Object)) = .{}, pub fn init() !Vm { const t = tracer.trace(@src(), "", .{}); @@ -50,10 +47,6 @@ pub fn init() !Vm { .allocator = undefined, .is_running = false, .current_co = undefined, - .stack = .{}, - .scope = .{}, - .co_stack = .{}, - .global_scope = .{}, }; } @@ -72,11 +65,17 @@ pub fn run( vm.allocator = allocator; // Setup - vm.scope = .{}; - vm.global_scope = .{}; + vm.scopes = .{}; vm.stack = .{}; vm.co_stack = .{}; + // The global scope + const global_scope: std.StringHashMapUnmanaged(Object) = .{}; + try vm.scopes.append(vm.allocator, global_scope); + + // The top level scope should be the global scope. + assert(vm.scopes.items.len == 1); + // Generate instruction wrapper. try co.process(vm.allocator); vm.current_co = co; @@ -89,7 +88,7 @@ pub fn run( vm.allocator, fn_ptr, ); - try vm.global_scope.put(vm.allocator, name, func_val); + try vm.scopes.items[0].put(vm.allocator, name, func_val); } vm.is_running = true; @@ -122,10 +121,13 @@ fn exec(vm: *Vm, inst: Instruction) !void { .LOAD_NAME => try vm.execLoadName(inst), .LOAD_METHOD => try vm.execLoadMethod(inst), .LOAD_GLOBAL => try vm.execLoadGlobal(inst), + .LOAD_FAST => try vm.execLoadFast(inst), + .BUILD_LIST => try vm.execBuildList(inst), .STORE_NAME => try vm.execStoreName(inst), .STORE_SUBSCR => try vm.execStoreSubScr(), + .STORE_FAST => try vm.execStoreFast(inst), .RETURN_VALUE => try vm.execReturnValue(), @@ -152,15 +154,14 @@ fn exec(vm: *Vm, inst: Instruction) !void { /// Stores an immediate Constant on the stack. fn execLoadConst(vm: *Vm, inst: Instruction) !void { const constant = vm.current_co.consts[inst.extra]; - const val = try vm.loadConst(constant); + const val = try loadConst(vm.allocator, constant); try vm.stack.append(vm.allocator, val); } fn execLoadName(vm: *Vm, inst: Instruction) !void { const name = vm.current_co.getName(inst.extra); - const val = vm.scope.get(name) orelse - vm.global_scope.get(name) orelse - std.debug.panic("couldn't find '{s}' on the scope or global scope", .{name}); + const val = vm.lookUpwards(name) orelse + std.debug.panic("couldn't find '{s}'", .{name}); try vm.stack.append(vm.allocator, val); } @@ -180,20 +181,22 @@ fn execLoadMethod(vm: *Vm, inst: Instruction) !void { fn execLoadGlobal(vm: *Vm, inst: Instruction) !void { const name = vm.current_co.getName(inst.extra); - const val = vm.global_scope.get(name) orelse + const val = vm.scopes.items[0].get(name) orelse std.debug.panic("couldn't find '{s}' on the global scope", .{name}); try vm.stack.append(vm.allocator, val); } +fn execLoadFast(vm: *Vm, inst: Instruction) !void { + const var_num = inst.extra; + const obj = vm.current_co.varnames[var_num]; + try vm.stack.append(vm.allocator, obj); +} + fn execStoreName(vm: *Vm, inst: Instruction) !void { const name = vm.current_co.getName(inst.extra); const tos = vm.stack.pop(); - if (vm.depth == 0) { - try vm.global_scope.put(vm.allocator, name, tos); - } else { - try vm.scope.put(vm.allocator, name, tos); - } + try vm.scopes.items[vm.depth].put(vm.allocator, name, tos); } fn execReturnValue(vm: *Vm) !void { @@ -202,6 +205,9 @@ fn execReturnValue(vm: *Vm) !void { return; } + // Only the return value should be left. + assert(vm.stack.items.len == 1); + vm.current_co.* = vm.co_stack.pop(); vm.depth -= 1; } @@ -368,6 +374,13 @@ fn execStoreSubScr(vm: *Vm) !void { } } +fn execStoreFast(vm: *Vm, inst: Instruction) !void { + const var_num = inst.extra; + const tos = vm.stack.pop(); + + vm.current_co.varnames[var_num] = tos; +} + fn execPopJump(vm: *Vm, inst: Instruction, case: bool) !void { const tos = vm.stack.pop(); @@ -387,8 +400,6 @@ fn execMakeFunction(vm: *Vm, inst: Instruction) !void { const name_object = vm.stack.pop(); const co_object = vm.stack.pop(); - // std.debug.print("Name Tag: {}\n", .{name_object.get(.int).int}); - assert(name_object.tag == .string); assert(co_object.tag == .codeobject); @@ -418,29 +429,63 @@ fn popNObjects(vm: *Vm, n: usize) ![]Object { return objects; } -fn loadConst(vm: *Vm, inst: Marshal.Result) !Object { +pub fn loadConst(allocator: Allocator, inst: Marshal.Result) !Object { switch (inst) { .Int => |int| { - const big_int = try BigIntManaged.initSet(vm.allocator, int); - return Object.create(.int, vm.allocator, .{ .int = big_int }); + const big_int = try BigIntManaged.initSet(allocator, int); + return Object.create(.int, allocator, .{ .int = big_int }); }, .String => |string| { - return Object.create(.string, vm.allocator, .{ .string = string }); + return Object.create(.string, allocator, .{ .string = string }); }, .Bool => |boolean| { - return Object.create(.boolean, vm.allocator, .{ .boolean = boolean }); + return Object.create(.boolean, allocator, .{ .boolean = boolean }); }, .None => return Object.init(.none), .Tuple => |tuple| { - var items = try vm.allocator.alloc(Object, tuple.len); + var items = try allocator.alloc(Object, tuple.len); for (tuple, 0..) |elem, i| { - items[i] = try vm.loadConst(elem); + items[i] = try loadConst(allocator, elem); } - return Object.create(.tuple, vm.allocator, items); + return Object.create(.tuple, allocator, items); }, .CodeObject => |co| { - return Object.create(.codeobject, vm.allocator, .{ .co = co }); + return Object.create(.codeobject, allocator, .{ .co = co }); }, else => std.debug.panic("TODO: loadConst {s}", .{@tagName(inst)}), } } + +/// Looks upwards in the scopes from the current depth and tries to find name. +/// +/// Looks at current scope -> global scope -> rest to up index 1, for what I think is the hottest paths. +fn lookUpwards(vm: *Vm, name: []const u8) ?Object { + const scopes = vm.scopes.items; + + return obj: { + // Check the immediate scope. + if (scopes[vm.depth].get(name)) |val| { + break :obj val; + } + + // If we didn't find it in the immediate scope, and there is only one scope + // means it doesn't exist. + if (scopes.len == 1) break :obj null; + + // Now there are at least two scopes, so check the global scope + // as it's pretty likely they are accessing a global. + if (scopes[0].get(name)) |val| { + break :obj val; + } + + // Now we just search upwards from vm.depth -> scopes[1] (as we already searched global) + for (1..vm.depth) |i| { + const index = vm.depth - i; + if (scopes[index].get(name)) |val| { + break :obj val; + } + } + + break :obj null; + }; +} From 9e3fff8ec1b050b3f5ee62f409988ffe49d182b0 Mon Sep 17 00:00:00 2001 From: David Rubin Date: Thu, 29 Feb 2024 05:27:35 -0800 Subject: [PATCH 5/5] figured out positional function args! --- demo/test.py | 12 +++++------- src/vm/Object.zig | 1 + src/vm/Vm.zig | 9 ++++++++- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/demo/test.py b/demo/test.py index b117674..9c4fe5f 100644 --- a/demo/test.py +++ b/demo/test.py @@ -1,9 +1,7 @@ -a = 1 -def b(): - c = 20 - print(c) +a = 10 +def b(x): + # c = "test" + print(x) -b() -print(c) - \ No newline at end of file +b(a) \ No newline at end of file diff --git a/src/vm/Object.zig b/src/vm/Object.zig index 64ef102..e1ea4d7 100644 --- a/src/vm/Object.zig +++ b/src/vm/Object.zig @@ -210,6 +210,7 @@ pub fn format( try writer.writeAll(")"); }, + else => try writer.print("TODO: Object.format '{s}'", .{@tagName(object.tag)}), } } diff --git a/src/vm/Vm.zig b/src/vm/Vm.zig index fa63039..57197af 100644 --- a/src/vm/Vm.zig +++ b/src/vm/Vm.zig @@ -189,13 +189,13 @@ fn execLoadGlobal(vm: *Vm, inst: Instruction) !void { fn execLoadFast(vm: *Vm, inst: Instruction) !void { const var_num = inst.extra; const obj = vm.current_co.varnames[var_num]; + log.debug("LoadFast obj tag: {s}", .{@tagName(obj.tag)}); try vm.stack.append(vm.allocator, obj); } fn execStoreName(vm: *Vm, inst: Instruction) !void { const name = vm.current_co.getName(inst.extra); const tos = vm.stack.pop(); - try vm.scopes.items[vm.depth].put(vm.allocator, name, tos); } @@ -242,6 +242,11 @@ fn execCallFunction(vm: *Vm, inst: Instruction) !void { vm.current_co.* = func.co.*; vm.depth += 1; + + // Set the args. + for (args, 0..) |arg, i| { + vm.current_co.varnames[i] = arg; + } } } @@ -462,6 +467,8 @@ pub fn loadConst(allocator: Allocator, inst: Marshal.Result) !Object { fn lookUpwards(vm: *Vm, name: []const u8) ?Object { const scopes = vm.scopes.items; + log.debug("lookUpwards depth {}", .{vm.depth}); + return obj: { // Check the immediate scope. if (scopes[vm.depth].get(name)) |val| {