DzLox

:)
git clone https://git.sr.ht/~ashymad/DzLox
Log | Files | Refs | Submodules | LICENSE

commit 21af2b44699d3f682c70983534e263ce8ebb4c83
parent d4f440b41b027c3333067b328ce19a5c3bab6aa1
Author: Szymon Mikulicz <szymon.mikulicz@posteo.net>
Date:   Fri,  6 Dec 2024 19:35:39 +0100

Bugfixes

Diffstat:
Mzlox/src/compiler.zig | 73++++++++++++++++++++++++++++++++++++-------------------------------------
Mzlox/src/gc.zig | 24+++++++++++-------------
Mzlox/src/obj/native.zig | 21+++++----------------
Mzlox/src/vm.zig | 64+++++++++++++++++++++++-----------------------------------------
Mzlox/src/vm/native.zig | 9++++-----
5 files changed, 79 insertions(+), 112 deletions(-)

diff --git a/zlox/src/compiler.zig b/zlox/src/compiler.zig @@ -262,11 +262,11 @@ pub fn Compiler(size: comptime_int) type { const argCount = self.argumentList(); self.emit(OP.CALL, argCount); } - + fn argumentList(self: *Self) u8 { var argCount: u8 = 0; if (!self.check(Token.RIGHT_PAREN)) { - while(true) { + while (true) { self.expression(); if (argCount == std.math.maxInt(u8)) { self.errorAtPrevious("Too many arguments"); @@ -287,7 +287,7 @@ pub fn Compiler(size: comptime_int) type { return err; })); } - + fn emitObj(self: *Self, comptime tp: Obj.Type, arg: tp.get().Arg) !void { self.emit(OP.CONSTANT, try self.makeObj(tp, arg)); } @@ -298,9 +298,9 @@ pub fn Compiler(size: comptime_int) type { var argCount: u8 = 0; var isList = true; - if (self.match(Token.RIGHT_BRACKET)) { - } else if (self.match(Token.COLON)) { + if (self.match(Token.RIGHT_BRACKET)) {} else if (self.match(Token.COLON)) { isList = false; + self.consume(Token.RIGHT_BRACKET, "Expect ']'"); } else { self.expression(); argCount += 1; @@ -309,12 +309,12 @@ pub fn Compiler(size: comptime_int) type { self.expression(); argCount += 1; } - while(!self.match(Token.RIGHT_BRACKET)) { + while (!self.match(Token.RIGHT_BRACKET)) { self.consume(Token.COMMA, "Expect ',' between expressions"); if (self.match(Token.RIGHT_BRACKET)) break; self.expression(); argCount += 1; - if(!isList) { + if (!isList) { self.consume(Token.COLON, "Expect ':' between key and value"); self.expression(); argCount += 1; @@ -322,9 +322,9 @@ pub fn Compiler(size: comptime_int) type { } } if (isList) { - self.currentChunk().code.set(offset, self.makeObj(.Native, Obj.Native.Arg{.name = "internal::list", .fun = vm_native.list}) catch return) catch return; + self.currentChunk().code.set(offset, self.makeObj(.Native, Obj.Native.Arg{ .name = "internal::list", .fun = vm_native.list }) catch return) catch return; } else { - self.currentChunk().code.set(offset, self.makeObj(.Native, Obj.Native.Arg{.name = "internal::table", .fun = vm_native.table}) catch return) catch return; + self.currentChunk().code.set(offset, self.makeObj(.Native, Obj.Native.Arg{ .name = "internal::table", .fun = vm_native.table }) catch return) catch return; } self.emit(OP.CALL, argCount); } @@ -345,15 +345,15 @@ pub fn Compiler(size: comptime_int) type { } fn namedVariable(self: *Self, tok: scanner.Token, canAssign: bool) void { - const OPs: struct {get: OP, set: OP, arg: u8} = if (self.resolveLocal(tok)) |arg| - .{.get = OP.GET_LOCAL, .set = OP.SET_LOCAL, .arg = arg} + const OPs: struct { get: OP, set: OP, arg: u8 } = if (self.resolveLocal(tok)) |arg| + .{ .get = OP.GET_LOCAL, .set = OP.SET_LOCAL, .arg = arg } else if (self.resolveUpvalue(tok)) |arg| - .{.get = OP.GET_UPVALUE, .set = OP.SET_UPVALUE, .arg = arg} + .{ .get = OP.GET_UPVALUE, .set = OP.SET_UPVALUE, .arg = arg } else - .{.get = OP.GET_GLOBAL, .set = OP.SET_GLOBAL, .arg = self.identifierConstant(tok) catch return}; + .{ .get = OP.GET_GLOBAL, .set = OP.SET_GLOBAL, .arg = self.identifierConstant(tok) catch return }; if (canAssign and self.match(Token.EQUAL)) { - if(OPs.get == OP.GET_LOCAL and self.locals[OPs.arg].con) { + if (OPs.get == OP.GET_LOCAL and self.locals[OPs.arg].con) { self.errorAtPrevious("Cannot assign to a constant"); return; } @@ -372,10 +372,10 @@ pub fn Compiler(size: comptime_int) type { } else if (enclosing.resolveUpvalue(name)) |upvalue| { return self.addUpvalue(upvalue, false) catch null; } - } + } return null; } - + fn addUpvalue(self: *Self, idx: u8, isLocal: bool) !u8 { const count = self.currentFunction.upvalue_count; @@ -391,7 +391,7 @@ pub fn Compiler(size: comptime_int) type { return self.lastError; } - self.upvalues[count] = .{.index = idx, .isLocal = isLocal}; + self.upvalues[count] = .{ .index = idx, .isLocal = isLocal }; self.currentFunction.upvalue_count += 1; return count; } @@ -399,9 +399,9 @@ pub fn Compiler(size: comptime_int) type { fn resolveLocal(self: *Self, name: scanner.Token) ?u8 { var i = self.localCount; while (i > 0) : (i -= 1) { - if (identifiersEql(self.locals[i-1].name, name)) { - if(self.locals[i-1].depth) |_| { - return @intCast(i-1); + if (identifiersEql(self.locals[i - 1].name, name)) { + if (self.locals[i - 1].depth) |_| { + return @intCast(i - 1); } else { self.errorAt(name, "Can't read local variable in it's own initializer"); } @@ -599,7 +599,7 @@ pub fn Compiler(size: comptime_int) type { self.consume(Token.IDENTIFIER, errorMessage); self.declareVariable(con); - if(self.scopeDepth > 0) return 0; + if (self.scopeDepth > 0) return 0; return self.identifierConstant(self.previous); } @@ -617,7 +617,7 @@ pub fn Compiler(size: comptime_int) type { var i = self.localCount; while (i > 0) : (i -= 1) { - const local = self.locals[i-1]; + const local = self.locals[i - 1]; if (local.depth) |depth| { if (depth < self.scopeDepth) break; } @@ -639,17 +639,17 @@ pub fn Compiler(size: comptime_int) type { self.errorAt(name, "Too many variables in function"); return; } - self.locals[self.localCount] = Local {.name = name, .con = con}; + self.locals[self.localCount] = Local{ .name = name, .con = con }; self.localCount += 1; } fn markInitialized(self: *Self) void { - if(self.scopeDepth == 0) return; - self.locals[self.localCount-1].depth = self.scopeDepth; + if (self.scopeDepth == 0) return; + self.locals[self.localCount - 1].depth = self.scopeDepth; } fn defineVariable(self: *Self, global: u8, con: bool) void { - if (self.scopeDepth > 0){ + if (self.scopeDepth > 0) { self.markInitialized(); return; } @@ -715,8 +715,8 @@ pub fn Compiler(size: comptime_int) type { self.consume(Token.LEFT_PAREN, "Expect '(' after 'switch'."); - self.emitObj(.Native, Obj.Native.Arg{.name = "switch", .fun = vm_native.table}) catch return; - + self.emitObj(.Native, Obj.Native.Arg{ .name = "switch", .fun = vm_native.table }) catch return; + var jumpOver = self.emitJump(OP.JUMP); const switchExpression = self.currentChunk().code.len; self.expression(); @@ -733,7 +733,7 @@ pub fn Compiler(size: comptime_int) type { self.consume(Token.LEFT_BRACE, "Expect '{' after switch()"); - while(!self.match(Token.RIGHT_BRACE)) { + while (!self.match(Token.RIGHT_BRACE)) { if (self.match(Token.CASE)) { self.expression(); argCount += 2; @@ -888,11 +888,10 @@ pub fn Compiler(size: comptime_int) type { self.currentChunk().code.set(offset + 1, @intCast(jump & 0xff)) catch { self.errorAtPrevious("Invalid jump offset"); }; - } fn block(self: *Self) void { - while(!self.check(Token.RIGHT_BRACE) and !self.check(Token.EOF)) { + while (!self.check(Token.RIGHT_BRACE) and !self.check(Token.EOF)) { self.declaration(); } @@ -906,13 +905,13 @@ pub fn Compiler(size: comptime_int) type { fn endScope(self: *Self) void { self.scopeDepth -= 1; - while(self.localCount > 0) { - if (self.locals[self.localCount-1].depth) |depth| { + while (self.localCount > 0) { + if (self.locals[self.localCount - 1].depth) |depth| { if (depth <= self.scopeDepth) break; } else { - self.errorAt(self.locals[self.localCount-1].name, "Unitialized variable at scope end"); + self.errorAt(self.locals[self.localCount - 1].name, "Unitialized variable at scope end"); } - if (self.locals[self.localCount-1].captured) { + if (self.locals[self.localCount - 1].captured) { self.emitOP(OP.CLOSE_UPVALUE); } else { self.emitOP(OP.POP); @@ -957,14 +956,14 @@ pub fn Compiler(size: comptime_int) type { .localCount = 1, .scopeDepth = 0, .enclosing = null, - .upvalues = [_]Upvalue{Upvalue{.index = 0, .isLocal = false}} ** upvalues_size, + .upvalues = [_]Upvalue{Upvalue{ .index = 0, .isLocal = false }} ** upvalues_size, }; self.locals[0].depth = 0; return self; } fn init_enclosed(enclosing: *Self, fun: *Obj.Function) Self { - var enclosed = Self.init(enclosing.scanner, enclosing.objects, fun); + var enclosed = Self.init(enclosing.scanner, enclosing.objects, fun); enclosed.current = enclosing.current; enclosed.enclosing = enclosing; enclosed.beginScope(); diff --git a/zlox/src/gc.zig b/zlox/src/gc.zig @@ -4,8 +4,7 @@ const list = @import("list.zig"); const Value = @import("value.zig").Value; pub const GC = struct { - - pub const Obj = @import("obj.zig").Obj(.{.mark = false}); + pub const Obj = @import("obj.zig").Obj(.{ .mark = false }); const Self = @This(); @@ -48,7 +47,7 @@ pub const GC = struct { if (DBG_STRESS) { self.collect(); } - dbg_print("Allocating {} at 0x{x}: {s}\n", .{obj.obj.type, @intFromPtr(obj), obj}); + dbg_print("Allocating {} at 0x{x}: {s}\n", .{ obj.obj.type, @intFromPtr(obj), obj }); try self.list.push(obj.cast()); } return obj; @@ -56,37 +55,36 @@ pub const GC = struct { pub fn markTable(table: anytype) void { const Table = @TypeOf(table); - table.for_each({}, struct{ + table.for_each({}, struct { pub fn fun(key: Table.Key, val: Table.Value) void { Self.mark(key); Self.mark(val); } }.fun); - } pub fn markArray(arr: anytype) void { - for(arr) |el| { + for (arr) |el| { Self.mark(el); } } pub fn mark(arg: anytype) void { const T = @TypeOf(arg); - switch(T) { - Value => switch(arg) { + switch (T) { + Value => switch (arg) { .obj => |o| { mark(o); }, - else => {} + else => {}, }, *Obj => { - dbg_print("Marking {} at 0x{x}: {s}\n", .{arg.type, @intFromPtr(arg), arg}); + dbg_print("Marking {} at 0x{x}: {s}\n", .{ arg.type, @intFromPtr(arg), arg }); arg.fields.mark = true; }, - else => if(Obj.isChild(T)) { + else => if (Obj.isChild(T)) { mark(arg.cast()); - } + }, } } @@ -97,7 +95,7 @@ pub const GC = struct { pub fn deinit(self: *Self) void { while (true) { const el = self.list.pop() catch break; - dbg_print("Freeing {} at 0x{x}: {s}\n", .{el.type, @intFromPtr(el), el}); + dbg_print("Freeing {} at 0x{x}: {s}\n", .{ el.type, @intFromPtr(el), el }); el.free(self.allocator); } self.list.free(); diff --git a/zlox/src/obj/native.zig b/zlox/src/obj/native.zig @@ -9,19 +9,14 @@ pub fn Native(fields: anytype) type { return packed struct { const Self = @This(); - pub const Error = error { OutOfMemory, Native }; + pub const Error = error{ OutOfMemory, Native }; pub const Fn = *const fn (*GC, []const Value) Error!Value; pub const ArityMin = 0; pub const ArityMax = std.math.maxInt(u8); - pub const Arg = struct { - fun: Fn, - arity_min: u8 = ArityMin, - arity_max: u8 = ArityMax, - name: []const u8 = "" - }; + pub const Arg = struct { fun: Fn, arity_min: u8 = ArityMin, arity_max: u8 = ArityMax, name: []const u8 = "" }; obj: Super, fun: Fn, @@ -32,19 +27,13 @@ pub fn Native(fields: anytype) type { pub fn init(arg: Arg, allocator: std.mem.Allocator) Error!*Self { const self: *Self = try allocator.create(Self); - self.* = Self{ - .obj = Super.make(Self), - .fun = arg.fun, - .arity_min = arg.arity_min, - .arity_max = arg.arity_max, - .name = arg.name.ptr, - .name_len = arg.name.len - }; + self.* = Self{ .obj = Super.make(Self), .fun = arg.fun, .arity_min = arg.arity_min, .arity_max = arg.arity_max, .name = arg.name.ptr, .name_len = arg.name.len }; return self; } pub fn call(self: *const Self, gc: *GC, argCount: u8, args: [*]Value) Error!Value { - return self.fun(gc, args[0..argCount]); + const callable = self.fun; // Compiler bug: https://github.com/ziglang/zig/issues/20539 + return callable(gc, args[0..argCount]); } pub fn cast(self: anytype) utils.copy_const(@TypeOf(self), *Super) { diff --git a/zlox/src/vm.zig b/zlox/src/vm.zig @@ -31,14 +31,14 @@ pub const VM = struct { } pub fn make_var(v: Value) Self { - return Self { + return Self{ .val = v, .con = false, }; } pub fn make_con(v: Value) Self { - return Self { + return Self{ .val = v, .con = true, }; @@ -54,27 +54,17 @@ pub const VM = struct { chunk: *const Chunk, pub fn init(comptime tp: Obj.Type, callee: *const tp.get(), slots: [*]Value) @This() { - return switch(tp) { - .Function => @This() { - .callee = callee.cast(), - .ip = callee.chunk.code.data.ptr, - .chunk = callee.chunk, - .slots = slots - }, - .Closure => @This() { - .callee = callee.cast(), - .ip = callee.function.chunk.code.data.ptr, - .chunk = callee.function.chunk, - .slots = slots - }, - else => @compileError("Invalid type") + return switch (tp) { + .Function => @This(){ .callee = callee.cast(), .ip = callee.chunk.code.data.ptr, .chunk = callee.chunk, .slots = slots }, + .Closure => @This(){ .callee = callee.cast(), .ip = callee.function.chunk.code.data.ptr, .chunk = callee.function.chunk, .slots = slots }, + else => @compileError("Invalid type"), }; } }; fn defineNative(self: *@This(), name: []const u8, arity_min: u8, arity_max: u8, fun: Obj.Native.Fn) !void { const nameObj = try self.objects.emplace(.String, &.{name}); - const funObj = try self.objects.emplace_cast(.Native, Obj.Native.Arg{.fun = fun, .name = name, .arity_min = arity_min, .arity_max = arity_max}); + const funObj = try self.objects.emplace_cast(.Native, Obj.Native.Arg{ .fun = fun, .name = name, .arity_min = arity_min, .arity_max = arity_max }); _ = try self.globals.set(nameObj, Global.make_con(Value.init(funObj))); } @@ -114,14 +104,7 @@ pub const VM = struct { open_upvalues: List, pub fn run(vm: *VM, function: *Obj.Function, dbg: bool) InterpreterError!void { - var self = @This(){ - .frames = [_]CallFrame{undefined} ** callstack_size, - .frameCount = 1, - .stack = [_]Value{Value.init({})} ** stack_size, - .stackTop = undefined, - .vm = vm, - .open_upvalues = List.init(vm.allocator) - }; + var self = @This(){ .frames = [_]CallFrame{undefined} ** callstack_size, .frameCount = 1, .stack = [_]Value{Value.init({})} ** stack_size, .stackTop = undefined, .vm = vm, .open_upvalues = List.init(vm.allocator) }; defer self.open_upvalues.free(); @@ -182,11 +165,11 @@ pub const VM = struct { } fn callValue(self: *@This(), callee: Value, argCount: u8) !void { - if(callee.is(Obj.Type.Function)) { + if (callee.is(Obj.Type.Function)) { try self.callFunction(callee.obj.cast(.Function) catch unreachable, argCount); - } else if(callee.is(Obj.Type.Closure)) { + } else if (callee.is(Obj.Type.Closure)) { try self.callClosure(callee.obj.cast(.Closure) catch unreachable, argCount); - } else if(callee.is(Obj.Type.Native)) { + } else if (callee.is(Obj.Type.Native)) { try self.callNative(callee.obj.cast(.Native) catch unreachable, argCount); } else { self.runtimeError("Can only call functions and classes", .{}); @@ -196,7 +179,7 @@ pub const VM = struct { fn callClosure(self: *@This(), callee: *Obj.Closure, argCount: u8) !void { if (argCount != callee.function.arity) { - self.runtimeError("Expected {d} arguments but got {d}", .{callee.function.arity, argCount}); + self.runtimeError("Expected {d} arguments but got {d}", .{ callee.function.arity, argCount }); return InterpreterError.RuntimeError; } if (self.frameCount == callstack_size - 1) @@ -205,10 +188,9 @@ pub const VM = struct { self.frames[self.frameCount - 1] = CallFrame.init(.Closure, callee, self.stackTop - argCount - 1); } - fn callFunction(self: *@This(), callee: *Obj.Function, argCount: u8) !void { if (argCount != callee.arity) { - self.runtimeError("Expected {d} arguments but got {d}", .{callee.arity, argCount}); + self.runtimeError("Expected {d} arguments but got {d}", .{ callee.arity, argCount }); return InterpreterError.RuntimeError; } if (self.frameCount == callstack_size - 1) @@ -219,7 +201,7 @@ pub const VM = struct { fn callNative(self: *@This(), native: *Obj.Native, argCount: u8) !void { if (argCount < native.arity_min or argCount > native.arity_max) { - self.runtimeError("Expected from {d} to {d} arguments but got {d}", .{native.arity_min, native.arity_max, argCount}); + self.runtimeError("Expected from {d} to {d} arguments but got {d}", .{ native.arity_min, native.arity_max, argCount }); return InterpreterError.RuntimeError; } const result = try native.call(&self.vm.objects, argCount, self.stackTop - argCount); @@ -230,20 +212,20 @@ pub const VM = struct { fn captureUpvalue(self: *@This(), slot: u8) !*Obj.Upvalue { var upvalue = self.open_upvalues.tip; - while(upvalue) |el| : (upvalue = el.next) { + while (upvalue) |el| : (upvalue = el.next) { const val = el.val.?; if (val.slot == slot) return val; if (val.slot > slot) break; } - const new = try self.vm.objects.emplace(.Upvalue, .{.val = &self.frame().slots[slot], .slot = slot}); + const new = try self.vm.objects.emplace(.Upvalue, .{ .val = &self.frame().slots[slot], .slot = slot }); try self.open_upvalues.insert_after(upvalue, new); return new; } fn closeUpvalues(self: *@This(), slot: u8) !void { - while(self.open_upvalues.tip) |el| { + while (self.open_upvalues.tip) |el| { if (el.val.?.slot < slot) break; const upval = self.open_upvalues.pop() catch unreachable; @@ -372,7 +354,7 @@ pub const VM = struct { const obj = self.pop(); var pushed = false; if (obj.is(Value.obj)) { - switch(obj.obj.type) { + switch (obj.obj.type) { .Function, .Native, .Closure, .Upvalue => {}, inline else => |tp| { self.push((obj.obj.cast(tp) catch unreachable).get(key) catch Value.init({})); @@ -391,7 +373,7 @@ pub const VM = struct { const obj = self.pop(); var pushed = false; if (obj.is(Value.obj)) { - switch(obj.obj.type) { + switch (obj.obj.type) { .Function, .Native, .Closure, .Upvalue, .String => {}, inline else => |tp| { var m = obj.obj.cast(tp) catch unreachable; @@ -401,7 +383,7 @@ pub const VM = struct { _ = try m.set(key, val); } pushed = true; - } + }, } } if (!pushed) { @@ -417,10 +399,10 @@ pub const VM = struct { @intFromEnum(OP.CLOSURE) => { const function = try self.read_constant().obj.cast(.Function); const closure = try self.vm.objects.emplace(.Closure, function); - for(closure.upvalues[0..closure.upvalues_len]) |*upvalue| { + for (closure.upvalues[0..closure.upvalues_len]) |*upvalue| { const isLocal = self.read_byte(); const slot = self.read_byte(); - if(isLocal == 1) { + if (isLocal == 1) { upvalue.* = try self.captureUpvalue(slot); } else { const callee = try self.frame().callee.cast(.Closure); @@ -451,7 +433,7 @@ pub const VM = struct { while (true) : (i -= 1) { const fram = self.frames[i]; const idx = @intFromPtr(fram.ip) - @intFromPtr(fram.chunk.code.data.ptr); - std.debug.print("[line {d}] in {s}\n", .{fram.chunk.lines.get(idx) catch 1, fram.callee}); + std.debug.print("[line {d}] in {s}\n", .{ fram.chunk.lines.get(idx) catch 1, fram.callee }); if (i == 0) break; } std.debug.print(fmt ++ "\n", args); diff --git a/zlox/src/vm/native.zig b/zlox/src/vm/native.zig @@ -11,14 +11,13 @@ pub const Clock = struct { start = try std.time.Instant.now(); } - pub fn clock(_: *GC, _: [] const Value) Error!Value { + pub fn clock(_: *GC, _: []const Value) Error!Value { const now = std.time.Instant.now() catch return Error.Native; const elapsed: f64 = @floatFromInt(now.since(start)); return Value.init(elapsed / std.time.ns_per_s); } }; - pub fn put(_: *GC, args: []const Value) Error!Value { std.debug.print("{s}", .{args[0]}); return Value.init({}); @@ -28,15 +27,15 @@ pub fn table(gc: *GC, args: []const Value) Error!Value { var tbl = gc.emplace(.Table, {}) catch return Error.Native; if (args.len % 2 != 0) return Error.Native; var i: usize = 0; - while(i < args.len) : (i += 2) { - _ = tbl.set(args[i], args[i+1]) catch return Error.Native; + while (i < args.len) : (i += 2) { + _ = tbl.set(args[i], args[i + 1]) catch return Error.Native; } return Value.init(tbl.cast()); } pub fn list(gc: *GC, args: []const Value) Error!Value { var lis = gc.emplace(.List, {}) catch return Error.Native; - for(args) |arg| { + for (args) |arg| { lis.list.push(arg) catch return Error.Native; } return Value.init(lis.cast());