DzLox

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

commit 77d90681b627f3334f30564e91d304ccc786014f
parent 89741115adeb2ccad04f08b4776b2fccec67584b
Author: Szymon Mikulicz <szymon.mikulicz@aptiv.com>
Date:   Mon, 22 Apr 2024 22:31:41 +0200

GLobal vars finished

Diffstat:
Mzlox/src/chunk.zig | 2++
Mzlox/src/compiler.zig | 56++++++++++++++++++++++++++++++++++++++------------------
Mzlox/src/debug.zig | 2++
Mzlox/src/table.zig | 11++++++++++-
Mzlox/src/vm.zig | 20+++++++++++++++++++-
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),