DzLox

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

commit ec78fb1986020c2c2b9d2b11fcad0fb6b49f17d1
parent f691de138bef1a690406ee83f179a8cee6469eaf
Author: Szymon Mikulicz <szymon.mikulicz@posteo.net>
Date:   Fri, 21 Aug 2026 19:27:15 +0200

Flat file hierarchy for easier imports

Diffstat:
Mzlox/src/chunk.zig | 6++++--
Mzlox/src/compiler.zig | 20++++++++++++--------
Mzlox/src/debug.zig | 5+++--
Mzlox/src/gc.zig | 5+++--
Rzlox/src/array.zig -> zlox/src/lib::array.zig | 0
Azlox/src/lib::list.zig | 225+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Azlox/src/lib::packed.zig | 135+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Azlox/src/lib::table.zig | 215+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Rzlox/src/comptime_utils.zig -> zlox/src/lib::utils.zig | 0
Dzlox/src/list.zig | 224-------------------------------------------------------------------------------
Mzlox/src/main.zig | 11++++++-----
Mzlox/src/obj.zig | 17+++++++++--------
Dzlox/src/obj/closure.zig | 52----------------------------------------------------
Dzlox/src/obj/function.zig | 69---------------------------------------------------------------------
Dzlox/src/obj/list.zig | 82-------------------------------------------------------------------------------
Dzlox/src/obj/native.zig | 63---------------------------------------------------------------
Dzlox/src/obj/string.zig | 111-------------------------------------------------------------------------------
Dzlox/src/obj/table.zig | 84-------------------------------------------------------------------------------
Dzlox/src/obj/template.zig | 41-----------------------------------------
Dzlox/src/obj/upvalue.zig | 54------------------------------------------------------
Azlox/src/obj::closure.zig | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Azlox/src/obj::function.zig | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Azlox/src/obj::list.zig | 86+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Azlox/src/obj::native.zig | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Azlox/src/obj::string.zig | 114+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Azlox/src/obj::table.zig | 87+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Azlox/src/obj::template.zig | 43+++++++++++++++++++++++++++++++++++++++++++
Azlox/src/obj::upvalue.zig | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dzlox/src/packed.zig | 134-------------------------------------------------------------------------------
Mzlox/src/scanner.zig | 2+-
Dzlox/src/table.zig | 214-------------------------------------------------------------------------------
Mzlox/src/value.zig | 7++++---
Mzlox/src/vm.zig | 57++++++++++++++++++++++++++++++---------------------------
Dzlox/src/vm/callbacks.zig | 66------------------------------------------------------------------
Dzlox/src/vm/native.zig | 40----------------------------------------
Azlox/src/vm::callbacks.zig | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Azlox/src/vm::native.zig | 41+++++++++++++++++++++++++++++++++++++++++
37 files changed, 1332 insertions(+), 1292 deletions(-)

