jorts/src/chunk.zig

287 lines
8.7 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 {
Return: u8 = 0,
Constant: u8 = 1,
ConstantLong: u8 = 2,
Add: u8 = 3,
Subtract: u8 = 4,
Multiply: u8 = 5,
Divide: u8 = 6,
Negate: u8 = 7,
// basic type op codes
Nil: u8 = 8,
True: u8 = 9,
False: u8 = 10,
Not: u8 = 11,
// comparison op codes!
Equal: u8 = 12,
Greater: u8 = 13,
Less: u8 = 14,
Print: u8 = 15,
Pop: u8 = 16,
DefineGlobal: u8 = 17,
DefineGlobalLong: u8 = 18,
GetGlobal: u8 = 19,
GetGlobalLong: u8 = 20,
SetGlobal: u8 = 21,
SetGlobalLong: u8 = 22,
GetLocal: u8 = 23,
SetLocal: u8 = 24,
};
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;
}
fn byteInstruction(
stdout: var,
name: []const u8,
chunk: *Chunk,
index: usize,
) !usize {
var slot: u8 = chunk.code[index + 1];
try stdout.print("{} {}", name, slot);
return index + 2;
}
pub const ConstantIndexTag = enum {
Small,
Long,
};
pub const ConstantIndex = union(ConstantIndexTag) {
Small: u8,
Long: [3]u8,
};
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 writeConstantRaw(
self: *Chunk,
val: value.Value,
line: usize,
) !ConstantIndex {
try self.constants.write(val);
var constant_idx = self.constants.count - 1;
if (constant_idx < 256) {
var idx_small = @intCast(u8, constant_idx);
return ConstantIndex{ .Small = idx_small };
} 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);
return ConstantIndex{ .Long = [_]u8{ v3, v2, v1 } };
}
}
pub fn writeConstant(
self: *Chunk,
val: value.Value,
line: usize,
) !ConstantIndex {
var idx = try self.writeConstantRaw(val, line);
switch (idx) {
.Small => |idx_small| blk: {
try self.write(OpCode.Constant, line);
try self.write(idx_small, line);
break :blk;
},
.Long => |long_u8| blk: {
try self.write(OpCode.ConstantLong, line);
try self.write(long_u8[0], line);
try self.write(long_u8[1], line);
try self.write(long_u8[2], line);
},
else => unreachable,
}
return idx;
}
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 if (instruction == OpCode.Not) {
return try simpleInstruction(stdout, "OP_NOT", index);
} else if (instruction == OpCode.Equal) {
return try simpleInstruction(stdout, "OP_EQUAL", index);
} else if (instruction == OpCode.Greater) {
return try simpleInstruction(stdout, "OP_GREATER", index);
} else if (instruction == OpCode.Less) {
return try simpleInstruction(stdout, "OP_LESS", index);
} else if (instruction == OpCode.Print) {
return try simpleInstruction(stdout, "OP_PRINT", index);
} else if (instruction == OpCode.Pop) {
return try simpleInstruction(stdout, "OP_POP", index);
} else if (instruction == OpCode.DefineGlobal) {
return try constantInstruction(stdout, "OP_DEFGLOBAL", self, index);
} else if (instruction == OpCode.DefineGlobalLong) {
return try constantLongInstruction(stdout, "OP_DEFGLOBAL_LONG", self, index);
} else if (instruction == OpCode.GetGlobal) {
return try constantInstruction(stdout, "OP_GETGLOBAL", self, index);
} else if (instruction == OpCode.GetGlobalLong) {
return try constantLongInstruction(stdout, "OP_GETGLOBAL_LONG", self, index);
} else if (instruction == OpCode.SetGlobal) {
return try constantInstruction(stdout, "OP_SETGLOBAL", self, index);
} else if (instruction == OpCode.SetGlobalLong) {
return try constantLongInstruction(stdout, "OP_SETGLOBAL_LONG", self, index);
} else if (instruction == OpCode.GetLocal) {
return try byteInstruction(stdout, "OP_GETLOCAL", self, index);
} else if (instruction == OpCode.SetLocal) {
return try byteInstruction(stdout, "OP_GETLOCAL", 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);
}
}
};