2019-06-01 17:55:11 +00:00
|
|
|
const std = @import("std");
|
|
|
|
const chunk = @import("chunk.zig");
|
|
|
|
const value = @import("value.zig");
|
|
|
|
|
|
|
|
const Chunk = chunk.Chunk;
|
|
|
|
const StdOut = *std.io.OutStream(std.fs.File.WriteError);
|
|
|
|
|
2019-06-01 18:23:23 +00:00
|
|
|
pub const STACK_MAX = 256;
|
|
|
|
|
2019-06-01 17:55:11 +00:00
|
|
|
pub const InterpretResult = enum {
|
|
|
|
Ok,
|
|
|
|
CompileError,
|
|
|
|
RuntimeError,
|
|
|
|
};
|
2019-06-01 04:20:06 +00:00
|
|
|
|
|
|
|
pub const VM = struct {
|
2019-06-01 17:55:11 +00:00
|
|
|
chk: *Chunk,
|
2019-06-01 18:23:23 +00:00
|
|
|
ip: usize = 0,
|
|
|
|
|
|
|
|
stack: [STACK_MAX]value.Value,
|
|
|
|
stackTop: usize = 0,
|
|
|
|
|
2019-06-01 17:55:11 +00:00
|
|
|
stdout: StdOut,
|
2019-06-01 18:01:39 +00:00
|
|
|
debug_flag: bool,
|
2019-06-01 17:55:11 +00:00
|
|
|
|
2019-06-01 18:23:23 +00:00
|
|
|
fn resetStack(self: *VM) void {
|
|
|
|
self.stackTop = 0;
|
|
|
|
}
|
|
|
|
|
2019-06-01 18:01:39 +00:00
|
|
|
pub fn init(stdout: StdOut, chk: *Chunk, debug_flag: bool) VM {
|
2019-06-01 18:23:23 +00:00
|
|
|
var self = VM{
|
2019-06-01 17:55:11 +00:00
|
|
|
.chk = chk,
|
2019-06-01 18:23:23 +00:00
|
|
|
|
|
|
|
// TODO move this to a nil value or something.
|
|
|
|
.stack = []value.Value{0} ** STACK_MAX,
|
|
|
|
.stdout = stdout,
|
2019-06-01 18:01:39 +00:00
|
|
|
.debug_flag = debug_flag,
|
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.Value {
|
|
|
|
return self.chk.constants.values[self.readByte()];
|
|
|
|
}
|
|
|
|
|
|
|
|
fn readConstLong(self: *VM) value.Value {
|
|
|
|
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 17:55:11 +00:00
|
|
|
fn run(self: *VM) !InterpretResult {
|
|
|
|
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();
|
2019-06-01 18:23:23 +00:00
|
|
|
self.push(constant);
|
2019-06-01 17:55:11 +00:00
|
|
|
break :blk;
|
|
|
|
},
|
|
|
|
chunk.OpCode.ConstantLong => blk: {
|
|
|
|
var constant = self.readConstLong();
|
2019-06-01 18:23:23 +00:00
|
|
|
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.Negate => self.push(-self.pop()),
|
2019-06-01 17:55:11 +00:00
|
|
|
else => blk: {
|
|
|
|
std.debug.warn("Unknown instruction: {x}\n", instruction);
|
|
|
|
return InterpretResult.RuntimeError;
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-06-01 04:20:06 +00:00
|
|
|
|
2019-06-01 17:55:11 +00:00
|
|
|
pub fn interpret(self: *VM) !InterpretResult {
|
|
|
|
self.ip = 0;
|
2019-06-01 18:01:39 +00:00
|
|
|
|
|
|
|
self.debug("VM start\n");
|
|
|
|
var res = try self.run();
|
|
|
|
self.debug("VM end\n");
|
|
|
|
return res;
|
2019-06-01 17:55:11 +00:00
|
|
|
}
|
2019-06-01 18:23:23 +00:00
|
|
|
|
|
|
|
pub fn push(self: *VM, val: value.Value) void {
|
|
|
|
self.stack[self.stackTop] = val;
|
|
|
|
self.stackTop += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn pop(self: *VM) value.Value {
|
|
|
|
self.stackTop -= 1;
|
|
|
|
return self.stack[self.stackTop];
|
|
|
|
}
|
2019-06-01 04:20:06 +00:00
|
|
|
};
|