DzLox

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

commit f691de138bef1a690406ee83f179a8cee6469eaf
parent a3ec95884dae6f80c5a3e9f8f2d3b425a53e1b56
Author: Szymon Mikulicz <szymon.mikulicz@posteo.net>
Date:   Thu, 20 Aug 2026 23:45:39 +0200

Rewrite list

Diffstat:
Mzlox/src/compiler.zig | 12++++++++++++
Mzlox/src/comptime_utils.zig | 12+++++++++---
Mzlox/src/gc.zig | 147++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Mzlox/src/list.zig | 336+++++++++++++++++++++++++++++++++++++++----------------------------------------
Mzlox/src/obj.zig | 7+++----
Mzlox/src/obj/closure.zig | 6+++---
Mzlox/src/obj/list.zig | 37++++++++++++++-----------------------
Mzlox/src/obj/table.zig | 2+-
Mzlox/src/table.zig | 2+-
Mzlox/src/vm.zig | 46++++++++++++++++++++++++++++++----------------
Mzlox/src/vm/native.zig | 2+-
11 files changed, 329 insertions(+), 280 deletions(-)

diff --git a/zlox/src/compiler.zig b/zlox/src/compiler.zig @@ -971,11 +971,23 @@ pub fn Compiler(size: comptime_int) type { return enclosed; } + fn gc_callback(self_ptr: *anyopaque) void { + var self: *@This() = @ptrCast(@alignCast(self_ptr)); + + self.objects.mark("C", self.currentFunction); + while (self.enclosing) |enclosed| : (self = enclosed) { + self.objects.mark("C", enclosed.currentFunction); + } + } + pub fn compile(source: []const u8, objects: *GC) CompilerError!*Obj.Function { var scan = try scanner.Scanner.init(source); const fun = try objects.emplace(Obj.Type.Function, Obj.Function.Type.Script); var self = Self.init(&scan, objects, fun); + try objects.push_callback(&gc_callback, &self); + defer objects.pop_callback(); + self.advance(); while (!self.match(Token.EOF)) { diff --git a/zlox/src/comptime_utils.zig b/zlox/src/comptime_utils.zig @@ -5,7 +5,7 @@ pub fn with_size(T: type, comptime size: std.lang.Type.Pointer.Size) type { } pub fn copy_const(T: type, U: type) type { - return mod_ptr_t(U, "const", @typeInfo(T).pointer.attrs.@"const"); + return mod_ptr_t(U, "const", is_const(T)); } pub fn mod_ptr_t(T: type, comptime field: []const u8, comptime val: anytype) type { @@ -33,6 +33,10 @@ pub fn is_type(T: type, comptime name: []const u8) bool { return @as(std.meta.Tag(std.lang.Type), @typeInfo(T)) == @field(std.meta.Tag(std.lang.Type), name); } +pub fn is_const(T: type) bool { + return @typeInfo(T).pointer.attrs.@"const"; +} + pub fn typeFromTag(T: type, comptime tag: std.meta.Tag(T)) type { return @TypeOf(@field(@unionInit(T, @tagName(tag), undefined), @tagName(tag))); } @@ -47,8 +51,10 @@ pub fn tagFromType(T: type, U: type) std.meta.Tag(T) { @compileError("No matching tag for type " ++ @typeName(U) ++ " in Union " ++ @typeName(T)); } -pub fn fn_error(comptime fun: anytype) type { - return @typeInfo(@typeInfo(@TypeOf(fun)).@"fn".return_type.?).error_union.error_set; +pub fn fn_error(comptime fun: anytype) ?type { + const return_type = @typeInfo(@TypeOf(fun)).@"fn".return_type.?; + + return if (is_type(return_type, "error_union")) @typeInfo(return_type).error_union.error_set else null; } pub fn param_type(comptime fun: anytype, idx: comptime_int) type { diff --git a/zlox/src/gc.zig b/zlox/src/gc.zig @@ -10,7 +10,9 @@ pub const GC = struct { const Self = @This(); - const List = list.List(*Obj); + const ObjList = list.List(*Obj); + const CallbackList = list.List(Callback); + const GreyList = list.List(*Obj); pub const Callback = struct { pub const Arg = *anyopaque; @@ -30,8 +32,9 @@ pub const GC = struct { allocator: std.mem.Allocator, io: std.Io, table: Obj.String.Table, - list: List, - callbacks: list.List(Callback), + objs: ObjList, + callbacks: CallbackList, + greys: GreyList, fn dbg_print(comptime fmt: []const u8, args: anytype) void { if (DBG_LOG) { @@ -44,49 +47,101 @@ pub const GC = struct { .allocator = allocator, .io = io, .table = Obj.String.Table.init(allocator), - .list = List.init(allocator), - .callbacks = list.List(Callback).init(allocator), + .objs = ObjList.init(allocator), + .callbacks = CallbackList.init(allocator), + .greys = GreyList.init(allocator), }; } fn collect(self: *Self) void { - self.callbacks.for_each({}, struct { - pub fn fun(cb_ptr: ?Callback) void { - if (cb_ptr) |cb| { - cb.call(); - } + self.mark_roots(); + self.trace_references(); + } + + fn trace_references(self: *Self) void { + while (true) { + const grey = self.greys.pop(0) catch break; + switch (grey.type) { + inline else => |tp| self.blacken_obj(grey.cast(tp) catch unreachable), } - }.fun); + } + } + + fn blacken_obj(self: *Self, obj: anytype) void { + const T = @TypeOf(obj); + + switch (T) { + *Obj.String => {}, + *Obj.Native => {}, + *Obj.Table => { + obj.table.ptr().for_each(self, struct { + pub fn fun(s: *Self, key: Obj.Table.Table.Key, val: Obj.Table.Table.Value) void { + s.mark("t", key); + s.mark("t", val); + } + }.fun); + }, + *Obj.Function => { + if (obj.name.ptr()) |name| + self.mark("f", name); + for (obj.chunk.ptr().constants.slice()) |constant| + self.mark("f", constant); + }, + *Obj.List => { + var iter = obj.list.ptr().iter(); + while (iter.next()) |val| { + self.mark("t", val); + } + }, + *Obj.Closure => { + self.mark("c", obj.function.ptr()); + for (obj.upvalues.ptr()) |upvalue_ptr| + if (upvalue_ptr) |upvalue| + self.mark("c", upvalue); + }, + *Obj.Upvalue => { + if (obj.closed) + self.mark("u", obj.location.get()); + }, + else => @compileError("Invalid type: " ++ @typeName(T)), + } + } + + fn mark_roots(self: *Self) void { + var iter = self.callbacks.iter(); + while (iter.next()) |cb| { + cb.call(); + } } pub fn push_callback(self: *Self, callback: Callback.Fn, arg: Callback.Arg) !void { - try self.callbacks.push(Callback{ .@"fn" = callback, .arg = arg }); + try self.callbacks.push(0, Callback{ .@"fn" = callback, .arg = arg }); } pub fn pop_callback(self: *Self) void { - _ = self.callbacks.pop() catch return; + _ = self.callbacks.pop(0) catch return; } - pub fn dbg_obj(info: []const u8, obj: anytype, comptime prin: bool) void { + pub fn dbg_obj(info: []const u8, msg: []const u8, obj: anytype, comptime prin: bool) void { if (prin) { - dbg_print("[{s}] {s: <8} [{s}] 0x{x} {f}\n", .{ + dbg_print("[{s}] {s: >5}: {s: <8} 0x{x} {f}\n", .{ info, + msg, @tagName(obj.type), - if (obj.fields.mark) "X" else " ", @intFromPtr(obj), obj, }); } else { - dbg_print("[{s}] {s: <8} [{s}] 0x{x}\n", .{ + dbg_print("[{s}] {s: >5}: {s: <8} 0x{x}\n", .{ info, + msg, @tagName(obj.type), - if (obj.fields.mark) "X" else " ", @intFromPtr(obj), }); } } - pub fn emplace(self: *Self, comptime tp: Obj.Type, arg: tp.get().Arg) (List.Error || tp.get().Error)!*tp.get() { + pub fn emplace(self: *Self, comptime tp: Obj.Type, arg: tp.get().Arg) (ObjList.Error || tp.get().Error)!*tp.get() { var newObj = true; const obj = switch (tp) { .String => try Obj.String.intern(arg, &self.table, &newObj, self.allocator), @@ -96,72 +151,48 @@ pub const GC = struct { if (DBG_STRESS) { self.collect(); } - dbg_obj("A", &obj.obj, true); - try self.list.push(obj.cast()); + dbg_obj("O", "new", &obj.obj, true); + try self.objs.push(0, obj.cast()); } return obj; } - pub fn markTable(table: anytype) void { - const Table = @TypeOf(table); - table.for_each({}, struct { - pub fn fun(key: Table.Key, val: Table.Value) void { - Self.mark(key); - Self.mark(val); - } - }.fun); - } - - pub fn markArray(arr: anytype) void { - for (arr) |el| { - Self.mark(el); - } - } - - pub fn mark(arg: anytype) void { + pub fn mark(self: *Self, msg: []const u8, arg: anytype) void { const T = @TypeOf(arg); switch (T) { Value => switch (arg) { .obj => |o| { - mark(o); + self.mark(msg, o); }, else => {}, }, - *Obj => { - dbg_obj("M", arg, true); - + *Obj => if (!arg.fields.mark) { + dbg_obj(msg, "mark", arg, true); arg.fields.mark = true; - switch (arg.type) { - .Function => if ((arg.cast(.Function) catch unreachable).name.ptr()) |name| { - mark(name); - }, - else => {}, - } + self.greys.push(-1, arg) catch @panic("Grey stack overflow"); }, - *const Obj => { - dbg_obj("S", arg, true); - }, - else => if (comptime Obj.isChild(T)) { - mark(arg.cast()); + else => if (comptime Obj.is_child(T)) { + self.mark(msg, arg.cast()); } else { @compileError("Unable to mark " ++ @typeName(T)); }, } } - pub fn emplace_cast(self: *Self, comptime tp: Obj.Type, arg: tp.get().Arg) (List.Error || tp.get().Error)!*Obj { + 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(); } pub fn deinit(self: *Self) void { + self.callbacks.free(); + self.greys.free(); while (true) { - const el = self.list.pop() catch break; - dbg_obj("F", el, false); + const el = self.objs.pop(0) catch break; + dbg_obj("O", "free", el, false); el.free(self.allocator); } - self.callbacks.free(); - self.list.free(); + self.objs.free(); self.table.deinit(); } }; diff --git a/zlox/src/list.zig b/zlox/src/list.zig @@ -1,227 +1,223 @@ const std = @import("std"); const utils = @import("comptime_utils.zig"); +fn sign(v: anytype) @TypeOf(v) { + return if (v >= 0) 1 else -1; +} + pub fn List(T: type) type { return struct { const Self = @This(); pub const Error = error{ OutOfMemory, IndexOutOfBounds, Empty }; + pub const Value = T; const Element = struct { - val: ?T, - next: ?*@This(), - prev: ?*@This(), - }; + val: Value, + next: *@This(), + prev: *@This(), - len: usize, - tip: ?*Element, - end: ?*Element, - allocator: std.mem.Allocator, + fn jmp(self: *Element, idx: isize) *Element { + var ret = self; - pub fn init(allocator: std.mem.Allocator) Self { - return Self{ - .len = 0, - .tip = null, - .end = null, - .allocator = allocator, - }; - } - - pub fn eql(self: *const Self, other: *const Self, eql_fn: fn (T, T) bool) bool { - if (self.len != other.len) return false; - if (self.len == 0) return true; - var tip1 = self.tip; - var tip2 = other.tip; - while (tip1) |el1| : (tip1 = el1.next) { - if (el1.val) |val1| { - if (tip2.?.val) |val2| { - if (!eql_fn(val1, val2)) return false; - } else { - return false; - } - } else if (tip2.?.val) |_| { - return false; + for (0..@abs(idx)) |_| { + ret = if (idx > 0) ret.next else ret.prev; } - tip2 = tip2.?.next; + + return ret; } - return true; - } - pub fn free(self: *Self) void { - while (true) { - _ = self.pop() catch return; + pub fn del(self: *Element, gpa: std.mem.Allocator) void { + self.next.prev = self.prev; + self.prev.next = self.next; + + gpa.destroy(self); } - } - pub fn get(self: *const Self, index: usize) Error!T { - if (index >= self.len) { - return Error.IndexOutOfBounds; + fn init(gpa: std.mem.Allocator, prv: ?*Element, nxt: ?*Element, val: Value) !*Element { + const ret = try gpa.create(Element); + ret.* = Element{ + .prev = prv orelse ret, + .next = nxt orelse ret, + .val = val, + }; + return ret; } - var idx_rev: usize = index; - var idx = self.len - idx_rev; - idx_rev += 1; - - if (idx < idx_rev) { - var tip = self.tip; - while (idx > 1) : (idx -= 1) { - tip = tip.?.next; - } - return tip.?.val orelse Error.IndexOutOfBounds; - } else { - var end = self.end; - while (idx_rev > 1) : (idx_rev -= 1) { - end = end.?.prev; - } - return end.?.val orelse Error.IndexOutOfBounds; + + pub fn add_prev(self: *Element, gpa: std.mem.Allocator, val: Value) !void { + self.prev.next = try Element.init(gpa, self.prev, self, val); + self.prev = self.prev.next; + if (self.next == self) self.next = self.prev; + } + + pub fn add_next(self: *Element, gpa: std.mem.Allocator, val: Value) !void { + self.next.prev = try Element.init(gpa, self, self.next, val); + self.next = self.next.prev; + if (self.prev == self) self.prev = self.next; } - } - fn _set(self: *Self, index: usize, val: ?T) Error!bool { - var idx_rev: isize = @intCast(index); - var idx: isize = @as(isize, @intCast(self.len)) - idx_rev; - idx_rev += 1; - var isNewVal = false; + pub fn new(gpa: std.mem.Allocator, val: Value) !*Element { + return try Element.init(gpa, null, null, val); + } + }; + + pub fn Iterator(@"const": bool) type { + return struct { + const Super = utils.mod_ptr_t(*Self, "const", @"const"); + const This = utils.mod_ptr_t(*Element, "const", @"const"); - if (idx <= 0) { - while (idx < 0) : (idx += 1) { - try self._push(null); + super: Super, + this: ?This, + + pub fn new(sup: Super) @This() { + return .{ + .super = sup, + .this = sup.tip, + }; } - try self._push(val); - isNewVal = true; - } else if (idx < idx_rev) { - var tip = self.tip; - while (idx > 1) : (idx -= 1) { - tip = tip.?.next; + + pub fn next(self: *@This()) ?Value { + if (self.this) |el| { + self.this = if (el.next == self.super.tip) null else el.next; + return el.val; + } else { + return null; + } } - tip.?.val = val; - } else { - var end = self.end; - while (idx_rev > 1) : (idx_rev -= 1) { - end = end.?.prev; + + pub fn pop(self: *@This()) void { + if (@"const") @compileError("Cannot call pop() on a const Iterator"); + + const el = if (self.this) |el| el.prev else self.super.tip orelse return; + + if (self.this == el) self.this = null; + + self.super.del(el); } - end.?.val = val; - } - return isNewVal; + + pub fn push(self: *@This(), val: Value) !void { + if (@"const") @compileError("Cannot call push() on a const Iterator"); + + if (self.this) |el| { + try self.super.insert(false, el, val); + } else if (self.super.tip) |el| { + try self.super.insert(true, el.prev, val); + } else { + try self.super.begin(val); + } + } + }; } - pub fn set(self: *Self, index: usize, val: T) Error!bool { - return self._set(index, val); + _len: isize, + tip: ?*Element, + + gpa: std.mem.Allocator, + + pub fn iter(self: anytype) Iterator(utils.is_const(@TypeOf(self))) { + return Iterator(utils.is_const(@TypeOf(self))).new(self); } - pub fn delete(self: *Self, index: usize) void { - if (index >= self.len) return; - if (index == self.len - 1) { - _ = self.pop() catch unreachable; - } else { - _ = self._set(index, null) catch unreachable; - } + pub fn init(gpa: std.mem.Allocator) Self { + return Self{ + ._len = 0, + .tip = null, + .gpa = gpa, + }; } - pub fn insert_before(self: *Self, element: ?*Element, val: T) Error!void { - if (element) |el| { - if (el.prev) |prev| { - const new = try self.allocator.create(Element); - new.* = .{ .val = val, .next = el, .prev = prev }; - prev.next = new; - el.prev = new; - self.len += 1; - return; - } + pub fn len(self: *const Self) usize { + return @intCast(self._len); + } + + pub fn eql(self: *const Self, other: *const Self, eql_fn: fn (Value, Value) bool) bool { + if (self._len != other._len) return false; + + var iter1 = self.iter(); + var iter2 = other.iter(); + + while (iter1.next()) |val1| { + if (!eql_fn(val1, iter2.next().?)) return false; } - try self.push(val); + + return true; } - pub fn insert_after(self: *Self, element: ?*Element, val: T) Error!void { - if (element) |el| { - if (el.next) |next| { - const new = try self.allocator.create(Element); - new.* = .{ .val = val, .next = next, .prev = el }; - next.prev = new; - el.next = new; - self.len += 1; - return; - } + pub fn free(self: *Self) void { + while (true) { + _ = self.pop(-1) catch break; } - try self.push_end(val); } - fn _pop(self: *Self) Error!?T { + fn _at(self: *Self, idx: isize) Error!*Element { if (self.tip) |tip| { - if (tip.next) |next| { - next.prev = null; - self.tip = next; - } else { - self.tip = null; - self.end = null; - } - self.len -= 1; - const val = tip.val; - self.allocator.destroy(tip); - return val; + const haf: isize = sign(idx) * @divTrunc(self._len, 2); + return tip.jmp(@rem(idx + haf, self._len) - haf); + } else { + return Error.Empty; } - return Error.Empty; } - pub fn pop(self: *Self) Error!T { - const val = try self._pop(); - while (self.tip) |tip| { - if (tip.val) |_| break; - _ = self._pop() catch unreachable; - } - return val.?; + fn at(self: *Self, idx: isize) Error!*Element { + return if (@abs(idx) >= self._len and idx < -self._len) + Error.IndexOutOfBounds + else + try self._at(idx); } - fn _push(self: *Self, val: ?T) Error!void { - const new_tip = try self.allocator.create(Element); - if (self.end == null) { - self.end = new_tip; - } - if (self.tip) |old_tip| { - old_tip.prev = new_tip; - } - new_tip.* = Element{ .val = val, .next = self.tip, .prev = null }; - self.tip = new_tip; - self.len += 1; + pub fn set(self: *Self, idx: isize, val: Value) Error!void { + (try self.at(idx)).val = val; } - pub fn push(self: *Self, val: T) Error!void { - return self._push(val); + pub fn get(self: *Self, idx: isize) Error!Value { + return (try self.at(idx)).val; } - fn _push_end(self: *Self, val: ?T) Error!void { - const new_end = try self.allocator.create(Element); - if (self.tip == null) { - self.tip = new_end; - } - if (self.end) |old_end| { - old_end.next = new_end; + fn del(self: *Self, el: *Element) void { + self._len -= 1; + + if (self.tip == el) { + self.tip = if (el.next == el) null else el.next; } - new_end.* = Element{ .val = val, .prev = self.end, .next = null }; - self.end = new_end; - self.len += 1; + + el.del(self.gpa); + } + + pub fn pop(self: *Self, idx: isize) Error!Value { + const el = try self.at(idx); + const ret = el.val; + + self.del(el); + + return ret; } - pub fn push_end(self: *Self, val: T) Error!void { - return self._push_end(val); + fn begin(self: *Self, val: Value) !void { + if (self.tip) |_| @panic("This function can only be called on an empty list"); + + self.tip = try Element.new(self.gpa, val); + self._len = 1; } - pub fn for_each(self: *const Self, arg: anytype, fun: if (@TypeOf(arg) == void) fn (?T) void else fn (@TypeOf(arg), ?T) void) void { - var end = self.end; - while (end) |el| : (end = el.prev) { - if (@TypeOf(arg) == void) - fun(el.val) - else - fun(arg, el.val); + fn insert(self: *Self, after: bool, anchor: *Element, val: Value) !void { + if (after) { + try anchor.add_next(self.gpa, val); + } else { + try anchor.add_prev(self.gpa, val); + + if (self.tip == anchor) self.tip = anchor.prev; } + + self._len += 1; } - pub fn for_each_try(self: *const Self, arg: anytype, fun: anytype) utils.fn_error(fun)!void { - var end = self.end; - while (end) |el| : (end = el.prev) { - if (@TypeOf(arg) == void) - try fun(el.val) - else - try fun(arg, el.val); + pub fn push(self: *Self, idx: isize, val: Value) Error!void { + if (@abs(idx) > self._len and idx < -self._len - 1) { + return Error.IndexOutOfBounds; + } else { + const el = self._at(idx) catch return self.begin(val); + + try self.insert(idx < 0, el, val); } } }; diff --git a/zlox/src/obj.zig b/zlox/src/obj.zig @@ -41,10 +41,9 @@ pub fn Obj(fields: anytype) type { } }; - pub fn isChild(T: type) bool { - 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; + pub fn is_child(T: type) bool { + inline for (std.meta.tags(Type)) |tag| { + if (*tag.get() == T) return true; } return false; } diff --git a/zlox/src/obj/closure.zig b/zlox/src/obj/closure.zig @@ -8,11 +8,11 @@ pub fn Closure(fields: anytype) type { return packed struct { const Self = @This(); - pub const Arg = *const Super.Function; + pub const Arg = *Super.Function; pub const Error = error{OutOfMemory}; obj: Super, - function: Packed(*const Super.Function), + function: Packed(*Super.Function), upvalues: Packed([]?*Super.Upvalue), pub fn init(arg: Arg, allocator: std.mem.Allocator) Error!*Self { @@ -20,7 +20,7 @@ pub fn Closure(fields: anytype) type { self.* = Self{ .obj = Super.make(Self), .upvalues = try Packed([]?*Super.Upvalue).alloc(allocator, arg.upvalue_count), - .function = Packed(*const Super.Function).init(arg), + .function = Packed(*Super.Function).init(arg), }; for (self.upvalues.ptr()) |*upvalue| upvalue.* = null; return self; diff --git a/zlox/src/obj/list.zig b/zlox/src/obj/list.zig @@ -32,26 +32,16 @@ pub fn List(fields: anytype) type { return @ptrCast(self); } - pub fn format(self: *const Self, writer: *std.Io.Writer) !void { - const Printer = struct { - writer: @TypeOf(writer), - count: usize, - - pub fn print(this: *@This(), val: ?Value) std.Io.Writer.Error!void { - this.count -= 1; - if (val) |v| { - try v.format(this.writer); - } else { - _ = try this.writer.write("-"); - } - if (this.count > 0) _ = try this.writer.write(", "); - } - }; - - var printer = Printer{ .writer = writer, .count = self.list.ptr().len }; + pub fn format(self: *const Self, writer: *std.Io.Writer) std.Io.Writer.Error!void { + var iter = self.list.ptr().iter(); + var count = self.list.ptr().len(); _ = try writer.write("["); - try self.list.ptr().for_each_try(&printer, Printer.print); + while (iter.next()) |val| { + count -= 1; + try val.format(writer); + if (count > 0) _ = try writer.write(", "); + } _ = try writer.writeAll("]"); } @@ -67,25 +57,26 @@ pub fn List(fields: anytype) type { pub fn delete(self: *Self, index: Value) void { var list = self.list.ptr(); - if (!index.is(Value.number) or index.number >= @as(Value.tagType(Value.number), @floatFromInt(list.len)) or index.number < 0) { + if (!index.is(Value.number) or index.number >= @as(Value.tagType(Value.number), @floatFromInt(list.len()))) { return; } - list.delete(@intFromFloat(index.number)); + _ = list.pop(@intFromFloat(index.number)) catch return; } pub fn get(self: *const Self, index: Value) Error!Value { var list = self.list.ptr(); - if (!index.is(Value.number) or index.number >= @as(Value.tagType(Value.number), @floatFromInt(list.len)) or index.number < 0) { + if (!index.is(Value.number) or index.number >= @as(Value.tagType(Value.number), @floatFromInt(list.len()))) { return Error.InvalidArgument; } return list.get(@intFromFloat(index.number)); } pub fn set(self: *Self, index: Value, val: Value) Error!void { - if (!index.is(Value.number) or index.number < 0) { + if (!index.is(Value.number)) { return Error.InvalidArgument; } - _ = try self.list.ptr().set(@intFromFloat(index.number), val); + _ = self.list.ptr().set(@intFromFloat(index.number), val) catch + try self.list.ptr().push(@intFromFloat(index.number), val); } }; } diff --git a/zlox/src/obj/table.zig b/zlox/src/obj/table.zig @@ -11,7 +11,7 @@ pub fn Table(fields: anytype) type { return packed struct { const Self = @This(); pub const Arg = void; - const Table = table.Table(Value, Value, hash.hash_t(Value), Value.eql); + pub const Table = table.Table(Value, Value, hash.hash_t(Value), Value.eql); pub const Error = error{OutOfMemory} || Self.Table.Error; obj: Super, diff --git a/zlox/src/table.zig b/zlox/src/table.zig @@ -103,7 +103,7 @@ pub fn Table(K: type, V: type, hash_fn: fn (K) u32, cmp_fn: fn (K, K) bool) type } } - pub fn for_each_try(self: *const Self, arg: anytype, fun: anytype) utils.fn_error(fun)!void { + pub fn for_each_try(self: *const Self, arg: anytype, fun: anytype) utils.fn_error(fun).?!void { for (self.entries) |entry| { switch (entry) { .some => |some| if (@TypeOf(arg) == void) diff --git a/zlox/src/vm.zig b/zlox/src/vm.zig @@ -48,12 +48,12 @@ pub const VM = struct { const Globals = table.Table(*Obj.String, Global, hash.hash_t(*Obj.String), Obj.String.eql); const CallFrame = struct { - callee: *const Obj, + callee: *Obj, ip: [*]const u8, slots: [*]Value, chunk: *const Chunk, - pub fn init(comptime tp: Obj.Type, callee: *const tp.get(), slots: [*]Value) @This() { + pub fn init(comptime tp: Obj.Type, callee: *tp.get(), slots: [*]Value) @This() { return switch (tp) { .Function => @This(){ .callee = callee.cast(), .ip = callee.chunk.ptr().code.data.ptr, .chunk = callee.chunk.ptr(), .slots = slots }, .Closure => @This(){ .callee = callee.cast(), .ip = callee.function.ptr().chunk.ptr().code.data.ptr, .chunk = callee.function.ptr().chunk.ptr(), .slots = slots }, @@ -76,10 +76,10 @@ pub const VM = struct { fn gc_callback(self_ptr: *anyopaque) void { const self: *@This() = @ptrCast(@alignCast(self_ptr)); - self.globals.for_each({}, struct { - pub fn fun(name: *Obj.String, val: Global) void { - GC.mark(name); - GC.mark(val.val); + self.globals.for_each(self, struct { + pub fn fun(this: @TypeOf(self), name: *Obj.String, val: Global) void { + this.objects.mark("G", name); + this.objects.mark("G", val.val); } }.fun); } @@ -151,9 +151,19 @@ pub const VM = struct { pub fn gc_callback(self_ptr: *anyopaque) void { const self: *@This() = @ptrCast(@alignCast(self_ptr)); - var stackPtr: [*]Value = &self.stack; - while (stackPtr != self.stackTop) : (stackPtr += 1) { - GC.mark(stackPtr[0]); + var stack_ptr: [*]Value = &self.stack; + while (stack_ptr != self.stackTop) : (stack_ptr += 1) { + self.vm.objects.mark("S", stack_ptr[0]); + } + + var frame_idx: usize = 0; + while (frame_idx < self.frameCount) : (frame_idx += 1) { + self.vm.objects.mark("F", self.frames[frame_idx].callee); + } + + var iter = self.open_upvalues.iter(); + while (iter.next()) |upval| { + self.vm.objects.mark("U", upval); } } @@ -254,25 +264,29 @@ pub const VM = struct { } fn captureUpvalue(self: *@This(), slot: u8) !*Obj.Upvalue { - var upvalue = self.open_upvalues.tip; - while (upvalue) |el| : (upvalue = el.next) { - const val = el.val.?; + var iter = self.open_upvalues.iter(); + + while (iter.next()) |val| { if (val.slot == slot) return val; if (val.slot > slot) break; } + _ = iter.next(); + const new = try self.vm.objects.emplace(.Upvalue, .{ .val = &self.frame().slots[slot], .slot = slot }); - try self.open_upvalues.insert_after(upvalue, new); + try iter.push(new); return new; } fn closeUpvalues(self: *@This(), slot: u8) !void { - while (self.open_upvalues.tip) |el| { - if (el.val.?.slot < slot) break; + var iter = self.open_upvalues.iter(); + + while (iter.next()) |upval| { + if (upval.slot < slot) break; - const upval = self.open_upvalues.pop() catch unreachable; try upval.close(self.vm.allocator); + iter.pop(); } } diff --git a/zlox/src/vm/native.zig b/zlox/src/vm/native.zig @@ -34,7 +34,7 @@ pub fn table(gc: *GC, args: []const Value) Error!Value { pub fn list(gc: *GC, args: []const Value) Error!Value { var lis = gc.emplace(.List, {}) catch return Error.Native; for (args) |arg| { - lis.list.ptr().push(arg) catch return Error.Native; + lis.list.ptr().push(-1, arg) catch return Error.Native; } return Value.init(lis.cast()); }