commit 11a446bd9f6769c66417fde56bdbbbd5fb11c607
parent 351564613a2016715a2a5cf8e4f4642ca043f90b
Author: Szymon Mikulicz <szymon.mikulicz@posteo.net>
Date: Tue, 25 Aug 2026 15:08:17 +0200
GC nearly working
Diffstat:
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);
+}