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); pub const STACK_MAX = 256; pub const InterpretResult = enum { Ok, CompileError, RuntimeError, }; pub const VM = struct { chk: *Chunk, ip: usize = 0, stack: [STACK_MAX]value.Value, stackTop: usize = 0, stdout: StdOut, debug_flag: bool, fn resetStack(self: *VM) void { self.stackTop = 0; } pub fn init(stdout: StdOut, chk: *Chunk, debug_flag: bool) VM { var self = VM{ .chk = chk, // TODO move this to a nil value or something. .stack = []value.Value{0} ** STACK_MAX, .stdout = stdout, .debug_flag = debug_flag, }; self.resetStack(); return self; } pub fn debug(self: *VM, comptime fmt: []const u8, args: ...) void { if (self.debug_flag) { std.debug.warn(fmt, args); } } 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]; } 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"); } fn run(self: *VM) !InterpretResult { while (true) { if (self.debug_flag) { try self.debugStack(); _ = try self.chk.disassembleInstruction(self.stdout, self.ip); } var instruction = self.readByte(); switch (instruction) { chunk.OpCode.Constant => blk: { var constant = self.readConst(); self.push(constant); break :blk; }, chunk.OpCode.ConstantLong => blk: { var constant = self.readConstLong(); self.push(constant); break :blk; }, chunk.OpCode.Return => blk: { try value.printValue(self.stdout, self.pop()); try self.stdout.print("\n"); return InterpretResult.Ok; }, chunk.OpCode.Negate => self.push(-self.pop()), else => blk: { std.debug.warn("Unknown instruction: {x}\n", instruction); return InterpretResult.RuntimeError; }, } } } pub fn interpret(self: *VM) !InterpretResult { self.ip = 0; self.debug("VM start\n"); var res = try self.run(); self.debug("VM end\n"); return res; } 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]; } };