jorts/src/chunk.zig

166 lines
4.5 KiB
Zig

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 Constant: u8 = 0,
pub ConstantLong: u8 = 1,
pub Return: u8 = 2,
};
pub const OpCode = AllOpcodes{};
fn simpleInstruction(
stdout: var,
comptime name: []const u8,
offset: usize,
) !usize {
try stdout.print("{}\n", name);
return offset + 1;
}
fn constantInstruction(
stdout: var,
comptime name: []const u8,
chunk: *Chunk,
offset: usize,
) !usize {
// get the constant's index in constants slice
var idx = chunk.code[offset + 1];
try stdout.print("\t{}\t{} '", name, idx);
try value.printValue(stdout, chunk.constants.values[idx]);
try stdout.print("'\n");
return offset + 2;
}
fn constantLongInstruction(
stdout: var,
comptime name: []const u8,
chunk: *Chunk,
offset: usize,
) !usize {
// get the constant's index in constants slice
var v0: u8 = chunk.code[offset + 3];
var v1: u8 = chunk.code[offset + 2];
var v2: u8 = chunk.code[offset + 1];
// TODO: this does not work. just decreased first term
// to fix a compile error.
// we should also move the actual printing into its own
// function too since constantInstruction and
// constantLongInstruction share code.
var idx: u24 = (v2 << 4) | (v1 << 7) | v0;
try stdout.print("\t{}\t{} '", name, idx);
try value.printValue(stdout, chunk.constants.values[idx]);
try stdout.print("'\n");
return offset + 2;
}
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 {
// TODO: convert the usize to u24, and from
// u24, split it into three u8's.
// also convert from u8 back to u24.
// i know that we can do from two u8's to go to a u16
// with (v1 << 7) | v0.
try self.write(0, 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 {
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);
}
}
};