commit e4a1d0de6da1c2569240e32806514596d6b78c70
parent 6ebfe7eb920247e8c0215ca568dec9634a388f65
Author: Szymon Mikulicz <szymon.mikulicz@posteo.net>
Date: Wed, 9 Sep 2026 00:32:06 +0200
Rewrite packed and array
Diffstat:
19 files changed, 341 insertions(+), 291 deletions(-)
diff --git a/zlox/src/compiler.zig b/zlox/src/compiler.zig
@@ -9,7 +9,6 @@ const value = @import("value.zig");
const Token = scanner.TokenType;
const OP = @import("op.zig").OP;
const Value = value.Value;
-const ValueArray = value.ValueArray;
const GC = @import("gc.zig").GC;
const Obj = GC.Obj;
const Chunk = Obj.Chunk;
@@ -65,11 +64,7 @@ pub fn Compiler(size: comptime_int) type {
pub const Stack = size;
pub const Upvalue = struct {
- pub const Type = enum(u8) {
- local = 0,
- remote = 1,
- empty = 2,
- };
+ pub const Type = enum(u8) { local = 0, remote = 1 };
index: u8,
type: Type,
@@ -354,12 +349,10 @@ pub fn Compiler(size: comptime_int) type {
if (isList) {
self.chunk.code.ptr().set(offset, self.makeObj(.Native, .{
.fun = vm_native.list,
- .type = .Literal,
}) catch return) catch return;
} else {
self.chunk.code.ptr().set(offset, self.makeObj(.Native, .{
.fun = vm_native.table,
- .type = .Literal,
}) catch return) catch return;
}
self.emit(OP.CALL, argCount);
@@ -423,11 +416,9 @@ pub fn Compiler(size: comptime_int) type {
fn addUpvalue(self: *Self, idx: u8, tp: Upvalue.Type) !u8 {
const count = self.upvaluesCount;
- if (tp != .empty) {
- for (self.upvalues[0..count], 0..) |upvalue, i| {
- if (upvalue.index == idx and upvalue.type == tp) {
- return @intCast(i);
- }
+ for (self.upvalues[0..count], 0..) |upvalue, i| {
+ if (upvalue.index == idx and upvalue.type == tp) {
+ return @intCast(i);
}
}
@@ -639,13 +630,15 @@ pub fn Compiler(size: comptime_int) type {
}
compiler.consume(Token.RIGHT_PAREN, "Expect ')' after parameters");
+ var offset: usize = 0;
+
if (isMethod) {
- _ = compiler.addUpvalue(0, .empty) catch return;
compiler.locals[0] = .{
.name = scanner.Token{ .type = Token.THIS, .lexeme = "this", .line = -1, .column = 0 },
.depth = compiler.scopeDepth,
};
compiler.emit(OP.GET_UPVALUE, 0);
+ offset = compiler.chunk.code.ptr().len - 1;
compiler.emit(OP.SET_LOCAL, 0);
compiler.emitOP(OP.POP);
}
@@ -654,6 +647,9 @@ pub fn Compiler(size: comptime_int) type {
compiler.block();
+ if (isMethod)
+ compiler.chunk.code.ptr().set(offset, compiler.upvaluesCount) catch unreachable;
+
self.current = compiler.current;
if (compiler.hadError) {
@@ -672,9 +668,9 @@ pub fn Compiler(size: comptime_int) type {
};
self.emit(OP.CONSTANT, self.makeConstant(Value.init(fun.cast())));
} else {
- self.emit(OP.CONSTANT, self.makeConstant(Value.init(endchunk.cast())));
- self.emit(OP.CLOSURE, arity);
+ self.emit(OP.CLOSURE, self.makeConstant(Value.init(endchunk.cast())));
+ self.emitByte(arity);
self.emitByte(compiler.upvaluesCount);
for (compiler.upvalues[0..compiler.upvaluesCount]) |upvalue| {
self.emitByte(@intFromEnum(upvalue.type));
@@ -832,7 +828,6 @@ pub fn Compiler(size: comptime_int) type {
self.emitObj(.Native, Obj.Native.Arg{
.fun = vm_native.table,
- .type = .Literal,
}) catch return;
var jumpOver = self.emitJump(OP.JUMP);
@@ -1074,7 +1069,7 @@ pub fn Compiler(size: comptime_int) type {
.localCount = 1,
.scopeDepth = 0,
.enclosing = null,
- .upvalues = @splat(Upvalue{ .index = 0, .type = .empty }),
+ .upvalues = @splat(Upvalue{ .index = 0, .type = .local }),
.upvaluesCount = 0,
.currentClass = null,
};
diff --git a/zlox/src/debug.zig b/zlox/src/debug.zig
@@ -21,8 +21,8 @@ pub fn disassembleChunk(ch: *const Obj.Chunk) Error!void {
pub fn print_offset(ch: *const Obj.Chunk, offset: usize) !void {
print("{d:0>4} ", .{offset});
- const line = try ch.lines.ptr().get(offset);
- if (offset > 0 and line == (try ch.lines.ptr().get(offset - 1))) {
+ const line = ch.lines.ptr().get(offset).?;
+ if (offset > 0 and line == ch.lines.ptr().get(offset - 1).?) {
print(" | ", .{});
} else {
print("{d:4} ", .{line});
@@ -36,7 +36,7 @@ pub fn disassembleInstruction(ch: *const Obj.Chunk, offset: usize) Error!usize {
fn _disassembleInstruction(ch: *const Obj.Chunk, offset: usize, print_fn: bool) Error!usize {
try print_offset(ch, offset);
- const op = try ch.code.ptr().get(offset);
+ const op = ch.code.ptr().get(offset).?;
const name = @tagName(@as(OP, @enumFromInt(op)));
return switch (op) {
@@ -89,8 +89,8 @@ fn simpleInstruction(name: []const u8, offset: usize) usize {
}
fn constantInstruction(name: []const u8, ch: *const Obj.Chunk, offset: usize, print_fn: bool) Error!usize {
- const constant = try ch.code.ptr().get(offset + 1);
- const constval = try ch.constants.ptr().get(constant);
+ const constant = ch.code.ptr().get(offset + 1).?;
+ const constval = ch.constants.ptr().get(constant).?;
print("{s:<32} {d:4} '{f}'\n", .{ name, constant, constval });
if (print_fn) {
if (constval.cast_if(Obj.Type.Function)) |function| {
@@ -103,13 +103,13 @@ fn constantInstruction(name: []const u8, ch: *const Obj.Chunk, offset: usize, pr
}
fn byteInstruction(name: []const u8, ch: *const Obj.Chunk, offset: usize) Error!usize {
- print("{s:<32} {d:4}\n", .{ name, try ch.code.ptr().get(offset + 1) });
+ print("{s:<32} {d:4}\n", .{ name, ch.code.ptr().get(offset + 1).? });
return offset + 2;
}
fn jumpInstruction(name: []const u8, sign: bool, ch: *const Obj.Chunk, offset: usize) !usize {
- const msb: u16 = try ch.code.ptr().get(offset + 1);
- const lsb: u16 = try ch.code.ptr().get(offset + 2);
+ const msb: u16 = ch.code.ptr().get(offset + 1).?;
+ const lsb: u16 = ch.code.ptr().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 });
@@ -118,18 +118,24 @@ fn jumpInstruction(name: []const u8, sign: bool, ch: *const Obj.Chunk, offset: u
fn closureInstruction(name: []const u8, ch: *const Obj.Chunk, offset: usize) Error!usize {
var off = offset + 1;
- const arity = try ch.code.ptr().get(off);
- const count = try ch.code.ptr().get(off + 1);
- off += 2;
+ const constant = ch.code.ptr().get(off).?;
+ const arity = ch.code.ptr().get(off + 1).?;
+ const count = ch.code.ptr().get(off + 2).?;
+ off += 3;
+
+ const constval = ch.constants.ptr().get(constant).?;
+ const chunk = try constval.obj.cast(.Chunk);
print("{s:<32} {d:4} {d}\n", .{ name, arity, count });
for (0..count) |_| {
- const tp = try ch.code.ptr().get(off);
- const idx = try ch.code.ptr().get(off + 1);
+ const tp = ch.code.ptr().get(off).?;
+ const idx = ch.code.ptr().get(off + 1).?;
try print_offset(ch, off + 1);
print("{s:<38}|-> {s} {d}\n", .{ "", @tagName(@as(Compiler.Upvalue.Type, @enumFromInt(tp))), idx });
off += 2;
}
+ try disassembleChunk(chunk);
+
return off + 1;
}
diff --git a/zlox/src/gc.zig b/zlox/src/gc.zig
@@ -98,10 +98,9 @@ pub const GC = struct {
},
*Obj.Function => {
self.mark("f", obj.chunk.ptr());
- if (obj.upvalues.ptr()) |upvalues|
- for (upvalues) |upvalue_ptr|
- if (upvalue_ptr) |upvalue|
- self.mark("f", upvalue);
+ for (obj.upvalues.ptr()) |upvalue_ptr|
+ if (upvalue_ptr) |upvalue|
+ self.mark("f", upvalue);
},
*Obj.Chunk => {
for (obj.constants.ptr().slice()) |constant|
@@ -249,14 +248,14 @@ pub const GC = struct {
}
pub fn deinit(self: *Self) void {
- self.callbacks.free();
- self.greys.free();
+ self.callbacks.deinit();
+ self.greys.deinit();
while (true) {
const el = self.objs.pop(0) catch break;
dbg_obj("O", "free", el, false);
el.free(self.allocator);
}
- self.objs.free();
+ self.objs.deinit();
self.pool.free();
}
};
diff --git a/zlox/src/lib::array.zig b/zlox/src/lib::array.zig
@@ -1,16 +1,46 @@
const std = @import("std");
-pub fn Array(comptime T: type, comptime S: type, comptime size: S) type {
+pub fn Array(comptime T: type) type {
return struct {
- len: S,
+ const Self = @This();
+ const Init = 8;
+
+ len: usize,
data: []T,
allocator: std.mem.Allocator,
- pub fn init(allocator: std.mem.Allocator) !@This() {
- return @This(){ .len = 0, .data = try allocator.alloc(T, size), .allocator = allocator };
+ pub const Iterator = struct {
+ cur: ?[*]T = null,
+ end: ?[*]T = null,
+
+ pub fn next(self: *Iterator) ?*const T {
+ if (self.cur) |cur| {
+ self.cur = if (cur == self.end)
+ null
+ else
+ cur + 1;
+ return cur[0];
+ }
+ return null;
+ }
+ };
+
+ pub fn init(allocator: std.mem.Allocator) !Self {
+ return Self{
+ .len = 0,
+ .data = try allocator.alloc(T, Init),
+ .allocator = allocator,
+ };
}
- pub fn add(self: *@This(), val: T) !void {
+ pub fn iter(self: *Self) Iterator {
+ return if (self.len == 0) Iterator{} else Iterator{
+ .cur = &self.data[0],
+ .end = &self.data[self.len - 1],
+ };
+ }
+
+ pub fn add(self: *Self, val: T) !void {
if (self.data.len <= self.len) {
self.data = try self.allocator.realloc(self.data, 2 * self.data.len);
}
@@ -18,81 +48,26 @@ pub fn Array(comptime T: type, comptime S: type, comptime size: S) type {
self.len += 1;
}
- pub fn slice(self: *const @This()) []const T {
+ pub fn slice(self: *const Self) []const T {
return self.data[0..self.len];
}
- pub fn get(self: *const @This(), idx: S) !T {
- if (idx >= self.len)
- return error.IndexOutOfBounds;
- return self.data[idx];
- }
-
- pub fn if_get(self: *const @This(), idx: S) ?T {
- if (idx >= self.len)
- return null;
- return self.data[idx];
+ pub fn get(self: *const Self, idx: usize) ?T {
+ return if (idx >= self.len) null else self.data[idx];
}
- pub fn set(self: *const @This(), idx: S, val: T) !void {
+ pub fn set(self: *const Self, idx: usize, val: T) !void {
if (idx >= self.len)
return error.IndexOutOfBounds;
self.data[idx] = val;
}
- pub fn last(self: *const @This()) !T {
- if (self.len == 0)
- return error.IndexOutOfBounds;
- return self.data[self.len - 1];
- }
-
- pub fn if_last(self: *const @This()) ?T {
- if (self.len == 0)
- return null;
- return self.data[self.len - 1];
+ pub fn last(self: *const Self) ?T {
+ return if (self.len == 0) null else self.data[self.len - 1];
}
- pub fn deinit(self: *@This()) void {
+ pub fn deinit(self: *Self) void {
self.allocator.free(self.data);
}
};
}
-
-pub fn RLEArray(comptime T: type, comptime size: usize) type {
- return struct {
- array: Array(element, usize, size),
-
- const element = struct { val: T, run: usize };
-
- pub fn init(allocator: std.mem.Allocator) !@This() {
- return @This(){
- .array = try Array(element, usize, size).init(allocator),
- };
- }
-
- pub fn add(self: *@This(), val: T) !void {
- const last = self.array.last() catch {
- try self.array.add(.{ .val = val, .run = 1 });
- return;
- };
- if (val == last.val) {
- self.array.data[self.array.len - 1].run += 1;
- } else {
- try self.array.add(.{ .val = val, .run = 1 });
- }
- }
-
- pub fn get(self: *const @This(), idx: usize) !T {
- var i: usize = 0;
- var sum: usize = 0;
- while (idx >= sum) : (i += 1) {
- sum += (try self.array.get(i)).run;
- }
- if (i > 0) return self.array.data[i - 1].val else return (try self.array.get(i)).val;
- }
-
- pub fn deinit(self: *@This()) void {
- self.array.deinit();
- }
- };
-}
diff --git a/zlox/src/lib::list.zig b/zlox/src/lib::list.zig
@@ -142,7 +142,7 @@ pub fn List(T: type) type {
return true;
}
- pub fn free(self: *Self) void {
+ pub fn deinit(self: *Self) void {
while (true) {
_ = self.pop(-1) catch break;
}
diff --git a/zlox/src/lib::packed.zig b/zlox/src/lib::packed.zig
@@ -2,163 +2,186 @@ const std = @import("std");
const utils = @import("lib::utils.zig");
-pub fn Packed(Type: type) type {
- return packed struct {
- const optional = utils.is_type(Type, "optional");
-
- const Ptr = if (optional) @typeInfo(Type).optional.child else Type;
+const Error = error{NullPointer};
- const Child = if (utils.is_type(Ptr, "pointer"))
- @typeInfo(Ptr).pointer.child
- else
- @compileError("Expected pointer type, got " ++ @typeName(Ptr));
+pub fn Pointer(Type: type) type {
+ return packed struct {
+ const Self = @This();
- const slice = @typeInfo(Ptr).pointer.size == .slice;
- const many = @typeInfo(Ptr).pointer.size == .many;
+ pub const optional = utils.is_type(Type, "optional");
- const Self = @This();
+ pub const Ptr = if (optional) @typeInfo(Type).optional.child else Type;
+ pub const Val = @typeInfo(Ptr).pointer.child;
+ pub const Opt = if (optional) ?Val else Val;
_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")
+ return Self.init(try allocator.create(Val));
+ }
+
+ pub fn init(arg: Type) Self {
+ return Self{
+ ._ptr = if (utils.optional(arg)) |val|
+ @intFromPtr(val)
+ else
+ 0,
+ };
+ }
+
+ pub fn ptr(self: Self) Type {
+ return if (optional and self._ptr == 0)
+ null
else
- Self.init(try allocator.create(Child));
+ @ptrFromInt(self._ptr);
}
- pub fn create2(allocator: std.mem.Allocator, val: Child) !Self {
- const self = try Self.create(allocator);
- self.set(val);
- return self;
+ pub fn get(self: Self) Opt {
+ return if (utils.optional(self.ptr())) |pointer|
+ pointer.*
+ else if (optional)
+ null
+ else
+ unreachable;
}
- pub fn alloc(allocator: std.mem.Allocator, count: usize) !Self {
- return if (count == 0 and optional)
- Self.init(null)
- else if (count == 0)
- @panic("Unable to create a non-optional 0 size pointer")
- else 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)
+ pub fn set(self: Self, val: Val) if (optional) Error.NullPointer!void else void {
+ if (utils.optional(self.ptr())) |pointer|
+ pointer.* = val
+ else if (optional)
+ return Error.NullPointer
else
- @panic("Cannot alloc() a single-item pointer with a count of more than one");
+ unreachable;
}
- pub fn alloc2(allocator: std.mem.Allocator, count: usize, val: Child) !Self {
- const self = try Self.alloc(allocator, count);
- var p = if (optional)
- if (self.ptr()) |p|
- p
- else
- return self
+ pub fn destroy(self: Self, allocator: std.mem.Allocator) void {
+ if (utils.optional(self.ptr())) |pointer|
+ allocator.destroy(pointer);
+ }
+ };
+}
+
+pub fn Object(Type: type) type {
+ return packed struct {
+ const Self = @This();
+
+ const Ptr = Pointer(Type);
+
+ _ptr: Ptr,
+ _own: bool,
+
+ pub fn create(allocator: std.mem.Allocator) !Self {
+ var self = Self.init(try allocator.create(Ptr.Val));
+ if (utils.fn_error(Ptr.Val.init)) |_|
+ self._ptr.set(try Ptr.Val.init(allocator))
else
- self.ptr();
+ self._ptr.set(Ptr.Val.init(allocator));
- for (0..count) |i| {
- p[i] = val;
- }
+ self._own = true;
return self;
}
pub fn init(arg: Type) Self {
return Self{
- ._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,
+ ._ptr = Ptr.init(arg),
+ ._own = false,
};
}
+ pub fn own(self: Self) bool {
+ return self._own;
+ }
+
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.ptr();
}
- 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 get(self: Self) Ptr.Opt {
+ return self._ptr.get();
}
- pub fn at(self: Self, idx: usize) if (optional and !utils.is_type(Child, "optional")) ?Child else Child {
- return if (!slice and !many)
- @compileError("Cannot call at() on a single-item pointer")
- else if (optional)
- if (self.ptr()) |val|
- val[idx]
- else
- null
- else
- self.ptr()[idx];
+ pub fn set(self: Self, val: Ptr.Val) if (Ptr.optional) Error.NullPointer!void else void {
+ self._ptr.set(val);
}
- pub fn len(self: Self) usize {
- return if (slice)
- self._len
- else
- @compileError("Cannot call len() on a non-slice pointer");
+ pub fn destroy(self: Self, allocator: std.mem.Allocator) void {
+ if (self._own)
+ self._ptr.ptr().deinit();
+ self._ptr.destroy(allocator);
+ }
+ };
+}
+
+pub fn Slice(Type: type) type {
+ return packed struct {
+ const Self = @This();
+ const Ptr = Pointer(utils.with_size(Type, .many));
+
+ _ptr: Ptr,
+ _len: usize,
+
+ pub fn create(allocator: std.mem.Allocator, count: usize) !Self {
+ return Self.init(try allocator.alloc(Ptr.Val, count));
}
- pub fn free(self: Self, allocator: std.mem.Allocator, count: usize) void {
- const pointer = if (optional)
- if (self.ptr()) |_ptr|
- _ptr
+ pub fn init(arg: Type) Self {
+ return Self{
+ ._ptr = Ptr.init(arg.ptr),
+ ._len = if (utils.optional(arg)) |val|
+ val.len
else
- return
- else
- self.ptr();
+ 0,
+ };
+ }
- 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");
+ pub fn ptr(self: Self) Type {
+ return if (utils.optional(self._ptr.ptr())) |pointer|
+ pointer[0..self._len]
+ else if (Ptr.optional)
+ null
+ else
+ unreachable;
+ }
- allocator.free(pointer);
- } else {
- if (count != 1)
- @panic("Count has to be equal 1 for a single-item pointer");
+ pub fn get(self: Self, idx: usize) Ptr.Opt {
+ return if (utils.optional(self.ptr())) |pointer|
+ pointer[idx]
+ else
+ null;
+ }
- allocator.destroy(pointer);
- }
+ pub fn len(self: Self) usize {
+ return self._len;
}
- pub fn set(self: Self, val: if (many or slice) utils.mod_ptr_t(ptr, "const", true) else Child) void {
- if (many or slice)
- @memcpy(self.ptr(), val)
+ pub fn set(self: Self, val: Ptr.Val) if (Ptr.optional) Error.NullPointer!void else void {
+ if (utils.optional(self.ptr())) |pointer|
+ @memcpy(pointer, val)
+ else if (Ptr.optional)
+ return Error.NullPointer
else
- self.ptr().* = val;
+ unreachable;
}
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);
+ if (utils.optional(self.ptr())) |pointer|
+ allocator.free(pointer);
}
};
}
+
+pub fn Packed(Type: type) type {
+ const Ptr = if (utils.is_type(Type, "optional")) @typeInfo(Type).optional.child else Type;
+ const Child = @typeInfo(Ptr).pointer.child;
+
+ return if (@typeInfo(Ptr).pointer.size == .slice)
+ Slice(Type)
+ else if ((utils.is_type(Child, "union") or
+ utils.is_type(Child, "enum") or
+ utils.is_type(Child, "struct")) and
+ @hasDecl(Child, "init") and
+ @hasDecl(Child, "deinit"))
+ Object(Type)
+ else
+ Pointer(Type);
+}
diff --git a/zlox/src/lib::utils.zig b/zlox/src/lib::utils.zig
@@ -12,8 +12,24 @@ pub fn copy_const(T: type, U: type) type {
return mod_ptr_t(U, "const", is_const(T));
}
+pub fn optional(val: anytype) if (is_type(@TypeOf(val), "optional")) @TypeOf(val) else ?@TypeOf(val) {
+ return val;
+}
+
+pub fn log_return(val: anytype) @TypeOf(val) {
+ if (@TypeOf(val) == type)
+ @compileLog("Returning: " ++ @typeName(val))
+ else
+ @compileLog("Returning: " ++ @typeName(@TypeOf(val)));
+ return val;
+}
+
pub fn mod_ptr_t(T: type, comptime field: []const u8, comptime val: anytype) type {
- comptime var new = @typeInfo(T).pointer;
+ const is_optional = is_type(T, "optional");
+
+ const old = if (is_optional) @typeInfo(T).optional.child else T;
+
+ comptime var new = @typeInfo(old).pointer;
if (@hasField(std.lang.Type.Pointer.Attributes, field)) {
@field(new.attrs, field) = val;
@@ -21,12 +37,14 @@ pub fn mod_ptr_t(T: type, comptime field: []const u8, comptime val: anytype) typ
@field(new, field) = val;
}
- return @Pointer(
+ const ret = @Pointer(
new.size,
new.attrs,
new.child,
std.lang.Type.Pointer.sentinel(new),
);
+
+ return if (is_optional) ?ret else ret;
}
pub fn enum_len(T: type) usize {
diff --git a/zlox/src/obj::chunk.zig b/zlox/src/obj::chunk.zig
@@ -17,9 +17,9 @@ pub fn Chunk(fields: anytype) type {
pub const Arg = void;
pub const Error = error{OutOfMemory};
- pub const Code = array.Array(u8, usize, 8);
- pub const Constants = Value.Array;
- pub const Lines = array.RLEArray(i32, 8);
+ pub const Code = array.Array(u8);
+ pub const Constants = array.Array(Value);
+ pub const Lines = array.Array(i32);
obj: Super,
code: Packed(*Code),
@@ -30,9 +30,9 @@ pub fn Chunk(fields: anytype) type {
const self: *Self = try allocator.create(Self);
self.* = Self{
.obj = Super.make(Self),
- .code = try Packed(*Code).create2(allocator, try Code.init(allocator)),
- .constants = try Packed(*Constants).create2(allocator, try Constants.init(allocator)),
- .lines = try Packed(*Lines).create2(allocator, try Lines.init(allocator)),
+ .code = try Packed(*Code).create(allocator),
+ .constants = try Packed(*Constants).create(allocator),
+ .lines = try Packed(*Lines).create(allocator),
};
return self;
}
@@ -42,7 +42,7 @@ pub fn Chunk(fields: anytype) type {
}
pub fn format(self: *const Self, writer: *std.Io.Writer) !void {
- _ = try writer.print("<chunk at {d}>", .{self.lines.ptr().get(0) catch 0});
+ _ = try writer.print("<chunk at {d}>", .{self.lines.ptr().get(0) orelse 0});
}
pub fn eql(_: *const Self, _: *const Self) bool {
@@ -50,11 +50,8 @@ pub fn Chunk(fields: anytype) type {
}
pub fn free(self: *const Self, allocator: std.mem.Allocator) void {
- self.constants.ptr().deinit();
self.constants.destroy(allocator);
- self.lines.ptr().deinit();
self.lines.destroy(allocator);
- self.code.ptr().deinit();
self.code.destroy(allocator);
allocator.destroy(self);
}
@@ -75,7 +72,7 @@ pub fn Chunk(fields: anytype) type {
}
}
try self.constants.ptr().add(val);
- return self.constants.ptr().len - 1;
+ return @intCast(self.constants.ptr().len - 1);
}
};
}
diff --git a/zlox/src/obj::class.zig b/zlox/src/obj::class.zig
@@ -26,7 +26,7 @@ pub fn Class(fields: anytype) type {
const self: *Self = try allocator.create(Self);
self.* = Self{
.obj = Super.make(Self),
- .methods = try Packed(*Methods).create2(allocator, Methods.init(allocator)),
+ .methods = try Packed(*Methods).create(allocator),
};
return self;
}
@@ -44,7 +44,6 @@ pub fn Class(fields: anytype) type {
}
pub fn free(self: *const Self, allocator: std.mem.Allocator) void {
- self.methods.ptr().deinit();
self.methods.destroy(allocator);
allocator.destroy(self);
}
diff --git a/zlox/src/obj::function.zig b/zlox/src/obj::function.zig
@@ -29,10 +29,10 @@ pub fn Function(fields: anytype) type {
arity: u8,
chunk: Packed(*Super.Chunk),
type: Type,
- upvalues: Packed(?[]Upvalue),
+ upvalues: Packed([]Upvalue),
pub fn init(arg: Arg, allocator: std.mem.Allocator) Error!*Self {
- if (if (arg.type == .Closure) arg.upvalues == 0 else arg.upvalues > 0)
+ if (if (arg.type == .Closure or arg.type == .Method) arg.upvalues == 0 else arg.upvalues > 0)
return Error.InvalidArguments;
const self: *Self = try allocator.create(Self);
@@ -41,8 +41,10 @@ pub fn Function(fields: anytype) type {
.chunk = Packed(Chunk).init(arg.chunk),
.arity = arg.arity,
.type = arg.type,
- .upvalues = try Packed(?[]Upvalue).alloc2(allocator, arg.upvalues, null),
+ .upvalues = try Packed([]Upvalue).create(allocator, arg.upvalues),
};
+
+ for (self.upvalues.ptr()) |*upvalue| upvalue.* = null;
return self;
}
diff --git a/zlox/src/obj::instance.zig b/zlox/src/obj::instance.zig
@@ -7,6 +7,7 @@ const hash = @import("hash.zig");
const Packed = @import("lib::packed.zig").Packed;
const Value = @import("value.zig").Value;
const Obj = @import("obj.zig").Obj;
+const GC = @import("gc.zig").GC;
pub fn Instance(fields: anytype) type {
const Super = Obj(fields);
@@ -28,11 +29,34 @@ pub fn Instance(fields: anytype) type {
self.* = Self{
.obj = Super.make(Self),
.cls = Packed(*Super.Class).init(cls),
- .fields = try Packed(*Self.Fields).create2(allocator, Fields.init(allocator)),
+ .fields = try Packed(*Self.Fields).create(allocator),
};
return self;
}
+ pub fn method(self: *Self, gc: *GC, name: *Super.String) !*Super.Function {
+ const met = try self.cls.ptr().methods.ptr().get(name);
+ const fun = try gc.emplace(.Function, .{
+ .type = .Method,
+ .chunk = met.chunk.ptr(),
+ .arity = met.arity,
+ .upvalues = @intCast(met.upvalues.len() + 1),
+ });
+
+ var val = Value.init(self.cast());
+ const len = fun.upvalues.len();
+ if (len > 1)
+ @memcpy(fun.upvalues.ptr()[0 .. len - 2], met.upvalues.ptr());
+
+ fun.upvalues.ptr()[len - 1] = try gc.emplace(.Upvalue, .{
+ .val = &val,
+ .slot = 0,
+ .closed = true,
+ });
+
+ return fun;
+ }
+
pub fn cast(self: anytype) utils.copy_const(@TypeOf(self), *Super) {
return @ptrCast(self);
}
@@ -46,7 +70,6 @@ pub fn Instance(fields: anytype) type {
}
pub fn free(self: *const Self, allocator: std.mem.Allocator) void {
- self.fields.ptr().deinit();
self.fields.destroy(allocator);
allocator.destroy(self);
}
diff --git a/zlox/src/obj::list.zig b/zlox/src/obj::list.zig
@@ -24,7 +24,7 @@ pub fn List(fields: anytype) type {
const self: *Self = try allocator.create(Self);
self.* = Self{
.obj = Super.make(Self),
- .list = try Packed(*Self.List).create2(allocator, Self.List.init(allocator)),
+ .list = try Packed(*Self.List).create(allocator),
};
return self;
}
@@ -52,7 +52,6 @@ pub fn List(fields: anytype) type {
}
pub fn free(self: *Self, allocator: std.mem.Allocator) void {
- self.list.ptr().free();
self.list.destroy(allocator);
allocator.destroy(self);
}
diff --git a/zlox/src/obj::native.zig b/zlox/src/obj::native.zig
@@ -19,20 +19,16 @@ pub fn Native(fields: anytype) type {
pub const ArityMin = 0;
pub const ArityMax = std.math.maxInt(u8);
- pub const Type = enum(u8) { Builtin, Literal };
-
pub const Arg = struct {
fun: Fn,
arity_min: u8 = ArityMin,
arity_max: u8 = ArityMax,
- type: Type = .Builtin,
};
obj: Super,
fun: Packed(Fn),
arity_min: u8,
arity_max: u8,
- type: Type,
pub fn init(arg: Arg, allocator: std.mem.Allocator) Error!*Self {
const self: *Self = try allocator.create(Self);
@@ -41,7 +37,6 @@ pub fn Native(fields: anytype) type {
.fun = Packed(Fn).init(arg.fun),
.arity_min = arg.arity_min,
.arity_max = arg.arity_max,
- .type = arg.type,
};
return self;
}
@@ -55,11 +50,8 @@ pub fn Native(fields: anytype) type {
return @ptrCast(self);
}
- pub fn format(self: *const Self, writer: *std.Io.Writer) !void {
- switch (self.type) {
- .Builtin => _ = try writer.write("<Builtin>"),
- .Literal => _ = try writer.write("<Literal> "),
- }
+ pub fn format(_: *const Self, writer: *std.Io.Writer) !void {
+ _ = try writer.write("<Builtin>");
}
pub fn eql(_: *const Self, _: *const Self) bool {
diff --git a/zlox/src/obj::table.zig b/zlox/src/obj::table.zig
@@ -24,7 +24,7 @@ pub fn Table(fields: anytype) type {
const self: *Self = try allocator.create(Self);
self.* = Self{
.obj = Super.make(Self),
- .table = try Packed(*Self.Table).create2(allocator, Self.Table.init(allocator)),
+ .table = try Packed(*Self.Table).create(allocator),
};
return self;
}
@@ -75,7 +75,6 @@ pub fn Table(fields: anytype) type {
}
pub fn free(self: *const Self, allocator: std.mem.Allocator) void {
- self.table.ptr().deinit();
self.table.destroy(allocator);
allocator.destroy(self);
}
diff --git a/zlox/src/obj::upvalue.zig b/zlox/src/obj::upvalue.zig
@@ -25,20 +25,21 @@ pub fn Upvalue(fields: anytype) type {
self.* = Self{
.obj = Super.make(Self),
.location = if (arg.closed)
- try Packed(*Value).create2(allocator, arg.val.*)
+ try Packed(*Value).create(allocator)
else
Packed(*Value).init(arg.val),
.closed = arg.closed,
.slot = arg.slot,
};
+ if (arg.closed) self.location.set(arg.val.*);
return self;
}
pub fn close(self: *Self, allocator: std.mem.Allocator) Error!void {
if (!self.closed) {
const old = self.location.get();
- self.location = try Packed(*Value).create2(allocator, old);
- self.closed = true;
+ self.location = try Packed(*Value).create(allocator);
+ self.location.set(old);
}
}
@@ -47,7 +48,12 @@ 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.get(), 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 {
@@ -55,8 +61,7 @@ pub fn Upvalue(fields: anytype) type {
}
pub fn free(self: *const Self, allocator: std.mem.Allocator) void {
- if (self.closed)
- self.location.destroy(allocator);
+ self.location.destroy(allocator);
allocator.destroy(self);
}
};
diff --git a/zlox/src/value.zig b/zlox/src/value.zig
@@ -15,8 +15,6 @@ pub const Value = union(enum) {
const Self = @This();
pub const Tag = std.meta.Tag(Self);
- pub const Array = array.Array(Value, u8, 8);
-
pub fn format(self: Self, writer: *std.Io.Writer) !void {
switch (self) {
.number => |val| try writer.print("{d}", .{val}),
diff --git a/zlox/src/vm.zig b/zlox/src/vm.zig
@@ -21,6 +21,7 @@ pub const VM = struct {
objects: GC,
globals: Globals,
allocator: std.mem.Allocator,
+ initializer: *Obj.String,
pub const CALLSTACK = 64;
pub const STACK = 256;
@@ -98,8 +99,13 @@ pub const VM = struct {
.globals = Globals.init(allocator),
.objects = try GC.init(allocator, io),
.allocator = allocator,
+ .initializer = undefined,
};
+ self.initializer = try self.objects.emplace(.String, &.{"init"});
+
+ GC.exclude(self.initializer.cast());
+
try self.defineNative("clock", 0, 0, native.Clock.clock);
try self.defineNative("put", 1, 1, native.put);
try self.defineNative("typeof", 1, 1, native.typeof);
@@ -150,7 +156,7 @@ pub const VM = struct {
try vm.objects.push_callback(&Self.gc_callback, &self);
defer vm.objects.pop_callback();
- defer self.upvalues.free();
+ defer self.upvalues.deinit();
self.push(Value.init(chunk.cast()));
@@ -210,7 +216,7 @@ pub const VM = struct {
}
fn read_constant(self: *@This()) Value {
- return self.frame().chunk.constants.ptr().get(self.read_byte()) catch unreachable;
+ return self.frame().chunk.constants.ptr().get(self.read_byte()).?;
}
fn read_string(self: *@This()) *Obj.String {
@@ -249,12 +255,18 @@ pub const VM = struct {
}
fn callClass(self: *@This(), callee: *Obj.Class, argCount: u8) !void {
- if (argCount != 0) {
- self.runtimeError("Expected {d} arguments but got {d}", .{ 0, argCount });
- return InterpreterError.RuntimeError;
- }
-
- self.pook(argCount, Value.init(try self.vm.objects.emplace_cast(.Instance, callee)));
+ const instance = try self.vm.objects.emplace(.Instance, callee);
+
+ const initializer = instance.method(&self.vm.objects, self.vm.initializer) catch
+ if (argCount != 0) {
+ self.runtimeError("Expected 0 arguments but got {d}", .{argCount});
+ return InterpreterError.RuntimeError;
+ } else {
+ self.pook(argCount, Value.init(instance.cast()));
+ return;
+ };
+
+ try self.callFunction(initializer, argCount);
}
fn callFunction(self: *@This(), callee: *Obj.Function, argCount: u8) !void {
@@ -396,23 +408,11 @@ pub const VM = struct {
var val = self.peek(0);
if (val.cast_if(Obj.Type.Instance)) |instance| {
const field = self.read_string();
- const prop = instance.fields.ptr().get(field) catch blk: {
- const method = instance.cls.ptr().methods.ptr().get(field) catch {
+ const prop = instance.fields.ptr().get(field) catch
+ Value.init((instance.method(&self.vm.objects, field) catch {
self.runtimeError("Undefined property '{f}'", .{field});
return InterpreterError.RuntimeError;
- };
- if (method.upvalues.ptr()) |upvalues| {
- if (upvalues[0] == null) {
- upvalues[0] = try self.vm.objects.emplace(.Upvalue, .{
- .val = &val,
- .slot = 0,
- .closed = true,
- });
- method.type = .Method;
- }
- }
- break :blk Value.init(method.cast());
- };
+ }).cast());
_ = self.pop();
self.push(prop);
} else {
@@ -453,12 +453,12 @@ pub const VM = struct {
@intFromEnum(OP.GET_UPVALUE) => {
const closure = self.frame().callee;
const index = self.read_byte();
- self.push(closure.upvalues.at(index).?.location.get());
+ self.push(closure.upvalues.get(index).?.location.get());
},
@intFromEnum(OP.SET_UPVALUE) => {
const closure = self.frame().callee;
const index = self.read_byte();
- closure.upvalues.at(index).?.location.set(self.peek(0));
+ closure.upvalues.get(index).?.location.set(self.peek(0));
},
@intFromEnum(OP.CLOSE_UPVALUE) => {
try self.closeUpvalues(self.current_slot());
@@ -515,9 +515,9 @@ pub const VM = struct {
try self.callValue(self.peek(argCount), argCount);
},
@intFromEnum(OP.CLOSURE) => {
+ const chunk = try self.read_constant().obj.cast(.Chunk);
const arity = self.read_byte();
const count = self.read_byte();
- const chunk = try self.pop().obj.cast(.Chunk);
const closure = try self.vm.objects.emplace(.Function, .{
.type = .Closure,
@@ -528,14 +528,13 @@ pub const VM = struct {
self.push(Value.init(closure.cast()));
- for (closure.upvalues.ptr().?) |*upvalue| {
+ for (closure.upvalues.ptr()) |*upvalue| {
const tp = self.read_byte();
const slot = self.read_byte();
const U = Compiler.Upvalue.Type;
upvalue.* = switch (tp) {
@intFromEnum(U.local) => try self.captureUpvalue(slot),
- @intFromEnum(U.remote) => self.frame().callee.upvalues.at(slot),
- @intFromEnum(U.empty) => null,
+ @intFromEnum(U.remote) => self.frame().callee.upvalues.get(slot),
else => return InterpreterError.RuntimeError,
};
}
@@ -568,7 +567,7 @@ pub const VM = struct {
while (true) : (i -= 1) {
const fram = self.frames[i];
const idx = @intFromPtr(fram.ip) - @intFromPtr(fram.chunk.code.ptr().data.ptr);
- std.debug.print("[line {d}] in {f}\n", .{ fram.chunk.lines.ptr().get(idx) catch 1, fram.callee });
+ std.debug.print("[line {d}] in {f}\n", .{ fram.chunk.lines.ptr().get(idx) orelse 1, fram.callee });
if (i == 0) break;
}
std.debug.print(fmt ++ "\n", args);
diff --git a/zlox/test/brunch.lox b/zlox/test/brunch.lox
@@ -0,0 +1,5 @@
+class Brunch {
+ init(food, drink) {}
+}
+
+Brunch("eggs", "coffee");
diff --git a/zlox/test/this.lox b/zlox/test/this.lox
@@ -0,0 +1,16 @@
+class P {
+ prin() {
+ print this.msg;
+ }
+}
+
+var A = P();
+A.msg = "I am A";
+
+con prin = A.prin;
+
+var B = P();
+B.msg = "I am B";
+
+B.prin();
+prin();