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:
Luna 2019-06-01 23:33:53 -03:00
parent 0f8e19adf1
commit 589413488c
5 changed files with 112 additions and 31 deletions

View File

@ -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.

View File

@ -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,
}
};
}
}

View File

@ -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);

View File

@ -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 {

View File

@ -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];
}
};