jorts/src/vm.zig

246 lines
6.8 KiB
Zig
Raw Normal View History

2019-06-01 17:55:11 +00:00
const std = @import("std");
const chunk = @import("chunk.zig");
const value = @import("value.zig");
const values = value;
const compiler = @import("compiler.zig");
2019-06-01 17:55:11 +00:00
const Chunk = chunk.Chunk;
const Value = value.Value;
const Compiler = compiler.Compiler;
2019-06-01 17:55:11 +00:00
pub const StdOut = *std.io.OutStream(std.fs.File.WriteError);
2019-06-01 18:23:23 +00:00
pub const InterpretResult = error{
2019-06-01 17:55:11 +00:00
Ok,
CompileError,
RuntimeError,
};
2019-06-02 03:02:37 +00:00
fn isFalsey(val: value.Value) bool {
return val.vtype == .Nil or (val.vtype == .Bool and !val.as.Bool);
}
pub const VM = struct {
chk: *Chunk = undefined,
src: []const u8,
2019-06-01 18:23:23 +00:00
ip: usize = 0,
stack: []Value,
2019-06-01 18:23:23 +00:00
stackTop: usize = 0,
2019-06-01 17:55:11 +00:00
stdout: StdOut,
2019-06-01 18:01:39 +00:00
debug_flag: bool,
allocator: *std.mem.Allocator,
2019-06-01 17:55:11 +00:00
2019-06-01 18:23:23 +00:00
fn resetStack(self: *VM) void {
self.stackTop = 0;
}
pub fn init(
allocator: *std.mem.Allocator,
stdout: StdOut,
source: []const u8,
debug_flag: bool,
) !VM {
2019-06-01 18:23:23 +00:00
var self = VM{
.src = source,
2019-06-01 18:23:23 +00:00
.stack = try allocator.alloc(Value, 256),
2019-06-01 18:23:23 +00:00
.stdout = stdout,
2019-06-01 18:01:39 +00:00
.debug_flag = debug_flag,
.allocator = allocator,
2019-06-01 17:55:11 +00:00
};
2019-06-01 18:23:23 +00:00
self.resetStack();
return self;
2019-06-01 17:55:11 +00:00
}
2019-06-01 18:01:39 +00:00
pub fn debug(self: *VM, comptime fmt: []const u8, args: ...) void {
if (self.debug_flag) {
std.debug.warn(fmt, args);
}
}
2019-06-01 17:55:11 +00:00
fn readByte(self: *VM) u8 {
var byte: u8 = self.chk.code[self.ip];
self.ip += 1;
return byte;
}
fn readConst(self: *VM) Value {
2019-06-01 17:55:11 +00:00
return self.chk.constants.values[self.readByte()];
}
fn readConstLong(self: *VM) Value {
2019-06-01 17:55:11 +00:00
const v3 = self.readByte();
const v2 = self.readByte();
const v1 = self.readByte();
const const_idx = (@intCast(u24, v3) << 16) |
(@intCast(u24, v2) << 8) |
v1;
return self.chk.constants.values[const_idx];
}
2019-06-01 18:23:23 +00:00
fn debugStack(self: *VM) !void {
try self.stdout.print(" ");
for (self.stack) |val, idx| {
if (idx >= self.stackTop) break;
try self.stdout.print("[ ");
try value.printValue(self.stdout, val);
try self.stdout.print(" ]");
}
try self.stdout.print("\n");
}
2019-06-01 18:40:18 +00:00
/// gets a f64 out of a value on the top of the stack.
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;
},
}
2019-06-01 18:40:18 +00:00
}
fn doAdd(self: *VM) !void {
var b = try self.popNum();
var a = try self.popNum();
try self.push(values.NumberVal(a + b));
2019-06-01 18:40:18 +00:00
}
fn doSub(self: *VM) !void {
var b = try self.popNum();
var a = try self.popNum();
try self.push(values.NumberVal(a - b));
2019-06-01 18:40:18 +00:00
}
fn doMul(self: *VM) !void {
var b = try self.popNum();
var a = try self.popNum();
try self.push(values.NumberVal(a * b));
2019-06-01 18:40:18 +00:00
}
fn doDiv(self: *VM) !void {
var b = try self.popNum();
var a = try self.popNum();
try self.push(values.NumberVal(a / b));
}
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();
2019-06-01 18:40:18 +00:00
}
fn run(self: *VM) !void {
2019-06-01 17:55:11 +00:00
while (true) {
2019-06-01 18:01:39 +00:00
if (self.debug_flag) {
2019-06-01 18:23:23 +00:00
try self.debugStack();
2019-06-01 18:01:39 +00:00
_ = try self.chk.disassembleInstruction(self.stdout, self.ip);
}
2019-06-01 17:55:11 +00:00
var instruction = self.readByte();
switch (instruction) {
chunk.OpCode.Constant => blk: {
var constant = self.readConst();
try self.push(constant);
2019-06-01 17:55:11 +00:00
break :blk;
},
chunk.OpCode.ConstantLong => blk: {
var constant = self.readConstLong();
try self.push(constant);
2019-06-01 17:55:11 +00:00
break :blk;
},
2019-06-01 18:23:23 +00:00
2019-06-01 17:55:11 +00:00
chunk.OpCode.Return => blk: {
2019-06-01 18:23:23 +00:00
try value.printValue(self.stdout, self.pop());
try self.stdout.print("\n");
2019-06-01 17:55:11 +00:00
return InterpretResult.Ok;
},
2019-06-01 18:27:19 +00:00
chunk.OpCode.Nil => try self.push(values.NilVal()),
chunk.OpCode.True => try self.push(values.BoolVal(true)),
chunk.OpCode.False => try self.push(values.BoolVal(false)),
chunk.OpCode.Add => try self.doAdd(),
chunk.OpCode.Subtract => try self.doSub(),
chunk.OpCode.Multiply => try self.doMul(),
chunk.OpCode.Divide => try self.doDiv(),
2019-06-02 03:02:37 +00:00
chunk.OpCode.Not => blk: {
try self.push(values.BoolVal(isFalsey(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,
}
},
2019-06-01 17:55:11 +00:00
else => blk: {
std.debug.warn("Unknown instruction: {x}\n", instruction);
return InterpretResult.RuntimeError;
},
}
}
}
pub fn interpret(self: *VM) !void {
//self.ip = 0;
//self.debug("VM start\n");
//var res = try self.run();
//self.debug("VM end\n");
//return res;
2019-06-01 23:33:43 +00:00
var chk = try Chunk.init(self.allocator);
var cmpr = Compiler.init(
self.allocator,
&chk,
self.stdout,
self.src,
self.debug_flag,
);
2019-06-01 23:33:43 +00:00
if (!try cmpr.compile(&chk)) {
return InterpretResult.CompileError;
}
self.chk = &chk;
self.ip = 0;
return try self.run();
2019-06-01 17:55:11 +00:00
}
2019-06-01 18:23:23 +00:00
pub fn push(self: *VM, val: Value) !void {
if (self.stackTop > 0 and self.stackTop - 1 > self.stack.len) {
self.stack = try self.allocator.realloc(self.stack, self.stack.len + 1);
}
2019-06-01 18:23:23 +00:00
self.stack[self.stackTop] = val;
self.stackTop += 1;
}
pub fn pop(self: *VM) Value {
2019-06-01 18:23:23 +00:00
self.stackTop -= 1;
return self.stack[self.stackTop];
}
pub fn peek(self: *VM, distance: usize) Value {
return self.stack[self.stackTop - 1 - distance];
}
};