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:
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];
}