DzLox

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

commit acdb947a6e36ec1d3f6cda2f2d1d2ee46367e697
parent bbec43d49e00f4958cb0de3bbc7b4bd33ff960df
Author: Szymon Mikulicz <szymon.mikulicz@posteo.net>
Date:   Wed, 30 Sep 2026 23:33:48 +0200

nanbox

Diffstat:
Azlox/src/lib::nanbox.zig | 137+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mzlox/src/lib::utils.zig | 16++++++++++++++++
Azlox/typun.zig | 20++++++++++++++++++++
3 files changed, 173 insertions(+), 0 deletions(-)

diff --git a/zlox/src/lib::nanbox.zig b/zlox/src/lib::nanbox.zig @@ -0,0 +1,137 @@ +const std = @import("std"); +const utils = @import("lib::utils.zig"); + +pub fn pun(Ret: type, val: anytype) Ret { + if (@alignOf(@TypeOf(val)) < @alignOf(Ret)) { + var ret: Ret = undefined; + @as(*@TypeOf(val), @ptrCast(&ret)).* = val; + return ret; + } else { + return @as(*const Ret, @ptrCast(&val)).*; + } +} + +fn expand(tag: u64) u64 { + return ((0b100 & tag) << 61) | ((0b11 & tag) << 48); +} + +pub fn NaNBox(comptime fields: anytype) type { + const Fields = @TypeOf(fields); + + if (!utils.is_type(Fields, "struct")) + @compileError("The fields have to be a struct of types"); + + const info = @typeInfo(Fields).@"struct"; + const enum_f64 = 0xcafe; + + const enums, const HAS_PTR = blk: { + var enums: [info.field_names.len]u64 = undefined; + var floated = false; + + var e_val: u64 = 0; + var e_ptr: u64 = 0; + + inline for (info.field_names, info.field_types, &enums) |nam, typ, *enm| { + if (typ != type) + @compileError("The fields have to be types, but field " ++ nam ++ " is " ++ @typeName(typ)); + + const field = @field(fields, nam); + const bitsz = @bitSizeOf(field); + + if (field == f64) { + if (floated) + @compileError("There can only be one f64 field"); + enm.* = enum_f64; + floated = true; + } else if (utils.is_type(field, "pointer")) { + if (e_ptr == 0 and e_val == 8) + @compileError("This NaNBox already has 8 non-pointer types"); + if (e_ptr == 8) + @compileError("This NaNBox already has 8 pointer types"); + enm.* = expand(0b111) | e_ptr; + e_ptr += 1; + } else if (bitsz < 48) { + if (e_val == 8) + @compileError("This NaNBox already has 8 non-pointer types"); + if (e_ptr > 0 and e_val == 7) + @compileError("This NaNBox already has 7 non-pointer types and at least 1 pointer type"); + enm.* = expand(e_val); + e_val += 1; + } else { + @compileError("The field has to fit in 48bits but field " ++ nam ++ " is " ++ @typeName(field)); + } + } + + if (!floated) + @compileError("At least one f64 is required"); + + break :blk .{ enums, e_ptr > 0 }; + }; + + return struct { + const Self = @This(); + + const QNAN: u64 = 0x7ffc000000000000; + const TAG: u64 = 0x8003000000000000; + const PTR_TAG: u64 = 0b111; + const VALUE: u64 = ~(QNAN | TAG); + const PTR_VALUE: u64 = ~(QNAN | TAG | PTR_TAG); + + pub const Type = @Enum( + u64, + std.lang.Type.Enum.Mode.exhaustive, + info.field_names, + &enums, + ); + + pub const Error = error{WrongTag}; + + value: u64, + + fn from(comptime tag: Type) type { + return @field(fields, @tagName(tag)); + } + + fn unmask(m: u64) u64 { + const ret = (m & 0x8000000000000000) | (m & 0x3000000000000); + return if (HAS_PTR and ret == TAG) ret | (m & PTR_TAG) else ret; + } + + pub fn of(comptime tag: Type, val: from(tag)) Self { + return Self{ + .value = if (from(tag) == f64) + pun(u64, val) + else + QNAN | @backingInt(tag) | (pun(u64, val) & VALUE), + }; + } + + pub fn to(self: *const Self, comptime tag: Type) !from(tag) { + std.debug.print("Msk {b:064} {s}\n", .{ @backingInt(tag), @tagName(tag) }); + std.debug.print("U64 {b:064}\nTag {b:064}\nVal {b:064}\n", .{ self.value, unmask(self.value), self.value & VALUE }); + + return if (self.is() == tag) + self.as(from(tag)) + else + Error.WrongTag; + } + + pub fn is(self: *const Self) Type { + return if ((self.value & QNAN) != QNAN) + @fromBackingInt(enum_f64) + else + @fromBackingInt(unmask(self.value)); + } + + fn as(self: *const Self, typ: type) typ { + return if (typ == f64) + pun(f64, self.value) + else if (typ == void) + @as(void, {}) + else if (utils.is_type(typ, "pointer")) + pun(typ, self.value & PTR_VALUE) + else + pun(typ, self.value & VALUE); + } + }; +} diff --git a/zlox/src/lib::utils.zig b/zlox/src/lib::utils.zig @@ -138,3 +138,19 @@ pub fn pack(s: anytype) pack_t(@TypeOf(s)) { return packed_struct; } + +pub fn enumFromStruct(str: type, backing: type) type { + const field_names = @typeInfo(str).@"struct".field_names; + + comptime var field_values: [field_names.len]backing = undefined; + + for (&field_values, 0..) |*pt, i| + pt.* = i; + + return @Enum( + backing, + std.lang.Type.Enum.Mode.exhaustive, + field_names, + &field_values, + ); +} diff --git a/zlox/typun.zig b/zlox/typun.zig @@ -0,0 +1,20 @@ +const std = @import("std"); +const NaNBox = @import("src/lib::nanbox.zig").NaNBox; + +pub fn main(_: std.process.Init) anyerror!u8 { + const Box = NaNBox(.{ + .u32 = u32, + .void = void, + .pf64 = *const f64, + .f64 = f64, + .pf32 = *const f32, + }); + + const p: f32 = 123; + + const box = Box.of(.pf32, &p); + + std.debug.print("Box contains: {any}\n", .{(try box.to(.pf32)).*}); + + return 0; +}