DzLox

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

commit 3555a25bf3df78d6be744a9d00b752d590208e6c
parent aec91897f03b022dec6bb6731f1986e193715463
Author: Ashymad <szymon.mikulicz@posteo.net>
Date:   Wed, 27 Mar 2024 09:23:49 +0100

More comptime magic

Diffstat:
Mzlox/src/compiler.zig | 5++---
Azlox/src/comptime_utils.zig | 5+++++
Mzlox/src/obj.zig | 103++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Mzlox/src/value.zig | 17+++++++++++------
Mzlox/src/vm.zig | 14+++++++-------
5 files changed, 82 insertions(+), 62 deletions(-)

diff --git a/zlox/src/compiler.zig b/zlox/src/compiler.zig @@ -238,12 +238,11 @@ pub const Compiler = struct { } fn string(self: *@This()) void { - const ret = Obj.String.init(self.previous.lexeme[1 .. self.previous.lexeme.len - 1], self.allocator) catch |err| { + self.emitConstant(Value.init(Obj.init(.String, self.previous.lexeme[1 .. self.previous.lexeme.len - 1], self.allocator) catch |err| { self.lastError = err; self.errorAtPrevious("Couldn't allocate object"); return; - }; - self.emitConstant(Value{ .obj = ret.cast() }); + })); } fn emitConstant(self: *@This(), val: Value) void { diff --git a/zlox/src/comptime_utils.zig b/zlox/src/comptime_utils.zig @@ -0,0 +1,5 @@ +pub fn copy_const(T: type, U: type) type { + comptime var info = @typeInfo(U); + info.Pointer.is_const = @typeInfo(T).Pointer.is_const; + return @Type(info); +} diff --git a/zlox/src/obj.zig b/zlox/src/obj.zig @@ -1,91 +1,102 @@ const std = @import("std"); +const utils = @import("comptime_utils.zig"); pub const Obj = packed struct { - const Self = @This(); + const Super = @This(); pub const Error = error{IllegalCastError}; type: Type, - next: ?*Self = null, + next: ?*Super = null, - pub const String = struct { - obj: Self, + pub const List = struct {}; + + pub const String = packed struct { + const Self = @This(); + const Arg = []const u8; + + obj: Super, len: usize, - fn ptr(self: *const @This()) []align(@alignOf(@This())) const u8 { - const p: [*]align(@alignOf(@This())) const u8 = @ptrCast(self); - return p[0 .. @sizeOf(@This()) + self.len]; - } - fn data(self: *@This()) [*]u8 { - const p: [*]u8 = @ptrCast(self); - return p + @sizeOf(@This()); - } - fn cdata(self: *const @This()) [*]const u8 { - const p: [*]const u8 = @ptrCast(self); - return p + @sizeOf(@This()); + 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(len: usize, allocator: std.mem.Allocator) !*@This() { - const ret: *@This() = @ptrCast(try allocator.alignedAlloc(u8, @alignOf(@This()), @sizeOf(@This()) + len)); - ret.* = @This(){ .obj = Self{ - .type = Self.Type.String, + fn new(len: usize, allocator: std.mem.Allocator) !*Self { + const ret: *Self = @ptrCast(try allocator.alignedAlloc(u8, @alignOf(Self), @sizeOf(Self) + len)); + ret.* = Self{ .obj = Super{ + .type = Super.Type.String, }, .len = len }; return ret; } - pub fn slice(self: *const @This()) []const u8 { - return self.cdata()[0..self.len]; - } - pub fn init(string: []const u8, allocator: std.mem.Allocator) !*@This() { - const ret = try new(string.len, allocator); - @memcpy(ret.data(), string); - return ret; + + pub fn slice(self: *const Self) []const u8 { + return self.data()[0..self.len]; } - pub fn cat(self: *const @This(), other: *const @This(), allocator: std.mem.Allocator) !*@This() { + pub fn cat(self: *const Self, other: *const Self, allocator: std.mem.Allocator) !*Self { const ret = try new(self.len + other.len, allocator); @memcpy(ret.data(), self.slice()); @memcpy(ret.data() + self.len, other.slice()); return ret; } - pub fn cast(self: *@This()) *Self { + + pub fn cast(self: *Self) *Super { return @ptrCast(self); } + pub fn print(self: *const Self) void { + std.debug.print("\"{s}\"", .{self.slice()}); + } + pub fn eql(self: *const Self, other: *const Self) bool { + return std.mem.eql(u8, self.slice(), other.slice()); + } + pub fn init(string: Arg, allocator: std.mem.Allocator) !*Self { + const ret = try new(string.len, allocator); + @memcpy(ret.data(), string); + return ret; + } + 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]); + } }; pub const Type = enum { String, pub fn get(comptime self: @This()) type { - return @field(Obj, @tagName(self)); + return @field(Super, @tagName(self)); } }; - pub fn free(obj: *Self, allocator: std.mem.Allocator) void { - return switch (obj.type) { - .String => allocator.free(obj._cast(.String).ptr()), - }; - } - - pub fn is(self: *const Self, tp: Type) bool { - return self.type == tp; + pub fn init(comptime tp: Type, arg: tp.get().Arg, allocator: std.mem.Allocator) !*Super { + return (try tp.get().init(arg, allocator)).cast(); } - - pub fn print(self: *const Self) void { + pub fn print(self: *const Super) void { switch (self.type) { - .String => std.debug.print("\"{s}\"", .{self._cast(.String).slice()}), + inline else => |tp| self._cast(tp).print(), } } - - pub fn equal(self: *const Self, other: *const Self) bool { + pub fn eql(self: *const Super, other: *const Super) bool { if (!self.is(other.type)) return false; return switch (self.type) { - .String => std.mem.eql(u8, self._cast(.String).slice(), other._cast(.String).slice()), + inline else => |tp| self._cast(tp).eql(other._cast(tp)), + }; + } + pub fn free(obj: *Super, allocator: std.mem.Allocator) void { + return switch (obj.type) { + inline else => |tp| obj._cast(tp).free(allocator), }; } - fn _cast(self: *const Self, comptime tp: Type) *const tp.get() { - return @ptrCast(@alignCast(self)); + pub fn is(self: *const Super, tp: Type) bool { + return self.type == tp; } - pub fn cast(self: *const Self, comptime tp: Type) Error!*const tp.get() { + pub fn cast(self: *const Super, comptime tp: Type) Error!*const tp.get() { if (!self.is(tp)) return Error.IllegalCastError; return self._cast(tp); } + + fn _cast(self: *const Super, comptime tp: Type) *const tp.get() { + return @ptrCast(@alignCast(self)); + } }; diff --git a/zlox/src/value.zig b/zlox/src/value.zig @@ -19,6 +19,15 @@ pub const Value = union(enum) { } } + pub fn init(val: anytype) @This() { + inline for (@typeInfo(@This()).Union.fields) |field| { + if (@TypeOf(val) == field.type) { + return @unionInit(@This(), field.name, val); + } + } + @compileError("Invalid union type"); + } + fn toTag(comptime from: anytype) Tag { return if (@TypeOf(from) == Obj.Type) .obj @@ -33,10 +42,6 @@ pub const Value = union(enum) { }; } - pub fn new(comptime tag: anytype, value: tagType(tag)) @This() { - return @unionInit(@This(), @tagName(toTag(tag)), value); - } - pub fn get(self: @This(), comptime tag: anytype) tagType(tag) { return @field(self, @tagName(toTag(tag))); } @@ -63,13 +68,13 @@ pub const Value = union(enum) { }; } - pub fn equal(self: @This(), other: @This()) bool { + pub fn eql(self: @This(), other: @This()) bool { if (@intFromEnum(self) != @intFromEnum(other)) return false; return switch (self) { .number => |x| x == other.number, .bool => |x| x == other.bool, .nil => true, - .obj => |x| x.equal(other.obj), + .obj => |x| x.eql(other.obj), }; } }; diff --git a/zlox/src/vm.zig b/zlox/src/vm.zig @@ -76,7 +76,7 @@ pub const VM = struct { const b = self.pop(); const a = self.pop(); if (a.is(in_tag) and b.is(in_tag)) { - self.push(Value.new(out_tag, try op.call(a.get(in_tag), b.get(in_tag)))); + self.push(Value.init(try op.call(a.get(in_tag), b.get(in_tag)))); } else { self.runtimeError("Operands have invalid types, expected: {s}", .{@tagName(in_tag)}); return InterpreterError.RuntimeError; @@ -116,7 +116,7 @@ pub const VM = struct { self.runtimeError("Operand must be a number.", .{}); return InterpreterError.RuntimeError; } - self.push(Value{ .number = -self.pop().number }); + self.push(Value.init(-self.pop().number)); }, @intFromEnum(OP.ADD) => { if (self.peek(0).is(Obj.Type.String)) { @@ -129,13 +129,13 @@ pub const VM = struct { @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.TRUE) => self.push(Value{ .bool = true }), - @intFromEnum(OP.FALSE) => self.push(Value{ .bool = false }), - @intFromEnum(OP.EQUAL) => self.push(Value{ .bool = self.pop().equal(self.pop()) }), + @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.NIL) => self.push(Value{ .nil = undefined }), - @intFromEnum(OP.NOT) => self.push(Value{ .bool = !self.pop().isTruthy() }), + @intFromEnum(OP.NIL) => self.push(Value.init({})), + @intFromEnum(OP.NOT) => self.push(Value.init(!self.pop().isTruthy())), else => return InterpreterError.CompileError, } }