DzLox

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

commit 669df913edb3924e356e2d4e6ec67a6cb1e38bdb
parent 310d12bb78444322e65394cdeed765c4b6b8657c
Author: Szymon Mikulicz <szymon.mikulicz@posteo.net>
Date:   Mon, 17 Aug 2026 18:30:57 +0200

Progressing with mark n sweep

Diffstat:
Mzlox/build.zig.zon | 2+-
Mzlox/src/comptime_utils.zig | 4++--
Mzlox/src/gc.zig | 33+++++++++++++++++++++++++++++++--
Mzlox/src/obj.zig | 4++--
Mzlox/src/table.zig | 14+++++++-------
Mzlox/src/vm.zig | 29++++++++++++++++++++++++++---
6 files changed, 69 insertions(+), 17 deletions(-)

diff --git a/zlox/build.zig.zon b/zlox/build.zig.zon @@ -7,7 +7,7 @@ // This field is optional. // This is currently advisory only; Zig does not yet do anything // with this value. - //.minimum_zig_version = "0.11.0", + .minimum_zig_version = "0.17.0", // This field is optional. // Each dependency must either provide a `url` and `hash`, or a `path`. diff --git a/zlox/src/comptime_utils.zig b/zlox/src/comptime_utils.zig @@ -1,6 +1,6 @@ const std = @import("std"); -pub fn with_size(T: type, comptime size: std.builtin.Type.Pointer.Size) type { +pub fn with_size(T: type, comptime size: std.lang.Type.Pointer.Size) type { return mod_ptr_t(T, "size", size); } @@ -30,7 +30,7 @@ pub fn enum_len(T: type) usize { } pub fn is_type(T: type, comptime name: []const u8) bool { - return @as(std.meta.Tag(std.builtin.Type), @typeInfo(T)) == @field(std.meta.Tag(std.builtin.Type), name); + return @as(std.meta.Tag(std.lang.Type), @typeInfo(T)) == @field(std.meta.Tag(std.lang.Type), name); } pub fn typeFromTag(T: type, comptime tag: std.meta.Tag(T)) type { diff --git a/zlox/src/gc.zig b/zlox/src/gc.zig @@ -2,6 +2,8 @@ const std = @import("std"); const list = @import("list.zig"); const Value = @import("value.zig").Value; +const utils = @import("comptime_utils.zig"); +const VM = @import("vm.zig").VM; pub const GC = struct { pub const Obj = @import("obj.zig").Obj(.{ .mark = false }); @@ -10,6 +12,9 @@ pub const GC = struct { const List = list.List(*Obj); + pub const CallbackArg = *anyopaque; + pub const Callback = *const fn (CallbackArg) void; + const DBG_STRESS = true; const DBG_LOG = true; @@ -17,6 +22,8 @@ pub const GC = struct { io: std.Io, table: Obj.String.Table, list: List, + callback: ?Callback, + callback_arg: ?CallbackArg, fn dbg_print(comptime fmt: []const u8, args: anytype) void { if (DBG_LOG) { @@ -30,15 +37,31 @@ pub const GC = struct { .io = io, .table = Obj.String.Table.init(allocator), .list = List.init(allocator), + .callback = null, + .callback_arg = null, }; } fn collect(self: *Self) void { dbg_print("Collection begin\n", .{}); - _ = self; + if (self.callback) |callback| { + if (self.callback_arg) |callback_arg| { + callback(callback_arg); + } + } dbg_print("Collection end\n", .{}); } + pub fn set_callback(self: *Self, callback: Callback, callback_arg: CallbackArg) void { + self.callback = callback; + self.callback_arg = callback_arg; + } + + pub fn reset_callback(self: *Self) void { + self.callback = null; + self.callback_arg = null; + } + pub fn emplace(self: *Self, comptime tp: Obj.Type, arg: tp.get().Arg) (List.Error || tp.get().Error)!*tp.get() { var newObj = true; const obj = switch (tp) { @@ -74,6 +97,9 @@ pub const GC = struct { pub fn mark(arg: anytype) void { const T = @TypeOf(arg); switch (T) { + VM.Global => { + mark(arg.val); + }, Value => switch (arg) { .obj => |o| { mark(o); @@ -84,6 +110,9 @@ pub const GC = struct { dbg_print("Marking {any} at 0x{x}: {f}\n", .{ arg.type, @intFromPtr(arg), arg }); arg.fields.mark = true; }, + *const Obj => { + dbg_print("Skipping const {any} at 0x{x}: {f}\n", .{ arg.type, @intFromPtr(arg), arg }); + }, else => if (Obj.isChild(T)) { mark(arg.cast()); }, @@ -97,7 +126,7 @@ pub const GC = struct { pub fn deinit(self: *Self) void { while (true) { const el = self.list.pop() catch break; - dbg_print("Freeing {any} at 0x{x}\n", .{ el.type, @intFromPtr(el)}); + dbg_print("Freeing {any} at 0x{x}\n", .{ el.type, @intFromPtr(el) }); el.free(self.allocator); } self.list.free(); diff --git a/zlox/src/obj.zig b/zlox/src/obj.zig @@ -42,8 +42,8 @@ pub fn Obj(fields: anytype) type { }; pub fn isChild(T: type) bool { - inline for (@typeInfo(Type).@"enum".fields) |field| { - const U = @field(Self, field.name); + inline for (@typeInfo(Type).@"enum".field_names) |field| { + const U = @field(Self, field); if (T == U or T == *U or T == *const U) return true; } return false; diff --git a/zlox/src/table.zig b/zlox/src/table.zig @@ -91,13 +91,13 @@ pub fn Table(K: type, V: type, hash_fn: fn (K) u32, cmp_fn: fn (K, K) bool) type } } - pub fn for_each(self: *const Self, arg: anytype, fun: fn (@TypeOf(arg), K, V) void) void { + pub fn for_each(self: *const Self, arg: anytype, fun: if (@TypeOf(arg) == void) fn (K, V) void else fn (@TypeOf(arg), K, V) void) void { for (self.entries) |entry| { switch (entry) { .some => |some| if (@TypeOf(arg) == void) - fun(some.key, some.value) - else - fun(arg, some.key, some.value), + fun(some.key, some.value) + else + fun(arg, some.key, some.value), else => {}, } } @@ -107,9 +107,9 @@ pub fn Table(K: type, V: type, hash_fn: fn (K) u32, cmp_fn: fn (K, K) bool) type for (self.entries) |entry| { switch (entry) { .some => |some| if (@TypeOf(arg) == void) - try fun(some.key, some.value) - else - try fun(arg, some.key, some.value), + try fun(some.key, some.value) + else + try fun(arg, some.key, some.value), else => {}, } } diff --git a/zlox/src/vm.zig b/zlox/src/vm.zig @@ -13,14 +13,14 @@ const hash = @import("hash.zig"); const utils = @import("comptime_utils.zig"); const vm_native = @import("vm/native.zig"); -pub const InterpreterError = Obj.Error || compiler.CompilerError || Callback.Error || error{ CompileError, RuntimeError, StackOverflow, IndexOutOfBounds, Overflow, DivisionByZero }; +const InterpreterError = Obj.Error || compiler.CompilerError || Callback.Error || error{ CompileError, RuntimeError, StackOverflow, IndexOutOfBounds, Overflow, DivisionByZero }; pub const VM = struct { objects: GC, globals: Globals, allocator: std.mem.Allocator, - const Global = struct { + pub const Global = struct { val: Value, con: bool, @@ -68,8 +68,18 @@ pub const VM = struct { _ = try self.globals.set(nameObj, Global.make_con(Value.init(funObj))); } + fn gc_callback(self_ptr: *anyopaque) void { + const self: *@This() = @ptrCast(@alignCast(self_ptr)); + + GC.markTable(self.globals); + } + pub fn init(allocator: std.mem.Allocator, io: std.Io) !@This() { - var self = @This(){ .globals = Globals.init(allocator), .objects = try GC.init(allocator, io), .allocator = allocator }; + var self = @This(){ + .globals = Globals.init(allocator), + .objects = try GC.init(allocator, io), + .allocator = allocator, + }; try self.defineNative("clock", 0, 0, vm_native.Clock.clock); try self.defineNative("put", 1, 1, vm_native.put); @@ -85,6 +95,9 @@ pub const VM = struct { const callstack_size = 64; const stack_size = 256; + self.objects.set_callback(&VM.gc_callback, self); + defer self.objects.reset_callback(); + const function = try compiler.Compiler(stack_size).compile(source, &self.objects); if (dbg) try debug.disassembleChunk(function.chunk.ptr(), "Main"); @@ -95,6 +108,7 @@ pub const VM = struct { fn Interpreter(callstack_size: comptime_int, stack_size: comptime_int) type { return struct { const List = list.List(*Obj.Upvalue); + const Self = @This(); frames: [callstack_size]CallFrame, frameCount: usize, @@ -113,6 +127,9 @@ pub const VM = struct { .open_upvalues = List.init(vm.allocator), }; + vm.objects.set_callback(&Self.gc_callback, &self); + defer vm.objects.reset_callback(); + defer self.open_upvalues.free(); self.stackTop = &self.stack; @@ -121,6 +138,12 @@ pub const VM = struct { try self.execute(dbg); } + pub fn gc_callback(self_ptr: *anyopaque) void { + const self: *@This() = @ptrCast(@alignCast(self_ptr)); + + VM.gc_callback(self.vm); + } + fn frame(self: anytype) utils.copy_const(@TypeOf(self), *CallFrame) { return &self.frames[self.frameCount - 1]; }