DzLox

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

commit 5ef503975c3563b670509f6d04d22ca33aaff051
parent 4a13f042541401587373f41eafdc6675f6fbc411
Author: Szymon Mikulicz <szymon.mikulicz@aptiv.com>
Date:   Fri,  2 Aug 2024 16:02:09 +0200

Global constants

Diffstat:
Mzlox/src/chunk.zig | 1+
Mzlox/src/compiler.zig | 17++++++++---------
Mzlox/src/table.zig | 17+++++++++++++++--
Mzlox/src/vm.zig | 41++++++++++++++++++++++++++++++++++++-----
4 files changed, 60 insertions(+), 16 deletions(-)

diff --git a/zlox/src/chunk.zig b/zlox/src/chunk.zig @@ -22,6 +22,7 @@ pub const OP = enum(u8) { PRINT, POP, DEFINE_GLOBAL, + DEFINE_GLOBAL_CONSTANT, GET_GLOBAL, SET_GLOBAL, GET_LOCAL, diff --git a/zlox/src/compiler.zig b/zlox/src/compiler.zig @@ -415,15 +415,10 @@ pub fn Compiler(size: comptime_int) type { self.consume(Token.SEMICOLON, "Expect ';' after variable declaration."); - self.defineVariable(global); + self.defineVariable(global, false); } fn conDeclaration(self: *Self) void { - if (self.scopeDepth == 0) { - self.errorAtPrevious("Global constans are not supported"); - return; - } - const global = self.parseVariable("Expect variable name.", true) catch return; self.consume(Token.EQUAL, "Constant variable has to be initialized."); @@ -432,7 +427,7 @@ pub fn Compiler(size: comptime_int) type { self.consume(Token.SEMICOLON, "Expect ';' after variable declaration."); - self.defineVariable(global); + self.defineVariable(global, true); } fn parseVariable(self: *Self, errorMessage: []const u8, con: bool) !u8 { @@ -487,13 +482,17 @@ pub fn Compiler(size: comptime_int) type { self.locals[self.localCount-1].depth = self.scopeDepth; } - fn defineVariable(self: *Self, global: u8) void { + fn defineVariable(self: *Self, global: u8, con: bool) void { if (self.scopeDepth > 0){ self.markInitialized(); return; } - self.emit(OP.DEFINE_GLOBAL, global); + if (con) { + self.emit(OP.DEFINE_GLOBAL_CONSTANT, global); + } else { + self.emit(OP.DEFINE_GLOBAL, global); + } } fn synchronize(self: *Self) void { diff --git a/zlox/src/table.zig b/zlox/src/table.zig @@ -114,8 +114,10 @@ pub fn Table(K: type, V: type, hash_fn: fn (K) u32, cmp_fn: fn (K, K) bool) type return self.set_(find(self.entries, key), key, val); } - pub fn set_existing(self: *Self, key: K, val: V) TableError!void { - try self.checkCapacity(); + pub fn replace(self: *Self, key: K, val: V) TableError!void { + if (self.entries.len == 0) + return TableError.KeyError; + const entry = find(self.entries, key); switch (entry.*) { .some => _ = self.set_(entry, key, val), @@ -123,6 +125,17 @@ pub fn Table(K: type, V: type, hash_fn: fn (K) u32, cmp_fn: fn (K, K) bool) type } } + pub fn replace_if(self: *Self, key: K, val: V, fun: fn (V) bool) TableError!bool { + if (self.entries.len == 0) + return TableError.KeyError; + + const entry = find(self.entries, key); + switch (entry.*) { + .some => |some| return fun(some.value) and !self.set_(entry, key, val), + else => return TableError.KeyError, + } + } + pub fn get(self: *const Self, key: K) TableError!V { if (self.entries.len == 0) return TableError.KeyError; diff --git a/zlox/src/vm.zig b/zlox/src/vm.zig @@ -16,7 +16,32 @@ pub const VM = struct { globals: Globals, allocator: std.mem.Allocator, - const Globals = table.Table(*const Obj.String, Value, hash.hash_t(*const Obj.String), Obj.String.eql); + const Global = struct { + val: Value, + con: bool, + + const Self = @This(); + + pub fn is_var(g: Self) bool { + return !g.con; + } + + pub fn make_var(v: Value) Self { + return Self { + .val = v, + .con = false, + }; + } + + pub fn make_con(v: Value) Self { + return Self { + .val = v, + .con = true, + }; + } + }; + + const Globals = table.Table(*const Obj.String, Global, hash.hash_t(*const Obj.String), Obj.String.eql); pub fn init(allocator: std.mem.Allocator) @This() { return @This(){ .globals = Globals.init(allocator), .objects = Obj.List.init(allocator), .allocator = allocator }; @@ -160,19 +185,25 @@ pub const VM = struct { }, @intFromEnum(OP.GET_GLOBAL) => { const name = self.read_string(); - self.push(self.vm.globals.get(name) catch { + const global = self.vm.globals.get(name) catch { self.runtimeError("Undefined variable: '{s}'", .{name.slice()}); return InterpreterError.RuntimeError; - }); + }; + self.push(global.val); }, @intFromEnum(OP.SET_GLOBAL) => { const name = self.read_string(); - self.vm.globals.set_existing(name, self.peek(0)) catch { + const replaced = self.vm.globals.replace_if(name, Global.make_var(self.peek(0)), Global.is_var) catch { self.runtimeError("Undefined variable: '{s}'", .{name.slice()}); return InterpreterError.RuntimeError; }; + if (!replaced) { + self.runtimeError("Cannot assign to a constant: '{s}'", .{name.slice()}); + return InterpreterError.RuntimeError; + } }, - @intFromEnum(OP.DEFINE_GLOBAL) => _ = try self.vm.globals.set(self.read_string(), self.pop()), + @intFromEnum(OP.DEFINE_GLOBAL) => _ = try self.vm.globals.set(self.read_string(), Global.make_var(self.pop())), + @intFromEnum(OP.DEFINE_GLOBAL_CONSTANT) => _ = try self.vm.globals.set(self.read_string(), Global.make_con(self.pop())), @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),