From 69dda36d168451070c6bfc3965776b211d1b53ad Mon Sep 17 00:00:00 2001 From: Luna Date: Mon, 3 Jun 2019 01:41:22 -0300 Subject: [PATCH] add SetGlobal/SetGlobalLong opcodes - main: split compile/runtime error messages --- src/chunk.zig | 6 ++++++ src/compiler.zig | 55 ++++++++++++++++++++++++++++++------------------ src/main.zig | 7 ++++-- src/vm.zig | 20 ++++++++++++++++++ 4 files changed, 66 insertions(+), 22 deletions(-) diff --git a/src/chunk.zig b/src/chunk.zig index 7a89c3e..5ce7062 100644 --- a/src/chunk.zig +++ b/src/chunk.zig @@ -33,6 +33,8 @@ const AllOpcodes = struct { pub DefineGlobalLong: u8 = 18, pub GetGlobal: u8 = 19, pub GetGlobalLong: u8 = 20, + pub SetGlobal: u8 = 21, + pub SetGlobalLong: u8 = 22, }; pub const OpCode = AllOpcodes{}; @@ -245,6 +247,10 @@ pub const Chunk = struct { return try constantInstruction(stdout, "OP_GETGLOBAL", self, index); } else if (instruction == OpCode.GetGlobalLong) { return try constantLongInstruction(stdout, "OP_GETGLOBAL_LONG", self, index); + } else if (instruction == OpCode.SetGlobal) { + return try constantInstruction(stdout, "OP_SETGLOBAL", self, index); + } else if (instruction == OpCode.SetGlobalLong) { + return try constantLongInstruction(stdout, "OP_SETGLOBAL_LONG", self, index); } else { try stdout.print("Unknown opcode: {}\n", instruction); return index + 1; diff --git a/src/compiler.zig b/src/compiler.zig index fb3ee39..ac6030a 100644 --- a/src/compiler.zig +++ b/src/compiler.zig @@ -39,7 +39,7 @@ const Precedence = enum(u5) { Primary, }; -const ParseFn = fn (*Compiler) anyerror!void; +const ParseFn = fn (*Compiler, bool) anyerror!void; const ParseRule = struct { prefix: ?ParseFn = null, @@ -229,13 +229,13 @@ pub const Compiler = struct { } } - fn grouping(self: *Compiler) !void { + fn grouping(self: *Compiler, canAssign: bool) !void { try self.expression(); try self.consume(.RIGHT_PAREN, "Expect ')' after expression."); } /// Emits bytecode for a number being loaded into the code. - fn number(self: *Compiler) !void { + fn number(self: *Compiler, canAssign: bool) !void { var value: f64 = try std.fmt.parseFloat( f64, self.parser.previous.lexeme, @@ -243,7 +243,7 @@ pub const Compiler = struct { try self.emitConstant(values.NumberVal(value)); } - fn string(self: *Compiler) !void { + fn string(self: *Compiler, canAssign: bool) !void { const lexeme_len = self.parser.previous.lexeme.len; try self.emitConstant(values.ObjVal(try objects.copyString( @@ -252,7 +252,7 @@ pub const Compiler = struct { ))); } - fn namedVariable(self: *Compiler, tok: *Token) !void { + fn namedVariable(self: *Compiler, tok: *Token, canAssign: bool) !void { // writeConstant always writes OP_CODE which may be not // what we want, so. var idx = try self.currentChunk().writeConstantRaw(values.ObjVal(try objects.copyString( @@ -260,19 +260,28 @@ pub const Compiler = struct { tok.lexeme, )), tok.line); - try self.emitConstWithIndex( - chunks.OpCode.GetGlobal, - chunks.OpCode.GetGlobalLong, - idx, - ); + if (canAssign and try self.match(.EQUAL)) { + try self.expression(); + try self.emitConstWithIndex( + chunks.OpCode.SetGlobal, + chunks.OpCode.SetGlobalLong, + idx, + ); + } else { + try self.emitConstWithIndex( + chunks.OpCode.GetGlobal, + chunks.OpCode.GetGlobalLong, + idx, + ); + } } - fn variable(self: *Compiler) !void { - try self.namedVariable(&self.parser.previous); + fn variable(self: *Compiler, canAssign: bool) !void { + try self.namedVariable(&self.parser.previous, canAssign); } /// Emits bytecode for a given unary. - fn unary(self: *Compiler) !void { + fn unary(self: *Compiler, canAssign: bool) !void { var ttype = self.parser.previous.ttype; try self.parsePrecedence(.Unary); @@ -283,7 +292,7 @@ pub const Compiler = struct { } } - fn binary(self: *Compiler) !void { + fn binary(self: *Compiler, canAssign: bool) !void { var op_type = self.parser.previous.ttype; var rule: *ParseRule = self.getRule(op_type); try self.parsePrecedence(@intToEnum(Precedence, @enumToInt(rule.precedence) + 1)); @@ -306,7 +315,7 @@ pub const Compiler = struct { } } - fn literal(self: *Compiler) !void { + fn literal(self: *Compiler, canAssign: bool) !void { switch (self.parser.previous.ttype) { .FALSE => try self.emitByte(OpCode.False), .NIL => try self.emitByte(OpCode.Nil), @@ -315,21 +324,27 @@ pub const Compiler = struct { } } - fn parsePrecedence(self: *Compiler, precedence: Precedence) !void { + fn parsePrecedence(self: *Compiler, precedence: Precedence) anyerror!void { try self.advance(); var as_int = @enumToInt(precedence); var prefix_rule_opt = self.getRule(self.parser.previous.ttype).prefix; if (prefix_rule_opt) |prefix_rule| { - try prefix_rule(self); + var canAssign: bool = as_int <= @enumToInt(Precedence.Assignment); + try prefix_rule(self, canAssign); while (as_int <= @enumToInt(self.getRule(self.parser.current.ttype).precedence)) { try self.advance(); var infix_rule_opt = self.getRule(self.parser.previous.ttype).infix; if (infix_rule_opt) |infix_rule| { - try infix_rule(self); + try infix_rule(self, canAssign); } } + + if (canAssign and try self.match(.EQUAL)) { + self.errorPrevious("Invalid assignment target."); + try self.expression(); + } } else { self.errorPrevious("Expect expression."); return; @@ -340,7 +355,7 @@ pub const Compiler = struct { return &rules[@enumToInt(ttype)]; } - fn expression(self: *Compiler) !void { + fn expression(self: *Compiler) anyerror!void { try self.parsePrecedence(.Assignment); } @@ -376,7 +391,7 @@ pub const Compiler = struct { self: *Compiler, token: *Token, ) !chunks.ConstantIndex { - return try self.currentChunk().writeConstant(values.ObjVal(try objects.copyString( + return try self.currentChunk().writeConstantRaw(values.ObjVal(try objects.copyString( self.vmach, token.lexeme, )), token.line); diff --git a/src/main.zig b/src/main.zig index 3983140..1162392 100644 --- a/src/main.zig +++ b/src/main.zig @@ -79,8 +79,11 @@ fn runPrompt(allocator: *Allocator) !void { runWithVM(&vmach, line) catch |err| { switch (err) { InterpretResult.Ok => {}, - InterpretResult.CompileError, InterpretResult.RuntimeError => blk: { - try stdout.print("compile/runtime error.\n"); + InterpretResult.CompileError => blk: { + try stdout.print("compile error.\n"); + }, + InterpretResult.RuntimeError => blk: { + try stdout.print("runtime error.\n"); }, else => return err, } diff --git a/src/vm.zig b/src/vm.zig index 24a72f8..d2b923d 100644 --- a/src/vm.zig +++ b/src/vm.zig @@ -245,6 +245,17 @@ pub const VM = struct { } } + fn doSetGlobal(self: *VM, name: []u8) !void { + var res = try self.globals.getOrPut(name); + + if (res.found_existing) { + res.kv.value = self.peek(0); + } else { + self.runtimeError("Undefined variable '{}'.", name); + return InterpretResult.RuntimeError; + } + } + fn run(self: *VM) !void { while (true) { if (self.debug_flag) { @@ -295,6 +306,15 @@ pub const VM = struct { break :blk; }, + chunk.OpCode.SetGlobal => blk: { + try self.doSetGlobal(self.readString()); + break :blk; + }, + chunk.OpCode.SetGlobalLong => blk: { + try self.doSetGlobal(self.readStringLong()); + break :blk; + }, + // extracting the name is different depending of the // op code since one just uses a single byte, the other // uses three bytes since its a u24.