commit 3555a25bf3df78d6be744a9d00b752d590208e6c
parent aec91897f03b022dec6bb6731f1986e193715463
Author: Ashymad <szymon.mikulicz@posteo.net>
Date: Wed, 27 Mar 2024 09:23:49 +0100
More comptime magic
Diffstat:
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,
}
}