DzLox

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

commit c28ffd6c01e94fde49e78fc710a307922efbaf0a
parent b7e7127585b30cb5b1f3148c3462e4958616a10c
Author: Szymon Mikulicz <szymon.mikulicz@posteo.net>
Date:   Wed, 26 Aug 2026 12:55:10 +0200

GC done

Diffstat:
Mzlox/src/gc.zig | 44+++++++++++++++++++++++++-------------------
Mzlox/src/obj.zig | 7+++++--
Mzlox/src/vm.zig | 10+++++++---
Mzlox/src/vm::native.zig | 5+++++
4 files changed, 42 insertions(+), 24 deletions(-)

diff --git a/zlox/src/gc.zig b/zlox/src/gc.zig @@ -7,7 +7,13 @@ const Value = @import("value.zig").Value; const VM = @import("vm.zig").VM; pub const GC = struct { - pub const Obj = @import("obj.zig").Obj(.{ .gc = true, .mark = false }); + pub const Color = enum(u8) { + White, + Black, + None, + }; + + pub const Obj = @import("obj.zig").Obj(.{ .color = Color.White }); const Self = @This(); @@ -28,7 +34,7 @@ pub const GC = struct { }; const DBG_STRESS = false; - const DBG_LOG = true; + const DBG_LOG = false; const GC_HEAP_GROW_FACTOR = 2; allocator: std.mem.Allocator, @@ -59,7 +65,7 @@ pub const GC = struct { }; } - fn collect(self: *Self) void { + pub fn collect(self: *Self) void { if (self.callbacks.len() > 0) { self.mark_roots(); self.trace_references(); @@ -131,7 +137,7 @@ pub const GC = struct { table.for_each(table, struct { pub fn fun(tbl: *Table, key: Table.Key, _: Table.Value) void { const obj = key.cast(); - if (obj.fields.gc and !obj.fields.mark) + if (obj.fields.color == .White) _ = tbl.delete(key); } }.fun); @@ -140,17 +146,16 @@ pub const GC = struct { 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); - switch (obj.type) { - inline else => |tp| self.allocated -= @sizeOf(tp.get()) + if (tp == .String) (obj.cast(tp) catch unreachable).len else 0, - } - } else { - obj.fields.mark = false; + if (obj.fields.color == .White) { + iter.pop(); + dbg_obj("O", "free", obj, false); + switch (obj.type) { + inline else => |tp| self.allocated -= @sizeOf(tp.get()), } + if (obj.cast_if(.String)) |str| self.allocated -= str.len; + obj.free(self.allocator); + } else if (obj.fields.color == .Black) { + obj.fields.color = .White; } } } @@ -193,7 +198,9 @@ pub const GC = struct { const chd = try tp.get().init(arg, self.allocator); - self.allocated += @sizeOf(tp.get()) + if (tp == .String) chd.len else 0; + self.allocated += @sizeOf(tp.get()); + if (tp == .String) self.allocated += chd.len; + if (DBG_STRESS or self.allocated > self.next) { self.collect(); } @@ -203,7 +210,6 @@ pub const GC = struct { const obj = chd.cast(); dbg_obj("O", "new", obj, true); - dbg_print("{d}/{d}\n", .{ self.allocated, self.next }); try self.objs.push(0, obj); @@ -212,16 +218,16 @@ pub const GC = struct { pub fn mark(self: *Self, msg: []const u8, arg: anytype) void { if (Obj.from(arg)) |obj| { - if (obj.fields.gc and !obj.fields.mark) { + if (obj.fields.color == .White) { dbg_obj(msg, "mark", obj, true); - obj.fields.mark = true; + obj.fields.color = .Black; self.greys.push(-1, obj) catch @panic("Grey stack overflow"); } } } pub fn exclude(obj: *Obj) void { - obj.fields.gc = false; + obj.fields.color = .None; } pub fn emplace_cast(self: *Self, comptime tp: Obj.Type, arg: tp.get().Arg) !*Obj { diff --git a/zlox/src/obj.zig b/zlox/src/obj.zig @@ -95,8 +95,11 @@ pub fn Obj(fields: anytype) type { } pub fn cast(self: anytype, comptime tp: Type) Error!utils.copy_const(@TypeOf(self), *tp.get()) { - if (!self.is(tp)) return Error.IllegalCastError; - return self._cast(tp); + return if (self.is(tp)) self._cast(tp) else Error.IllegalCastError; + } + + pub fn cast_if(self: anytype, comptime tp: Type) ?utils.copy_const(@TypeOf(self), *tp.get()) { + return if (self.is(tp)) self._cast(tp) else null; } fn _cast(self: anytype, comptime tp: Type) utils.copy_const(@TypeOf(self), *tp.get()) { diff --git a/zlox/src/vm.zig b/zlox/src/vm.zig @@ -101,6 +101,7 @@ pub const VM = struct { 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); + try self.defineNative("rungc", 0, 0, native.rungc); native.Clock.set_start(io); @@ -297,10 +298,13 @@ pub const VM = struct { } fn binary_op(self: *@This(), comptime in_tag: anytype, comptime out_tag: anytype, op: callbacks.Type(in_tag, out_tag)) InterpreterError!void { - const b = self.pop(); - const a = self.pop(); + const b = self.peek(0); + const a = self.peek(1); if (a.is(in_tag) and b.is(in_tag)) { - self.push(Value.init(try op.call(a.get(in_tag), b.get(in_tag)))); + const val = Value.init(try op.call(a.get(in_tag), b.get(in_tag))); + _ = self.pop(); + _ = self.pop(); + self.push(val); } else { self.runtimeError("Operands have invalid types, expected: {s}", .{@tagName(in_tag)}); return InterpreterError.RuntimeError; diff --git a/zlox/src/vm::native.zig b/zlox/src/vm::native.zig @@ -43,3 +43,8 @@ pub fn list(gc: *GC, args: []const Value) Error!Value { pub fn typeof(gc: *GC, args: []const Value) Error!Value { return Value.init(gc.emplace_cast(.String, &.{args[0].typeName()}) catch return Error.Native); } + +pub fn rungc(gc: *GC, _: []const Value) Error!Value { + gc.collect(); + return Value.init({}); +}