diff --git a/zlox/src/chunk.zig b/zlox/src/chunk.zig @@ -1,6 +1,8 @@ const std = @import("std"); + +const array = @import("lib::array.zig"); + const Value = @import("value.zig").Value; -const array = @import("array.zig"); pub const OP = enum(u8) { CONSTANT, @@ -35,7 +37,7 @@ pub const OP = enum(u8) { GET_INDEX, CALL, CLOSURE, - CLOSE_UPVALUE + CLOSE_UPVALUE, }; pub const Chunk = struct { diff --git a/zlox/src/compiler.zig b/zlox/src/compiler.zig @@ -1,15 +1,19 @@ const std = @import("std"); + +const vm_native = @import("vm::native.zig"); +const utils = @import("lib::utils.zig"); const scanner = @import("scanner.zig"); -const Chunk = @import("chunk.zig").Chunk; -const OP = @import("chunk.zig").OP; -const Value = @import("value.zig").Value; -const ValueArray = @import("value.zig").ValueArray; -const GC = @import("gc.zig").GC; -const Obj = GC.Obj; const debug = @import("debug.zig"); +const chunk = @import("chunk.zig"); +const value = @import("value.zig"); + const Token = scanner.TokenType; -const vm_native = @import("vm/native.zig"); -const utils = @import("comptime_utils.zig"); +const Chunk = chunk.Chunk; +const OP = chunk.OP; +const Value = value.Value; +const ValueArray = value.ValueArray; +const GC = @import("gc.zig").GC; +const Obj = GC.Obj; pub const CompilerError = Obj.Error || scanner.ScannerError || Chunk.Error || Value.ParseNumberError || error{ UnexpectedToken, NotAnExpression }; diff --git a/zlox/src/debug.zig b/zlox/src/debug.zig @@ -1,10 +1,11 @@ const std = @import("std"); + const chunk = @import("chunk.zig"); const value = @import("value.zig"); -const Obj = @import("gc.zig").GC.Obj; -const print = std.debug.print; +const Obj = @import("gc.zig").GC.Obj; const Error = Obj.Error; +const print = std.debug.print; pub fn disassembleChunk(ch: *const chunk.Chunk, name: []const u8) Error!void { print("/= {s} =\\\n", .{name}); diff --git a/zlox/src/gc.zig b/zlox/src/gc.zig @@ -1,8 +1,9 @@ const std = @import("std"); -const list = @import("list.zig"); +const list = @import("lib::list.zig"); +const utils = @import("lib::utils.zig"); + const Value = @import("value.zig").Value; -const utils = @import("comptime_utils.zig"); const VM = @import("vm.zig").VM; pub const GC = struct { diff --git a/zlox/src/array.zig b/zlox/src/lib::array.zig diff --git a/zlox/src/lib::list.zig b/zlox/src/lib::list.zig @@ -0,0 +1,225 @@ +const std = @import("std"); + +const utils = @import("lib::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: Value, + next: *@This(), + prev: *@This(), + + fn jmp(self: *Element, idx: isize) *Element { + var ret = self; + + for (0..@abs(idx)) |_| { + ret = if (idx > 0) ret.next else ret.prev; + } + + return ret; + } + + pub fn del(self: *Element, gpa: std.mem.Allocator) void { + self.next.prev = self.prev; + self.prev.next = self.next; + + gpa.destroy(self); + } + + 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; + } + + 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; + } + + 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"); + + super: Super, + this: ?This, + + pub fn new(sup: Super) @This() { + return .{ + .super = sup, + .this = sup.tip, + }; + } + + 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; + } + } + + 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); + } + + 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); + } + } + }; + } + + _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 init(gpa: std.mem.Allocator) Self { + return Self{ + ._len = 0, + .tip = null, + .gpa = gpa, + }; + } + + 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; + } + + return true; + } + + pub fn free(self: *Self) void { + while (true) { + _ = self.pop(-1) catch break; + } + } + + fn _at(self: *Self, idx: isize) Error!*Element { + if (self.tip) |tip| { + const haf: isize = sign(idx) * @divTrunc(self._len, 2); + return tip.jmp(@rem(idx + haf, self._len) - haf); + } else { + return Error.Empty; + } + } + + 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); + } + + pub fn set(self: *Self, idx: isize, val: Value) Error!void { + (try self.at(idx)).val = val; + } + + pub fn get(self: *Self, idx: isize) Error!Value { + return (try self.at(idx)).val; + } + + fn del(self: *Self, el: *Element) void { + self._len -= 1; + + if (self.tip == el) { + self.tip = if (el.next == el) null else el.next; + } + + 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; + } + + 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; + } + + 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 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/lib::packed.zig b/zlox/src/lib::packed.zig @@ -0,0 +1,135 @@ +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 Child = if (utils.is_type(Ptr, "pointer")) + @typeInfo(Ptr).pointer.child + else + @compileError("Expected pointer type, got " ++ @typeName(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 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: 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, + }; + } + + 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); + } + + 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 len(self: Self) usize { + return if (slice) + self._len + else + @compileError("Cannot call len() on a non-slice pointer"); + } + + 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 { + if (count != 1) + @panic("Count has to be equal 1 for a single-item pointer"); + + allocator.destroy(pointer); + } + } + + 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/lib::table.zig b/zlox/src/lib::table.zig @@ -0,0 +1,215 @@ +const std = @import("std"); + +const utils = @import("lib::utils.zig"); + +pub fn Table(K: type, V: type, hash_fn: fn (K) u32, cmp_fn: fn (K, K) bool) type { + return struct { + const Self = @This(); + const MaxLoad: f32 = 0.75; + + pub const Error = error{ OutOfMemory, KeyError }; + + pub const Key = K; + pub const Value = V; + + pub const Entry = union(enum) { + const Some = struct { + key: K, + value: V, + }; + some: Some, + none, + tomb, + }; + + count: usize, + entries: []Entry, + allocator: std.mem.Allocator, + + pub fn init(allocator: std.mem.Allocator) Self { + return Self{ .count = 0, .entries = &.{}, .allocator = allocator }; + } + + fn growCapacity(self: *const Self) usize { + return if (self.entries.len > 0) + self.entries.len * 2 + else + 10; + } + + fn adjustCapacity(self: *Self, newsize: usize) Error!void { + const entries = try self.allocator.alloc(Entry, newsize); + for (entries) |*entry| { + entry.* = .none; + } + self.count = 0; + for (self.entries) |entry| { + switch (entry) { + .some => |some| { + find(entries, some.key).* = entry; + self.count += 1; + }, + else => {}, + } + } + self.allocator.free(self.entries); + self.entries = entries; + } + pub fn find_check(key: K) struct { + k: K, + pub fn check(self: *const @This(), k2: K) bool { + return cmp_fn(self.k, k2); + } + } { + return @TypeOf(find_check(key)){ .k = key }; + } + + pub fn find(entries: []Entry, key: K) *Entry { + return find_(entries, hash_fn(key), find_check(key)); + } + + pub fn find_(entries: []Entry, hash: u32, check: anytype) *Entry { + var idx = hash % entries.len; + var tomb: ?*Entry = null; + + while (true) { + const entry = &entries[idx]; + switch (entry.*) { + .some => |some| if (check.check(some.key)) return entry, + .tomb => tomb = if (tomb) |t| t else entry, + .none => return if (tomb) |t| t else entry, + } + idx = (idx + 1) % entries.len; + } + } + + pub fn addAll(self: *Self, other: *const Self) Error!void { + for (other.entries) |entry| { + switch (entry) { + .some => |some| self.set(some.key, some.value), + else => {}, + } + } + } + + pub fn for_each(self: *const Self, arg: anytype, fun: if (@TypeOf(arg) == void) fn (K, V) void else fn (@TypeOf(arg), K, V) void) void { + for (self.entries) |entry| { + switch (entry) { + .some => |some| if (@TypeOf(arg) == void) + fun(some.key, some.value) + else + fun(arg, some.key, some.value), + else => {}, + } + } + } + + 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) + try fun(some.key, some.value) + else + try fun(arg, some.key, some.value), + else => {}, + } + } + } + + pub fn set_(self: *Self, entry: *Entry, key: K, val: V) bool { + const isNewKey = switch (entry.*) { + .none => blk: { + self.count += 1; + break :blk true; + }, + .tomb => true, + .some => false, + }; + + entry.* = Entry{ .some = Entry.Some{ .key = key, .value = val } }; + return isNewKey; + } + + pub fn eql(self: *const Self, other: *const Self, cmpval_fn: fn (V, V) bool) bool { + for (self.entries) |entry| { + switch (entry) { + .some => |some| { + switch (find(other.entries, some.key).*) { + .some => |some2| if (!cmpval_fn(some.value, some2.value)) { + return false; + }, + else => return false, + } + }, + else => {}, + } + } + return true; + } + + pub fn checkCapacity(self: *Self) Error!void { + const len: f32 = @floatFromInt(self.entries.len); + const count: f32 = @floatFromInt(self.count); + if (count + 1.0 > len * MaxLoad) { + try self.adjustCapacity(self.growCapacity()); + } + } + + pub fn set(self: *Self, key: K, val: V) Error!bool { + try self.checkCapacity(); + return self.set_(find(self.entries, key), key, val); + } + + pub fn replace(self: *Self, key: K, val: V) Error!void { + if (self.entries.len == 0) + return Error.KeyError; + + const entry = find(self.entries, key); + switch (entry.*) { + .some => _ = self.set_(entry, key, val), + else => return Error.KeyError, + } + } + + pub fn replace_if(self: *Self, key: K, val: V, fun: fn (V) bool) Error!bool { + if (self.entries.len == 0) + return Error.KeyError; + + const entry = find(self.entries, key); + switch (entry.*) { + .some => |some| return fun(some.value) and !self.set_(entry, key, val), + else => return Error.KeyError, + } + } + + pub fn get(self: *const Self, key: K) Error!V { + if (self.entries.len == 0) + return Error.KeyError; + + return switch (find(self.entries, key).*) { + .some => |some| some.value, + else => Error.KeyError, + }; + } + + pub fn delete(self: *Self, key: K) bool { + if (self.entries.len == 0) return false; + + const entry = find(self.entries, key); + switch (entry.*) { + .some => entry.* = .tomb, + else => return false, + } + + return true; + } + + pub fn deinit(self: *@This()) void { + if (self.entries.len > 0) { + self.allocator.free(self.entries); + self.entries = &.{}; + self.count = 0; + } + } + }; +} diff --git a/zlox/src/comptime_utils.zig b/zlox/src/lib::utils.zig diff --git a/zlox/src/list.zig b/zlox/src/list.zig @@ -1,224 +0,0 @@ -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: Value, - next: *@This(), - prev: *@This(), - - fn jmp(self: *Element, idx: isize) *Element { - var ret = self; - - for (0..@abs(idx)) |_| { - ret = if (idx > 0) ret.next else ret.prev; - } - - return ret; - } - - pub fn del(self: *Element, gpa: std.mem.Allocator) void { - self.next.prev = self.prev; - self.prev.next = self.next; - - gpa.destroy(self); - } - - 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; - } - - 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; - } - - 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"); - - super: Super, - this: ?This, - - pub fn new(sup: Super) @This() { - return .{ - .super = sup, - .this = sup.tip, - }; - } - - 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; - } - } - - 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); - } - - 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); - } - } - }; - } - - _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 init(gpa: std.mem.Allocator) Self { - return Self{ - ._len = 0, - .tip = null, - .gpa = gpa, - }; - } - - 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; - } - - return true; - } - - pub fn free(self: *Self) void { - while (true) { - _ = self.pop(-1) catch break; - } - } - - fn _at(self: *Self, idx: isize) Error!*Element { - if (self.tip) |tip| { - const haf: isize = sign(idx) * @divTrunc(self._len, 2); - return tip.jmp(@rem(idx + haf, self._len) - haf); - } else { - return Error.Empty; - } - } - - 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); - } - - pub fn set(self: *Self, idx: isize, val: Value) Error!void { - (try self.at(idx)).val = val; - } - - pub fn get(self: *Self, idx: isize) Error!Value { - return (try self.at(idx)).val; - } - - fn del(self: *Self, el: *Element) void { - self._len -= 1; - - if (self.tip == el) { - self.tip = if (el.next == el) null else el.next; - } - - 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; - } - - 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; - } - - 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 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/main.zig b/zlox/src/main.zig @@ -1,6 +1,7 @@ const std = @import("std"); +const linenoise = @import("linenoise"); + const vm = @import("vm.zig"); -const Linenoise = @import("linenoise"); pub fn main(init: std.process.Init) anyerror!u8 { const allocator = init.gpa; @@ -37,13 +38,13 @@ 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); + _ = linenoise.linenoiseHistorySetMaxLen(100); - while (Linenoise.linenoise("lox> ")) |line| { - defer Linenoise.linenoiseFree(line); + while (linenoise.linenoise("lox> ")) |line| { + defer linenoise.linenoiseFree(line); VM.interpret(std.mem.span(line), dbg) catch |err| { std.debug.print("\nError: {any}\n", .{err}); }; - _ = Linenoise.linenoiseHistoryAdd(line); + _ = linenoise.linenoiseHistoryAdd(line); } } diff --git a/zlox/src/obj.zig b/zlox/src/obj.zig @@ -1,5 +1,6 @@ const std = @import("std"); -const utils = @import("comptime_utils.zig"); + +const utils = @import("lib::utils.zig"); fn nameOf(fqn: []const u8) []const u8 { var lastDot = 0; @@ -17,13 +18,13 @@ pub fn Obj(fields: anytype) type { type: Type, 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); - pub const Table = @import("obj/table.zig").Table(fields); - pub const Function = @import("obj/function.zig").Function(fields); - pub const Native = @import("obj/native.zig").Native(fields); - pub const Closure = @import("obj/closure.zig").Closure(fields); - pub const Upvalue = @import("obj/upvalue.zig").Upvalue(fields); + pub const List = @import("obj::list.zig").List(fields); + pub const String = @import("obj::string.zig").String(fields); + pub const Table = @import("obj::table.zig").Table(fields); + pub const Function = @import("obj::function.zig").Function(fields); + pub const Native = @import("obj::native.zig").Native(fields); + 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; diff --git a/zlox/src/obj/closure.zig b/zlox/src/obj/closure.zig @@ -1,52 +0,0 @@ -const std = @import("std"); -const utils = @import("../comptime_utils.zig"); -const Packed = @import("../packed.zig").Packed; - -pub fn Closure(fields: anytype) type { - const Super = @import("../obj.zig").Obj(fields); - - return packed struct { - const Self = @This(); - - pub const Arg = *Super.Function; - pub const Error = error{OutOfMemory}; - - obj: Super, - function: Packed(*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{ - .obj = Super.make(Self), - .upvalues = try Packed([]?*Super.Upvalue).alloc(allocator, arg.upvalue_count), - .function = Packed(*Super.Function).init(arg), - }; - for (self.upvalues.ptr()) |*upvalue| upvalue.* = null; - return self; - } - - 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.write("<C: "); - if (self.function.ptr().name.get()) |fn_name| { - _ = try writer.write(fn_name.slice()); - } else { - _ = try writer.write("-"); - } - _ = try writer.writeAll(">"); - } - - pub fn eql(_: *const Self, _: *const Self) bool { - return false; - } - - pub fn free(self: *const Self, allocator: std.mem.Allocator) void { - self.upvalues.destroy(allocator); - allocator.destroy(self); - } - }; -} diff --git a/zlox/src/obj/function.zig b/zlox/src/obj/function.zig @@ -1,69 +0,0 @@ -const std = @import("std"); -const chunk = @import("../chunk.zig"); -const utils = @import("../comptime_utils.zig"); -const Packed = @import("../packed.zig").Packed; - -pub fn Function(fields: anytype) type { - const Super = @import("../obj.zig").Obj(fields); - const String = Super.String; - - return packed struct { - const Self = @This(); - pub const Arg = Type; - pub const Error = error{OutOfMemory}; - - pub const Type = enum(u8) { Function, Script }; - - obj: Super, - arity: u8, - chunk: Packed(*chunk.Chunk), - name: Packed(?*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{ - .obj = Super.make(Self), - .chunk = try Packed(*chunk.Chunk).create(allocator), - .arity = 0, - .name = Packed(?*String).init(null), - .type = tp, - .upvalue_count = 0, - }; - self.chunk.set(try chunk.Chunk.init(allocator)); - return self; - } - - pub fn set_name(self: *Self, name: *String) void { - self.name = Packed(?*String).init(name); - } - - 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 { - switch (self.type) { - .Function => _ = try writer.write("<F: "), - .Script => _ = try writer.write("<S: "), - } - if (self.name.ptr()) |name| { - _ = try writer.write(name.slice()); - } else { - _ = try writer.write("-"); - } - _ = try writer.writeAll(">"); - } - - pub fn eql(_: *const Self, _: *const Self) bool { - return false; - } - - pub fn free(self: *const Self, allocator: std.mem.Allocator) void { - self.chunk.ptr().deinit(); - self.chunk.destroy(allocator); - allocator.destroy(self); - } - }; -} diff --git a/zlox/src/obj/list.zig b/zlox/src/obj/list.zig @@ -1,82 +0,0 @@ -const std = @import("std"); - -const Value = @import("../value.zig").Value; -const utils = @import("../comptime_utils.zig"); -const list_zig = @import("../list.zig"); -const Packed = @import("../packed.zig").Packed; - -pub fn List(fields: anytype) type { - const Super = @import("../obj.zig").Obj(fields); - - return packed struct { - const Self = @This(); - - pub const List = list_zig.List(Value); - pub const Arg = void; - pub const Error = error{ OutOfMemory, InvalidArgument } || Self.List.Error; - - obj: Super, - list: 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 Packed(*Self.List).create(allocator), - }; - self.list.set(Self.List.init(allocator)); - return self; - } - - pub fn cast(self: anytype) utils.copy_const(@TypeOf(self), *Super) { - return @ptrCast(self); - } - - 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("["); - while (iter.next()) |val| { - count -= 1; - try val.format(writer); - if (count > 0) _ = try writer.write(", "); - } - _ = try writer.writeAll("]"); - } - - pub fn eql(self: *const Self, other: *const Self) bool { - return self.list.ptr().eql(other.list.ptr(), Value.eql); - } - - pub fn free(self: *Self, allocator: std.mem.Allocator) void { - self.list.ptr().free(); - self.list.destroy(allocator); - allocator.destroy(self); - } - - 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()))) { - return; - } - _ = 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()))) { - 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)) { - return Error.InvalidArgument; - } - _ = self.list.ptr().set(@intFromFloat(index.number), val) catch - try self.list.ptr().push(@intFromFloat(index.number), val); - } - }; -} diff --git a/zlox/src/obj/native.zig b/zlox/src/obj/native.zig @@ -1,63 +0,0 @@ -const std = @import("std"); - -const utils = @import("../comptime_utils.zig"); -const GC = @import("../gc.zig").GC; -const Value = @import("../value.zig").Value; -const Packed = @import("../packed.zig").Packed; - -pub fn Native(fields: anytype) type { - const Super = @import("../obj.zig").Obj(fields); - - return packed struct { - const Self = @This(); - pub const Error = error{ OutOfMemory, Native }; - - pub const Fn = *const fn (*GC, []const Value) Error!Value; - - pub const ArityMin = 0; - pub const ArityMax = std.math.maxInt(u8); - - pub const Arg = struct { fun: Fn, arity_min: u8 = ArityMin, arity_max: u8 = ArityMax, name: []const u8 = "" }; - - obj: Super, - fun: Packed(Fn), - arity_min: u8, - arity_max: u8, - 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 = Packed(Fn).init(arg.fun), - .arity_min = arg.arity_min, - .arity_max = arg.arity_max, - .name = Packed([]const u8).init(arg.name), - }; - return self; - } - - pub fn call(self: *const Self, gc: *GC, argCount: u8, args: [*]Value) Error!Value { - const callable = self.fun.ptr(); - return callable(gc, args[0..argCount]); - } - - 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.write("<N: "); - _ = try writer.write(self.name.ptr()); - _ = try writer.writeAll(">"); - } - - pub fn eql(_: *const Self, _: *const Self) bool { - return false; - } - - pub fn free(self: *const Self, allocator: std.mem.Allocator) void { - allocator.destroy(self); - } - }; -} diff --git a/zlox/src/obj/string.zig b/zlox/src/obj/string.zig @@ -1,111 +0,0 @@ -const std = @import("std"); -const table = @import("../table.zig"); -const hash = @import("../hash.zig"); -const utils = @import("../comptime_utils.zig"); -const value = @import("../value.zig"); - -pub fn String(fields: anytype) type { - const Super = @import("../obj.zig").Obj(fields); - - return packed struct { - const Self = @This(); - pub const Table = table.Table(*Self, void, hash.hash_t(*const Self), Self.eql); - pub const Arg = []const []const u8; - pub const Error = error{ OutOfMemory, IndexOutOfBounds } || Table.Error; - - obj: Super, - len: usize = 0, - hash: u32, - - fn data(self: anytype) utils.copy_const(@TypeOf(self), [*]u8) { - const p: utils.copy_const(@TypeOf(self), [*]u8) = @ptrCast(self); - return p + @sizeOf(Self); - } - - fn new(arg: Arg, params: ArgParams, allocator: std.mem.Allocator) Error!*Self { - const ret: *Self = @ptrCast(try allocator.alignedAlloc(u8, std.mem.Alignment.of(Self), @sizeOf(Self) + params.len)); - ret.* = Self{ - .obj = Super.make(Self), - .hash = params.hash, - }; - for (arg) |el| { - @memcpy(ret.data() + ret.len, el); - ret.len += el.len; - } - return ret; - } - - pub fn slice(self: *const Self) []const u8 { - return self.data()[0..self.len]; - } - - 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.writeAll(self.slice()); - } - pub fn eql(self: *const Self, other: *const Self) bool { - return @intFromPtr(self) == @intFromPtr(other); - } - pub fn get(self: *const Self, index: value.Value) Error!value.Value { - if (!index.is(value.Value.number) or index.number >= @as(value.Value.tagType(value.Value.number), @floatFromInt(self.len)) or index.number < 0) { - return Error.IndexOutOfBounds; - } - return value.Value.init(self.data()[@intFromFloat(index.number)]); - } - - const ArgParams = struct { len: usize, hash: u32 }; - - fn table_check(m_arg: Arg, m_params: ArgParams) struct { - arg: Arg, - params: ArgParams, - pub fn check(self: *const @This(), k2: *const Self) bool { - if (k2.hash == self.params.hash and k2.len == self.params.len) { - var idx: usize = 0; - for (self.arg) |el| { - if (!std.mem.eql(u8, k2.data()[idx .. idx + el.len], el)) - return false; - idx += el.len; - } - return true; - } - return false; - } - } { - return @TypeOf(table_check(m_arg, m_params)){ .arg = m_arg, .params = m_params }; - } - - fn arg_params(arg: Arg) ArgParams { - var ret = ArgParams{ .len = 0, .hash = hash.hash_t([]const u8)(&.{}) }; - - for (arg) |el| { - ret.len += el.len; - ret.hash = hash.hash_append(ret.hash, el); - } - return ret; - } - - pub fn intern(arg: Arg, tabl: *Self.Table, isNewKey: *bool, allocator: std.mem.Allocator) Error!*Self { - const params = arg_params(arg); - - try tabl.checkCapacity(); - const entry = Self.Table.find_(tabl.entries, params.hash, table_check(arg, params)); - isNewKey.* = entry.* != Self.Table.Entry.some; - if (isNewKey.*) { - _ = tabl.set_(entry, try new(arg, params, allocator), {}); - } - return entry.some.key; - } - - pub fn init(_: Arg, _: std.mem.Allocator) Error!*Self { - @compileError("The String Obj has to be interned"); - } - - pub fn free(self: *const Self, allocator: std.mem.Allocator) void { - const p: [*]align(@alignOf(Self)) const u8 = @ptrCast(self); - allocator.free(p[0 .. @sizeOf(Self) + self.len]); - } - }; -} diff --git a/zlox/src/obj/table.zig b/zlox/src/obj/table.zig @@ -1,84 +0,0 @@ -const std = @import("std"); -const table = @import("../table.zig"); -const hash = @import("../hash.zig"); -const Value = @import("../value.zig").Value; -const utils = @import("../comptime_utils.zig"); -const Packed = @import("../packed.zig").Packed; - -pub fn Table(fields: anytype) type { - const Super = @import("../obj.zig").Obj(fields); - - return packed struct { - const Self = @This(); - pub const Arg = void; - pub const Table = table.Table(Value, Value, hash.hash_t(Value), Value.eql); - pub const Error = error{OutOfMemory} || Self.Table.Error; - - obj: Super, - 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{ - .obj = Super.make(Self), - .table = try Packed(*Self.Table).create(allocator), - .len = 0, - }; - self.table.set(Self.Table.init(allocator)); - return self; - } - - pub fn cast(self: anytype) utils.copy_const(@TypeOf(self), *Super) { - return @ptrCast(self); - } - - pub fn set(self: *Self, key: Value, val: Value) Error!bool { - self.len += 1; - return self.table.ptr().set(key, val); - } - - pub fn get(self: *Self, key: Value) Error!Value { - return self.table.ptr().get(key); - } - - pub fn delete(self: *Self, key: Value) void { - if (self.table.ptr().delete(key)) self.len -= 1; - } - - pub fn format(self: *const Self, writer: *std.Io.Writer) !void { - const Printer = struct { - writer: @TypeOf(writer), - count: usize, - - pub fn print(this: *@This(), key: Value, val: Value) std.Io.Writer.Error!void { - this.count -= 1; - - try key.format(this.writer); - _ = try this.writer.write(":"); - try val.format(this.writer); - if (this.count > 0) _ = try this.writer.write(", "); - } - }; - - 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); - } else { - _ = try writer.write(":"); - } - _ = try writer.writeAll("]"); - } - - pub fn eql(self: *const Self, other: *const Self) bool { - return self.table.ptr().eql(other.table.ptr(), Value.eql); - } - - 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/template.zig b/zlox/src/obj/template.zig @@ -1,41 +0,0 @@ -const std = @import("std"); - -const utils = @import("../comptime_utils.zig"); - -pub fn Template(fields: anytype) type { - const Super = @import("../obj.zig").Obj(fields); - - return packed struct { - const Self = @This(); - - pub const Arg = void; - pub const Error = error { OutOfMemory }; - - obj: Super, - pub fn init(arg: Arg, allocator: std.mem.Allocator) Error!*Self { - _ = arg; - _ = allocator; - return Error.OutOfMemory; - } - - 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 { - _ = self; - _ = writer; - } - - pub fn eql(self: *const Self, other: *const Self) bool { - _ = self; - _ = other; - return false; - } - - pub fn free(self: *const Self, allocator: std.mem.Allocator) void { - _ = self; - _ = allocator; - } - }; -} diff --git a/zlox/src/obj/upvalue.zig b/zlox/src/obj/upvalue.zig @@ -1,54 +0,0 @@ -const std = @import("std"); - -const utils = @import("../comptime_utils.zig"); -const Value = @import("../value.zig").Value; -const Packed = @import("../packed.zig").Packed; - -pub fn Upvalue(fields: anytype) type { - const Super = @import("../obj.zig").Obj(fields); - - return packed struct { - const Self = @This(); - - pub const Arg = struct { val: *Value, slot: u8 }; - pub const Error = error{OutOfMemory}; - - obj: Super, - 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 = 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 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.get(), self.location._ptr, self.closed, self.slot }); - } - - pub fn eql(_: *const Self, _: *const Self) bool { - return false; - } - - pub fn free(self: *const Self, allocator: std.mem.Allocator) void { - if (self.closed) self.location.destroy(allocator); - allocator.destroy(self); - } - }; -} diff --git a/zlox/src/obj::closure.zig b/zlox/src/obj::closure.zig @@ -0,0 +1,55 @@ +const std = @import("std"); + +const utils = @import("lib::utils.zig"); + +const Packed = @import("lib::packed.zig").Packed; +const Obj = @import("obj.zig").Obj; + +pub fn Closure(fields: anytype) type { + const Super = Obj(fields); + + return packed struct { + const Self = @This(); + + pub const Arg = *Super.Function; + pub const Error = error{OutOfMemory}; + + obj: Super, + function: Packed(*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{ + .obj = Super.make(Self), + .upvalues = try Packed([]?*Super.Upvalue).alloc(allocator, arg.upvalue_count), + .function = Packed(*Super.Function).init(arg), + }; + for (self.upvalues.ptr()) |*upvalue| upvalue.* = null; + return self; + } + + 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.write("<C: "); + if (self.function.ptr().name.get()) |fn_name| { + _ = try writer.write(fn_name.slice()); + } else { + _ = try writer.write("-"); + } + _ = try writer.writeAll(">"); + } + + pub fn eql(_: *const Self, _: *const Self) bool { + return false; + } + + pub fn free(self: *const Self, allocator: std.mem.Allocator) void { + self.upvalues.destroy(allocator); + allocator.destroy(self); + } + }; +} diff --git a/zlox/src/obj::function.zig b/zlox/src/obj::function.zig @@ -0,0 +1,72 @@ +const std = @import("std"); + +const utils = @import("lib::utils.zig"); +const chunk = @import("chunk.zig"); + +const Packed = @import("lib::packed.zig").Packed; +const Obj = @import("obj.zig").Obj; + +pub fn Function(fields: anytype) type { + const Super = Obj(fields); + const String = Super.String; + + return packed struct { + const Self = @This(); + pub const Arg = Type; + pub const Error = error{OutOfMemory}; + + pub const Type = enum(u8) { Function, Script }; + + obj: Super, + arity: u8, + chunk: Packed(*chunk.Chunk), + name: Packed(?*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{ + .obj = Super.make(Self), + .chunk = try Packed(*chunk.Chunk).create(allocator), + .arity = 0, + .name = Packed(?*String).init(null), + .type = tp, + .upvalue_count = 0, + }; + self.chunk.set(try chunk.Chunk.init(allocator)); + return self; + } + + pub fn set_name(self: *Self, name: *String) void { + self.name = Packed(?*String).init(name); + } + + 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 { + switch (self.type) { + .Function => _ = try writer.write("<F: "), + .Script => _ = try writer.write("<S: "), + } + if (self.name.ptr()) |name| { + _ = try writer.write(name.slice()); + } else { + _ = try writer.write("-"); + } + _ = try writer.writeAll(">"); + } + + pub fn eql(_: *const Self, _: *const Self) bool { + return false; + } + + pub fn free(self: *const Self, allocator: std.mem.Allocator) void { + self.chunk.ptr().deinit(); + self.chunk.destroy(allocator); + allocator.destroy(self); + } + }; +} diff --git a/zlox/src/obj::list.zig b/zlox/src/obj::list.zig @@ -0,0 +1,86 @@ +const std = @import("std"); + +const utils = @import("lib::utils.zig"); +const list = @import("lib::list.zig"); + +const Packed = @import("lib::packed.zig").Packed; +const Value = @import("value.zig").Value; +const Obj = @import("obj.zig").Obj; + +pub fn List(fields: anytype) type { + const Super = Obj(fields); + + return packed struct { + const Self = @This(); + + pub const List = list.List(Value); + pub const Arg = void; + pub const Error = error{ OutOfMemory, InvalidArgument } || Self.List.Error; + + obj: Super, + list: 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 Packed(*Self.List).create(allocator), + }; + self.list.set(Self.List.init(allocator)); + return self; + } + + pub fn cast(self: anytype) utils.copy_const(@TypeOf(self), *Super) { + return @ptrCast(self); + } + + pub fn format(self: *const Self, writer: *std.Io.Writer) std.Io.Writer.Error!void { + var lis = self.list.ptr(); + var iter = lis.iter(); + var count = lis.len(); + + _ = try writer.write("["); + while (iter.next()) |val| { + count -= 1; + try val.format(writer); + if (count > 0) _ = try writer.write(", "); + } + _ = try writer.writeAll("]"); + } + + pub fn eql(self: *const Self, other: *const Self) bool { + return self.list.ptr().eql(other.list.ptr(), Value.eql); + } + + pub fn free(self: *Self, allocator: std.mem.Allocator) void { + self.list.ptr().free(); + self.list.destroy(allocator); + allocator.destroy(self); + } + + pub fn delete(self: *Self, index: Value) void { + var lis = self.list.ptr(); + if (!index.is(Value.number) or index.number >= @as(Value.tagType(Value.number), @floatFromInt(lis.len()))) { + return; + } + _ = lis.pop(@intFromFloat(index.number)) catch return; + } + + pub fn get(self: *const Self, index: Value) Error!Value { + var lis = self.list.ptr(); + if (!index.is(Value.number) or index.number >= @as(Value.tagType(Value.number), @floatFromInt(lis.len()))) { + return Error.InvalidArgument; + } + return lis.get(@intFromFloat(index.number)); + } + + pub fn set(self: *Self, index: Value, val: Value) Error!void { + var lis = self.list.ptr(); + if (!index.is(Value.number)) { + return Error.InvalidArgument; + } + _ = lis.set(@intFromFloat(index.number), val) catch + try lis.push(@intFromFloat(index.number), val); + } + }; +} diff --git a/zlox/src/obj::native.zig b/zlox/src/obj::native.zig @@ -0,0 +1,65 @@ +const std = @import("std"); + +const utils = @import("lib::utils.zig"); + +const Packed = @import("lib::packed.zig").Packed; +const GC = @import("gc.zig").GC; +const Value = @import("value.zig").Value; +const Obj = @import("obj.zig").Obj; + +pub fn Native(fields: anytype) type { + const Super = Obj(fields); + + return packed struct { + const Self = @This(); + pub const Error = error{ OutOfMemory, Native }; + + pub const Fn = *const fn (*GC, []const Value) Error!Value; + + pub const ArityMin = 0; + pub const ArityMax = std.math.maxInt(u8); + + pub const Arg = struct { fun: Fn, arity_min: u8 = ArityMin, arity_max: u8 = ArityMax, name: []const u8 = "" }; + + obj: Super, + fun: Packed(Fn), + arity_min: u8, + arity_max: u8, + 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 = Packed(Fn).init(arg.fun), + .arity_min = arg.arity_min, + .arity_max = arg.arity_max, + .name = Packed([]const u8).init(arg.name), + }; + return self; + } + + pub fn call(self: *const Self, gc: *GC, argCount: u8, args: [*]Value) Error!Value { + const callable = self.fun.ptr(); + return callable(gc, args[0..argCount]); + } + + 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.write("<N: "); + _ = try writer.write(self.name.ptr()); + _ = try writer.writeAll(">"); + } + + pub fn eql(_: *const Self, _: *const Self) bool { + return false; + } + + pub fn free(self: *const Self, allocator: std.mem.Allocator) void { + allocator.destroy(self); + } + }; +} diff --git a/zlox/src/obj::string.zig b/zlox/src/obj::string.zig @@ -0,0 +1,114 @@ +const std = @import("std"); + +const table = @import("lib::table.zig"); +const utils = @import("lib::utils.zig"); +const hash = @import("hash.zig"); +const value = @import("value.zig"); + +const Obj = @import("obj.zig").Obj; + +pub fn String(fields: anytype) type { + const Super = Obj(fields); + + return packed struct { + const Self = @This(); + pub const Table = table.Table(*Self, void, hash.hash_t(*const Self), Self.eql); + pub const Arg = []const []const u8; + pub const Error = error{ OutOfMemory, IndexOutOfBounds } || Table.Error; + + obj: Super, + len: usize = 0, + hash: u32, + + fn data(self: anytype) utils.copy_const(@TypeOf(self), [*]u8) { + const p: utils.copy_const(@TypeOf(self), [*]u8) = @ptrCast(self); + return p + @sizeOf(Self); + } + + fn new(arg: Arg, params: ArgParams, allocator: std.mem.Allocator) Error!*Self { + const ret: *Self = @ptrCast(try allocator.alignedAlloc(u8, std.mem.Alignment.of(Self), @sizeOf(Self) + params.len)); + ret.* = Self{ + .obj = Super.make(Self), + .hash = params.hash, + }; + for (arg) |el| { + @memcpy(ret.data() + ret.len, el); + ret.len += el.len; + } + return ret; + } + + pub fn slice(self: *const Self) []const u8 { + return self.data()[0..self.len]; + } + + 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.writeAll(self.slice()); + } + pub fn eql(self: *const Self, other: *const Self) bool { + return @intFromPtr(self) == @intFromPtr(other); + } + pub fn get(self: *const Self, index: value.Value) Error!value.Value { + if (!index.is(value.Value.number) or index.number >= @as(value.Value.tagType(value.Value.number), @floatFromInt(self.len)) or index.number < 0) { + return Error.IndexOutOfBounds; + } + return value.Value.init(self.data()[@intFromFloat(index.number)]); + } + + const ArgParams = struct { len: usize, hash: u32 }; + + fn table_check(m_arg: Arg, m_params: ArgParams) struct { + arg: Arg, + params: ArgParams, + pub fn check(self: *const @This(), k2: *const Self) bool { + if (k2.hash == self.params.hash and k2.len == self.params.len) { + var idx: usize = 0; + for (self.arg) |el| { + if (!std.mem.eql(u8, k2.data()[idx .. idx + el.len], el)) + return false; + idx += el.len; + } + return true; + } + return false; + } + } { + return @TypeOf(table_check(m_arg, m_params)){ .arg = m_arg, .params = m_params }; + } + + fn arg_params(arg: Arg) ArgParams { + var ret = ArgParams{ .len = 0, .hash = hash.hash_t([]const u8)(&.{}) }; + + for (arg) |el| { + ret.len += el.len; + ret.hash = hash.hash_append(ret.hash, el); + } + return ret; + } + + pub fn intern(arg: Arg, tabl: *Self.Table, isNewKey: *bool, allocator: std.mem.Allocator) Error!*Self { + const params = arg_params(arg); + + try tabl.checkCapacity(); + const entry = Self.Table.find_(tabl.entries, params.hash, table_check(arg, params)); + isNewKey.* = entry.* != Self.Table.Entry.some; + if (isNewKey.*) { + _ = tabl.set_(entry, try new(arg, params, allocator), {}); + } + return entry.some.key; + } + + pub fn init(_: Arg, _: std.mem.Allocator) Error!*Self { + @compileError("The String Obj has to be interned"); + } + + pub fn free(self: *const Self, allocator: std.mem.Allocator) void { + const p: [*]align(@alignOf(Self)) const u8 = @ptrCast(self); + allocator.free(p[0 .. @sizeOf(Self) + self.len]); + } + }; +} diff --git a/zlox/src/obj::table.zig b/zlox/src/obj::table.zig @@ -0,0 +1,87 @@ +const std = @import("std"); + +const table = @import("lib::table.zig"); +const utils = @import("lib::utils.zig"); +const hash = @import("hash.zig"); + +const Packed = @import("lib::packed.zig").Packed; +const Value = @import("value.zig").Value; +const Obj = @import("obj.zig").Obj; + +pub fn Table(fields: anytype) type { + const Super = Obj(fields); + + return packed struct { + const Self = @This(); + pub const Arg = void; + pub const Table = table.Table(Value, Value, hash.hash_t(Value), Value.eql); + pub const Error = error{OutOfMemory} || Self.Table.Error; + + obj: Super, + 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{ + .obj = Super.make(Self), + .table = try Packed(*Self.Table).create(allocator), + .len = 0, + }; + self.table.set(Self.Table.init(allocator)); + return self; + } + + pub fn cast(self: anytype) utils.copy_const(@TypeOf(self), *Super) { + return @ptrCast(self); + } + + pub fn set(self: *Self, key: Value, val: Value) Error!bool { + self.len += 1; + return self.table.ptr().set(key, val); + } + + pub fn get(self: *Self, key: Value) Error!Value { + return self.table.ptr().get(key); + } + + pub fn delete(self: *Self, key: Value) void { + if (self.table.ptr().delete(key)) self.len -= 1; + } + + pub fn format(self: *const Self, writer: *std.Io.Writer) !void { + const Printer = struct { + writer: @TypeOf(writer), + count: usize, + + pub fn print(this: *@This(), key: Value, val: Value) std.Io.Writer.Error!void { + this.count -= 1; + + try key.format(this.writer); + _ = try this.writer.write(":"); + try val.format(this.writer); + if (this.count > 0) _ = try this.writer.write(", "); + } + }; + + 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); + } else { + _ = try writer.write(":"); + } + _ = try writer.writeAll("]"); + } + + pub fn eql(self: *const Self, other: *const Self) bool { + return self.table.ptr().eql(other.table.ptr(), Value.eql); + } + + 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::template.zig b/zlox/src/obj::template.zig @@ -0,0 +1,43 @@ +const std = @import("std"); + +const utils = @import("lib::utils.zig"); + +const Obj = @import("obj.zig").Obj; + +pub fn Template(fields: anytype) type { + const Super = Obj(fields); + + return packed struct { + const Self = @This(); + + pub const Arg = void; + pub const Error = error{OutOfMemory}; + + obj: Super, + pub fn init(arg: Arg, allocator: std.mem.Allocator) Error!*Self { + _ = arg; + _ = allocator; + return Error.OutOfMemory; + } + + 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 { + _ = self; + _ = writer; + } + + pub fn eql(self: *const Self, other: *const Self) bool { + _ = self; + _ = other; + return false; + } + + pub fn free(self: *const Self, allocator: std.mem.Allocator) void { + _ = self; + _ = allocator; + } + }; +} diff --git a/zlox/src/obj::upvalue.zig b/zlox/src/obj::upvalue.zig @@ -0,0 +1,56 @@ +const std = @import("std"); + +const utils = @import("lib::utils.zig"); + +const Packed = @import("lib::packed.zig").Packed; +const Value = @import("value.zig").Value; +const Obj = @import("obj.zig").Obj; + +pub fn Upvalue(fields: anytype) type { + const Super = Obj(fields); + + return packed struct { + const Self = @This(); + + pub const Arg = struct { val: *Value, slot: u8 }; + pub const Error = error{OutOfMemory}; + + obj: Super, + 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 = 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 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.get(), self.location._ptr, self.closed, self.slot }); + } + + pub fn eql(_: *const Self, _: *const Self) bool { + return false; + } + + pub fn free(self: *const Self, allocator: std.mem.Allocator) void { + if (self.closed) self.location.destroy(allocator); + allocator.destroy(self); + } + }; +} diff --git a/zlox/src/packed.zig b/zlox/src/packed.zig @@ -1,134 +0,0 @@ -const std = @import("std"); -const utils = @import("comptime_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 Child = if (utils.is_type(Ptr, "pointer")) - @typeInfo(Ptr).pointer.child - else - @compileError("Expected pointer type, got " ++ @typeName(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 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: 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, - }; - } - - 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); - } - - 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 len(self: Self) usize { - return if (slice) - self._len - else - @compileError("Cannot call len() on a non-slice pointer"); - } - - 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 { - if (count != 1) - @panic("Count has to be equal 1 for a single-item pointer"); - - allocator.destroy(pointer); - } - } - - 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/scanner.zig b/zlox/src/scanner.zig @@ -156,7 +156,7 @@ pub const Scanner = struct { fn char(self: *@This()) Token { if (self.isAtEnd()) return self.makeToken(ScannerError.UnterminatedChar); - _ = self.advance(); + _ = self.advance(); if (self.isAtEnd() or self.peek() != '\'') return self.makeToken(ScannerError.UnterminatedChar); _ = self.advance(); diff --git a/zlox/src/table.zig b/zlox/src/table.zig @@ -1,214 +0,0 @@ -const std = @import("std"); -const utils = @import("comptime_utils.zig"); - -pub fn Table(K: type, V: type, hash_fn: fn (K) u32, cmp_fn: fn (K, K) bool) type { - return struct { - const Self = @This(); - const MaxLoad: f32 = 0.75; - - pub const Error = error{ OutOfMemory, KeyError }; - - pub const Key = K; - pub const Value = V; - - pub const Entry = union(enum) { - const Some = struct { - key: K, - value: V, - }; - some: Some, - none, - tomb, - }; - - count: usize, - entries: []Entry, - allocator: std.mem.Allocator, - - pub fn init(allocator: std.mem.Allocator) Self { - return Self{ .count = 0, .entries = &.{}, .allocator = allocator }; - } - - fn growCapacity(self: *const Self) usize { - return if (self.entries.len > 0) - self.entries.len * 2 - else - 10; - } - - fn adjustCapacity(self: *Self, newsize: usize) Error!void { - const entries = try self.allocator.alloc(Entry, newsize); - for (entries) |*entry| { - entry.* = .none; - } - self.count = 0; - for (self.entries) |entry| { - switch (entry) { - .some => |some| { - find(entries, some.key).* = entry; - self.count += 1; - }, - else => {}, - } - } - self.allocator.free(self.entries); - self.entries = entries; - } - pub fn find_check(key: K) struct { - k: K, - pub fn check(self: *const @This(), k2: K) bool { - return cmp_fn(self.k, k2); - } - } { - return @TypeOf(find_check(key)){ .k = key }; - } - - pub fn find(entries: []Entry, key: K) *Entry { - return find_(entries, hash_fn(key), find_check(key)); - } - - pub fn find_(entries: []Entry, hash: u32, check: anytype) *Entry { - var idx = hash % entries.len; - var tomb: ?*Entry = null; - - while (true) { - const entry = &entries[idx]; - switch (entry.*) { - .some => |some| if (check.check(some.key)) return entry, - .tomb => tomb = if (tomb) |t| t else entry, - .none => return if (tomb) |t| t else entry, - } - idx = (idx + 1) % entries.len; - } - } - - pub fn addAll(self: *Self, other: *const Self) Error!void { - for (other.entries) |entry| { - switch (entry) { - .some => |some| self.set(some.key, some.value), - else => {}, - } - } - } - - pub fn for_each(self: *const Self, arg: anytype, fun: if (@TypeOf(arg) == void) fn (K, V) void else fn (@TypeOf(arg), K, V) void) void { - for (self.entries) |entry| { - switch (entry) { - .some => |some| if (@TypeOf(arg) == void) - fun(some.key, some.value) - else - fun(arg, some.key, some.value), - else => {}, - } - } - } - - 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) - try fun(some.key, some.value) - else - try fun(arg, some.key, some.value), - else => {}, - } - } - } - - pub fn set_(self: *Self, entry: *Entry, key: K, val: V) bool { - const isNewKey = switch (entry.*) { - .none => blk: { - self.count += 1; - break :blk true; - }, - .tomb => true, - .some => false, - }; - - entry.* = Entry{ .some = Entry.Some{ .key = key, .value = val } }; - return isNewKey; - } - - pub fn eql(self: *const Self, other: *const Self, cmpval_fn: fn (V, V) bool) bool { - for (self.entries) |entry| { - switch (entry) { - .some => |some| { - switch (find(other.entries, some.key).*) { - .some => |some2| if (!cmpval_fn(some.value, some2.value)) { - return false; - }, - else => return false, - } - }, - else => {}, - } - } - return true; - } - - pub fn checkCapacity(self: *Self) Error!void { - const len: f32 = @floatFromInt(self.entries.len); - const count: f32 = @floatFromInt(self.count); - if (count + 1.0 > len * MaxLoad) { - try self.adjustCapacity(self.growCapacity()); - } - } - - pub fn set(self: *Self, key: K, val: V) Error!bool { - try self.checkCapacity(); - return self.set_(find(self.entries, key), key, val); - } - - pub fn replace(self: *Self, key: K, val: V) Error!void { - if (self.entries.len == 0) - return Error.KeyError; - - const entry = find(self.entries, key); - switch (entry.*) { - .some => _ = self.set_(entry, key, val), - else => return Error.KeyError, - } - } - - pub fn replace_if(self: *Self, key: K, val: V, fun: fn (V) bool) Error!bool { - if (self.entries.len == 0) - return Error.KeyError; - - const entry = find(self.entries, key); - switch (entry.*) { - .some => |some| return fun(some.value) and !self.set_(entry, key, val), - else => return Error.KeyError, - } - } - - pub fn get(self: *const Self, key: K) Error!V { - if (self.entries.len == 0) - return Error.KeyError; - - return switch (find(self.entries, key).*) { - .some => |some| some.value, - else => Error.KeyError, - }; - } - - pub fn delete(self: *Self, key: K) bool { - if (self.entries.len == 0) return false; - - const entry = find(self.entries, key); - switch (entry.*) { - .some => entry.* = .tomb, - else => return false, - } - - return true; - } - - pub fn deinit(self: *@This()) void { - if (self.entries.len > 0) { - self.allocator.free(self.entries); - self.entries = &.{}; - self.count = 0; - } - } - }; -} diff --git a/zlox/src/value.zig b/zlox/src/value.zig @@ -1,7 +1,9 @@ const std = @import("std"); -const array = @import("array.zig"); + +const array = @import("lib::array.zig"); +const utils = @import("lib::utils.zig"); + const Obj = @import("gc.zig").GC.Obj; -const utils = @import("comptime_utils.zig"); pub const Value = union(enum) { number: f64, @@ -40,7 +42,6 @@ pub const Value = union(enum) { return @unionInit(Self, tagNameOf(@TypeOf(val)), val); } - fn toTag(comptime from: anytype) Tag { return if (@TypeOf(from) == Obj.Type) .obj diff --git a/zlox/src/vm.zig b/zlox/src/vm.zig @@ -1,19 +1,22 @@ -const Chunk = @import("chunk.zig").Chunk; -const OP = @import("chunk.zig").OP; -const Value = @import("value.zig").Value; const std = @import("std"); + +const table = @import("lib::table.zig"); +const list = @import("lib::list.zig"); +const utils = @import("lib::utils.zig"); +const callbacks = @import("vm::callbacks.zig"); +const native = @import("vm::native.zig"); const debug = @import("debug.zig"); const compiler = @import("compiler.zig"); +const hash = @import("hash.zig"); +const chunk = @import("chunk.zig"); + +const Chunk = chunk.Chunk; +const OP = chunk.OP; +const Value = @import("value.zig").Value; const GC = @import("gc.zig").GC; const Obj = GC.Obj; -const Callback = @import("vm/callbacks.zig"); -const table = @import("table.zig"); -const list = @import("list.zig"); -const hash = @import("hash.zig"); -const utils = @import("comptime_utils.zig"); -const vm_native = @import("vm/native.zig"); -const InterpreterError = Obj.Error || compiler.CompilerError || Callback.Error || error{ CompileError, RuntimeError, StackOverflow, IndexOutOfBounds, Overflow, DivisionByZero }; +const InterpreterError = Obj.Error || compiler.CompilerError || callbacks.Error || error{ CompileError, RuntimeError, StackOverflow, IndexOutOfBounds, Overflow, DivisionByZero }; pub const VM = struct { objects: GC, @@ -91,12 +94,12 @@ pub const VM = struct { .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 self.defineNative("clock", 0, 0, native.Clock.clock); + try self.defineNative("put", 1, 1, native.put); + try self.defineNative("table", 0, Obj.Native.ArityMax, native.table); + try self.defineNative("list", 0, Obj.Native.ArityMax, native.list); - vm_native.Clock.set_start(io); + native.Clock.set_start(io); return self; } @@ -252,12 +255,12 @@ pub const VM = struct { self.frames[self.frameCount - 1] = CallFrame.init(.Function, callee, self.stackTop - argCount - 1); } - fn callNative(self: *@This(), native: *Obj.Native, argCount: u8) !void { - if (argCount < native.arity_min or argCount > native.arity_max) { - self.runtimeError("Expected from {d} to {d} arguments but got {d}", .{ native.arity_min, native.arity_max, argCount }); + fn callNative(self: *@This(), obj: *Obj.Native, argCount: u8) !void { + if (argCount < obj.arity_min or argCount > obj.arity_max) { + self.runtimeError("Expected from {d} to {d} arguments but got {d}", .{ obj.arity_min, obj.arity_max, argCount }); return InterpreterError.RuntimeError; } - const result = try native.call(&self.vm.objects, argCount, self.stackTop - argCount); + const result = try obj.call(&self.vm.objects, argCount, self.stackTop - argCount); self.stackTop -= argCount + 1; self.push(result); return; @@ -290,7 +293,7 @@ pub const VM = struct { } } - fn binary_op(self: *@This(), comptime in_tag: anytype, comptime out_tag: anytype, op: Callback.Type(in_tag, out_tag)) InterpreterError!void { + fn binary_op(self: *@This(), comptime in_tag: anytype, comptime out_tag: anytype, op: callbacks.Type(in_tag, out_tag)) InterpreterError!void { const b = self.pop(); const a = self.pop(); if (a.is(in_tag) and b.is(in_tag)) { @@ -347,9 +350,9 @@ pub const VM = struct { }, @intFromEnum(OP.ADD) => { if (self.peek(0).is(Obj.Type.String)) { - try self.binary_op(Obj.Type.String, Obj.Type.String, Callback.concatenate(&self.vm.objects)); + try self.binary_op(Obj.Type.String, Obj.Type.String, callbacks.concatenate(&self.vm.objects)); } else { - try self.binary_op(Value.number, Value.number, Callback.add); + try self.binary_op(Value.number, Value.number, callbacks.add); } }, @intFromEnum(OP.JUMP_IF_FALSE) => { @@ -470,14 +473,14 @@ pub const VM = struct { }, @intFromEnum(OP.DEFINE_GLOBAL) => _ = try self.vm.globals.set(self.read_string(), Global.make_var(self.pop())), @intFromEnum(OP.DEFINE_GLOBAL_CONSTANT) => _ = try self.vm.globals.set(self.read_string(), Global.make_con(self.pop())), - @intFromEnum(OP.SUBTRACT) => try self.binary_op(Value.number, Value.number, Callback.sub), - @intFromEnum(OP.MULTIPLY) => try self.binary_op(Value.number, Value.number, Callback.mul), - @intFromEnum(OP.DIVIDE) => try self.binary_op(Value.number, Value.number, Callback.div), + @intFromEnum(OP.SUBTRACT) => try self.binary_op(Value.number, Value.number, callbacks.sub), + @intFromEnum(OP.MULTIPLY) => try self.binary_op(Value.number, Value.number, callbacks.mul), + @intFromEnum(OP.DIVIDE) => try self.binary_op(Value.number, Value.number, callbacks.div), @intFromEnum(OP.TRUE) => self.push(Value.init(true)), @intFromEnum(OP.FALSE) => self.push(Value.init(false)), @intFromEnum(OP.EQUAL) => self.push(Value.init(self.pop().eql(self.pop()))), - @intFromEnum(OP.LESS) => try self.binary_op(Value.number, Value.bool, Callback.less), - @intFromEnum(OP.GREATER) => try self.binary_op(Value.number, Value.bool, Callback.more), + @intFromEnum(OP.LESS) => try self.binary_op(Value.number, Value.bool, callbacks.less), + @intFromEnum(OP.GREATER) => try self.binary_op(Value.number, Value.bool, callbacks.more), @intFromEnum(OP.NIL) => self.push(Value.init({})), @intFromEnum(OP.NOT) => self.push(Value.init(!self.pop().isTruthy())), else => return InterpreterError.CompileError, diff --git a/zlox/src/vm/callbacks.zig b/zlox/src/vm/callbacks.zig @@ -1,66 +0,0 @@ -const std = @import("std"); -const GC = @import("../gc.zig").GC; -const Obj = GC.Obj; -const Value = @import("../value.zig").Value; - -const Number = Value.tagType(.number); -const Bool = Value.tagType(.bool); - -pub const Error = Obj.Error; - -pub fn Type(comptime in_tag: anytype, comptime out_tag: anytype) type { - if (@TypeOf(in_tag) == Obj.Type) { - return struct { - objects: *GC, - _call: *const fn (self: *const @This(), Value.tagType(in_tag), Value.tagType(in_tag)) Error!Value.tagType(out_tag), - pub fn call(self: *const @This(), a: Value.tagType(in_tag), b: Value.tagType(in_tag)) Error!Value.tagType(out_tag) { - return self._call(self, a, b); - } - }; - } else { - return struct { - call: fn (Value.tagType(in_tag), Value.tagType(in_tag)) callconv(.@"inline") Error!Value.tagType(out_tag), - }; - } -} - -pub fn concatenate(objects: *GC) Type(Obj.Type.String, Obj.Type.String) { - const Ret = Type(Obj.Type.String, Obj.Type.String); - const ret = Ret{ .objects = objects, ._call = struct { - pub fn concatenate(self: *const Ret, lhs: *Obj, rhs: *Obj) Error!*Obj { - return try self.objects.emplace_cast(.String, &.{ (lhs.cast(.String) catch unreachable).slice(), (rhs.cast(.String) catch unreachable).slice() }); - } - }.concatenate }; - return ret; -} - -pub const add = Type(Value.number, Value.number){ .call = struct { - pub inline fn add(a: Number, b: Number) Error!Number { - return a + b; - } -}.add }; -pub const mul = Type(Value.number, Value.number){ .call = struct { - pub inline fn mul(a: Number, b: Number) Error!Number { - return a * b; - } -}.mul }; -pub const sub = Type(Value.number, Value.number){ .call = struct { - pub inline fn sub(a: Number, b: Number) Error!Number { - return a - b; - } -}.sub }; -pub const div = Type(Value.number, Value.number){ .call = struct { - pub inline fn div(a: Number, b: Number) Error!Number { - return a / b; - } -}.div }; -pub const less = Type(Value.number, Value.bool){ .call = struct { - pub inline fn less(a: Number, b: Number) Error!Bool { - return a < b; - } -}.less }; -pub const more = Type(Value.number, Value.bool){ .call = struct { - pub inline fn more(a: Number, b: Number) Error!Bool { - return a > b; - } -}.more }; diff --git a/zlox/src/vm/native.zig b/zlox/src/vm/native.zig @@ -1,40 +0,0 @@ -const std = @import("std"); -const GC = @import("../gc.zig").GC; -const Value = @import("../value.zig").Value; - -pub const Error = GC.Obj.Native.Error; - -pub const Clock = struct { - var start: std.Io.Timestamp = undefined; - - pub fn set_start(io: std.Io) void { - start = std.Io.Timestamp.now(io, std.Io.Clock.awake); - } - - 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).toMilliseconds()))); - } -}; - -pub fn put(_: *GC, args: []const Value) Error!Value { - std.debug.print("{f}", .{args[0]}); - return Value.init({}); -} - -pub fn table(gc: *GC, args: []const Value) Error!Value { - var tbl = gc.emplace(.Table, {}) catch return Error.Native; - if (args.len % 2 != 0) return Error.Native; - var i: usize = 0; - while (i < args.len) : (i += 2) { - _ = tbl.set(args[i], args[i + 1]) catch return Error.Native; - } - return Value.init(tbl.cast()); -} - -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(-1, arg) catch return Error.Native; - } - return Value.init(lis.cast()); -} diff --git a/zlox/src/vm::callbacks.zig b/zlox/src/vm::callbacks.zig @@ -0,0 +1,66 @@ +const std = @import("std"); + +const GC = @import("gc.zig").GC; +const Obj = GC.Obj; +const Value = @import("value.zig").Value; +pub const Error = Obj.Error; + +const Number = Value.tagType(.number); +const Bool = Value.tagType(.bool); + +pub fn Type(comptime in_tag: anytype, comptime out_tag: anytype) type { + if (@TypeOf(in_tag) == Obj.Type) { + return struct { + objects: *GC, + _call: *const fn (self: *const @This(), Value.tagType(in_tag), Value.tagType(in_tag)) Error!Value.tagType(out_tag), + pub fn call(self: *const @This(), a: Value.tagType(in_tag), b: Value.tagType(in_tag)) Error!Value.tagType(out_tag) { + return self._call(self, a, b); + } + }; + } else { + return struct { + call: fn (Value.tagType(in_tag), Value.tagType(in_tag)) callconv(.@"inline") Error!Value.tagType(out_tag), + }; + } +} + +pub fn concatenate(objects: *GC) Type(Obj.Type.String, Obj.Type.String) { + const Ret = Type(Obj.Type.String, Obj.Type.String); + const ret = Ret{ .objects = objects, ._call = struct { + pub fn concatenate(self: *const Ret, lhs: *Obj, rhs: *Obj) Error!*Obj { + return try self.objects.emplace_cast(.String, &.{ (lhs.cast(.String) catch unreachable).slice(), (rhs.cast(.String) catch unreachable).slice() }); + } + }.concatenate }; + return ret; +} + +pub const add = Type(Value.number, Value.number){ .call = struct { + pub inline fn add(a: Number, b: Number) Error!Number { + return a + b; + } +}.add }; +pub const mul = Type(Value.number, Value.number){ .call = struct { + pub inline fn mul(a: Number, b: Number) Error!Number { + return a * b; + } +}.mul }; +pub const sub = Type(Value.number, Value.number){ .call = struct { + pub inline fn sub(a: Number, b: Number) Error!Number { + return a - b; + } +}.sub }; +pub const div = Type(Value.number, Value.number){ .call = struct { + pub inline fn div(a: Number, b: Number) Error!Number { + return a / b; + } +}.div }; +pub const less = Type(Value.number, Value.bool){ .call = struct { + pub inline fn less(a: Number, b: Number) Error!Bool { + return a < b; + } +}.less }; +pub const more = Type(Value.number, Value.bool){ .call = struct { + pub inline fn more(a: Number, b: Number) Error!Bool { + return a > b; + } +}.more }; diff --git a/zlox/src/vm::native.zig b/zlox/src/vm::native.zig @@ -0,0 +1,41 @@ +const std = @import("std"); + +const GC = @import("gc.zig").GC; +const Value = @import("value.zig").Value; + +pub const Error = GC.Obj.Native.Error; + +pub const Clock = struct { + var start: std.Io.Timestamp = undefined; + + pub fn set_start(io: std.Io) void { + start = std.Io.Timestamp.now(io, std.Io.Clock.awake); + } + + 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).toMilliseconds()))); + } +}; + +pub fn put(_: *GC, args: []const Value) Error!Value { + std.debug.print("{f}", .{args[0]}); + return Value.init({}); +} + +pub fn table(gc: *GC, args: []const Value) Error!Value { + var tbl = gc.emplace(.Table, {}) catch return Error.Native; + if (args.len % 2 != 0) return Error.Native; + var i: usize = 0; + while (i < args.len) : (i += 2) { + _ = tbl.set(args[i], args[i + 1]) catch return Error.Native; + } + return Value.init(tbl.cast()); +} + +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(-1, arg) catch return Error.Native; + } + return Value.init(lis.cast()); +}