add SetGlobal/SetGlobalLong opcodes
- main: split compile/runtime error messages
This commit is contained in:
parent
9f45dea2c0
commit
69dda36d16
4 changed files with 66 additions and 22 deletions
|
@ -33,6 +33,8 @@ const AllOpcodes = struct {
|
||||||
pub DefineGlobalLong: u8 = 18,
|
pub DefineGlobalLong: u8 = 18,
|
||||||
pub GetGlobal: u8 = 19,
|
pub GetGlobal: u8 = 19,
|
||||||
pub GetGlobalLong: u8 = 20,
|
pub GetGlobalLong: u8 = 20,
|
||||||
|
pub SetGlobal: u8 = 21,
|
||||||
|
pub SetGlobalLong: u8 = 22,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const OpCode = AllOpcodes{};
|
pub const OpCode = AllOpcodes{};
|
||||||
|
@ -245,6 +247,10 @@ pub const Chunk = struct {
|
||||||
return try constantInstruction(stdout, "OP_GETGLOBAL", self, index);
|
return try constantInstruction(stdout, "OP_GETGLOBAL", self, index);
|
||||||
} else if (instruction == OpCode.GetGlobalLong) {
|
} else if (instruction == OpCode.GetGlobalLong) {
|
||||||
return try constantLongInstruction(stdout, "OP_GETGLOBAL_LONG", self, index);
|
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 {
|
} else {
|
||||||
try stdout.print("Unknown opcode: {}\n", instruction);
|
try stdout.print("Unknown opcode: {}\n", instruction);
|
||||||
return index + 1;
|
return index + 1;
|
||||||
|
|
|
@ -39,7 +39,7 @@ const Precedence = enum(u5) {
|
||||||
Primary,
|
Primary,
|
||||||
};
|
};
|
||||||
|
|
||||||
const ParseFn = fn (*Compiler) anyerror!void;
|
const ParseFn = fn (*Compiler, bool) anyerror!void;
|
||||||
|
|
||||||
const ParseRule = struct {
|
const ParseRule = struct {
|
||||||
prefix: ?ParseFn = null,
|
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.expression();
|
||||||
try self.consume(.RIGHT_PAREN, "Expect ')' after expression.");
|
try self.consume(.RIGHT_PAREN, "Expect ')' after expression.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Emits bytecode for a number being loaded into the code.
|
/// 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(
|
var value: f64 = try std.fmt.parseFloat(
|
||||||
f64,
|
f64,
|
||||||
self.parser.previous.lexeme,
|
self.parser.previous.lexeme,
|
||||||
|
@ -243,7 +243,7 @@ pub const Compiler = struct {
|
||||||
try self.emitConstant(values.NumberVal(value));
|
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;
|
const lexeme_len = self.parser.previous.lexeme.len;
|
||||||
|
|
||||||
try self.emitConstant(values.ObjVal(try objects.copyString(
|
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
|
// writeConstant always writes OP_CODE which may be not
|
||||||
// what we want, so.
|
// what we want, so.
|
||||||
var idx = try self.currentChunk().writeConstantRaw(values.ObjVal(try objects.copyString(
|
var idx = try self.currentChunk().writeConstantRaw(values.ObjVal(try objects.copyString(
|
||||||
|
@ -260,19 +260,28 @@ pub const Compiler = struct {
|
||||||
tok.lexeme,
|
tok.lexeme,
|
||||||
)), tok.line);
|
)), tok.line);
|
||||||
|
|
||||||
|
if (canAssign and try self.match(.EQUAL)) {
|
||||||
|
try self.expression();
|
||||||
|
try self.emitConstWithIndex(
|
||||||
|
chunks.OpCode.SetGlobal,
|
||||||
|
chunks.OpCode.SetGlobalLong,
|
||||||
|
idx,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
try self.emitConstWithIndex(
|
try self.emitConstWithIndex(
|
||||||
chunks.OpCode.GetGlobal,
|
chunks.OpCode.GetGlobal,
|
||||||
chunks.OpCode.GetGlobalLong,
|
chunks.OpCode.GetGlobalLong,
|
||||||
idx,
|
idx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn variable(self: *Compiler) !void {
|
fn variable(self: *Compiler, canAssign: bool) !void {
|
||||||
try self.namedVariable(&self.parser.previous);
|
try self.namedVariable(&self.parser.previous, canAssign);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Emits bytecode for a given unary.
|
/// Emits bytecode for a given unary.
|
||||||
fn unary(self: *Compiler) !void {
|
fn unary(self: *Compiler, canAssign: bool) !void {
|
||||||
var ttype = self.parser.previous.ttype;
|
var ttype = self.parser.previous.ttype;
|
||||||
try self.parsePrecedence(.Unary);
|
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 op_type = self.parser.previous.ttype;
|
||||||
var rule: *ParseRule = self.getRule(op_type);
|
var rule: *ParseRule = self.getRule(op_type);
|
||||||
try self.parsePrecedence(@intToEnum(Precedence, @enumToInt(rule.precedence) + 1));
|
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) {
|
switch (self.parser.previous.ttype) {
|
||||||
.FALSE => try self.emitByte(OpCode.False),
|
.FALSE => try self.emitByte(OpCode.False),
|
||||||
.NIL => try self.emitByte(OpCode.Nil),
|
.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();
|
try self.advance();
|
||||||
var as_int = @enumToInt(precedence);
|
var as_int = @enumToInt(precedence);
|
||||||
var prefix_rule_opt = self.getRule(self.parser.previous.ttype).prefix;
|
var prefix_rule_opt = self.getRule(self.parser.previous.ttype).prefix;
|
||||||
|
|
||||||
if (prefix_rule_opt) |prefix_rule| {
|
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)) {
|
while (as_int <= @enumToInt(self.getRule(self.parser.current.ttype).precedence)) {
|
||||||
try self.advance();
|
try self.advance();
|
||||||
var infix_rule_opt = self.getRule(self.parser.previous.ttype).infix;
|
var infix_rule_opt = self.getRule(self.parser.previous.ttype).infix;
|
||||||
if (infix_rule_opt) |infix_rule| {
|
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 {
|
} else {
|
||||||
self.errorPrevious("Expect expression.");
|
self.errorPrevious("Expect expression.");
|
||||||
return;
|
return;
|
||||||
|
@ -340,7 +355,7 @@ pub const Compiler = struct {
|
||||||
return &rules[@enumToInt(ttype)];
|
return &rules[@enumToInt(ttype)];
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expression(self: *Compiler) !void {
|
fn expression(self: *Compiler) anyerror!void {
|
||||||
try self.parsePrecedence(.Assignment);
|
try self.parsePrecedence(.Assignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -376,7 +391,7 @@ pub const Compiler = struct {
|
||||||
self: *Compiler,
|
self: *Compiler,
|
||||||
token: *Token,
|
token: *Token,
|
||||||
) !chunks.ConstantIndex {
|
) !chunks.ConstantIndex {
|
||||||
return try self.currentChunk().writeConstant(values.ObjVal(try objects.copyString(
|
return try self.currentChunk().writeConstantRaw(values.ObjVal(try objects.copyString(
|
||||||
self.vmach,
|
self.vmach,
|
||||||
token.lexeme,
|
token.lexeme,
|
||||||
)), token.line);
|
)), token.line);
|
||||||
|
|
|
@ -79,8 +79,11 @@ fn runPrompt(allocator: *Allocator) !void {
|
||||||
runWithVM(&vmach, line) catch |err| {
|
runWithVM(&vmach, line) catch |err| {
|
||||||
switch (err) {
|
switch (err) {
|
||||||
InterpretResult.Ok => {},
|
InterpretResult.Ok => {},
|
||||||
InterpretResult.CompileError, InterpretResult.RuntimeError => blk: {
|
InterpretResult.CompileError => blk: {
|
||||||
try stdout.print("compile/runtime error.\n");
|
try stdout.print("compile error.\n");
|
||||||
|
},
|
||||||
|
InterpretResult.RuntimeError => blk: {
|
||||||
|
try stdout.print("runtime error.\n");
|
||||||
},
|
},
|
||||||
else => return err,
|
else => return err,
|
||||||
}
|
}
|
||||||
|
|
20
src/vm.zig
20
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 {
|
fn run(self: *VM) !void {
|
||||||
while (true) {
|
while (true) {
|
||||||
if (self.debug_flag) {
|
if (self.debug_flag) {
|
||||||
|
@ -295,6 +306,15 @@ pub const VM = struct {
|
||||||
break :blk;
|
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
|
// extracting the name is different depending of the
|
||||||
// op code since one just uses a single byte, the other
|
// op code since one just uses a single byte, the other
|
||||||
// uses three bytes since its a u24.
|
// uses three bytes since its a u24.
|
||||||
|
|
Loading…
Reference in a new issue