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 ]
|
||||
000b 2 OP_RETURN
|
||||
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_NEGATE) => return utils.simple_instruction("OP_NEGATE", 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 => {
|
||||
debug.print("unknown opcode {d}\n", .{instruction});
|
||||
return offset + 1;
|
||||
|
@ -106,16 +106,20 @@ const Parser = struct {
|
||||
self.error_at(self.previous.?, error_message);
|
||||
}
|
||||
|
||||
fn emit_byte(self: *Parser, byte: u8) !void {
|
||||
try self.chunk.write(byte, self.previous.?.line);
|
||||
fn emit_byte(self: *Parser, byte: u8) ParsingError!void {
|
||||
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(byte1);
|
||||
}
|
||||
|
||||
fn emit_return(self: *Parser) !void {
|
||||
fn emit_return(self: *Parser) ParsingError!void {
|
||||
try self.emit_byte(@intFromEnum(OpCode.OP_RETURN));
|
||||
}
|
||||
|
||||
@ -131,9 +135,12 @@ const Parser = struct {
|
||||
self.error_msg("Failed converting float.");
|
||||
return ParsingError.FloatConv;
|
||||
};
|
||||
self.emit_constant(value) catch {
|
||||
self.emit_constant(Value.number_val(value)) catch |err| {
|
||||
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
|
||||
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.");
|
||||
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 => {},
|
||||
}
|
||||
@ -178,13 +195,19 @@ const Parser = struct {
|
||||
|
||||
try self.parse_precedence(@enumFromInt(1 + @intFromEnum(parser_rule.precedence)));
|
||||
|
||||
switch (operator_type) {
|
||||
TokenType.PLUS => self.emit_byte(@intFromEnum(OpCode.OP_ADD)) catch {},
|
||||
TokenType.MINUS => self.emit_byte(@intFromEnum(OpCode.OP_SUBSTRACT)) catch {},
|
||||
TokenType.STAR => self.emit_byte(@intFromEnum(OpCode.OP_MULTIPLY)) catch {},
|
||||
TokenType.SLASH => self.emit_byte(@intFromEnum(OpCode.OP_DIVIDE)) catch {},
|
||||
return switch (operator_type) {
|
||||
TokenType.BANG_EQUAL => self.emit_bytes(@intFromEnum(OpCode.OP_EQUAL), @intFromEnum(OpCode.OP_NOT)),
|
||||
TokenType.EQUAL_EQUAL => self.emit_byte(@intFromEnum(OpCode.OP_EQUAL)),
|
||||
TokenType.GREATER => self.emit_byte(@intFromEnum(OpCode.OP_GREATER)),
|
||||
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,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
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.SLASH => 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_EQUAL => 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 = binary, .precedence = Precedence.Equality },
|
||||
TokenType.EQUAL => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
||||
TokenType.EQUAL_EQUAL => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
||||
TokenType.GREATER => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
||||
TokenType.GREATER_EQUAL => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
||||
TokenType.LESS => ParserRule{ .prefix = null, .infix = null, .precedence = Precedence.None },
|
||||
TokenType.LESS_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 = 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_EQUAL => ParserRule{ .prefix = null, .infix = binary, .precedence = Precedence.Comparison },
|
||||
TokenType.IDENTIFIER => 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.AND => 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.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.FUN => 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.PRINT => 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.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.WHILE => 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);
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -1,4 +1,5 @@
|
||||
pub const ParsingError = error{
|
||||
FloatConv,
|
||||
ChunkError,
|
||||
OutOfMemory,
|
||||
Unknown,
|
||||
};
|
||||
|
@ -1,9 +1,16 @@
|
||||
pub const OpCode = enum(u8) {
|
||||
OP_CONSTANT,
|
||||
OP_NIL,
|
||||
OP_TRUE,
|
||||
OP_FALSE,
|
||||
OP_EQUAL,
|
||||
OP_GREATER,
|
||||
OP_LESS,
|
||||
OP_ADD,
|
||||
OP_SUBSTRACT,
|
||||
OP_MULTIPLY,
|
||||
OP_DIVIDE,
|
||||
OP_NOT,
|
||||
OP_NEGATE,
|
||||
OP_RETURN,
|
||||
};
|
||||
|
@ -5,7 +5,82 @@ const Allocator = std.mem.Allocator;
|
||||
|
||||
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 {
|
||||
capacity: usize,
|
||||
@ -39,5 +114,9 @@ pub const ValueArray = struct {
|
||||
};
|
||||
|
||||
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();
|
||||
|
||||
try switch (instruction) {
|
||||
switch (instruction) {
|
||||
@intFromEnum(OpCode.OP_CONSTANT) => {
|
||||
const constant = self.read_constant();
|
||||
try self.push(constant);
|
||||
},
|
||||
@intFromEnum(OpCode.OP_ADD) => self.binary_op(OpCode.OP_ADD),
|
||||
@intFromEnum(OpCode.OP_SUBSTRACT) => self.binary_op(OpCode.OP_SUBSTRACT),
|
||||
@intFromEnum(OpCode.OP_MULTIPLY) => self.binary_op(OpCode.OP_MULTIPLY),
|
||||
@intFromEnum(OpCode.OP_DIVIDE) => self.binary_op(OpCode.OP_DIVIDE),
|
||||
@intFromEnum(OpCode.OP_NIL) => try self.push(Value.nil_val()),
|
||||
@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_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) => {
|
||||
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) => {
|
||||
print_value(self.pop());
|
||||
debug.print("\n", .{});
|
||||
return InterpretResult.OK;
|
||||
},
|
||||
@intFromEnum(OpCode.OP_EQUAL) => {
|
||||
try self.push(Value.bool_val(self.pop().equals(self.pop())));
|
||||
},
|
||||
else => {
|
||||
debug.print("Invalid instruction: {d}\n", .{instruction});
|
||||
return InterpretResult.RUNTIME_ERROR;
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return InterpretResult.OK;
|
||||
@ -117,18 +132,39 @@ pub const VM = struct {
|
||||
return self.stack.pop();
|
||||
}
|
||||
|
||||
pub fn binary_op(self: *VM, op: OpCode) !void {
|
||||
const b = self.pop();
|
||||
const a = self.pop();
|
||||
pub fn binary_op(self: *VM, op: OpCode) !InterpretResult {
|
||||
if (!self.peek(0).is_number() or !self.peek(0).is_number()) {
|
||||
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) {
|
||||
OpCode.OP_ADD => a + b,
|
||||
OpCode.OP_SUBSTRACT => a - b,
|
||||
OpCode.OP_MULTIPLY => a * b,
|
||||
OpCode.OP_DIVIDE => a / b,
|
||||
OpCode.OP_ADD => Value.number_val(a + b),
|
||||
OpCode.OP_SUBSTRACT => Value.number_val(a - b),
|
||||
OpCode.OP_MULTIPLY => Value.number_val(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,
|
||||
};
|
||||
|
||||
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