implementing global variables (ch21)
This commit is contained in:
parent
062d2e44de
commit
5fb7d361cf
@ -1,8 +1,8 @@
|
|||||||
build:
|
build:
|
||||||
zig build
|
zig build
|
||||||
|
|
||||||
run:
|
run *ARGS:
|
||||||
zig build run
|
zig build run -- {{ARGS}}
|
||||||
|
|
||||||
test:
|
test:
|
||||||
zig build test
|
zig build test
|
||||||
|
@ -11,7 +11,7 @@ While reading [Crafting Interpreters](https://craftinginterpreters.com/), after
|
|||||||
- [x] 18 - Types of Values
|
- [x] 18 - Types of Values
|
||||||
- [x] 19 - Strings
|
- [x] 19 - Strings
|
||||||
- [x] 20 - Hash Tables
|
- [x] 20 - Hash Tables
|
||||||
- [ ] 21 - Global Variables
|
- [x] 21 - Global Variables
|
||||||
- [ ] 22 - Local Variables
|
- [ ] 22 - Local Variables
|
||||||
- [ ] 23 - Jumping Back and Forth
|
- [ ] 23 - Jumping Back and Forth
|
||||||
- [ ] 24 - Calls and Functions
|
- [ ] 24 - Calls and Functions
|
||||||
|
5
samples/ch21_breakfast.lox
Normal file
5
samples/ch21_breakfast.lox
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
var breakfast = "beignets";
|
||||||
|
var beverage = "cafe au lait";
|
||||||
|
breakfast = "beignets with " + beverage;
|
||||||
|
|
||||||
|
print breakfast;
|
@ -92,6 +92,11 @@ pub const Chunk = struct {
|
|||||||
@intFromEnum(OpCode.OP_EQUAL) => return utils.simple_instruction("OP_EQUAL", offset),
|
@intFromEnum(OpCode.OP_EQUAL) => return utils.simple_instruction("OP_EQUAL", offset),
|
||||||
@intFromEnum(OpCode.OP_GREATER) => return utils.simple_instruction("OP_GREATER", offset),
|
@intFromEnum(OpCode.OP_GREATER) => return utils.simple_instruction("OP_GREATER", offset),
|
||||||
@intFromEnum(OpCode.OP_LESS) => return utils.simple_instruction("OP_LESS", offset),
|
@intFromEnum(OpCode.OP_LESS) => return utils.simple_instruction("OP_LESS", offset),
|
||||||
|
@intFromEnum(OpCode.OP_PRINT) => return utils.simple_instruction("OP_PRINT", offset),
|
||||||
|
@intFromEnum(OpCode.OP_POP) => return utils.simple_instruction("OP_POP", offset),
|
||||||
|
@intFromEnum(OpCode.OP_DEFINE_GLOBAL) => return utils.constant_instruction("OP_DEFINE_GLOBAL", self, offset),
|
||||||
|
@intFromEnum(OpCode.OP_GET_GLOBAL) => return utils.constant_instruction("OP_GET_GLOBAL", self, offset),
|
||||||
|
@intFromEnum(OpCode.OP_SET_GLOBAL) => return utils.constant_instruction("OP_SET_GLOBAL", self, offset),
|
||||||
else => {
|
else => {
|
||||||
debug.print("unknown opcode {d}\n", .{instruction});
|
debug.print("unknown opcode {d}\n", .{instruction});
|
||||||
return offset + 1;
|
return offset + 1;
|
||||||
|
156
src/compile.zig
156
src/compile.zig
@ -30,8 +30,8 @@ const Precedence = enum {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ParserRule = struct {
|
const ParserRule = struct {
|
||||||
prefix: ?*const fn (*Parser) ParsingError!void,
|
prefix: ?*const fn (*Parser, bool) ParsingError!void,
|
||||||
infix: ?*const fn (*Parser) ParsingError!void,
|
infix: ?*const fn (*Parser, bool) ParsingError!void,
|
||||||
precedence: Precedence,
|
precedence: Precedence,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -134,7 +134,9 @@ const Parser = struct {
|
|||||||
try self.emit_return();
|
try self.emit_return();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn number(self: *Parser) ParsingError!void {
|
fn number(self: *Parser, can_assign: bool) ParsingError!void {
|
||||||
|
_ = can_assign;
|
||||||
|
|
||||||
const value = std.fmt.parseFloat(f64, self.previous.?.start[0..self.previous.?.length]) catch {
|
const value = std.fmt.parseFloat(f64, self.previous.?.start[0..self.previous.?.length]) catch {
|
||||||
self.error_msg("Failed converting float.");
|
self.error_msg("Failed converting float.");
|
||||||
return ParsingError.FloatConv;
|
return ParsingError.FloatConv;
|
||||||
@ -162,12 +164,16 @@ const Parser = struct {
|
|||||||
return @intCast(constant);
|
return @intCast(constant);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn grouping(self: *Parser) ParsingError!void {
|
fn grouping(self: *Parser, can_assign: bool) ParsingError!void {
|
||||||
|
_ = can_assign;
|
||||||
|
|
||||||
try self.expression();
|
try self.expression();
|
||||||
self.consume(TokenType.RIGHT_PAREN, "Expect ')' after expression.");
|
self.consume(TokenType.RIGHT_PAREN, "Expect ')' after expression.");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unary(self: *Parser) ParsingError!void {
|
fn unary(self: *Parser, can_assign: bool) ParsingError!void {
|
||||||
|
_ = can_assign;
|
||||||
|
|
||||||
const operation_type = self.previous.?.token_type;
|
const operation_type = self.previous.?.token_type;
|
||||||
|
|
||||||
// Compile the operand
|
// Compile the operand
|
||||||
@ -193,7 +199,9 @@ const Parser = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn binary(self: *Parser) ParsingError!void {
|
fn binary(self: *Parser, can_assign: bool) ParsingError!void {
|
||||||
|
_ = can_assign;
|
||||||
|
|
||||||
const operator_type = self.previous.?.token_type;
|
const operator_type = self.previous.?.token_type;
|
||||||
const parser_rule = Parser.get_rule(operator_type);
|
const parser_rule = Parser.get_rule(operator_type);
|
||||||
|
|
||||||
@ -235,7 +243,7 @@ const Parser = struct {
|
|||||||
TokenType.GREATER_EQUAL => ParserRule{ .prefix = null, .infix = binary, .precedence = Precedence.Comparison },
|
TokenType.GREATER_EQUAL => ParserRule{ .prefix = null, .infix = binary, .precedence = Precedence.Comparison },
|
||||||
TokenType.LESS => ParserRule{ .prefix = null, .infix = binary, .precedence = Precedence.Comparison },
|
TokenType.LESS => ParserRule{ .prefix = null, .infix = binary, .precedence = Precedence.Comparison },
|
||||||
TokenType.LESS_EQUAL => ParserRule{ .prefix = null, .infix = binary, .precedence = Precedence.Comparison },
|
TokenType.LESS_EQUAL => ParserRule{ .prefix = null, .infix = binary, .precedence = Precedence.Comparison },
|
||||||
TokenType.IDENTIFIER => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.IDENTIFIER => ParserRule{ .prefix = variable, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.STRING => ParserRule{ .prefix = string, .infix = null, .precedence = Precedence.None },
|
TokenType.STRING => ParserRule{ .prefix = string, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.NUMBER => ParserRule{ .prefix = number, .infix = null, .precedence = Precedence.None },
|
TokenType.NUMBER => ParserRule{ .prefix = number, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.AND => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.AND => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
||||||
@ -268,16 +276,23 @@ const Parser = struct {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try prefix_rule.?(self);
|
const can_assign = @intFromEnum(precedence) <= @intFromEnum(Precedence.Assignement);
|
||||||
|
try prefix_rule.?(self, can_assign);
|
||||||
|
|
||||||
while (@intFromEnum(precedence) <= @intFromEnum(Parser.get_rule(self.current.?.token_type).precedence)) {
|
while (@intFromEnum(precedence) <= @intFromEnum(Parser.get_rule(self.current.?.token_type).precedence)) {
|
||||||
self.advance();
|
self.advance();
|
||||||
const infix_rule = Parser.get_rule(self.previous.?.token_type).infix;
|
const infix_rule = Parser.get_rule(self.previous.?.token_type).infix;
|
||||||
try infix_rule.?(self);
|
try infix_rule.?(self, can_assign);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (can_assign and self.match(TokenType.EQUAL)) {
|
||||||
|
self.error_msg("Invalid assignment target.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn literal(self: *Parser) ParsingError!void {
|
fn literal(self: *Parser, can_assign: bool) ParsingError!void {
|
||||||
|
_ = can_assign;
|
||||||
|
|
||||||
try switch (self.previous.?.token_type) {
|
try switch (self.previous.?.token_type) {
|
||||||
TokenType.NIL => self.emit_byte(@intFromEnum(OpCode.OP_NIL)),
|
TokenType.NIL => self.emit_byte(@intFromEnum(OpCode.OP_NIL)),
|
||||||
TokenType.TRUE => self.emit_byte(@intFromEnum(OpCode.OP_TRUE)),
|
TokenType.TRUE => self.emit_byte(@intFromEnum(OpCode.OP_TRUE)),
|
||||||
@ -286,7 +301,9 @@ const Parser = struct {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn string(self: *Parser) ParsingError!void {
|
fn string(self: *Parser, can_assign: bool) ParsingError!void {
|
||||||
|
_ = can_assign;
|
||||||
|
|
||||||
const str = self.previous.?.start[1 .. self.previous.?.length - 1];
|
const str = self.previous.?.start[1 .. self.previous.?.length - 1];
|
||||||
|
|
||||||
var string_obj = self.vm.copy_string(str);
|
var string_obj = self.vm.copy_string(str);
|
||||||
@ -295,6 +312,116 @@ const Parser = struct {
|
|||||||
|
|
||||||
try self.emit_constant(Value.obj_val(&string_obj.obj));
|
try self.emit_constant(Value.obj_val(&string_obj.obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn variable(self: *Parser, can_assign: bool) ParsingError!void {
|
||||||
|
try self.named_variable(self.previous.?, can_assign);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn named_variable(self: *Parser, token: Token, can_assign: bool) ParsingError!void {
|
||||||
|
const constant = try self.identifier_constant(token);
|
||||||
|
if (can_assign and self.match(TokenType.EQUAL)) {
|
||||||
|
try self.expression();
|
||||||
|
try self.emit_bytes(@intFromEnum(OpCode.OP_SET_GLOBAL), constant);
|
||||||
|
} else {
|
||||||
|
try self.emit_bytes(@intFromEnum(OpCode.OP_GET_GLOBAL), constant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn declaration(self: *Parser) ParsingError!void {
|
||||||
|
if (self.match(TokenType.VAR)) {
|
||||||
|
try self.var_declaration();
|
||||||
|
} else {
|
||||||
|
try self.statement();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.panic_mode) {
|
||||||
|
self.synchronize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn statement(self: *Parser) ParsingError!void {
|
||||||
|
if (self.match(TokenType.PRINT)) {
|
||||||
|
try self.print_statement();
|
||||||
|
} else {
|
||||||
|
try self.expression_statement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn match(self: *Parser, token_type: TokenType) bool {
|
||||||
|
if (!self.check(token_type))
|
||||||
|
return false;
|
||||||
|
self.advance();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check(self: *Parser, token_type: TokenType) bool {
|
||||||
|
return self.current.?.token_type == token_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_statement(self: *Parser) ParsingError!void {
|
||||||
|
try self.expression();
|
||||||
|
self.consume(TokenType.SEMICOLON, "Expect ';' after value.");
|
||||||
|
try self.emit_byte(@intFromEnum(OpCode.OP_PRINT));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expression_statement(self: *Parser) ParsingError!void {
|
||||||
|
try self.expression();
|
||||||
|
self.consume(TokenType.SEMICOLON, "Expect ';' after value.");
|
||||||
|
try self.emit_byte(@intFromEnum(OpCode.OP_POP));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn synchronize(self: *Parser) void {
|
||||||
|
self.panic_mode = false;
|
||||||
|
|
||||||
|
while (self.current.?.token_type != TokenType.EOF) {
|
||||||
|
if (self.previous.?.token_type == TokenType.SEMICOLON) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (self.current.?.token_type) {
|
||||||
|
TokenType.CLASS,
|
||||||
|
TokenType.FUN,
|
||||||
|
TokenType.VAR,
|
||||||
|
TokenType.FOR,
|
||||||
|
TokenType.IF,
|
||||||
|
TokenType.WHILE,
|
||||||
|
TokenType.PRINT,
|
||||||
|
TokenType.RETURN,
|
||||||
|
=> return,
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
|
||||||
|
self.advance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn var_declaration(self: *Parser) ParsingError!void {
|
||||||
|
const global = try self.parse_variable("Expect variable name.");
|
||||||
|
|
||||||
|
if (self.match(TokenType.EQUAL)) {
|
||||||
|
try self.expression();
|
||||||
|
} else {
|
||||||
|
try self.emit_byte(@intFromEnum(OpCode.OP_NIL));
|
||||||
|
}
|
||||||
|
self.consume(TokenType.SEMICOLON, "Expect ';' after variable declaration.");
|
||||||
|
|
||||||
|
try self.define_variable(global);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_variable(self: *Parser, err_msg: []const u8) ParsingError!u8 {
|
||||||
|
self.consume(TokenType.IDENTIFIER, err_msg);
|
||||||
|
return self.identifier_constant(self.previous.?);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn identifier_constant(self: *Parser, token: Token) ParsingError!u8 {
|
||||||
|
const copy = &self.vm.copy_string(token.start[0..token.length]).obj;
|
||||||
|
self.vm.add_reference(copy);
|
||||||
|
return self.make_constant(Value.obj_val(copy));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn define_variable(self: *Parser, global: u8) ParsingError!void {
|
||||||
|
return self.emit_bytes(@intFromEnum(OpCode.OP_DEFINE_GLOBAL), global);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn compile(allocator: Allocator, vm: *VM, contents: []const u8, chunk: *Chunk) !bool {
|
pub fn compile(allocator: Allocator, vm: *VM, contents: []const u8, chunk: *Chunk) !bool {
|
||||||
@ -304,8 +431,11 @@ pub fn compile(allocator: Allocator, vm: *VM, contents: []const u8, chunk: *Chun
|
|||||||
var parser = Parser.new(vm, &scanner, chunk);
|
var parser = Parser.new(vm, &scanner, chunk);
|
||||||
|
|
||||||
parser.advance();
|
parser.advance();
|
||||||
try parser.expression();
|
|
||||||
parser.consume(TokenType.EOF, "Expect end of expression.");
|
while (!parser.match(TokenType.EOF)) {
|
||||||
|
try parser.declaration();
|
||||||
|
}
|
||||||
|
|
||||||
try parser.end_parser();
|
try parser.end_parser();
|
||||||
|
|
||||||
return !parser.had_error;
|
return !parser.had_error;
|
||||||
|
@ -17,7 +17,11 @@ pub fn repl(allocator: Allocator, vm: *VM) !void {
|
|||||||
const stdout = std.io.getStdOut().writer();
|
const stdout = std.io.getStdOut().writer();
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
try stdout.print("> ", .{});
|
if (vm.has_tracing()) {
|
||||||
|
vm.globals.dump();
|
||||||
|
}
|
||||||
|
|
||||||
|
try stdout.print("zlox> ", .{});
|
||||||
|
|
||||||
@memset(&line, 0);
|
@memset(&line, 0);
|
||||||
|
|
||||||
|
@ -3,6 +3,10 @@ pub const OpCode = enum(u8) {
|
|||||||
OP_NIL,
|
OP_NIL,
|
||||||
OP_TRUE,
|
OP_TRUE,
|
||||||
OP_FALSE,
|
OP_FALSE,
|
||||||
|
OP_POP,
|
||||||
|
OP_GET_GLOBAL,
|
||||||
|
OP_DEFINE_GLOBAL,
|
||||||
|
OP_SET_GLOBAL,
|
||||||
OP_EQUAL,
|
OP_EQUAL,
|
||||||
OP_GREATER,
|
OP_GREATER,
|
||||||
OP_LESS,
|
OP_LESS,
|
||||||
@ -12,5 +16,6 @@ pub const OpCode = enum(u8) {
|
|||||||
OP_DIVIDE,
|
OP_DIVIDE,
|
||||||
OP_NOT,
|
OP_NOT,
|
||||||
OP_NEGATE,
|
OP_NEGATE,
|
||||||
|
OP_PRINT,
|
||||||
OP_RETURN,
|
OP_RETURN,
|
||||||
};
|
};
|
||||||
|
@ -123,7 +123,7 @@ pub const Table = struct {
|
|||||||
for (self.entries, 0..) |entry, idx| {
|
for (self.entries, 0..) |entry, idx| {
|
||||||
if (entry.key != null) {
|
if (entry.key != null) {
|
||||||
std.debug.print("{d} ({d}) - {s}: ", .{ idx, entry.key.?.hash, entry.key.?.chars });
|
std.debug.print("{d} ({d}) - {s}: ", .{ idx, entry.key.?.hash, entry.key.?.chars });
|
||||||
entry.value.print();
|
entry.value.type_print();
|
||||||
std.debug.print("\n", .{});
|
std.debug.print("\n", .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ pub const Value = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_string(self: Value) *Obj.String {
|
pub fn as_string(self: Value) *Obj.String {
|
||||||
const obj: *Obj.String = self.as_obj();
|
const obj: *Obj.String = self.as_obj().as_string();
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
@ -126,6 +126,18 @@ pub const Value = struct {
|
|||||||
ValueType.Obj => self.as_obj().print(),
|
ValueType.Obj => self.as_obj().print(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn type_print(self: Value) void {
|
||||||
|
switch (self.value_type) {
|
||||||
|
ValueType.Nil => debug.print("(nil)", .{}),
|
||||||
|
ValueType.Bool => debug.print("(bool)", .{}),
|
||||||
|
ValueType.Number => debug.print("(number)", .{}),
|
||||||
|
ValueType.Obj => debug.print("(obj)", .{}),
|
||||||
|
}
|
||||||
|
|
||||||
|
debug.print(" ", .{});
|
||||||
|
self.print();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const ValueArray = struct {
|
pub const ValueArray = struct {
|
||||||
|
40
src/vm.zig
40
src/vm.zig
@ -32,6 +32,7 @@ pub const VM = struct {
|
|||||||
// In the book, a linked list between objects is used to handle this.
|
// In the book, a linked list between objects is used to handle this.
|
||||||
references: std.ArrayList(*Obj),
|
references: std.ArrayList(*Obj),
|
||||||
strings: Table,
|
strings: Table,
|
||||||
|
globals: Table,
|
||||||
tracing: bool,
|
tracing: bool,
|
||||||
|
|
||||||
pub fn new(allocator: Allocator) VM {
|
pub fn new(allocator: Allocator) VM {
|
||||||
@ -42,6 +43,7 @@ pub const VM = struct {
|
|||||||
.stack = std.ArrayList(Value).init(allocator),
|
.stack = std.ArrayList(Value).init(allocator),
|
||||||
.references = std.ArrayList(*Obj).init(allocator),
|
.references = std.ArrayList(*Obj).init(allocator),
|
||||||
.strings = Table.new(allocator),
|
.strings = Table.new(allocator),
|
||||||
|
.globals = Table.new(allocator),
|
||||||
.tracing = false,
|
.tracing = false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -54,6 +56,7 @@ pub const VM = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.strings.deinit();
|
self.strings.deinit();
|
||||||
|
self.globals.deinit();
|
||||||
self.clean_references();
|
self.clean_references();
|
||||||
self.references.deinit();
|
self.references.deinit();
|
||||||
}
|
}
|
||||||
@ -106,6 +109,7 @@ pub const VM = struct {
|
|||||||
@intFromEnum(OpCode.OP_NIL) => try self.push(Value.nil_val()),
|
@intFromEnum(OpCode.OP_NIL) => try self.push(Value.nil_val()),
|
||||||
@intFromEnum(OpCode.OP_FALSE) => try self.push(Value.bool_val(false)),
|
@intFromEnum(OpCode.OP_FALSE) => try self.push(Value.bool_val(false)),
|
||||||
@intFromEnum(OpCode.OP_TRUE) => try self.push(Value.bool_val(true)),
|
@intFromEnum(OpCode.OP_TRUE) => try self.push(Value.bool_val(true)),
|
||||||
|
@intFromEnum(OpCode.OP_POP) => _ = self.pop(),
|
||||||
@intFromEnum(OpCode.OP_ADD),
|
@intFromEnum(OpCode.OP_ADD),
|
||||||
@intFromEnum(OpCode.OP_SUBSTRACT),
|
@intFromEnum(OpCode.OP_SUBSTRACT),
|
||||||
@intFromEnum(OpCode.OP_MULTIPLY),
|
@intFromEnum(OpCode.OP_MULTIPLY),
|
||||||
@ -128,14 +132,48 @@ pub const VM = struct {
|
|||||||
}
|
}
|
||||||
try self.push(Value.number_val(-self.pop().as_number()));
|
try self.push(Value.number_val(-self.pop().as_number()));
|
||||||
},
|
},
|
||||||
@intFromEnum(OpCode.OP_RETURN) => {
|
@intFromEnum(OpCode.OP_PRINT) => {
|
||||||
print_value(self.pop());
|
print_value(self.pop());
|
||||||
debug.print("\n", .{});
|
debug.print("\n", .{});
|
||||||
|
},
|
||||||
|
@intFromEnum(OpCode.OP_RETURN) => {
|
||||||
return InterpretResult.OK;
|
return InterpretResult.OK;
|
||||||
},
|
},
|
||||||
@intFromEnum(OpCode.OP_EQUAL) => {
|
@intFromEnum(OpCode.OP_EQUAL) => {
|
||||||
try self.push(Value.bool_val(self.pop().equals(self.pop())));
|
try self.push(Value.bool_val(self.pop().equals(self.pop())));
|
||||||
},
|
},
|
||||||
|
@intFromEnum(OpCode.OP_DEFINE_GLOBAL) => {
|
||||||
|
const name = self.read_constant().as_string();
|
||||||
|
|
||||||
|
_ = self.globals.set(name, self.peek(0));
|
||||||
|
_ = self.pop();
|
||||||
|
},
|
||||||
|
@intFromEnum(OpCode.OP_GET_GLOBAL) => {
|
||||||
|
const name: *Obj.String = self.read_constant().as_string();
|
||||||
|
var value = Value.nil_val();
|
||||||
|
|
||||||
|
if (!self.globals.get(name, &value)) {
|
||||||
|
const err_msg = try std.fmt.allocPrint(self.allocator, "Undefined variable '{s}'.", .{name.chars});
|
||||||
|
defer self.allocator.free(err_msg);
|
||||||
|
self.runtime_error(err_msg);
|
||||||
|
return InterpretResult.RUNTIME_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
try self.push(value);
|
||||||
|
},
|
||||||
|
@intFromEnum(OpCode.OP_SET_GLOBAL) => {
|
||||||
|
const name: *Obj.String = self.read_constant().as_string();
|
||||||
|
|
||||||
|
if (self.globals.set(name, self.peek(0))) {
|
||||||
|
_ = self.globals.del(name);
|
||||||
|
|
||||||
|
const err_msg = try std.fmt.allocPrint(self.allocator, "Undefined variable '{s}'.", .{name.chars});
|
||||||
|
defer self.allocator.free(err_msg);
|
||||||
|
|
||||||
|
self.runtime_error(err_msg);
|
||||||
|
return InterpretResult.RUNTIME_ERROR;
|
||||||
|
}
|
||||||
|
},
|
||||||
else => {
|
else => {
|
||||||
debug.print("Invalid instruction: {d}\n", .{instruction});
|
debug.print("Invalid instruction: {d}\n", .{instruction});
|
||||||
return InterpretResult.RUNTIME_ERROR;
|
return InterpretResult.RUNTIME_ERROR;
|
||||||
|
Loading…
Reference in New Issue
Block a user