move towards dynamically typed values
- remove InterpretResult as an enum, replace by error. - scanner: fix peekNext() - vm: add runtime errors, add VM.peek()
This commit is contained in:
parent
0f8e19adf1
commit
589413488c
5 changed files with 112 additions and 31 deletions
|
@ -167,8 +167,6 @@ pub const Compiler = struct {
|
|||
if (token_opt) |token| {
|
||||
self.parser.current = token;
|
||||
break;
|
||||
} else {
|
||||
self.errorCurrent(self.parser.current.lexeme);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -221,11 +219,12 @@ pub const Compiler = struct {
|
|||
|
||||
/// Emits bytecode for a number being loaded into the code.
|
||||
fn number(self: *Compiler) !void {
|
||||
std.debug.warn("parsing number: '{}'\n", self.parser.previous.lexeme);
|
||||
var value: f64 = try std.fmt.parseFloat(
|
||||
f64,
|
||||
self.parser.previous.lexeme,
|
||||
);
|
||||
try self.emitConstant(value);
|
||||
try self.emitConstant(values.NumberVal(value));
|
||||
}
|
||||
|
||||
/// Emits bytecode for a given unary.
|
||||
|
|
25
src/main.zig
25
src/main.zig
|
@ -12,12 +12,12 @@ const InterpretResult = vm.InterpretResult;
|
|||
|
||||
pub var hadError = false;
|
||||
|
||||
fn run(allocator: *Allocator, data: []u8) !InterpretResult {
|
||||
fn run(allocator: *Allocator, data: []u8) !void {
|
||||
var stdout_file = try std.io.getStdOut();
|
||||
const stdout = &stdout_file.outStream().stream;
|
||||
|
||||
var vmach = try vm.VM.init(allocator, stdout, data, true);
|
||||
return try vmach.interpret();
|
||||
try vmach.interpret();
|
||||
}
|
||||
|
||||
pub fn doError(line: usize, message: []const u8) !void {
|
||||
|
@ -40,9 +40,14 @@ fn runFile(allocator: *Allocator, path: []const u8) !void {
|
|||
var slice = try allocator.alloc(u8, total_bytes);
|
||||
_ = try lox_file.read(slice);
|
||||
|
||||
var res = try run(allocator, slice);
|
||||
if (res == vm.InterpretResult.CompileError) std.os.exit(65);
|
||||
if (res == vm.InterpretResult.RuntimeError) std.os.exit(70);
|
||||
run(allocator, slice) catch |err| {
|
||||
switch (err) {
|
||||
InterpretResult.Ok => {},
|
||||
InterpretResult.CompileError => std.os.exit(65),
|
||||
InterpretResult.RuntimeError => std.os.exit(70),
|
||||
else => return err,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn runPrompt(allocator: *Allocator) !void {
|
||||
|
@ -59,7 +64,15 @@ fn runPrompt(allocator: *Allocator) !void {
|
|||
return err;
|
||||
};
|
||||
|
||||
_ = try run(allocator, line);
|
||||
run(allocator, line) catch |err| {
|
||||
switch (err) {
|
||||
InterpretResult.Ok => {},
|
||||
InterpretResult.CompileError, InterpretResult.RuntimeError => blk: {
|
||||
try stdout.print("compile/runtime error.\n");
|
||||
},
|
||||
else => return err,
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -147,8 +147,8 @@ pub const Scanner = struct {
|
|||
}
|
||||
|
||||
fn peekNext(self: *Scanner) u8 {
|
||||
if (self.isAtEnd()) return 0;
|
||||
return self.source[self.current - 1];
|
||||
if (self.current + 1 >= self.source.len) return 0;
|
||||
return self.source[self.current + 1];
|
||||
}
|
||||
|
||||
fn skipWhitespace(self: *Scanner) void {
|
||||
|
@ -255,10 +255,12 @@ pub const Scanner = struct {
|
|||
'>' => self.makeMatchToken('=', .GREATER_EQUAL, .GREATER),
|
||||
|
||||
'/' => blk: {
|
||||
std.debug.warn("next: {}\n", self.peekNext());
|
||||
if (self.peekNext() == '/') {
|
||||
while (self.peek() != '\n' and !self.isAtEnd()) {
|
||||
_ = self.advance();
|
||||
}
|
||||
|
||||
break :blk null;
|
||||
} else {
|
||||
break :blk self.makeToken(.SLASH);
|
||||
|
|
|
@ -3,10 +3,42 @@ const std = @import("std");
|
|||
const Allocator = std.mem.Allocator;
|
||||
|
||||
// NOTE: right now, only numbers.
|
||||
pub const Value = f64;
|
||||
|
||||
pub const ValueType = enum(u8) {
|
||||
Bool,
|
||||
Nil,
|
||||
Number,
|
||||
};
|
||||
|
||||
pub const ValueValue = union(ValueType) {
|
||||
Bool: bool,
|
||||
Nil: void,
|
||||
Number: f64,
|
||||
};
|
||||
|
||||
pub const Value = struct {
|
||||
vtype: ValueType,
|
||||
as: ValueValue,
|
||||
};
|
||||
|
||||
// helper functions
|
||||
pub fn BoolVal(val: bool) Value {
|
||||
return Value{ .vtype = .Bool, .as = ValueValue{ .Bool = val } };
|
||||
}
|
||||
|
||||
pub fn NilVal() Value {
|
||||
return Value{ .vtype = .Nil, .as = ValueValue{ .Nil = {} } };
|
||||
}
|
||||
|
||||
pub fn NumberVal(val: f64) Value {
|
||||
return Value{ .vtype = .Number, .as = ValueValue{ .Number = val } };
|
||||
}
|
||||
|
||||
pub fn printValue(stdout: var, value: Value) !void {
|
||||
try stdout.print("{}", value);
|
||||
switch (value.as) {
|
||||
.Number => try stdout.print("{}", value.as.Number),
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub const ValueList = struct {
|
||||
|
|
71
src/vm.zig
71
src/vm.zig
|
@ -1,6 +1,7 @@
|
|||
const std = @import("std");
|
||||
const chunk = @import("chunk.zig");
|
||||
const value = @import("value.zig");
|
||||
const values = value;
|
||||
const compiler = @import("compiler.zig");
|
||||
|
||||
const Chunk = chunk.Chunk;
|
||||
|
@ -9,7 +10,7 @@ const Compiler = compiler.Compiler;
|
|||
|
||||
pub const StdOut = *std.io.OutStream(std.fs.File.WriteError);
|
||||
|
||||
pub const InterpretResult = enum {
|
||||
pub const InterpretResult = error{
|
||||
Ok,
|
||||
CompileError,
|
||||
RuntimeError,
|
||||
|
@ -91,35 +92,50 @@ pub const VM = struct {
|
|||
}
|
||||
|
||||
/// gets a f64 out of a value on the top of the stack.
|
||||
fn popNum(self: *VM) f64 {
|
||||
return self.pop();
|
||||
fn popNum(self: *VM) !f64 {
|
||||
var val: Value = self.pop();
|
||||
|
||||
switch (val.vtype) {
|
||||
.Number => return val.as.Number,
|
||||
|
||||
else => |vtype| blk: {
|
||||
self.runtimeError("Expected number, got {x}", vtype);
|
||||
return InterpretResult.RuntimeError;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn doAdd(self: *VM) !void {
|
||||
var b = self.popNum();
|
||||
var a = self.popNum();
|
||||
try self.push(a + b);
|
||||
var b = try self.popNum();
|
||||
var a = try self.popNum();
|
||||
try self.push(values.NumberVal(a + b));
|
||||
}
|
||||
|
||||
fn doSub(self: *VM) !void {
|
||||
var b = self.popNum();
|
||||
var a = self.popNum();
|
||||
try self.push(a * b);
|
||||
var b = try self.popNum();
|
||||
var a = try self.popNum();
|
||||
try self.push(values.NumberVal(a - b));
|
||||
}
|
||||
|
||||
fn doMul(self: *VM) !void {
|
||||
var b = self.popNum();
|
||||
var a = self.popNum();
|
||||
try self.push(a * b);
|
||||
var b = try self.popNum();
|
||||
var a = try self.popNum();
|
||||
try self.push(values.NumberVal(a * b));
|
||||
}
|
||||
|
||||
fn doDiv(self: *VM) !void {
|
||||
var b = self.popNum();
|
||||
var a = self.popNum();
|
||||
try self.push(a / b);
|
||||
var b = try self.popNum();
|
||||
var a = try self.popNum();
|
||||
try self.push(values.NumberVal(a / b));
|
||||
}
|
||||
|
||||
fn run(self: *VM) !InterpretResult {
|
||||
fn runtimeError(self: *VM, comptime fmt: []const u8, args: ...) void {
|
||||
std.debug.warn(fmt, args);
|
||||
std.debug.warn("\n[line {}] in script\n", self.chk.lines[self.ip]);
|
||||
self.resetStack();
|
||||
}
|
||||
|
||||
fn run(self: *VM) !void {
|
||||
while (true) {
|
||||
if (self.debug_flag) {
|
||||
try self.debugStack();
|
||||
|
@ -150,7 +166,22 @@ pub const VM = struct {
|
|||
chunk.OpCode.Subtract => try self.doSub(),
|
||||
chunk.OpCode.Multiply => try self.doMul(),
|
||||
chunk.OpCode.Divide => try self.doDiv(),
|
||||
chunk.OpCode.Negate => try self.push(-self.pop()),
|
||||
chunk.OpCode.Negate => blk: {
|
||||
var val = self.peek(0);
|
||||
if (val.vtype != .Bool) {
|
||||
self.runtimeError("Operand must be a number.");
|
||||
return InterpretResult.RuntimeError;
|
||||
}
|
||||
|
||||
val = self.pop();
|
||||
switch (val.as) {
|
||||
.Number => |num| {
|
||||
try self.push(values.NumberVal(-num));
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
},
|
||||
|
||||
else => blk: {
|
||||
std.debug.warn("Unknown instruction: {x}\n", instruction);
|
||||
return InterpretResult.RuntimeError;
|
||||
|
@ -159,7 +190,7 @@ pub const VM = struct {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn interpret(self: *VM) !InterpretResult {
|
||||
pub fn interpret(self: *VM) !void {
|
||||
//self.ip = 0;
|
||||
//self.debug("VM start\n");
|
||||
//var res = try self.run();
|
||||
|
@ -196,4 +227,8 @@ pub const VM = struct {
|
|||
self.stackTop -= 1;
|
||||
return self.stack[self.stackTop];
|
||||
}
|
||||
|
||||
pub fn peek(self: *VM, distance: usize) Value {
|
||||
return self.stack[self.stackTop - 1 - distance];
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue