DzLox

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

commit 310d12bb78444322e65394cdeed765c4b6b8657c
parent 887d23fcc9859fb6ab70003af88f7e4d25a96af1
Author: Szymon Mikulicz <szymon.mikulicz@posteo.net>
Date:   Mon, 17 Aug 2026 14:18:21 +0200

Zig 0.16 is horribly broken, so back to master

Diffstat:
Mzlox/build.zig | 4+---
Mzlox/src/compiler.zig | 7++++---
Mzlox/src/comptime_utils.zig | 91+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Mzlox/src/debug.zig | 27+++++++++++++--------------
Mzlox/src/obj.zig | 18+++++-------------
Mzlox/src/obj/closure.zig | 25+++++++++++--------------
Mzlox/src/obj/function.zig | 32++++++++++++++------------------
Mzlox/src/obj/list.zig | 17++++++++---------
Mzlox/src/obj/native.zig | 14++++++--------
Mzlox/src/obj/table.zig | 17++++++++---------
Mzlox/src/obj/upvalue.zig | 27+++++++++++----------------
Mzlox/src/packed.zig | 148++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Mzlox/src/vm.zig | 17++++++++++++-----
13 files changed, 249 insertions(+), 195 deletions(-)

diff --git a/zlox/build.zig b/zlox/build.zig @@ -66,9 +66,7 @@ pub fn build(b: *std.Build) void { // This allows the user to pass arguments to the application in the build // command itself, like this: `zig build run -- arg1 arg2 etc` - if (b.args) |args| { - run_cmd.addArgs(args); - } + run_cmd.addPassthruArgs(); // This creates a build step. It will be visible in the `zig build --help` menu, // and can be selected like this: `zig build run` diff --git a/zlox/src/compiler.zig b/zlox/src/compiler.zig @@ -9,6 +9,7 @@ const Obj = GC.Obj; const debug = @import("debug.zig"); const Token = scanner.TokenType; const vm_native = @import("vm/native.zig"); +const utils = @import("comptime_utils.zig"); pub const CompilerError = Obj.Error || scanner.ScannerError || Chunk.Error || Value.ParseNumberError || error{ UnexpectedToken, NotAnExpression }; @@ -78,7 +79,7 @@ pub fn Compiler(size: comptime_int) type { }; const rules = init: { - var new: [@typeInfo(Token).@"enum".fields.len]ParseRule = undefined; + var new: [utils.enum_len(Token)]ParseRule = undefined; for (&new, 0..) |*v, i| { const T = Token; const S = Self; @@ -952,11 +953,11 @@ pub fn Compiler(size: comptime_int) type { .lastError = scanner.ScannerError.EmptyToken, .currentFunction = fun, .objects = objects, - .locals = [_]Local{Local{}} ** size, + .locals = @splat(Local{}), .localCount = 1, .scopeDepth = 0, .enclosing = null, - .upvalues = [_]Upvalue{Upvalue{ .index = 0, .isLocal = false }} ** upvalues_size, + .upvalues = @splat(Upvalue{ .index = 0, .isLocal = false }), }; self.locals[0].depth = 0; return self; diff --git a/zlox/src/comptime_utils.zig b/zlox/src/comptime_utils.zig @@ -1,30 +1,47 @@ const std = @import("std"); +pub fn with_size(T: type, comptime size: std.builtin.Type.Pointer.Size) type { + return mod_ptr_t(T, "size", size); +} + pub fn copy_const(T: type, U: type) type { - const info = @typeInfo(U).pointer; + return mod_ptr_t(U, "const", @typeInfo(T).pointer.attrs.@"const"); +} + +pub fn mod_ptr_t(T: type, comptime field: []const u8, comptime val: anytype) type { + comptime var new = @typeInfo(T).pointer; + + if (@hasField(std.lang.Type.Pointer.Attributes, field)) { + @field(new.attrs, field) = val; + } else { + @field(new, field) = val; + } + return @Pointer( - info.size, - std.builtin.Type.Pointer.Attributes{ - .@"const" = @typeInfo(T).pointer.is_const, - .@"volatile" = info.is_volatile, - .@"allowzero" = info.is_allowzero, - .@"addrspace" = info.address_space, - .@"align" = info.alignment - }, - info.child, - std.builtin.Type.Pointer.sentinel(info) + new.size, + new.attrs, + new.child, + std.lang.Type.Pointer.sentinel(new), ); } +pub fn enum_len(T: type) usize { + return @typeInfo(T).@"enum".field_names.len; +} + +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); +} + pub fn typeFromTag(T: type, comptime tag: std.meta.Tag(T)) type { return @TypeOf(@field(@unionInit(T, @tagName(tag), undefined), @tagName(tag))); - } pub fn tagFromType(T: type, U: type) std.meta.Tag(T) { - inline for (@typeInfo(T).@"union".fields) |field| { - if (U == field.type) { - return @field(T, field.name); + const info = @typeInfo(T).@"union"; + inline for (info.field_types, info.field_names) |field_type, field_name| { + if (U == field_type) { + return @field(T, field_name); } } @compileError("No matching tag for type " ++ @typeName(U) ++ " in Union " ++ @typeName(T)); @@ -48,41 +65,33 @@ pub fn if_not_null(comptime fun: anytype) fn (?param_type(fun, 0)) void { }.function; } -pub fn make_packed_t(s: type) type { +pub fn pack_t(s: type) type { const info = @typeInfo(s).@"struct"; - const Attributes = std.builtin.Type.StructField.Attributes; - - const attr = Attributes{ - .@"align" = null, - .@"comptime" = false, - }; - - const attrs = [_]Attributes{attr} ** info.fields.len; - comptime var names: [info.fields.len][]const u8 = undefined; - comptime var types: [info.fields.len]type = undefined; - - inline for (info.fields, 0..) |field, i| { - names[i] = field.name; - types[i] = field.type; - } + const Attributes = std.lang.Type.Struct.FieldAttributes; return @Struct( - std.builtin.Type.ContainerLayout.@"packed", + std.lang.Type.ContainerLayout.@"packed", info.backing_integer, - &names, - &types, - &attrs + info.field_names, + info.field_types, + &@as( + [info.field_attrs.len]Attributes, + @splat(Attributes{ + .@"align" = null, + .@"comptime" = false, + }), + ), ); } -pub fn make_packed(s: anytype) make_packed_t(@TypeOf(s)) { +pub fn pack(s: anytype) pack_t(@TypeOf(s)) { const T = @TypeOf(s); - const fields = @typeInfo(T).@"struct".fields; - var packd: make_packed_t(T) = undefined; + const fields = @typeInfo(T).@"struct".field_names; + var packed_struct: pack_t(T) = undefined; - for (fields) |field| { - @field(packd, field.name) = @field(s, field.name); + inline for (fields) |field| { + @field(packed_struct, field) = @field(s, field); } - return packd; + return packed_struct; } diff --git a/zlox/src/debug.zig b/zlox/src/debug.zig @@ -68,11 +68,11 @@ fn _disassembleInstruction(ch: *const chunk.Chunk, offset: usize, print_fn: bool @intFromEnum(OP.LOOP) => try jumpInstruction(name, false, ch, offset), @intFromEnum(OP.SET_INDEX) => simpleInstruction(name, offset), @intFromEnum(OP.GET_INDEX) => simpleInstruction(name, offset), - @intFromEnum(OP.CALL) => try byteInstruction(name, ch, offset), + @intFromEnum(OP.CALL) => try byteInstruction(name, ch, offset), @intFromEnum(OP.CLOSURE) => try closureInstruction(name, ch, offset, print_fn), @intFromEnum(OP.CLOSE_UPVALUE) => simpleInstruction(name, offset), else => blk: { - print("Unknown opcode {d} {s}\n", .{op, name}); + print("Unknown opcode {d} {s}\n", .{ op, name }); break :blk offset + 1; }, }; @@ -86,21 +86,20 @@ fn simpleInstruction(name: []const u8, offset: usize) usize { fn constantInstruction(name: []const u8, ch: *const chunk.Chunk, offset: usize, print_fn: bool) Error!usize { const constant = try ch.code.get(offset + 1); const constval = try ch.constants.get(constant); - print("{s:<32} {d:4} '{f}'\n", .{ name, constant, constval}); + print("{s:<32} {d:4} '{f}'\n", .{ name, constant, constval }); if (print_fn and constval.is(Obj.Type.Function)) { const function = constval.obj.cast(.Function) catch unreachable; - if (function.name.valid()) { - try disassembleChunk(function.chunk.ptr(), function.name.ptr().slice()); + if (function.name.get()) |fn_name| { + try disassembleChunk(function.chunk.ptr(), fn_name.slice()); } else { try disassembleChunk(function.chunk.ptr(), "<anon>"); } - } return offset + 2; } -fn byteInstruction(name: []const u8, ch:*const chunk.Chunk, offset: usize) Error!usize { - print("{s:<32} {d:4}\n", .{name, try ch.code.get(offset+1)}); +fn byteInstruction(name: []const u8, ch: *const chunk.Chunk, offset: usize) Error!usize { + print("{s:<32} {d:4}\n", .{ name, try ch.code.get(offset + 1) }); return offset + 2; } @@ -109,7 +108,7 @@ fn jumpInstruction(name: []const u8, sign: bool, ch: *const chunk.Chunk, offset: const lsb: u16 = try ch.code.get(offset + 2); const jump = (msb << 8) | lsb; - print("{s:<32} {d:4} -> {d}\n", .{name, offset, if (sign) offset + 3 + jump else offset + 3 - jump}); + print("{s:<32} {d:4} -> {d}\n", .{ name, offset, if (sign) offset + 3 + jump else offset + 3 - jump }); return offset + 3; } @@ -118,21 +117,21 @@ fn closureInstruction(name: []const u8, ch: *const chunk.Chunk, offset: usize, p const constant = try ch.code.get(off); const val = try ch.constants.get(constant); const function = try val.obj.cast(.Function); - print("{s:<32} {d:4} '{f}'\n", .{ name, constant, function}); + print("{s:<32} {d:4} '{f}'\n", .{ name, constant, function }); for (0..function.upvalue_count) |_| { const isLocal = try ch.code.get(off + 1); const idx = try ch.code.get(off + 2); try print_offset(ch, off + 1); - print("{s:<38}|-> {s} {d}\n", .{ "", if (isLocal == 1) "local" else "upvalue", idx}); + print("{s:<38}|-> {s} {d}\n", .{ "", if (isLocal == 1) "local" else "upvalue", idx }); off += 2; } if (print_fn) { - if (function.name.valid()) { - try disassembleChunk(function.chunk.ptr(), function.name.ptr().slice()); + if (function.name.get()) |fn_name| { + try disassembleChunk(function.chunk.ptr(), fn_name.slice()); } else { try disassembleChunk(function.chunk.ptr(), "<anon>"); } } - + return off + 1; } diff --git a/zlox/src/obj.zig b/zlox/src/obj.zig @@ -3,7 +3,7 @@ const utils = @import("comptime_utils.zig"); fn nameOf(fqn: []const u8) []const u8 { var lastDot = 0; - for(fqn, 0..) |c, i| { + for (fqn, 0..) |c, i| { if (c == '.') lastDot = i + 1; if (c == '(') return fqn[lastDot..i]; } @@ -15,7 +15,7 @@ pub fn Obj(fields: anytype) type { const Self = @This(); type: Type, - fields: utils.make_packed_t(@TypeOf(fields)) = utils.make_packed(fields), + fields: utils.pack_t(@TypeOf(fields)) = utils.pack(fields), pub const List = @import("obj/list.zig").List(fields); pub const String = @import("obj/string.zig").String(fields); @@ -25,15 +25,7 @@ pub fn Obj(fields: anytype) type { pub const Closure = @import("obj/closure.zig").Closure(fields); pub const Upvalue = @import("obj/upvalue.zig").Upvalue(fields); - pub const Error = error {IllegalCastError} - || List.Error - || String.Error - || Table.Error - || Function.Error - || Native.Error - || List.Error - || Closure.Error - || Upvalue.Error; + pub const Error = error{IllegalCastError} || List.Error || String.Error || Table.Error || Function.Error || Native.Error || List.Error || Closure.Error || Upvalue.Error; pub const Type = enum(u8) { String, @@ -50,7 +42,7 @@ pub fn Obj(fields: anytype) type { }; pub fn isChild(T: type) bool { - inline for(@typeInfo(Type).@"enum".fields) |field| { + inline for (@typeInfo(Type).@"enum".fields) |field| { const U = @field(Self, field.name); if (T == U or T == *U or T == *const U) return true; } @@ -58,7 +50,7 @@ pub fn Obj(fields: anytype) type { } pub fn make(child: type) Self { - return Self { + return Self{ .type = @field(Type, nameOf(@typeName(child))), }; } diff --git a/zlox/src/obj/closure.zig b/zlox/src/obj/closure.zig @@ -1,6 +1,6 @@ const std = @import("std"); const utils = @import("../comptime_utils.zig"); -const pack = @import("../packed.zig"); +const Packed = @import("../packed.zig").Packed; pub fn Closure(fields: anytype) type { const Super = @import("../obj.zig").Obj(fields); @@ -9,23 +9,20 @@ pub fn Closure(fields: anytype) type { const Self = @This(); pub const Arg = *const Super.Function; - pub const Error = error { OutOfMemory }; + pub const Error = error{OutOfMemory}; obj: Super, - function: pack.Packed(*const Super.Function), - upvalues: pack.Packed([*]?*Super.Upvalue), - upvalues_len: u8, + function: Packed(*const Super.Function), + upvalues: Packed([]?*Super.Upvalue), pub fn init(arg: Arg, allocator: std.mem.Allocator) Error!*Self { const self: *Self = try allocator.create(Self); - self.* = Self{ + self.* = Self{ .obj = Super.make(Self), - .upvalues = pack.Packed([*]?*Super.Upvalue).init((try allocator.alloc(?*Super.Upvalue, arg.upvalue_count)).ptr), - .upvalues_len = arg.upvalue_count, - .function = pack.Packed(*const Super.Function).init(arg), + .upvalues = try Packed([]?*Super.Upvalue).alloc(allocator, arg.upvalue_count), + .function = Packed(*const Super.Function).init(arg), }; - for(self.upvalues.ptr()[0..self.upvalues_len]) - |*upvalue| upvalue.* = null; + for (self.upvalues.ptr()) |*upvalue| upvalue.* = null; return self; } @@ -35,8 +32,8 @@ pub fn Closure(fields: anytype) type { pub fn format(self: *const Self, writer: *std.Io.Writer) !void { _ = try writer.write("<C: "); - if (self.function.ptr().name.valid()) { - _ = try writer.write(self.function.ptr().name.ptr().slice()); + if (self.function.ptr().name.get()) |fn_name| { + _ = try writer.write(fn_name.slice()); } else { _ = try writer.write("-"); } @@ -48,7 +45,7 @@ pub fn Closure(fields: anytype) type { } pub fn free(self: *const Self, allocator: std.mem.Allocator) void { - allocator.free(self.upvalues.ptr()[0..self.upvalues_len]); + self.upvalues.destroy(allocator); allocator.destroy(self); } }; diff --git a/zlox/src/obj/function.zig b/zlox/src/obj/function.zig @@ -1,7 +1,7 @@ const std = @import("std"); const chunk = @import("../chunk.zig"); const utils = @import("../comptime_utils.zig"); -const pack = @import("../packed.zig"); +const Packed = @import("../packed.zig").Packed; pub fn Function(fields: anytype) type { const Super = @import("../obj.zig").Obj(fields); @@ -10,36 +10,33 @@ pub fn Function(fields: anytype) type { return packed struct { const Self = @This(); pub const Arg = Type; - pub const Error = error { OutOfMemory }; + pub const Error = error{OutOfMemory}; - pub const Type = enum(u8) { - Function, - Script - }; + pub const Type = enum(u8) { Function, Script }; obj: Super, arity: u8, - chunk: pack.Packed(*chunk.Chunk), - name: pack.Packed(?*const String), + chunk: Packed(*chunk.Chunk), + name: Packed(?*const String), type: Type, upvalue_count: u8, pub fn init(tp: Arg, allocator: std.mem.Allocator) Error!*Self { const self: *Self = try allocator.create(Self); - self.* = Self{ + self.* = Self{ .obj = Super.make(Self), - .chunk = try pack.Packed(*chunk.Chunk).new(allocator), + .chunk = try Packed(*chunk.Chunk).create(allocator), .arity = 0, - .name = pack.Packed(?*const String).nil(), + .name = Packed(?*const String).init(null), .type = tp, .upvalue_count = 0, }; - self.chunk.ptr().* = try chunk.Chunk.init(allocator); + self.chunk.set(try chunk.Chunk.init(allocator)); return self; } pub fn set_name(self: *Self, name: *const String) void { - self.name = pack.Packed(?*const String).init(name); + self.name = Packed(?*const String).init(name); } pub fn cast(self: anytype) utils.copy_const(@TypeOf(self), *Super) { @@ -47,12 +44,12 @@ pub fn Function(fields: anytype) type { } pub fn format(self: *const Self, writer: *std.Io.Writer) !void { - switch(self.type) { + switch (self.type) { .Function => _ = try writer.write("<F: "), .Script => _ = try writer.write("<S: "), } - if (self.name.valid()) { - _ = try writer.write(self.name.ptr().slice()); + if (self.name.get()) |name| { + _ = try writer.write(name.slice()); } else { _ = try writer.write("-"); } @@ -65,9 +62,8 @@ pub fn Function(fields: anytype) type { pub fn free(self: *const Self, allocator: std.mem.Allocator) void { self.chunk.ptr().deinit(); - self.chunk.free(allocator); + self.chunk.destroy(allocator); allocator.destroy(self); } - }; } diff --git a/zlox/src/obj/list.zig b/zlox/src/obj/list.zig @@ -3,7 +3,7 @@ const std = @import("std"); const Value = @import("../value.zig").Value; const utils = @import("../comptime_utils.zig"); const list_zig = @import("../list.zig"); -const pack = @import("../packed.zig"); +const Packed = @import("../packed.zig").Packed; pub fn List(fields: anytype) type { const Super = @import("../obj.zig").Obj(fields); @@ -13,18 +13,18 @@ pub fn List(fields: anytype) type { pub const List = list_zig.List(Value); pub const Arg = void; - pub const Error = error { OutOfMemory, InvalidArgument } || Self.List.Error; + pub const Error = error{ OutOfMemory, InvalidArgument } || Self.List.Error; obj: Super, - list: pack.Packed(*Self.List), + list: Packed(*Self.List), pub fn init(_: Arg, allocator: std.mem.Allocator) Error!*Self { const self: *Self = try allocator.create(Self); - self.* = Self{ + self.* = Self{ .obj = Super.make(Self), - .list = try pack.Packed(*Self.List).new(allocator) + .list = try Packed(*Self.List).create(allocator), }; - self.list.ptr().* = Self.List.init(allocator); + self.list.set(Self.List.init(allocator)); return self; } @@ -46,10 +46,9 @@ pub fn List(fields: anytype) type { } if (this.count > 0) _ = try this.writer.write(", "); } - }; - var printer = Printer{.writer = writer, .count = self.list.ptr().len}; + var printer = Printer{ .writer = writer, .count = self.list.ptr().len }; _ = try writer.write("["); try self.list.ptr().for_each_try(&printer, Printer.print); @@ -62,7 +61,7 @@ pub fn List(fields: anytype) type { pub fn free(self: *Self, allocator: std.mem.Allocator) void { self.list.ptr().free(); - self.list.free(allocator); + self.list.destroy(allocator); allocator.destroy(self); } diff --git a/zlox/src/obj/native.zig b/zlox/src/obj/native.zig @@ -3,7 +3,7 @@ const std = @import("std"); const utils = @import("../comptime_utils.zig"); const GC = @import("../gc.zig").GC; const Value = @import("../value.zig").Value; -const pack = @import("../packed.zig"); +const Packed = @import("../packed.zig").Packed; pub fn Native(fields: anytype) type { const Super = @import("../obj.zig").Obj(fields); @@ -20,21 +20,19 @@ pub fn Native(fields: anytype) type { pub const Arg = struct { fun: Fn, arity_min: u8 = ArityMin, arity_max: u8 = ArityMax, name: []const u8 = "" }; obj: Super, - fun: pack.Packed(Fn), + fun: Packed(Fn), arity_min: u8, arity_max: u8, - name: pack.Packed([*]const u8), - name_len: usize, + name: Packed([]const u8), pub fn init(arg: Arg, allocator: std.mem.Allocator) Error!*Self { const self: *Self = try allocator.create(Self); self.* = Self{ .obj = Super.make(Self), - .fun = pack.Packed(Fn).init(arg.fun), + .fun = Packed(Fn).init(arg.fun), .arity_min = arg.arity_min, .arity_max = arg.arity_max, - .name = pack.Packed([*]const u8).init(arg.name.ptr), - .name_len = arg.name.len + .name = Packed([]const u8).init(arg.name), }; return self; } @@ -50,7 +48,7 @@ pub fn Native(fields: anytype) type { pub fn format(self: *const Self, writer: *std.Io.Writer) !void { _ = try writer.write("<N: "); - _ = try writer.write(self.name.ptr()[0..self.name_len]); + _ = try writer.write(self.name.ptr()); _ = try writer.writeAll(">"); } diff --git a/zlox/src/obj/table.zig b/zlox/src/obj/table.zig @@ -3,7 +3,7 @@ const table = @import("../table.zig"); const hash = @import("../hash.zig"); const Value = @import("../value.zig").Value; const utils = @import("../comptime_utils.zig"); -const pack = @import("../packed.zig"); +const Packed = @import("../packed.zig").Packed; pub fn Table(fields: anytype) type { const Super = @import("../obj.zig").Obj(fields); @@ -12,20 +12,20 @@ pub fn Table(fields: anytype) type { const Self = @This(); pub const Arg = void; const Table = table.Table(Value, Value, hash.hash_t(Value), Value.eql); - pub const Error = error { OutOfMemory } || Self.Table.Error; + pub const Error = error{OutOfMemory} || Self.Table.Error; obj: Super, - table: pack.Packed(*Self.Table), + table: Packed(*Self.Table), len: usize, pub fn init(_: Arg, allocator: std.mem.Allocator) Error!*Self { const self: *Self = try allocator.create(Self); - self.* = Self{ + self.* = Self{ .obj = Super.make(Self), - .table = try pack.Packed(*Self.Table).new(allocator), + .table = try Packed(*Self.Table).create(allocator), .len = 0, }; - self.table.ptr().* = Self.Table.init(allocator); + self.table.set(Self.Table.init(allocator)); return self; } @@ -59,10 +59,9 @@ pub fn Table(fields: anytype) type { try val.format(this.writer); if (this.count > 0) _ = try this.writer.write(", "); } - }; - var printer = Printer{.writer = writer, .count = self.len}; + var printer = Printer{ .writer = writer, .count = self.len }; _ = try writer.write("["); if (self.table.ptr().count > 0) { try self.table.ptr().for_each_try(&printer, Printer.print); @@ -78,7 +77,7 @@ pub fn Table(fields: anytype) type { pub fn free(self: *const Self, allocator: std.mem.Allocator) void { self.table.ptr().deinit(); - self.table.free(allocator); + self.table.destroy(allocator); allocator.destroy(self); } }; diff --git a/zlox/src/obj/upvalue.zig b/zlox/src/obj/upvalue.zig @@ -2,7 +2,7 @@ const std = @import("std"); const utils = @import("../comptime_utils.zig"); const Value = @import("../value.zig").Value; -const pack = @import("../packed.zig"); +const Packed = @import("../packed.zig").Packed; pub fn Upvalue(fields: anytype) type { const Super = @import("../obj.zig").Obj(fields); @@ -10,41 +10,36 @@ pub fn Upvalue(fields: anytype) type { return packed struct { const Self = @This(); - pub const Arg = struct {val: *Value, slot: u8}; - pub const Error = error { OutOfMemory }; + pub const Arg = struct { val: *Value, slot: u8 }; + pub const Error = error{OutOfMemory}; obj: Super, - location: pack.Packed(*Value), + location: Packed(*Value), closed: bool, slot: u8, pub fn init(arg: Arg, allocator: std.mem.Allocator) Error!*Self { const self: *Self = try allocator.create(Self); - self.* = Self{ - .obj = Super.make(Self), - .location = pack.Packed(*Value).init(arg.val), - .closed = false, - .slot = arg.slot - }; + self.* = Self{ .obj = Super.make(Self), .location = Packed(*Value).init(arg.val), .closed = false, .slot = arg.slot }; return self; } pub fn close(self: *Self, allocator: std.mem.Allocator) Error!void { if (!self.closed) { - const new = try allocator.create(Value); - new.* = self.location.ptr().*; - self.location = pack.Packed(*Value).init(new); + const old = self.location.get(); + + self.location = try Packed(*Value).create(allocator); + self.location.set(old); self.closed = true; } } - pub fn cast(self: anytype) utils.copy_const(@TypeOf(self), *Super) { return @ptrCast(self); } pub fn format(self: *const Self, writer: *std.Io.Writer) !void { - try writer.print("<Upvalue{{{f} at 0x{x}, {any}, {d}}}>", .{self.location.ptr().*, self.location._ptr, self.closed, self.slot}); + try writer.print("<Upvalue{{{f} at 0x{x}, {any}, {d}}}>", .{ self.location.get(), self.location._ptr, self.closed, self.slot }); } pub fn eql(_: *const Self, _: *const Self) bool { @@ -52,7 +47,7 @@ pub fn Upvalue(fields: anytype) type { } pub fn free(self: *const Self, allocator: std.mem.Allocator) void { - if (self.closed) allocator.destroy(self.location.ptr()); + if (self.closed) self.location.destroy(allocator); allocator.destroy(self); } }; diff --git a/zlox/src/packed.zig b/zlox/src/packed.zig @@ -1,70 +1,134 @@ const std = @import("std"); +const utils = @import("comptime_utils.zig"); -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); -} +pub fn Packed(Type: type) type { + return packed struct { + const optional = utils.is_type(Type, "optional"); -pub fn Packed(T: type) type { - comptime var _Type = T; - comptime var _Ptr =T; + const Ptr = if (optional) @typeInfo(Type).optional.child else Type; - const _optional = is_type(_Type, "optional"); - if (_optional) { - _Type = @typeInfo(_Type).optional.child; - } + const Child = if (utils.is_type(Ptr, "pointer")) + @typeInfo(Ptr).pointer.child + else + @compileError("Expected pointer type, got " ++ @typeName(Ptr)); - const _pointer = is_type(_Type, "pointer"); - if (_pointer) { - _Ptr = _Type; - _Type = @typeInfo(_Type).pointer.child; - } else { - @compileError("Expected pointer type, got " ++ @typeName(_Type)); - } - - return packed struct { - const pointer = _pointer; - const optional = _optional; - const Type = _Type; - const Ptr = _Ptr; + const slice = @typeInfo(Ptr).pointer.size == .slice; + const many = @typeInfo(Ptr).pointer.size == .many; const Self = @This(); _ptr: usize, + _len: if (slice) usize else void, + + pub fn create(allocator: std.mem.Allocator) !Self { + return if (slice or many) + @compileError("Cannot create() a slice or many pointer, use alloc() instead") + else + Self.init(try allocator.create(Child)); + } - pub fn new(allocator: std.mem.Allocator) !Self { - return Self.init(try allocator.create(Type)); + pub fn alloc(allocator: std.mem.Allocator, count: usize) !Self { + return if (slice) + Self.init(try allocator.alloc(Child, count)) + else if (many) + Self.init((try allocator.alloc(Child, count)).ptr) + else if (count == 1) + Self.create(allocator) + else + @panic("Cannot alloc() a single-item pointer with a count of more than one"); } - pub fn init(arg: Ptr) Self { + pub fn init(arg: Type) Self { return Self{ - ._ptr = @intFromPtr(arg), + ._ptr = if (optional) + if (arg) |val| + @intFromPtr(if (slice) val.ptr else val) + else + 0 + else + @intFromPtr(if (slice) arg.ptr else arg), + + ._len = if (slice) + if (optional) + if (arg) |val| + val.len + else + 0 + else + arg.len, }; } - pub fn nil() Self { - if (!optional) { - @compileError("Cannot create nil for non-optional type"); - } + pub fn ptr(self: Self) Type { + return if (optional and self._ptr == 0) + null + else if (slice) + @as(utils.with_size(Ptr, .many), @ptrFromInt(self._ptr))[0..self._len] + else + @ptrFromInt(self._ptr); + } - return Self{ - ._ptr = 0, - }; + pub fn get(self: Self) if (optional) ?Child else Child { + return if (slice or many) + @compileError("Cannot call get() on a slice or many pointer") + else if (optional and self._ptr == 0) + null + else + @as(Ptr, @ptrFromInt(self._ptr)).*; + } + + pub fn at(self: Self, idx: usize) if (optional) ?Child else Child { + return if (!slice and !many) + @compileError("Cannot call at() on a single-item pointer") + else if (optional and self._ptr == 0) + null + else + self.ptr()[idx]; } - pub fn valid(self: Self) bool { - return !optional or self._ptr != 0; + pub fn len(self: Self) usize { + return if (slice) + self._len + else + @compileError("Cannot call len() on a non-slice pointer"); } - pub fn ptr(self: Self) Ptr { - if (self.valid()) { - return @ptrFromInt(self._ptr); + pub fn free(self: Self, allocator: std.mem.Allocator, count: usize) void { + const pointer = if (optional) + if (self.ptr()) |_ptr| + _ptr + else + return + else + self.ptr(); + + if (many) { + allocator.free(pointer[0..count]); + } else if (slice) { + if (count != self._len) + @panic("Count has to be equal to the length of the slice"); + + allocator.free(pointer); } else { - @panic("Attempt to access null pointer"); + if (count != 1) + @panic("Count has to be equal 1 for a single-item pointer"); + + allocator.destroy(pointer); } } - pub fn free(self: Self, allocator: std.mem.Allocator) void { - allocator.destroy(self.ptr()); + pub fn set(self: Self, val: if (many or slice) Ptr else Child) void { + if (many or slice) + @memcpy(self.ptr(), val) + else + self.ptr().* = val; + } + + pub fn destroy(self: Self, allocator: std.mem.Allocator) void { + if (many) + @compileError("Cannot use destroy() on a many-pointer, call free() instead"); + + self.free(allocator, if (slice) self._len else 1); } }; } diff --git a/zlox/src/vm.zig b/zlox/src/vm.zig @@ -104,7 +104,14 @@ pub const VM = struct { open_upvalues: List, pub fn run(vm: *VM, function: *Obj.Function, dbg: bool) InterpreterError!void { - var self = @This(){ .frames = [_]CallFrame{undefined} ** callstack_size, .frameCount = 1, .stack = [_]Value{Value.init({})} ** stack_size, .stackTop = undefined, .vm = vm, .open_upvalues = List.init(vm.allocator) }; + var self = @This(){ + .frames = @splat(undefined), + .frameCount = 1, + .stack = @splat(Value.init({})), + .stackTop = undefined, + .vm = vm, + .open_upvalues = List.init(vm.allocator), + }; defer self.open_upvalues.free(); @@ -338,12 +345,12 @@ pub const VM = struct { @intFromEnum(OP.GET_UPVALUE) => { const closure = try self.frame().callee.cast(.Closure); const index = self.read_byte(); - self.push(closure.upvalues.ptr()[index].?.location.ptr().*); + self.push(closure.upvalues.at(index).?.location.ptr().*); }, @intFromEnum(OP.SET_UPVALUE) => { const closure = try self.frame().callee.cast(.Closure); const index = self.read_byte(); - closure.upvalues.ptr()[index].?.location.ptr().* = self.peek(0); + closure.upvalues.at(index).?.location.ptr().* = self.peek(0); }, @intFromEnum(OP.CLOSE_UPVALUE) => { try self.closeUpvalues(self.current_slot()); @@ -399,14 +406,14 @@ pub const VM = struct { @intFromEnum(OP.CLOSURE) => { const function = try self.read_constant().obj.cast(.Function); const closure = try self.vm.objects.emplace(.Closure, function); - for (closure.upvalues.ptr()[0..closure.upvalues_len]) |*upvalue| { + for (closure.upvalues.ptr()) |*upvalue| { const isLocal = self.read_byte(); const slot = self.read_byte(); if (isLocal == 1) { upvalue.* = try self.captureUpvalue(slot); } else { const callee = try self.frame().callee.cast(.Closure); - upvalue.* = callee.upvalues.ptr()[slot]; + upvalue.* = callee.upvalues.at(slot); } } self.push(Value.init(closure.cast()));