commit b941bf2bbdf0470c8f3ccb8b5f23b2d9c16138e7
parent 5ef503975c3563b670509f6d04d22ca33aaff051
Author: Szymon Mikulicz <szymon.mikulicz@aptiv.com>
Date: Sat, 3 Aug 2024 20:54:09 +0200
Literal maps
Diffstat:
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);