commit 5ef503975c3563b670509f6d04d22ca33aaff051
parent 4a13f042541401587373f41eafdc6675f6fbc411
Author: Szymon Mikulicz <szymon.mikulicz@aptiv.com>
Date: Fri, 2 Aug 2024 16:02:09 +0200
Global constants
Diffstat:
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),