DzLox

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

commit b941bf2bbdf0470c8f3ccb8b5f23b2d9c16138e7
parent 5ef503975c3563b670509f6d04d22ca33aaff051
Author: Szymon Mikulicz <szymon.mikulicz@aptiv.com>
Date:   Sat,  3 Aug 2024 20:54:09 +0200

Literal maps

Diffstat:
Mzlox/src/compiler.zig | 45++++++++++++++++++++++++++++++++++++++++++++-
Mzlox/src/hash.zig | 48+++++++++++++++++++++++++++++++++++++++---------
Mzlox/src/obj.zig | 65++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Mzlox/src/scanner.zig | 4++++
Mzlox/src/table.zig | 26++++++++++++++++++++++++++
5 files changed, 175 insertions(+), 13 deletions(-)

diff --git a/zlox/src/compiler.zig b/zlox/src/compiler.zig @@ -3,6 +3,7 @@ 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 Obj = @import("obj.zig").Obj; const debug = @import("debug.zig"); const Token = scanner.TokenType; @@ -79,8 +80,10 @@ pub fn Compiler(size: comptime_int) type { // zig fmt: off T.LEFT_PAREN => R(S.grouping, null, P.NONE ), T.RIGHT_PAREN => R(null, null, P.NONE ), - T.LEFT_BRACE => R(null, null, P.NONE ), + T.LEFT_BRACE => R(null, null, P.NONE ), T.RIGHT_BRACE => R(null, null, P.NONE ), + T.LEFT_BRACKET => R(S.map, null, P.NONE ), + T.RIGHT_BRACKET => R(null, null, P.NONE ), T.COMMA => R(null, null, P.NONE ), T.DOT => R(null, null, P.NONE ), T.MINUS => R(S.unary, S.binary, P.TERM ), @@ -262,6 +265,46 @@ pub fn Compiler(size: comptime_int) type { })); } + fn parseLiteralValue(self: *Self) CompilerError!Value { + if (self.match(Token.STRING)) { + return Value.init(try self.objects.emplace(.String, &.{self.previous.lexeme[1 .. self.previous.lexeme.len - 1]})); + } else if (self.match(Token.NUMBER)) { + return try Value.parseNumber(self.previous.lexeme); + } else if (self.match(Token.FALSE)) { + return Value.init(false); + } else if (self.match(Token.TRUE)) { + return Value.init(true); + } else if (self.match(Token.NIL)) { + return Value.init({}); + } else if (self.match(Token.LEFT_BRACKET)) { + return self.parseLiteralMap(); + } else { + self.errorAtCurrent("Map initalizer can only contain literals"); + return error.UnexpectedToken; + } + } + + fn parseLiteralMap(self: *Self) CompilerError!Value { + var array = try ValueArray.init(self.allocator); + defer array.deinit(); + while (!self.match(Token.RIGHT_BRACKET)) { + try array.add(try self.parseLiteralValue()); + self.consume(Token.COLON, "Expect ':' after key in map initalizer"); + try array.add(try self.parseLiteralValue()); + if (self.match(Token.RIGHT_BRACKET)) + break; + self.consume(Token.COMMA, "Expect ',' after value in map initalizer"); + } + return Value.init(try self.objects.emplace(.Map, array)); + } + + fn map(self: *Self, _: bool) void { + self.emitConstant(self.parseLiteralMap() catch |err| { + self.lastError = err; + return; + }); + } + fn variable(self: *Self, canAssign: bool) void { self.namedVariable(self.previous, canAssign); } diff --git a/zlox/src/hash.zig b/zlox/src/hash.zig @@ -1,18 +1,31 @@ const Obj = @import("obj.zig").Obj; +const Value = @import("value.zig").Value; pub fn hash_append_t(T: type) fn (u32, T) u32 { return switch (T) { - []const u8, [*]const u8, []u8, [*]u8 => struct { + []const u8, []u8 => struct { pub fn fun(old: u32, val: T) u32 { var ret = old; for (val) |char| { - ret ^= char; - ret *%= 16777619; + ret = (ret ^ char) *% 16777619; } return ret; } }.fun, - else => @compileError("Unsupported type"), + f64 => struct { + pub fn fun(old: u32, val: T) u32 { + const vali: *const u64 = @ptrCast(&val); + const int = vali.* & 0xfffffffffffff000; + const ptr: [*]const u8 = @ptrCast(&int); + return hash_append_t([]const u8)(old, ptr[0..@sizeOf(T)]); + } + }.fun, + bool => struct { + pub fn fun(old: u32, val: T) u32 { + return hash_append_t([]const u8)(old, if (val) "\xff" else "\x00"); + } + }.fun, + else => @compileError("hash_append_t(" ++ @typeName(T) ++ "): Unsupported type"), }; } @@ -22,17 +35,34 @@ pub fn hash_append(ret: u32, val: anytype) u32 { pub fn hash_t(T: type) fn (T) u32 { return switch (T) { - []const u8, [*]const u8, []u8, [*]u8 => struct { + *Obj.Map, *const Obj.Map, *Obj.String, *const Obj.String => struct { pub fn fun(val: T) u32 { - return hash_append_t(T)(2166136261, val); + return val.hash; } }.fun, - Obj.String, *Obj.String, *const Obj.String => struct { + *Obj, *const Obj => struct { pub fn fun(val: T) u32 { - return val.hash; + return switch (val.type) { + .String => hash_append_t([]const u8)(hash(val.cast(.String) catch unreachable), "\x01"), + .Map => hash_append_t([]const u8)(hash(val.cast(.Map) catch unreachable), "\x01"), + }; + } + }.fun, + Value => struct { + pub fn fun(val: T) u32 { + return switch(val) { + .number => |v| hash_append_t([]const u8)(hash(v), "\x01"), + .bool => |v| hash_append_t([]const u8)(hash(v), "\x02"), + .nil => hash_t([]const u8)("\x03"), + .obj => |v| hash_append_t([]const u8)(hash(v), "\x04"), + }; + } + }.fun, + else => struct { + pub fn fun(val: T) u32 { + return hash_append_t(T)(2166136261, val); } }.fun, - else => @compileError("Unsupported type"), }; } diff --git a/zlox/src/obj.zig b/zlox/src/obj.zig @@ -2,6 +2,7 @@ const std = @import("std"); const utils = @import("comptime_utils.zig"); const hash = @import("hash.zig"); const table = @import("table.zig"); +const value = @import("value.zig"); pub const Obj = packed struct { const Super = @This(); @@ -35,6 +36,7 @@ pub const Obj = packed struct { var newObj = true; const obj = switch (tp) { .String => (try String.intern(arg, &self.map, &newObj, self.allocator)).cast(), + else => try Super.init(tp, arg, self.allocator), }; if (newObj) try self.push(obj); return obj; @@ -145,14 +147,71 @@ pub const Obj = packed struct { @compileError("The String Obj has to be interned"); } - fn free(self: *const Self, allocator: std.mem.Allocator) void { + 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]); } }; + pub const Map = packed struct { + const Self = @This(); + pub const Arg = value.ValueArray; + const Table = table.Table(value.Value, value.Value, hash.hash_t(value.Value), value.Value.eql); + + obj: Super, + map: *Table, + hash: u32, + + pub fn init(arg: Arg, allocator: std.mem.Allocator) Error!*Self { + const self: *Self = try allocator.create(Self); + self.* = Self{ + .obj = Super{ + .type = Super.Type.Map, + }, + .map = try allocator.create(Table), + .hash = 0, + }; + self.map.* = Table.init(allocator); + var i: u8 = 0; + while (i < arg.len) : (i += 2) { + const key = arg.get(i) catch unreachable; + const val = arg.get(i+1) catch unreachable; + self.hash +%= hash.hash(key) +% hash.hash(val); + if (!try self.map.set(key, val)) { + return error.KeyError; + } + } + return self; + } + pub fn cast(self: *Self) *Super { + return @ptrCast(self); + } + fn print_element(key: value.Value, val: value.Value) void { + key.print(); + std.debug.print(":", .{}); + val.print(); + std.debug.print(",", .{}); + + } + pub fn print(self: *const Self) void { + std.debug.print("[", .{}); + self.map.for_each(Self.print_element); + std.debug.print("]", .{}); + } + pub fn eql(self: *const Self, other: *const Self) bool { + return self.map.eql(other.map, value.Value.eql); + } + pub fn free(self: *const Self, allocator: std.mem.Allocator) void { + self.map.deinit(); + allocator.destroy(self.map); + allocator.destroy(self); + } + + }; + pub const Type = enum(u8) { String, + Map, pub fn get(comptime self: @This()) type { return @field(Super, @tagName(self)); @@ -183,12 +242,12 @@ pub const Obj = packed struct { return self.type == tp; } - pub fn cast(self: *const Super, comptime tp: Type) Error!*const tp.get() { + pub fn cast(self: anytype, comptime tp: Type) Error!utils.copy_const(@TypeOf(self), *tp.get()) { if (!self.is(tp)) return Error.IllegalCastError; return self._cast(tp); } - fn _cast(self: *const Super, comptime tp: Type) *const tp.get() { + fn _cast(self: anytype, comptime tp: Type) utils.copy_const(@TypeOf(self), *tp.get()) { return @ptrCast(@alignCast(self)); } }; diff --git a/zlox/src/scanner.zig b/zlox/src/scanner.zig @@ -5,6 +5,8 @@ pub const TokenType = enum { RIGHT_PAREN, LEFT_BRACE, RIGHT_BRACE, + LEFT_BRACKET, + RIGHT_BRACKET, COMMA, DOT, MINUS, @@ -106,6 +108,8 @@ pub const Scanner = struct { ')' => return self.makeToken(TokenType.RIGHT_PAREN), '{' => return self.makeToken(TokenType.LEFT_BRACE), '}' => return self.makeToken(TokenType.RIGHT_BRACE), + '[' => return self.makeToken(TokenType.LEFT_BRACKET), + ']' => return self.makeToken(TokenType.RIGHT_BRACKET), ':' => return self.makeToken(TokenType.COLON), ';' => return self.makeToken(TokenType.SEMICOLON), ',' => return self.makeToken(TokenType.COMMA), diff --git a/zlox/src/table.zig b/zlox/src/table.zig @@ -87,6 +87,15 @@ pub fn Table(K: type, V: type, hash_fn: fn (K) u32, cmp_fn: fn (K, K) bool) type } } + pub fn for_each(self: *const Self, fun: fn (K, V) void) void { + for (self.entries) |entry| { + switch (entry) { + .some => |some| fun(some.key, some.value), + else => {}, + } + } + } + pub fn set_(self: *Self, entry: *Entry, key: K, val: V) bool { const isNewKey = switch (entry.*) { .none => blk: { @@ -101,6 +110,23 @@ pub fn Table(K: type, V: type, hash_fn: fn (K) u32, cmp_fn: fn (K, K) bool) type 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) TableError!void { const len: f32 = @floatFromInt(self.entries.len); const count: f32 = @floatFromInt(self.count);