implementing types of values (ch18)
This commit is contained in:
parent
f5a60501c9
commit
58cf9e15c6
40
README.md
40
README.md
@ -60,6 +60,44 @@ zig build run
|
|||||||
[ 18 ]
|
[ 18 ]
|
||||||
000b 2 OP_RETURN
|
000b 2 OP_RETURN
|
||||||
18
|
18
|
||||||
>
|
> !(5 - 4 > 3 * 2 == !nil)
|
||||||
|
== code ==
|
||||||
|
0000 1 OP_CONSTANT 0 '5'
|
||||||
|
0002 | OP_CONSTANT 1 '4'
|
||||||
|
0004 | OP_SUBSTRACT
|
||||||
|
0005 | OP_CONSTANT 2 '3'
|
||||||
|
0007 | OP_CONSTANT 3 '2'
|
||||||
|
0009 | OP_MULTIPLY
|
||||||
|
000a | OP_GREATER
|
||||||
|
000b | OP_NIL
|
||||||
|
000c | OP_NOT
|
||||||
|
000d | OP_EQUAL
|
||||||
|
000e | OP_NOT
|
||||||
|
== end of code ==
|
||||||
|
|
||||||
|
0000 1 OP_CONSTANT 0 '5'
|
||||||
|
[ 5 ]
|
||||||
|
0002 | OP_CONSTANT 1 '4'
|
||||||
|
[ 5 ][ 4 ]
|
||||||
|
0004 | OP_SUBSTRACT
|
||||||
|
[ 1 ]
|
||||||
|
0005 | OP_CONSTANT 2 '3'
|
||||||
|
[ 1 ][ 3 ]
|
||||||
|
0007 | OP_CONSTANT 3 '2'
|
||||||
|
[ 1 ][ 3 ][ 2 ]
|
||||||
|
0009 | OP_MULTIPLY
|
||||||
|
[ 1 ][ 6 ]
|
||||||
|
000a | OP_GREATER
|
||||||
|
[ false ]
|
||||||
|
000b | OP_NIL
|
||||||
|
[ false ][ nil ]
|
||||||
|
000c | OP_NOT
|
||||||
|
[ false ][ true ]
|
||||||
|
000d | OP_EQUAL
|
||||||
|
[ false ]
|
||||||
|
000e | OP_NOT
|
||||||
|
[ true ]
|
||||||
|
000f 2 OP_RETURN
|
||||||
|
true
|
||||||
|
>
|
||||||
```
|
```
|
||||||
|
@ -85,6 +85,13 @@ pub const Chunk = struct {
|
|||||||
@intFromEnum(OpCode.OP_DIVIDE) => return utils.simple_instruction("OP_DIVIDE", offset),
|
@intFromEnum(OpCode.OP_DIVIDE) => return utils.simple_instruction("OP_DIVIDE", offset),
|
||||||
@intFromEnum(OpCode.OP_NEGATE) => return utils.simple_instruction("OP_NEGATE", offset),
|
@intFromEnum(OpCode.OP_NEGATE) => return utils.simple_instruction("OP_NEGATE", offset),
|
||||||
@intFromEnum(OpCode.OP_CONSTANT) => return utils.constant_instruction("OP_CONSTANT", self, offset),
|
@intFromEnum(OpCode.OP_CONSTANT) => return utils.constant_instruction("OP_CONSTANT", self, offset),
|
||||||
|
@intFromEnum(OpCode.OP_NIL) => return utils.simple_instruction("OP_NIL", offset),
|
||||||
|
@intFromEnum(OpCode.OP_TRUE) => return utils.simple_instruction("OP_TRUE", offset),
|
||||||
|
@intFromEnum(OpCode.OP_FALSE) => return utils.simple_instruction("OP_FALSE", offset),
|
||||||
|
@intFromEnum(OpCode.OP_NOT) => return utils.simple_instruction("OP_NOT", 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_LESS) => return utils.simple_instruction("OP_LESS", offset),
|
||||||
else => {
|
else => {
|
||||||
debug.print("unknown opcode {d}\n", .{instruction});
|
debug.print("unknown opcode {d}\n", .{instruction});
|
||||||
return offset + 1;
|
return offset + 1;
|
||||||
|
@ -106,16 +106,20 @@ const Parser = struct {
|
|||||||
self.error_at(self.previous.?, error_message);
|
self.error_at(self.previous.?, error_message);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_byte(self: *Parser, byte: u8) !void {
|
fn emit_byte(self: *Parser, byte: u8) ParsingError!void {
|
||||||
try self.chunk.write(byte, self.previous.?.line);
|
self.chunk.write(byte, self.previous.?.line) catch |err| {
|
||||||
|
switch (err) {
|
||||||
|
error.OutOfMemory => return ParsingError.OutOfMemory,
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_bytes(self: *Parser, byte0: u8, byte1: u8) !void {
|
fn emit_bytes(self: *Parser, byte0: u8, byte1: u8) ParsingError!void {
|
||||||
try self.emit_byte(byte0);
|
try self.emit_byte(byte0);
|
||||||
try self.emit_byte(byte1);
|
try self.emit_byte(byte1);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_return(self: *Parser) !void {
|
fn emit_return(self: *Parser) ParsingError!void {
|
||||||
try self.emit_byte(@intFromEnum(OpCode.OP_RETURN));
|
try self.emit_byte(@intFromEnum(OpCode.OP_RETURN));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,9 +135,12 @@ const Parser = struct {
|
|||||||
self.error_msg("Failed converting float.");
|
self.error_msg("Failed converting float.");
|
||||||
return ParsingError.FloatConv;
|
return ParsingError.FloatConv;
|
||||||
};
|
};
|
||||||
self.emit_constant(value) catch {
|
self.emit_constant(Value.number_val(value)) catch |err| {
|
||||||
self.error_msg("Failed emiting constant.");
|
self.error_msg("Failed emiting constant.");
|
||||||
return ParsingError.ChunkError;
|
return switch (err) {
|
||||||
|
error.OutOfMemory => ParsingError.OutOfMemory,
|
||||||
|
else => ParsingError.Unknown,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,9 +171,19 @@ const Parser = struct {
|
|||||||
|
|
||||||
// Emit the operator instruction
|
// Emit the operator instruction
|
||||||
switch (operation_type) {
|
switch (operation_type) {
|
||||||
TokenType.MINUS => self.emit_byte(@intFromEnum(OpCode.OP_NEGATE)) catch {
|
TokenType.MINUS => self.emit_byte(@intFromEnum(OpCode.OP_NEGATE)) catch |err| {
|
||||||
self.error_msg("Failed emiting NEGATE opcode.");
|
self.error_msg("Failed emiting NEGATE opcode.");
|
||||||
return ParsingError.ChunkError;
|
return switch (err) {
|
||||||
|
error.OutOfMemory => ParsingError.OutOfMemory,
|
||||||
|
else => ParsingError.Unknown,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
TokenType.BANG => self.emit_byte(@intFromEnum(OpCode.OP_NOT)) catch |err| {
|
||||||
|
self.error_msg("Failed emiting NOT opcode.");
|
||||||
|
return switch (err) {
|
||||||
|
error.OutOfMemory => ParsingError.OutOfMemory,
|
||||||
|
else => ParsingError.Unknown,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
@ -178,13 +195,19 @@ const Parser = struct {
|
|||||||
|
|
||||||
try self.parse_precedence(@enumFromInt(1 + @intFromEnum(parser_rule.precedence)));
|
try self.parse_precedence(@enumFromInt(1 + @intFromEnum(parser_rule.precedence)));
|
||||||
|
|
||||||
switch (operator_type) {
|
return switch (operator_type) {
|
||||||
TokenType.PLUS => self.emit_byte(@intFromEnum(OpCode.OP_ADD)) catch {},
|
TokenType.BANG_EQUAL => self.emit_bytes(@intFromEnum(OpCode.OP_EQUAL), @intFromEnum(OpCode.OP_NOT)),
|
||||||
TokenType.MINUS => self.emit_byte(@intFromEnum(OpCode.OP_SUBSTRACT)) catch {},
|
TokenType.EQUAL_EQUAL => self.emit_byte(@intFromEnum(OpCode.OP_EQUAL)),
|
||||||
TokenType.STAR => self.emit_byte(@intFromEnum(OpCode.OP_MULTIPLY)) catch {},
|
TokenType.GREATER => self.emit_byte(@intFromEnum(OpCode.OP_GREATER)),
|
||||||
TokenType.SLASH => self.emit_byte(@intFromEnum(OpCode.OP_DIVIDE)) catch {},
|
TokenType.GREATER_EQUAL => self.emit_bytes(@intFromEnum(OpCode.OP_LESS), @intFromEnum(OpCode.OP_NOT)),
|
||||||
|
TokenType.LESS => self.emit_byte(@intFromEnum(OpCode.OP_LESS)),
|
||||||
|
TokenType.LESS_EQUAL => self.emit_bytes(@intFromEnum(OpCode.OP_GREATER), @intFromEnum(OpCode.OP_NOT)),
|
||||||
|
TokenType.PLUS => self.emit_byte(@intFromEnum(OpCode.OP_ADD)),
|
||||||
|
TokenType.MINUS => self.emit_byte(@intFromEnum(OpCode.OP_SUBSTRACT)),
|
||||||
|
TokenType.STAR => self.emit_byte(@intFromEnum(OpCode.OP_MULTIPLY)),
|
||||||
|
TokenType.SLASH => self.emit_byte(@intFromEnum(OpCode.OP_DIVIDE)),
|
||||||
else => return,
|
else => return,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_rule(operator_type: TokenType) ParserRule {
|
fn get_rule(operator_type: TokenType) ParserRule {
|
||||||
@ -200,31 +223,31 @@ const Parser = struct {
|
|||||||
TokenType.SEMICOLON => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.SEMICOLON => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.SLASH => ParserRule{ .prefix = null, .infix = binary, .precedence = Precedence.Factor },
|
TokenType.SLASH => ParserRule{ .prefix = null, .infix = binary, .precedence = Precedence.Factor },
|
||||||
TokenType.STAR => ParserRule{ .prefix = null, .infix = binary, .precedence = Precedence.Factor },
|
TokenType.STAR => ParserRule{ .prefix = null, .infix = binary, .precedence = Precedence.Factor },
|
||||||
TokenType.BANG => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.BANG => ParserRule{ .prefix = unary, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.BANG_EQUAL => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.BANG_EQUAL => ParserRule{ .prefix = null, .infix = binary, .precedence = Precedence.Equality },
|
||||||
TokenType.EQUAL => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.EQUAL => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.EQUAL_EQUAL => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.EQUAL_EQUAL => ParserRule{ .prefix = null, .infix = binary, .precedence = Precedence.Equality },
|
||||||
TokenType.GREATER => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.GREATER => ParserRule{ .prefix = null, .infix = binary, .precedence = Precedence.Comparison },
|
||||||
TokenType.GREATER_EQUAL => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.GREATER_EQUAL => ParserRule{ .prefix = null, .infix = binary, .precedence = Precedence.Comparison },
|
||||||
TokenType.LESS => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.LESS => ParserRule{ .prefix = null, .infix = binary, .precedence = Precedence.Comparison },
|
||||||
TokenType.LESS_EQUAL => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
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 = null, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.STRING => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.STRING => ParserRule{ .prefix = null, .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 },
|
||||||
TokenType.CLASS => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.CLASS => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.ELSE => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.ELSE => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.FALSE => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.FALSE => ParserRule{ .prefix = literal, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.FOR => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.FOR => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.FUN => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.FUN => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.IF => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.IF => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.NIL => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.NIL => ParserRule{ .prefix = literal, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.OR => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.OR => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.PRINT => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.PRINT => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.RETURN => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.RETURN => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.SUPER => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.SUPER => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.THIS => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.THIS => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.TRUE => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.TRUE => ParserRule{ .prefix = literal, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.VAR => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.VAR => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.WHILE => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.WHILE => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
||||||
TokenType.ERROR => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
TokenType.ERROR => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
||||||
@ -249,6 +272,15 @@ const Parser = struct {
|
|||||||
try infix_rule.?(self);
|
try infix_rule.?(self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn literal(self: *Parser) ParsingError!void {
|
||||||
|
try switch (self.previous.?.token_type) {
|
||||||
|
TokenType.NIL => self.emit_byte(@intFromEnum(OpCode.OP_NIL)),
|
||||||
|
TokenType.TRUE => self.emit_byte(@intFromEnum(OpCode.OP_TRUE)),
|
||||||
|
TokenType.FALSE => self.emit_byte(@intFromEnum(OpCode.OP_FALSE)),
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn compile(allocator: Allocator, contents: []const u8, chunk: *Chunk) !bool {
|
pub fn compile(allocator: Allocator, contents: []const u8, chunk: *Chunk) !bool {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
pub const ParsingError = error{
|
pub const ParsingError = error{
|
||||||
FloatConv,
|
FloatConv,
|
||||||
ChunkError,
|
OutOfMemory,
|
||||||
|
Unknown,
|
||||||
};
|
};
|
||||||
|
@ -1,9 +1,16 @@
|
|||||||
pub const OpCode = enum(u8) {
|
pub const OpCode = enum(u8) {
|
||||||
OP_CONSTANT,
|
OP_CONSTANT,
|
||||||
|
OP_NIL,
|
||||||
|
OP_TRUE,
|
||||||
|
OP_FALSE,
|
||||||
|
OP_EQUAL,
|
||||||
|
OP_GREATER,
|
||||||
|
OP_LESS,
|
||||||
OP_ADD,
|
OP_ADD,
|
||||||
OP_SUBSTRACT,
|
OP_SUBSTRACT,
|
||||||
OP_MULTIPLY,
|
OP_MULTIPLY,
|
||||||
OP_DIVIDE,
|
OP_DIVIDE,
|
||||||
|
OP_NOT,
|
||||||
OP_NEGATE,
|
OP_NEGATE,
|
||||||
OP_RETURN,
|
OP_RETURN,
|
||||||
};
|
};
|
||||||
|
@ -5,7 +5,82 @@ const Allocator = std.mem.Allocator;
|
|||||||
|
|
||||||
const utils = @import("./utils.zig");
|
const utils = @import("./utils.zig");
|
||||||
|
|
||||||
pub const Value = f64;
|
pub const ValueType = enum {
|
||||||
|
Bool,
|
||||||
|
Nil,
|
||||||
|
Number,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Value = struct {
|
||||||
|
value_type: ValueType,
|
||||||
|
as: union {
|
||||||
|
boolean: bool,
|
||||||
|
number: f64,
|
||||||
|
},
|
||||||
|
|
||||||
|
pub fn bool_val(value: bool) Value {
|
||||||
|
return Value{
|
||||||
|
.value_type = ValueType.Bool,
|
||||||
|
.as = .{
|
||||||
|
.boolean = value,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn nil_val() Value {
|
||||||
|
return Value{
|
||||||
|
.value_type = ValueType.Nil,
|
||||||
|
.as = .{
|
||||||
|
.boolean = false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn number_val(value: f64) Value {
|
||||||
|
return Value{
|
||||||
|
.value_type = ValueType.Number,
|
||||||
|
.as = .{
|
||||||
|
.number = value,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_bool(self: Value) bool {
|
||||||
|
return self.as.boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_number(self: Value) f64 {
|
||||||
|
return self.as.number;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_bool(self: Value) bool {
|
||||||
|
return self.value_type == ValueType.Bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_number(self: Value) bool {
|
||||||
|
return self.value_type == ValueType.Number;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_nil(self: Value) bool {
|
||||||
|
return self.value_type == ValueType.Nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_falsey(self: Value) bool {
|
||||||
|
return self.is_nil() or (self.is_bool() and !self.as_bool());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn equals(self: Value, other: Value) bool {
|
||||||
|
if (self.value_type != other.value_type) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return switch (self.value_type) {
|
||||||
|
ValueType.Nil => true,
|
||||||
|
ValueType.Bool => self.as_bool() == other.as_bool(),
|
||||||
|
ValueType.Number => self.as_number() == other.as_number(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
pub const ValueArray = struct {
|
pub const ValueArray = struct {
|
||||||
capacity: usize,
|
capacity: usize,
|
||||||
@ -39,5 +114,9 @@ pub const ValueArray = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub fn print_value(value: Value) void {
|
pub fn print_value(value: Value) void {
|
||||||
debug.print("{d}", .{value});
|
switch (value.value_type) {
|
||||||
|
ValueType.Nil => debug.print("nil", .{}),
|
||||||
|
ValueType.Bool => debug.print("{any}", .{value.as_bool()}),
|
||||||
|
ValueType.Number => debug.print("{d}", .{value.as_number()}),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
64
src/vm.zig
64
src/vm.zig
@ -69,28 +69,43 @@ pub const VM = struct {
|
|||||||
|
|
||||||
const instruction = self.read_byte();
|
const instruction = self.read_byte();
|
||||||
|
|
||||||
try switch (instruction) {
|
switch (instruction) {
|
||||||
@intFromEnum(OpCode.OP_CONSTANT) => {
|
@intFromEnum(OpCode.OP_CONSTANT) => {
|
||||||
const constant = self.read_constant();
|
const constant = self.read_constant();
|
||||||
try self.push(constant);
|
try self.push(constant);
|
||||||
},
|
},
|
||||||
@intFromEnum(OpCode.OP_ADD) => self.binary_op(OpCode.OP_ADD),
|
@intFromEnum(OpCode.OP_NIL) => try self.push(Value.nil_val()),
|
||||||
@intFromEnum(OpCode.OP_SUBSTRACT) => self.binary_op(OpCode.OP_SUBSTRACT),
|
@intFromEnum(OpCode.OP_FALSE) => try self.push(Value.bool_val(false)),
|
||||||
@intFromEnum(OpCode.OP_MULTIPLY) => self.binary_op(OpCode.OP_MULTIPLY),
|
@intFromEnum(OpCode.OP_TRUE) => try self.push(Value.bool_val(true)),
|
||||||
@intFromEnum(OpCode.OP_DIVIDE) => self.binary_op(OpCode.OP_DIVIDE),
|
@intFromEnum(OpCode.OP_ADD), @intFromEnum(OpCode.OP_SUBSTRACT), @intFromEnum(OpCode.OP_MULTIPLY), @intFromEnum(OpCode.OP_DIVIDE), @intFromEnum(OpCode.OP_LESS), @intFromEnum(OpCode.OP_GREATER) => {
|
||||||
|
const res = try self.binary_op(@enumFromInt(instruction));
|
||||||
|
if (res != InterpretResult.OK) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
@intFromEnum(OpCode.OP_NOT) => {
|
||||||
|
try self.push(Value.bool_val(self.pop().is_falsey()));
|
||||||
|
},
|
||||||
@intFromEnum(OpCode.OP_NEGATE) => {
|
@intFromEnum(OpCode.OP_NEGATE) => {
|
||||||
try self.push(-self.pop());
|
if (!self.peek(0).is_number()) {
|
||||||
|
self.runtime_error("Operand must be a number.");
|
||||||
|
return InterpretResult.RUNTIME_ERROR;
|
||||||
|
}
|
||||||
|
try self.push(Value.number_val(-self.pop().as_number()));
|
||||||
},
|
},
|
||||||
@intFromEnum(OpCode.OP_RETURN) => {
|
@intFromEnum(OpCode.OP_RETURN) => {
|
||||||
print_value(self.pop());
|
print_value(self.pop());
|
||||||
debug.print("\n", .{});
|
debug.print("\n", .{});
|
||||||
return InterpretResult.OK;
|
return InterpretResult.OK;
|
||||||
},
|
},
|
||||||
|
@intFromEnum(OpCode.OP_EQUAL) => {
|
||||||
|
try self.push(Value.bool_val(self.pop().equals(self.pop())));
|
||||||
|
},
|
||||||
else => {
|
else => {
|
||||||
debug.print("Invalid instruction: {d}\n", .{instruction});
|
debug.print("Invalid instruction: {d}\n", .{instruction});
|
||||||
return InterpretResult.RUNTIME_ERROR;
|
return InterpretResult.RUNTIME_ERROR;
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return InterpretResult.OK;
|
return InterpretResult.OK;
|
||||||
@ -117,18 +132,39 @@ pub const VM = struct {
|
|||||||
return self.stack.pop();
|
return self.stack.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn binary_op(self: *VM, op: OpCode) !void {
|
pub fn binary_op(self: *VM, op: OpCode) !InterpretResult {
|
||||||
const b = self.pop();
|
if (!self.peek(0).is_number() or !self.peek(0).is_number()) {
|
||||||
const a = self.pop();
|
self.runtime_error("Operands must be numbers");
|
||||||
|
return InterpretResult.RUNTIME_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
const b = self.pop().as_number();
|
||||||
|
const a = self.pop().as_number();
|
||||||
|
|
||||||
const res: Value = switch (op) {
|
const res: Value = switch (op) {
|
||||||
OpCode.OP_ADD => a + b,
|
OpCode.OP_ADD => Value.number_val(a + b),
|
||||||
OpCode.OP_SUBSTRACT => a - b,
|
OpCode.OP_SUBSTRACT => Value.number_val(a - b),
|
||||||
OpCode.OP_MULTIPLY => a * b,
|
OpCode.OP_MULTIPLY => Value.number_val(a * b),
|
||||||
OpCode.OP_DIVIDE => a / b,
|
OpCode.OP_DIVIDE => Value.number_val(a / b),
|
||||||
|
OpCode.OP_LESS => Value.bool_val(a < b),
|
||||||
|
OpCode.OP_GREATER => Value.bool_val(a > b),
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
};
|
};
|
||||||
|
|
||||||
try self.push(res);
|
try self.push(res);
|
||||||
|
|
||||||
|
return InterpretResult.OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn peek(self: *VM, distance: usize) Value {
|
||||||
|
return self.stack.items[self.stack.items.len - 1 - distance];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn runtime_error(self: *VM, err_msg: []const u8) void {
|
||||||
|
const instruction = self.ip.?;
|
||||||
|
const line = self.chunk.?.lines[instruction];
|
||||||
|
|
||||||
|
debug.print("err: {s}\n", .{err_msg});
|
||||||
|
debug.print("[line {d}] in script\n", .{line});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user