commit f691de138bef1a690406ee83f179a8cee6469eaf
parent a3ec95884dae6f80c5a3e9f8f2d3b425a53e1b56
Author: Szymon Mikulicz <szymon.mikulicz@posteo.net>
Date: Thu, 20 Aug 2026 23:45:39 +0200
Rewrite list
Diffstat:
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());
}