const std = @import("std"); const value = @import("value.zig"); const Allocator = std.mem.Allocator; // hack. ugly hack. zig has compiler crash. const AllOpcodes = struct { pub Return: u8 = 0, pub Constant: u8 = 1, pub ConstantLong: u8 = 2, pub Add: u8 = 3, pub Subtract: u8 = 4, pub Multiply: u8 = 5, pub Divide: u8 = 6, pub Negate: u8 = 7, // basic type op codes pub Nil: u8 = 8, pub True: u8 = 9, pub False: u8 = 10, pub Not: u8 = 11, }; pub const OpCode = AllOpcodes{}; fn simpleInstruction( stdout: var, comptime name: []const u8, index: usize, ) !usize { try stdout.print("{}\n", name); return index + 1; } fn constantInstruction( stdout: var, comptime name: []const u8, chunk: *Chunk, index: usize, ) !usize { // get the constant's index in constants slice var idx = chunk.code[index + 1]; try stdout.print("\t{}\t{} '", name, idx); try value.printValue(stdout, chunk.constants.values[idx]); try stdout.print("'\n"); return index + 2; } fn constantLongInstruction( stdout: var, comptime name: []const u8, chunk: *Chunk, offset: usize, ) !usize { // constantLong uses three u8's that encode a u24 as the // contants' index. var v3: u8 = chunk.code[offset + 1]; var v2: u8 = chunk.code[offset + 2]; var v1: u8 = chunk.code[offset + 3]; var idx: u24 = (@intCast(u24, v3) << 16) | (@intCast(u24, v2) << 8) | v1; try stdout.print("\t{}\t{} '", name, idx); try value.printValue(stdout, chunk.constants.values[idx]); try stdout.print("'\n"); return offset + 4; } pub const Chunk = struct { count: usize, lines: []usize, code: []u8, allocator: *Allocator, constants: value.ValueList, pub fn init(allocator: *Allocator) !Chunk { return Chunk{ .count = 0, .allocator = allocator, .code = try allocator.alloc(u8, 0), .lines = try allocator.alloc(usize, 0), .constants = try value.ValueList.init(allocator), }; } pub fn write(self: *Chunk, byte: u8, line: usize) !void { if (self.code.len < self.count + 1) { self.code = try self.allocator.realloc( self.code, self.count + 1, ); self.lines = try self.allocator.realloc( self.lines, self.count + 1, ); } self.code[self.count] = byte; self.lines[self.count] = line; self.count += 1; } pub fn addConstant(self: *Chunk, val: value.Value) !u8 { try self.constants.write(val); return self.constants.count - 1; } pub fn writeConstant(self: *Chunk, val: value.Value, line: usize) !void { try self.constants.write(val); var constant_idx = self.constants.count - 1; if (constant_idx < 256) { try self.write(OpCode.Constant, line); try self.write(@intCast(u8, constant_idx), line); } else { var idx_u24: u24 = @intCast(u24, constant_idx); const mask = @intCast(u24, 0xff); const v1: u8 = @intCast(u8, idx_u24 & mask); const v2: u8 = @intCast(u8, (idx_u24 >> 8) & mask); const v3: u8 = @intCast(u8, (idx_u24 >> 16) & mask); try self.write(OpCode.ConstantLong, line); try self.write(v3, line); try self.write(v2, line); try self.write(v1, line); } } pub fn disassembleInstruction( self: *Chunk, stdout: var, index: usize, ) !usize { try stdout.print("{} ", index); if (index > 0 and self.lines[index] == self.lines[index - 1]) { try stdout.print(" | "); } else { try stdout.print("{} ", self.lines[index]); } var instruction = self.code[index]; if (instruction == OpCode.Return) { return try simpleInstruction(stdout, "OP_RETURN", index); } else if (instruction == OpCode.Constant) { return try constantInstruction(stdout, "OP_CONSTANT", self, index); } else if (instruction == OpCode.ConstantLong) { return try constantLongInstruction( stdout, "OP_CONSTANT_LONG", self, index, ); } else if (instruction == OpCode.Negate) { return try simpleInstruction(stdout, "OP_NEGATE", index); } else if (instruction == OpCode.Add) { return try simpleInstruction(stdout, "OP_ADD", index); } else if (instruction == OpCode.Subtract) { return try simpleInstruction(stdout, "OP_SUBTRACT", index); } else if (instruction == OpCode.Multiply) { return try simpleInstruction(stdout, "OP_MULTIPLY", index); } else if (instruction == OpCode.Divide) { return try simpleInstruction(stdout, "OP_DIVIDE", index); } else if (instruction == OpCode.Nil) { return try simpleInstruction(stdout, "OP_NIL", index); } else if (instruction == OpCode.True) { return try simpleInstruction(stdout, "OP_TRUE", index); } else if (instruction == OpCode.False) { return try simpleInstruction(stdout, "OP_FALSE", index); } else { try stdout.print("Unknown opcode: {}\n", instruction); return index + 1; } } pub fn disassemble(self: *Chunk, stdout: var, name: []const u8) !void { try stdout.print("== {} ==\n", name); var i: usize = 0; while (i < self.count) { i = try self.disassembleInstruction(stdout, i); } } };