commit 887d23fcc9859fb6ab70003af88f7e4d25a96af1
parent 984223b8bd9855452cdc43920e27261c6b0d680a
Author: Szymon Mikulicz <szymon.mikulicz@posteo.net>
Date: Tue, 11 Aug 2026 18:47:21 +0200
Update to 0.16
Diffstat:
17 files changed, 247 insertions(+), 129 deletions(-)
diff --git a/zlox/.gitignore b/zlox/.gitignore
@@ -4,3 +4,4 @@ zig-out/
.envrc
.tool-versions
.zig-cache/
+zig-pkg
diff --git a/zlox/build.zig b/zlox/build.zig
@@ -15,12 +15,36 @@ pub fn build(b: *std.Build) void {
// set a preferred release mode, allowing the user to decide how to optimize.
const optimize = b.standardOptimizeOption(.{});
+ const linenoise_dep = b.dependency("linenoise", .{
+ .target = target,
+ .optimize = optimize,
+ });
+
+ const linenoise_tc = b.addTranslateC(.{
+ .root_source_file = linenoise_dep.path("linenoise.h"),
+ .target = target,
+ .optimize = optimize,
+ });
+
+ const linenoise_mod = linenoise_tc.createModule();
+
+ linenoise_mod.addCSourceFile(.{
+ .file = linenoise_dep.path("linenoise.c"),
+ .language = .c,
+ });
+
const exe = b.addExecutable(.{
.name = "zlox",
.root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
+ .imports = &.{
+ .{
+ .name = "linenoise",
+ .module = linenoise_mod,
+ },
+ },
}),
});
@@ -52,15 +76,6 @@ pub fn build(b: *std.Build) void {
const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);
- const linenoise = b.dependency("linenoise", .{
- .target = target,
- .optimize = optimize,
- });
-
- exe.addCSourceFile(.{ .file = linenoise.path("linenoise.c") });
- exe.addIncludePath(linenoise.path(""));
-
- exe.linkLibC();
// Creates a step for unit testing. This only builds the test executable
// but does not run it.
const unit_tests = b.addTest(.{
diff --git a/zlox/build.zig.zon b/zlox/build.zig.zon
@@ -16,8 +16,8 @@
// internet connectivity.
.dependencies = .{
.linenoise = .{
- .url = "https://github.com/antirez/linenoise/archive/refs/heads/master.tar.gz",
- .hash = "1220129271545fccd186dd1ce05c9ee7fb52c97cd86884e70597a0eef254a909e320",
+ .url = "https://github.com/antirez/linenoise/archive/a473823d74b93eab2ba83480df16ed37617493f2.zip",
+ .hash = "N-V-__8AAOJ5AgChoMZ8F65t1NdLCUv2YLyHpCZ1ydGIfwbc",
},
},
.paths = .{
diff --git a/zlox/src/compiler.zig b/zlox/src/compiler.zig
@@ -137,7 +137,7 @@ pub fn Compiler(size: comptime_int) type {
}
fn currentChunk(self: *Self) *Chunk {
- return self.currentFunction.chunk;
+ return self.currentFunction.chunk.ptr();
}
fn emitByte(self: *Self, byte: u8) void {
@@ -528,11 +528,11 @@ pub fn Compiler(size: comptime_int) type {
self.lastError = err;
return;
};
- fun.name = self.objects.emplace(Obj.Type.String, &.{name}) catch |err| {
+ 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);
diff --git a/zlox/src/comptime_utils.zig b/zlox/src/comptime_utils.zig
@@ -1,9 +1,19 @@
const std = @import("std");
pub fn copy_const(T: type, U: type) type {
- comptime var info = @typeInfo(U);
- info.pointer.is_const = @typeInfo(T).pointer.is_const;
- return @Type(info);
+ const info = @typeInfo(U).pointer;
+ 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)
+ );
}
pub fn typeFromTag(T: type, comptime tag: std.meta.Tag(T)) type {
@@ -39,21 +49,30 @@ pub fn if_not_null(comptime fun: anytype) fn (?param_type(fun, 0)) void {
}
pub fn make_packed_t(s: type) type {
- const oldFields = @typeInfo(s).@"struct".fields;
- var newFields: [oldFields.len]std.builtin.Type.StructField = undefined;
+ const info = @typeInfo(s).@"struct";
+ const Attributes = std.builtin.Type.StructField.Attributes;
- for (oldFields, &newFields) |oldField, *newField| {
- newField.* = oldField;
- newField.alignment = 0;
- newField.is_comptime = false;
+ 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;
}
- return @Type(std.builtin.Type{.@"struct" = .{
- .layout = std.builtin.Type.ContainerLayout.@"packed",
- .fields = &newFields,
- .decls = &[_]std.builtin.Type.Declaration{},
- .is_tuple = false
- }});
+ return @Struct(
+ std.builtin.Type.ContainerLayout.@"packed",
+ info.backing_integer,
+ &names,
+ &types,
+ &attrs
+ );
}
pub fn make_packed(s: anytype) make_packed_t(@TypeOf(s)) {
diff --git a/zlox/src/debug.zig b/zlox/src/debug.zig
@@ -89,10 +89,10 @@ fn constantInstruction(name: []const u8, ch: *const chunk.Chunk, offset: usize,
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) |str| {
- try disassembleChunk(function.chunk, str.slice());
+ if (function.name.valid()) {
+ try disassembleChunk(function.chunk.ptr(), function.name.ptr().slice());
} else {
- try disassembleChunk(function.chunk, "<anon>");
+ try disassembleChunk(function.chunk.ptr(), "<anon>");
}
}
@@ -127,10 +127,10 @@ fn closureInstruction(name: []const u8, ch: *const chunk.Chunk, offset: usize, p
off += 2;
}
if (print_fn) {
- if (function.name) |str| {
- try disassembleChunk(function.chunk, str.slice());
+ if (function.name.valid()) {
+ try disassembleChunk(function.chunk.ptr(), function.name.ptr().slice());
} else {
- try disassembleChunk(function.chunk, "<anon>");
+ try disassembleChunk(function.chunk.ptr(), "<anon>");
}
}
diff --git a/zlox/src/gc.zig b/zlox/src/gc.zig
@@ -14,6 +14,7 @@ pub const GC = struct {
const DBG_LOG = true;
allocator: std.mem.Allocator,
+ io: std.Io,
table: Obj.String.Table,
list: List,
@@ -23,9 +24,10 @@ pub const GC = struct {
}
}
- pub fn init(allocator: std.mem.Allocator) !Self {
+ pub fn init(allocator: std.mem.Allocator, io: std.Io) !Self {
return Self{
.allocator = allocator,
+ .io = io,
.table = Obj.String.Table.init(allocator),
.list = List.init(allocator),
};
diff --git a/zlox/src/main.zig b/zlox/src/main.zig
@@ -1,26 +1,20 @@
const std = @import("std");
const vm = @import("vm.zig");
-const Linenoise = @cImport({
- @cInclude("stddef.h");
- @cInclude("linenoise.h");
-});
+const Linenoise = @import("linenoise");
-pub fn main() anyerror!u8 {
- var gpa = std.heap.GeneralPurposeAllocator(.{}){};
- defer std.debug.assert(gpa.deinit() == std.heap.Check.ok);
+pub fn main(init: std.process.Init) anyerror!u8 {
+ const allocator = init.gpa;
+ const io = init.io;
- const allocator = gpa.allocator();
-
- const args = try std.process.argsAlloc(allocator);
- defer std.process.argsFree(allocator, args);
+ const args = try init.minimal.args.toSlice(init.arena.allocator());
if (args.len == 1) {
- try repl(allocator, false);
+ try repl(allocator, io, false);
} else if (args.len == 2) {
if (std.mem.eql(u8, args[1], "-d")) {
- try repl(allocator, true);
+ try repl(allocator, io, true);
} else {
- try runFile(allocator, args[1]);
+ try runFile(allocator, io, args[1]);
}
} else {
std.debug.print("Usage: {s} [path]\n", .{args[0]});
@@ -30,17 +24,17 @@ pub fn main() anyerror!u8 {
return 0;
}
-pub fn runFile(allocator: std.mem.Allocator, path: []const u8) anyerror!void {
- const text = try std.fs.cwd().readFileAlloc(allocator, path, 999999);
+pub fn runFile(allocator: std.mem.Allocator, io: std.Io, path: []const u8) anyerror!void {
+ const text = try std.Io.Dir.cwd().readFileAlloc(io, path, allocator, std.Io.Limit.unlimited);
defer allocator.free(text);
- var VM = try vm.VM.init(allocator);
+ var VM = try vm.VM.init(allocator, io);
defer VM.deinit();
try VM.interpret(text, false);
}
-pub fn repl(allocator: std.mem.Allocator, dbg: bool) anyerror!void {
- var VM = try vm.VM.init(allocator);
+pub fn repl(allocator: std.mem.Allocator, io: std.Io, dbg: bool) anyerror!void {
+ var VM = try vm.VM.init(allocator, io);
defer VM.deinit();
_ = Linenoise.linenoiseHistorySetMaxLen(100);
diff --git a/zlox/src/obj/closure.zig b/zlox/src/obj/closure.zig
@@ -1,5 +1,6 @@
const std = @import("std");
const utils = @import("../comptime_utils.zig");
+const pack = @import("../packed.zig");
pub fn Closure(fields: anytype) type {
const Super = @import("../obj.zig").Obj(fields);
@@ -11,19 +12,19 @@ pub fn Closure(fields: anytype) type {
pub const Error = error { OutOfMemory };
obj: Super,
- function: *const Super.Function,
- upvalues: [*]?*Super.Upvalue,
+ function: pack.Packed(*const Super.Function),
+ upvalues: pack.Packed([*]?*Super.Upvalue),
upvalues_len: 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),
- .upvalues = (try allocator.alloc(?*Super.Upvalue, arg.upvalue_count)).ptr,
+ .upvalues = pack.Packed([*]?*Super.Upvalue).init((try allocator.alloc(?*Super.Upvalue, arg.upvalue_count)).ptr),
.upvalues_len = arg.upvalue_count,
- .function = arg,
+ .function = pack.Packed(*const Super.Function).init(arg),
};
- for(self.upvalues[0..self.upvalues_len])
+ for(self.upvalues.ptr()[0..self.upvalues_len])
|*upvalue| upvalue.* = null;
return self;
}
@@ -34,8 +35,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.name) |name| {
- _ = try writer.write(name.slice());
+ if (self.function.ptr().name.valid()) {
+ _ = try writer.write(self.function.ptr().name.ptr().slice());
} else {
_ = try writer.write("-");
}
@@ -47,7 +48,7 @@ pub fn Closure(fields: anytype) type {
}
pub fn free(self: *const Self, allocator: std.mem.Allocator) void {
- allocator.free(self.upvalues[0..self.upvalues_len]);
+ allocator.free(self.upvalues.ptr()[0..self.upvalues_len]);
allocator.destroy(self);
}
};
diff --git a/zlox/src/obj/function.zig b/zlox/src/obj/function.zig
@@ -1,6 +1,7 @@
const std = @import("std");
const chunk = @import("../chunk.zig");
const utils = @import("../comptime_utils.zig");
+const pack = @import("../packed.zig");
pub fn Function(fields: anytype) type {
const Super = @import("../obj.zig").Obj(fields);
@@ -18,8 +19,8 @@ pub fn Function(fields: anytype) type {
obj: Super,
arity: u8,
- chunk: *chunk.Chunk,
- name: ?*const String,
+ chunk: pack.Packed(*chunk.Chunk),
+ name: pack.Packed(?*const String),
type: Type,
upvalue_count: u8,
@@ -27,16 +28,20 @@ pub fn Function(fields: anytype) type {
const self: *Self = try allocator.create(Self);
self.* = Self{
.obj = Super.make(Self),
- .chunk = try allocator.create(chunk.Chunk),
+ .chunk = try pack.Packed(*chunk.Chunk).new(allocator),
.arity = 0,
- .name = null,
+ .name = pack.Packed(?*const String).nil(),
.type = tp,
.upvalue_count = 0,
};
- self.chunk.* = try chunk.Chunk.init(allocator);
+ self.chunk.ptr().* = 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);
+ }
+
pub fn cast(self: anytype) utils.copy_const(@TypeOf(self), *Super) {
return @ptrCast(self);
}
@@ -46,8 +51,8 @@ pub fn Function(fields: anytype) type {
.Function => _ = try writer.write("<F: "),
.Script => _ = try writer.write("<S: "),
}
- if (self.name) |name| {
- _ = try writer.write(name.slice());
+ if (self.name.valid()) {
+ _ = try writer.write(self.name.ptr().slice());
} else {
_ = try writer.write("-");
}
@@ -59,8 +64,8 @@ pub fn Function(fields: anytype) type {
}
pub fn free(self: *const Self, allocator: std.mem.Allocator) void {
- self.chunk.deinit();
- allocator.destroy(self.chunk);
+ self.chunk.ptr().deinit();
+ self.chunk.free(allocator);
allocator.destroy(self);
}
diff --git a/zlox/src/obj/list.zig b/zlox/src/obj/list.zig
@@ -2,7 +2,8 @@ const std = @import("std");
const Value = @import("../value.zig").Value;
const utils = @import("../comptime_utils.zig");
-const list = @import("../list.zig");
+const list_zig = @import("../list.zig");
+const pack = @import("../packed.zig");
pub fn List(fields: anytype) type {
const Super = @import("../obj.zig").Obj(fields);
@@ -10,20 +11,20 @@ pub fn List(fields: anytype) type {
return packed struct {
const Self = @This();
- pub const List = list.List(Value);
+ pub const List = list_zig.List(Value);
pub const Arg = void;
pub const Error = error { OutOfMemory, InvalidArgument } || Self.List.Error;
obj: Super,
- list: *Self.List,
+ list: pack.Packed(*Self.List),
pub fn init(_: Arg, allocator: std.mem.Allocator) Error!*Self {
const self: *Self = try allocator.create(Self);
self.* = Self{
.obj = Super.make(Self),
- .list = try allocator.create(Self.List)
+ .list = try pack.Packed(*Self.List).new(allocator)
};
- self.list.* = Self.List.init(allocator);
+ self.list.ptr().* = Self.List.init(allocator);
return self;
}
@@ -48,42 +49,44 @@ pub fn List(fields: anytype) type {
};
- var printer = Printer{.writer = writer, .count = self.list.len};
+ var printer = Printer{.writer = writer, .count = self.list.ptr().len};
_ = try writer.write("[");
- try self.list.for_each_try(&printer, Printer.print);
+ try self.list.ptr().for_each_try(&printer, Printer.print);
_ = try writer.writeAll("]");
}
pub fn eql(self: *const Self, other: *const Self) bool {
- return self.list.eql(other.list, Value.eql);
+ return self.list.ptr().eql(other.list.ptr(), Value.eql);
}
pub fn free(self: *Self, allocator: std.mem.Allocator) void {
- self.list.free();
- allocator.destroy(self.list);
+ self.list.ptr().free();
+ self.list.free(allocator);
allocator.destroy(self);
}
pub fn delete(self: *Self, index: Value) void {
- if (!index.is(Value.number) or index.number >= @as(Value.tagType(Value.number), @floatFromInt(self.list.len)) or index.number < 0) {
+ 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) {
return;
}
- self.list.delete(@intFromFloat(index.number));
+ list.delete(@intFromFloat(index.number));
}
pub fn get(self: *const Self, index: Value) Error!Value {
- if (!index.is(Value.number) or index.number >= @as(Value.tagType(Value.number), @floatFromInt(self.list.len)) or index.number < 0) {
+ 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) {
return Error.InvalidArgument;
}
- return self.list.get(@intFromFloat(index.number));
+ 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) {
return Error.InvalidArgument;
}
- _ = try self.list.set(@intFromFloat(index.number), val);
+ _ = try self.list.ptr().set(@intFromFloat(index.number), val);
}
};
}
diff --git a/zlox/src/obj/native.zig b/zlox/src/obj/native.zig
@@ -3,6 +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");
pub fn Native(fields: anytype) type {
const Super = @import("../obj.zig").Obj(fields);
@@ -19,20 +20,27 @@ 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: Fn,
+ fun: pack.Packed(Fn),
arity_min: u8,
arity_max: u8,
- name: [*]const u8,
+ name: pack.Packed([*]const u8),
name_len: usize,
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 = arg.fun, .arity_min = arg.arity_min, .arity_max = arg.arity_max, .name = arg.name.ptr, .name_len = arg.name.len };
+ self.* = Self{
+ .obj = Super.make(Self),
+ .fun = pack.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
+ };
return self;
}
pub fn call(self: *const Self, gc: *GC, argCount: u8, args: [*]Value) Error!Value {
- const callable = self.fun; // Compiler bug: https://github.com/ziglang/zig/issues/20539
+ const callable = self.fun.ptr();
return callable(gc, args[0..argCount]);
}
@@ -42,7 +50,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[0..self.name_len]);
+ _ = try writer.write(self.name.ptr()[0..self.name_len]);
_ = try writer.writeAll(">");
}
diff --git a/zlox/src/obj/table.zig b/zlox/src/obj/table.zig
@@ -3,6 +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");
pub fn Table(fields: anytype) type {
const Super = @import("../obj.zig").Obj(fields);
@@ -14,17 +15,17 @@ pub fn Table(fields: anytype) type {
pub const Error = error { OutOfMemory } || Self.Table.Error;
obj: Super,
- table: *Self.Table,
+ table: pack.Packed(*Self.Table),
len: usize,
pub fn init(_: Arg, allocator: std.mem.Allocator) Error!*Self {
const self: *Self = try allocator.create(Self);
self.* = Self{
.obj = Super.make(Self),
- .table = try allocator.create(Self.Table),
+ .table = try pack.Packed(*Self.Table).new(allocator),
.len = 0,
};
- self.table.* = Self.Table.init(allocator);
+ self.table.ptr().* = Self.Table.init(allocator);
return self;
}
@@ -34,15 +35,15 @@ pub fn Table(fields: anytype) type {
pub fn set(self: *Self, key: Value, val: Value) Error!bool {
self.len += 1;
- return self.table.set(key, val);
+ return self.table.ptr().set(key, val);
}
pub fn get(self: *Self, key: Value) Error!Value {
- return self.table.get(key);
+ return self.table.ptr().get(key);
}
pub fn delete(self: *Self, key: Value) void {
- if (self.table.delete(key)) self.len -= 1;
+ if (self.table.ptr().delete(key)) self.len -= 1;
}
pub fn format(self: *const Self, writer: *std.Io.Writer) !void {
@@ -63,8 +64,8 @@ pub fn Table(fields: anytype) type {
var printer = Printer{.writer = writer, .count = self.len};
_ = try writer.write("[");
- if (self.table.count > 0) {
- try self.table.for_each_try(&printer, Printer.print);
+ if (self.table.ptr().count > 0) {
+ try self.table.ptr().for_each_try(&printer, Printer.print);
} else {
_ = try writer.write(":");
}
@@ -72,12 +73,12 @@ pub fn Table(fields: anytype) type {
}
pub fn eql(self: *const Self, other: *const Self) bool {
- return self.table.eql(other.table, Value.eql);
+ return self.table.ptr().eql(other.table.ptr(), Value.eql);
}
pub fn free(self: *const Self, allocator: std.mem.Allocator) void {
- self.table.deinit();
- allocator.destroy(self.table);
+ self.table.ptr().deinit();
+ self.table.free(allocator);
allocator.destroy(self);
}
};
diff --git a/zlox/src/obj/upvalue.zig b/zlox/src/obj/upvalue.zig
@@ -2,6 +2,7 @@ const std = @import("std");
const utils = @import("../comptime_utils.zig");
const Value = @import("../value.zig").Value;
+const pack = @import("../packed.zig");
pub fn Upvalue(fields: anytype) type {
const Super = @import("../obj.zig").Obj(fields);
@@ -13,7 +14,7 @@ pub fn Upvalue(fields: anytype) type {
pub const Error = error { OutOfMemory };
obj: Super,
- location: *Value,
+ location: pack.Packed(*Value),
closed: bool,
slot: u8,
@@ -21,7 +22,7 @@ pub fn Upvalue(fields: anytype) type {
const self: *Self = try allocator.create(Self);
self.* = Self{
.obj = Super.make(Self),
- .location = arg.val,
+ .location = pack.Packed(*Value).init(arg.val),
.closed = false,
.slot = arg.slot
};
@@ -31,8 +32,8 @@ pub fn Upvalue(fields: anytype) type {
pub fn close(self: *Self, allocator: std.mem.Allocator) Error!void {
if (!self.closed) {
const new = try allocator.create(Value);
- new.* = self.location.*;
- self.location = new;
+ new.* = self.location.ptr().*;
+ self.location = pack.Packed(*Value).init(new);
self.closed = true;
}
}
@@ -43,7 +44,7 @@ pub fn Upvalue(fields: anytype) type {
}
pub fn format(self: *const Self, writer: *std.Io.Writer) !void {
- try writer.print("<Upvalue{{{f} at 0x{x}, {any}, {d}}}>", .{self.location.*, @intFromPtr(self.location), self.closed, self.slot});
+ try writer.print("<Upvalue{{{f} at 0x{x}, {any}, {d}}}>", .{self.location.ptr().*, self.location._ptr, self.closed, self.slot});
}
pub fn eql(_: *const Self, _: *const Self) bool {
@@ -51,7 +52,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);
+ if (self.closed) allocator.destroy(self.location.ptr());
allocator.destroy(self);
}
};
diff --git a/zlox/src/packed.zig b/zlox/src/packed.zig
@@ -0,0 +1,70 @@
+const std = @import("std");
+
+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(T: type) type {
+ comptime var _Type = T;
+ comptime var _Ptr =T;
+
+ const _optional = is_type(_Type, "optional");
+ if (_optional) {
+ _Type = @typeInfo(_Type).optional.child;
+ }
+
+ 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 Self = @This();
+
+ _ptr: usize,
+
+ pub fn new(allocator: std.mem.Allocator) !Self {
+ return Self.init(try allocator.create(Type));
+ }
+
+ pub fn init(arg: Ptr) Self {
+ return Self{
+ ._ptr = @intFromPtr(arg),
+ };
+ }
+
+ pub fn nil() Self {
+ if (!optional) {
+ @compileError("Cannot create nil for non-optional type");
+ }
+
+ return Self{
+ ._ptr = 0,
+ };
+ }
+
+ pub fn valid(self: Self) bool {
+ return !optional or self._ptr != 0;
+ }
+
+ pub fn ptr(self: Self) Ptr {
+ if (self.valid()) {
+ return @ptrFromInt(self._ptr);
+ } else {
+ @panic("Attempt to access null pointer");
+ }
+ }
+
+ pub fn free(self: Self, allocator: std.mem.Allocator) void {
+ allocator.destroy(self.ptr());
+ }
+ };
+}
diff --git a/zlox/src/vm.zig b/zlox/src/vm.zig
@@ -55,8 +55,8 @@ pub const VM = struct {
pub fn init(comptime tp: Obj.Type, callee: *const tp.get(), slots: [*]Value) @This() {
return switch (tp) {
- .Function => @This(){ .callee = callee.cast(), .ip = callee.chunk.code.data.ptr, .chunk = callee.chunk, .slots = slots },
- .Closure => @This(){ .callee = callee.cast(), .ip = callee.function.chunk.code.data.ptr, .chunk = callee.function.chunk, .slots = slots },
+ .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 },
else => @compileError("Invalid type"),
};
}
@@ -68,15 +68,15 @@ pub const VM = struct {
_ = try self.globals.set(nameObj, Global.make_con(Value.init(funObj)));
}
- pub fn init(allocator: std.mem.Allocator) !@This() {
- var self = @This(){ .globals = Globals.init(allocator), .objects = try GC.init(allocator), .allocator = allocator };
+ 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 };
try self.defineNative("clock", 0, 0, vm_native.Clock.clock);
try self.defineNative("put", 1, 1, vm_native.put);
try self.defineNative("table", 0, Obj.Native.ArityMax, vm_native.table);
try self.defineNative("list", 0, Obj.Native.ArityMax, vm_native.list);
- try vm_native.Clock.set_start();
+ vm_native.Clock.set_start(io);
return self;
}
@@ -87,7 +87,7 @@ pub const VM = struct {
const function = try compiler.Compiler(stack_size).compile(source, &self.objects);
- if (dbg) try debug.disassembleChunk(function.chunk, "Main");
+ if (dbg) try debug.disassembleChunk(function.chunk.ptr(), "Main");
try Interpreter(callstack_size, stack_size).run(self, function, dbg);
}
@@ -178,8 +178,8 @@ pub const VM = struct {
}
fn callClosure(self: *@This(), callee: *Obj.Closure, argCount: u8) !void {
- if (argCount != callee.function.arity) {
- self.runtimeError("Expected {d} arguments but got {d}", .{ callee.function.arity, argCount });
+ if (argCount != callee.function.ptr().arity) {
+ self.runtimeError("Expected {d} arguments but got {d}", .{ callee.function.ptr().arity, argCount });
return InterpreterError.RuntimeError;
}
if (self.frameCount == callstack_size - 1)
@@ -338,12 +338,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[index].?.location.*);
+ self.push(closure.upvalues.ptr()[index].?.location.ptr().*);
},
@intFromEnum(OP.SET_UPVALUE) => {
const closure = try self.frame().callee.cast(.Closure);
const index = self.read_byte();
- closure.upvalues[index].?.location.* = self.peek(0);
+ closure.upvalues.ptr()[index].?.location.ptr().* = self.peek(0);
},
@intFromEnum(OP.CLOSE_UPVALUE) => {
try self.closeUpvalues(self.current_slot());
@@ -399,14 +399,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[0..closure.upvalues_len]) |*upvalue| {
+ for (closure.upvalues.ptr()[0..closure.upvalues_len]) |*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[slot];
+ upvalue.* = callee.upvalues.ptr()[slot];
}
}
self.push(Value.init(closure.cast()));
diff --git a/zlox/src/vm/native.zig b/zlox/src/vm/native.zig
@@ -5,16 +5,14 @@ const Value = @import("../value.zig").Value;
pub const Error = GC.Obj.Native.Error;
pub const Clock = struct {
- var start: std.time.Instant = undefined;
+ var start: std.Io.Timestamp = undefined;
- pub fn set_start() !void {
- start = try std.time.Instant.now();
+ pub fn set_start(io: std.Io) void {
+ start = std.Io.Timestamp.now(io, std.Io.Clock.awake);
}
- pub fn clock(_: *GC, _: []const Value) Error!Value {
- const now = std.time.Instant.now() catch return Error.Native;
- const elapsed: f64 = @floatFromInt(now.since(start));
- return Value.init(elapsed / std.time.ns_per_s);
+ pub fn clock(gc: *GC, _: []const Value) Error!Value {
+ return Value.init(@as(f64, @floatFromInt(std.Io.Timestamp.untilNow(start, gc.io, std.Io.Clock.awake).toSeconds())));
}
};
@@ -36,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.push(arg) catch return Error.Native;
+ lis.list.ptr().push(arg) catch return Error.Native;
}
return Value.init(lis.cast());
}