DzLox

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

commit 11a446bd9f6769c66417fde56bdbbbd5fb11c607
parent 351564613a2016715a2a5cf8e4f4642ca043f90b
Author: Szymon Mikulicz <szymon.mikulicz@posteo.net>
Date:   Tue, 25 Aug 2026 15:08:17 +0200

GC nearly working

Diffstat:
Mzlox/src/compiler.zig | 31+++++++++++++++++++++++++------
Mzlox/src/gc.zig | 62++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Mzlox/src/obj::native.zig | 17+++++++++++++++--
Mzlox/src/obj::upvalue.zig | 1-
Mzlox/src/vm.zig | 3+++
Mzlox/src/vm::native.zig | 4++++
6 files changed, 99 insertions(+), 19 deletions(-)

diff --git a/zlox/src/compiler.zig b/zlox/src/compiler.zig @@ -327,9 +327,17 @@ 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 = "list", + .fun = vm_native.list, + .type = .Literal, + }) 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 = "table", + .fun = vm_native.table, + .type = .Literal, + }) catch return) catch return; } self.emit(OP.CALL, argCount); } @@ -533,14 +541,19 @@ pub fn Compiler(size: comptime_int) type { self.lastError = err; return; }; + + var compiler = Self.init_enclosed(self, fun) catch |err| { + self.errorAtPrevious("Couldn't init enclosed function"); + self.lastError = err; + return; + }; + fun.set_name(self.objects.emplace(Obj.Type.String, &.{name}) catch |err| { self.errorAtPrevious("Couldn't allocate function name"); self.lastError = err; return; }); - var compiler = Self.init_enclosed(self, fun); - compiler.consume(Token.LEFT_PAREN, "Expect '(' after function name"); if (!compiler.check(Token.RIGHT_PAREN)) { while (true) { @@ -720,7 +733,11 @@ pub fn Compiler(size: comptime_int) type { self.consume(Token.LEFT_PAREN, "Expect '(' after 'switch'."); - self.emitObj(.Native, Obj.Native.Arg{ .name = "internal::switch", .fun = vm_native.table }) catch return; + self.emitObj(.Native, Obj.Native.Arg{ + .name = "switch", + .fun = vm_native.table, + .type = .Literal, + }) catch return; var jumpOver = self.emitJump(OP.JUMP); const switchExpression = self.currentChunk().code.len; @@ -967,11 +984,13 @@ pub fn Compiler(size: comptime_int) type { return self; } - fn init_enclosed(enclosing: *Self, fun: *Obj.Function) Self { + fn init_enclosed(enclosing: *Self, fun: *Obj.Function) !Self { var enclosed = Self.init(enclosing.scanner, enclosing.objects, fun); enclosed.current = enclosing.current; enclosed.enclosing = enclosing; enclosed.beginScope(); + + try enclosing.objects.swap_callback(&gc_callback, &enclosed); return enclosed; } diff --git a/zlox/src/gc.zig b/zlox/src/gc.zig @@ -7,7 +7,7 @@ const Value = @import("value.zig").Value; const VM = @import("vm.zig").VM; pub const GC = struct { - pub const Obj = @import("obj.zig").Obj(.{ .mark = false }); + pub const Obj = @import("obj.zig").Obj(.{ .gc = true, .mark = false }); const Self = @This(); @@ -55,8 +55,12 @@ pub const GC = struct { } fn collect(self: *Self) void { - self.mark_roots(); - self.trace_references(); + if (self.callbacks.len() > 0) { + self.mark_roots(); + self.trace_references(); + self.table_remove_white(); + self.sweep(); + } } fn trace_references(self: *Self) void { @@ -110,8 +114,33 @@ pub const GC = struct { fn mark_roots(self: *Self) void { var iter = self.callbacks.iter(); - while (iter.next()) |cb| { + while (iter.next()) |cb| cb.call(); + } + + fn table_remove_white(self: *Self) void { + const tbl = &self.table; + tbl.for_each(tbl, struct { + pub fn fun(table: *Obj.String.Table, key: Obj.String.Table.Key, _: Obj.String.Table.Value) void { + const obj = key.cast(); + if (obj.fields.gc and !obj.fields.mark) + _ = table.delete(key); + } + }.fun); + } + + fn sweep(self: *Self) void { + var iter = self.objs.iter(); + while (iter.next()) |obj| { + if (obj.fields.gc) { + if (!obj.fields.mark) { + iter.pop(); + dbg_obj("O", "free", obj, false); + obj.free(self.allocator); + } else { + obj.fields.mark = false; + } + } } } @@ -119,6 +148,10 @@ pub const GC = struct { try self.callbacks.push(0, Callback{ .@"fn" = callback, .arg = arg }); } + pub fn swap_callback(self: *Self, callback: Callback.Fn, arg: Callback.Arg) !void { + try self.callbacks.set(0, Callback{ .@"fn" = callback, .arg = arg }); + } + pub fn pop_callback(self: *Self) void { _ = self.callbacks.pop(0) catch return; } @@ -143,18 +176,23 @@ pub const GC = struct { } pub fn emplace(self: *Self, comptime tp: Obj.Type, arg: tp.get().Arg) (ObjList.Error || tp.get().Error)!*tp.get() { + if (DBG_STRESS) { + self.collect(); + } + var newObj = true; const obj = switch (tp) { .String => try Obj.String.intern(arg, &self.table, &newObj, self.allocator), else => try tp.get().init(arg, self.allocator), }; + if (newObj) { - if (DBG_STRESS) { - self.collect(); - } - dbg_obj("O", "new", &obj.obj, true); - try self.objs.push(0, obj.cast()); + const obj_p = obj.cast(); + + dbg_obj("O", "new", obj_p, true); + try self.objs.push(0, obj_p); } + return obj; } @@ -168,7 +206,7 @@ pub const GC = struct { }, else => {}, }, - *Obj => if (!arg.fields.mark) { + *Obj => if (arg.fields.gc and !arg.fields.mark) { dbg_obj(msg, "mark", arg, true); arg.fields.mark = true; self.greys.push(-1, arg) catch @panic("Grey stack overflow"); @@ -181,6 +219,10 @@ pub const GC = struct { } } + pub fn exclude(obj: *Obj) void { + obj.fields.gc = false; + } + pub fn emplace_cast(self: *Self, comptime tp: Obj.Type, arg: tp.get().Arg) (ObjList.Error || tp.get().Error)!*Obj { return (try self.emplace(tp, arg)).cast(); } diff --git a/zlox/src/obj::native.zig b/zlox/src/obj::native.zig @@ -19,13 +19,22 @@ pub fn Native(fields: anytype) type { 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 Type = enum(u8) { Builtin, Literal }; + + pub const Arg = struct { + fun: Fn, + arity_min: u8 = ArityMin, + arity_max: u8 = ArityMax, + name: []const u8 = "", + type: Type = .Builtin, + }; obj: Super, fun: Packed(Fn), arity_min: u8, arity_max: u8, name: Packed([]const u8), + type: Type, pub fn init(arg: Arg, allocator: std.mem.Allocator) Error!*Self { const self: *Self = try allocator.create(Self); @@ -35,6 +44,7 @@ pub fn Native(fields: anytype) type { .arity_min = arg.arity_min, .arity_max = arg.arity_max, .name = Packed([]const u8).init(arg.name), + .type = arg.type, }; return self; } @@ -49,7 +59,10 @@ pub fn Native(fields: anytype) type { } pub fn format(self: *const Self, writer: *std.Io.Writer) !void { - _ = try writer.write("<N: "); + switch (self.type) { + .Builtin => _ = try writer.write("<B: "), + .Literal => _ = try writer.write("<L: "), + } _ = try writer.write(self.name.ptr()); _ = try writer.writeAll(">"); } diff --git a/zlox/src/obj::upvalue.zig b/zlox/src/obj::upvalue.zig @@ -49,7 +49,6 @@ pub fn Upvalue(fields: anytype) type { } pub fn free(self: *const Self, allocator: std.mem.Allocator) void { - if (self.closed) self.location.destroy(allocator); allocator.destroy(self); } }; diff --git a/zlox/src/vm.zig b/zlox/src/vm.zig @@ -67,12 +67,14 @@ pub const VM = struct { 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}); + GC.exclude(nameObj.cast()); const funObj = try self.objects.emplace_cast(.Native, Obj.Native.Arg{ .fun = fun, .name = nameObj.slice(), .arity_min = arity_min, .arity_max = arity_max, }); + GC.exclude(funObj); _ = try self.globals.set(nameObj, Global.make_con(Value.init(funObj))); } @@ -96,6 +98,7 @@ pub const VM = struct { try self.defineNative("clock", 0, 0, native.Clock.clock); try self.defineNative("put", 1, 1, native.put); + try self.defineNative("typeof", 1, 1, native.typeof); try self.defineNative("table", 0, Obj.Native.ArityMax, native.table); try self.defineNative("list", 0, Obj.Native.ArityMax, native.list); diff --git a/zlox/src/vm::native.zig b/zlox/src/vm::native.zig @@ -39,3 +39,7 @@ pub fn list(gc: *GC, args: []const Value) Error!Value { } return Value.init(lis.cast()); } + +pub fn typeof(gc: *GC, args: []const Value) Error!Value { + return Value.init(gc.emplace_cast(.String, &.{args[0].typeName()}) catch return Error.Native); +}