commit 77d90681b627f3334f30564e91d304ccc786014f
parent 89741115adeb2ccad04f08b4776b2fccec67584b
Author: Szymon Mikulicz <szymon.mikulicz@aptiv.com>
Date: Mon, 22 Apr 2024 22:31:41 +0200
GLobal vars finished
Diffstat:
5 files changed, 71 insertions(+), 20 deletions(-)
diff --git a/zlox/src/chunk.zig b/zlox/src/chunk.zig
@@ -22,6 +22,8 @@ pub const OP = enum(u8) {
PRINT,
POP,
DEFINE_GLOBAL,
+ GET_GLOBAL,
+ SET_GLOBAL,
};
pub const Chunk = struct {
diff --git a/zlox/src/compiler.zig b/zlox/src/compiler.zig
@@ -45,7 +45,7 @@ pub const Compiler = struct {
const Self = @This();
- const ParseFn = *const fn (*Self) void;
+ const ParseFn = *const fn (*Self, bool) void;
const ParseRule = struct {
prefix: ?ParseFn,
@@ -87,7 +87,7 @@ pub const Compiler = struct {
T.GREATER_EQUAL => R(null, S.binary, P.COMPARISON ),
T.LESS => R(null, S.binary, P.COMPARISON ),
T.LESS_EQUAL => R(null, S.binary, P.COMPARISON ),
- T.IDENTIFIER => R(null, null, P.NONE ),
+ T.IDENTIFIER => R(S.variable, null, P.NONE ),
T.STRING => R(S.string, null, P.NONE ),
T.NUMBER => R(S.number, null, P.NONE ),
T.AND => R(null, null, P.NONE ),
@@ -181,9 +181,11 @@ pub const Compiler = struct {
}
fn parsePrecedence(self: *Self, precedence: Precedence) void {
+ const canAssign = precedence.lessOrEq(Precedence.ASSIGNMENT);
+
self.advance();
if (getRule(self.previous.type catch unreachable).prefix) |prefixRule| {
- prefixRule(self);
+ prefixRule(self, canAssign);
} else {
self.lastError = CompilerError.NotAnExpression;
self.errorAtPrevious("Expect expression.");
@@ -193,13 +195,17 @@ pub const Compiler = struct {
while (precedence.lessOrEq(getRule(self.current.type catch unreachable).precedence)) {
self.advance();
if (getRule(self.previous.type catch unreachable).infix) |infixRule| {
- infixRule(self);
+ infixRule(self, canAssign);
} else {
self.lastError = CompilerError.NotAnExpression;
self.errorAtPrevious("Expect expression.");
return;
}
}
+
+ if (canAssign and self.match(Token.EQUAL)) {
+ self.errorAtPrevious("Invalid assignment target.");
+ }
}
fn errorAt(self: *Self, token: scanner.Token, message: []const u8) void {
@@ -228,7 +234,7 @@ pub const Compiler = struct {
self.errorAtCurrent(message);
}
- fn number(self: *Self) void {
+ fn number(self: *Self, _: bool) void {
self.emitConstant(Value.parseNumber(self.previous.lexeme) catch |err| {
self.lastError = err;
self.errorAtPrevious("Invalid numeric literal");
@@ -236,7 +242,7 @@ pub const Compiler = struct {
});
}
- fn string(self: *Self) void {
+ fn string(self: *Self, _: bool) void {
self.emitConstant(Value.init(self.objects.emplace(.String, &.{self.previous.lexeme[1 .. self.previous.lexeme.len - 1]}) catch |err| {
self.lastError = err;
self.errorAtPrevious("Couldn't allocate object");
@@ -244,6 +250,20 @@ pub const Compiler = struct {
}));
}
+ fn variable(self: *Self, canAssign: bool) void {
+ self.namedVariable(self.previous, canAssign);
+ }
+
+ fn namedVariable(self: *Self, tok: scanner.Token, canAssign: bool) void {
+ const arg = self.identifierConstant(tok) catch return;
+ if (canAssign and self.match(Token.EQUAL)) {
+ self.expression();
+ self.emit(OP.SET_GLOBAL, arg);
+ } else {
+ self.emit(OP.GET_GLOBAL, arg);
+ }
+ }
+
fn emitConstant(self: *Self, val: Value) void {
self.emit(OP.CONSTANT, self.makeConstant(val));
}
@@ -256,12 +276,12 @@ pub const Compiler = struct {
};
}
- fn grouping(self: *Self) void {
+ fn grouping(self: *Self, _: bool) void {
self.expression();
self.consume(Token.RIGHT_PAREN, "Expected ')' after expression");
}
- fn unary(self: *Self) void {
+ fn unary(self: *Self, _: bool) void {
const operatorType = self.previous.type catch unreachable;
self.parsePrecedence(Precedence.UNARY);
@@ -273,7 +293,7 @@ pub const Compiler = struct {
}
}
- fn literal(self: *Self) void {
+ fn literal(self: *Self, _: bool) void {
switch (self.previous.type catch unreachable) {
Token.FALSE => self.emitOP(OP.FALSE),
Token.TRUE => self.emitOP(OP.TRUE),
@@ -282,7 +302,7 @@ pub const Compiler = struct {
}
}
- fn binary(self: *Self) void {
+ fn binary(self: *Self, _: bool) void {
const operatorType = self.previous.type catch unreachable;
self.parsePrecedence(getRule(operatorType).precedence.inc());
@@ -301,7 +321,7 @@ pub const Compiler = struct {
}
}
- fn ternary(self: *Self) void {
+ fn ternary(self: *Self, _: bool) void {
const operatorType = self.previous.type catch unreachable;
self.parsePrecedence(getRule(operatorType).precedence.inc());
@@ -325,11 +345,7 @@ pub const Compiler = struct {
}
fn varDeclaration(self: *Self) void {
- const global = self.parseVariable("Expect variable name.") catch |err| {
- self.lastError = err;
- self.errorAtPrevious("Couldn't create variable");
- return;
- };
+ const global = self.parseVariable("Expect variable name.") catch return;
if (self.match(Token.EQUAL)) {
self.expression();
@@ -344,11 +360,15 @@ pub const Compiler = struct {
fn parseVariable(self: *Self, errorMessage: []const u8) !u8 {
self.consume(Token.IDENTIFIER, errorMessage);
- return try self.identifierConstant(self.previous);
+ return self.identifierConstant(self.previous);
}
fn identifierConstant(self: *Self, tok: scanner.Token) !u8 {
- return self.makeConstant(Value.init(try self.objects.emplace(.String, &.{tok.lexeme})));
+ return self.makeConstant(Value.init(self.objects.emplace(.String, &.{tok.lexeme}) catch |err| {
+ self.lastError = err;
+ self.errorAtPrevious("Couldn't allocate identifier");
+ return err;
+ }));
}
fn defineVariable(self: *Self, global: u8) void {
diff --git a/zlox/src/debug.zig b/zlox/src/debug.zig
@@ -38,6 +38,8 @@ pub fn disassembleInstruction(ch: chunk.Chunk, offset: usize) !usize {
@intFromEnum(OP.NOT) => simpleInstruction("OP_NOT", offset),
@intFromEnum(OP.CONSTANT) => try constantInstruction("OP_CONSTANT", ch, offset),
@intFromEnum(OP.DEFINE_GLOBAL) => try constantInstruction("OP_DEFINE_GLOBAL", ch, offset),
+ @intFromEnum(OP.GET_GLOBAL) => try constantInstruction("OP_GET_GLOBAL", ch, offset),
+ @intFromEnum(OP.SET_GLOBAL) => try constantInstruction("OP_SET_GLOBAL", ch, offset),
@intFromEnum(OP.PRINT) => simpleInstruction("OP_PRINT", offset),
@intFromEnum(OP.POP) => simpleInstruction("OP_POP", offset),
else => blk: {
diff --git a/zlox/src/table.zig b/zlox/src/table.zig
@@ -114,11 +114,20 @@ 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();
+ const entry = find(self.entries, key);
+ switch (entry.*) {
+ .some => _ = 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;
- return switch (find(self.entries, key)) {
+ return switch (find(self.entries, key).*) {
.some => |some| some.value,
else => TableError.KeyError,
};
diff --git a/zlox/src/vm.zig b/zlox/src/vm.zig
@@ -53,6 +53,10 @@ pub const VM = struct {
return self.chunk.constants.data[self.read_byte()];
}
+ fn read_string(self: *@This()) *const Obj.String {
+ return self.read_constant().obj.cast(.String) catch unreachable;
+ }
+
fn push(self: *@This(), val: Value) void {
self.stackTop[0] = val;
self.stackTop += 1;
@@ -118,7 +122,21 @@ pub const VM = struct {
try self.binary_op(Value.number, Value.number, Callback.add);
}
},
- @intFromEnum(OP.DEFINE_GLOBAL) => _ = try self.vm.globals.set(self.read_constant().obj.cast(.String) catch unreachable, self.pop()),
+ @intFromEnum(OP.GET_GLOBAL) => {
+ const name = self.read_string();
+ self.push(self.vm.globals.get(name) catch {
+ self.runtimeError("Undefined variable: '{s}'", .{name.slice()});
+ return InterpreterError.RuntimeError;
+ });
+ },
+ @intFromEnum(OP.SET_GLOBAL) => {
+ const name = self.read_string();
+ self.vm.globals.set_existing(name, self.peek(0)) catch {
+ self.runtimeError("Undefined variable: '{s}'", .{name.slice()});
+ return InterpreterError.RuntimeError;
+ };
+ },
+ @intFromEnum(OP.DEFINE_GLOBAL) => _ = try self.vm.globals.set(self.read_string(), 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),