2019-06-01 04:20:06 +00:00
|
|
|
const std = @import("std");
|
|
|
|
|
2019-06-01 04:46:01 +00:00
|
|
|
const value = @import("value.zig");
|
2019-06-01 04:20:06 +00:00
|
|
|
const Allocator = std.mem.Allocator;
|
|
|
|
|
|
|
|
// hack. ugly hack. zig has compiler crash.
|
|
|
|
const AllOpcodes = struct {
|
2019-06-01 18:40:18 +00:00
|
|
|
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,
|
2019-06-02 02:44:59 +00:00
|
|
|
|
|
|
|
// basic type op codes
|
|
|
|
pub Nil: u8 = 8,
|
|
|
|
pub True: u8 = 9,
|
|
|
|
pub False: u8 = 10,
|
2019-06-02 03:02:37 +00:00
|
|
|
|
|
|
|
pub Not: u8 = 11,
|
2019-06-02 03:16:33 +00:00
|
|
|
|
|
|
|
// comparison op codes!
|
|
|
|
pub Equal: u8 = 12,
|
|
|
|
pub Greater: u8 = 13,
|
|
|
|
pub Less: u8 = 14,
|
2019-06-02 20:28:54 +00:00
|
|
|
|
|
|
|
pub Print: u8 = 15,
|
2019-06-01 04:20:06 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
pub const OpCode = AllOpcodes{};
|
|
|
|
|
|
|
|
fn simpleInstruction(
|
|
|
|
stdout: var,
|
|
|
|
comptime name: []const u8,
|
2019-06-01 17:18:44 +00:00
|
|
|
index: usize,
|
2019-06-01 04:20:06 +00:00
|
|
|
) !usize {
|
|
|
|
try stdout.print("{}\n", name);
|
2019-06-01 17:18:44 +00:00
|
|
|
return index + 1;
|
2019-06-01 04:20:06 +00:00
|
|
|
}
|
|
|
|
|
2019-06-01 04:46:01 +00:00
|
|
|
fn constantInstruction(
|
|
|
|
stdout: var,
|
|
|
|
comptime name: []const u8,
|
|
|
|
chunk: *Chunk,
|
2019-06-01 17:18:44 +00:00
|
|
|
index: usize,
|
2019-06-01 04:46:01 +00:00
|
|
|
) !usize {
|
|
|
|
// get the constant's index in constants slice
|
2019-06-01 17:18:44 +00:00
|
|
|
var idx = chunk.code[index + 1];
|
2019-06-01 04:46:01 +00:00
|
|
|
|
|
|
|
try stdout.print("\t{}\t{} '", name, idx);
|
|
|
|
try value.printValue(stdout, chunk.constants.values[idx]);
|
|
|
|
try stdout.print("'\n");
|
|
|
|
|
2019-06-01 17:18:44 +00:00
|
|
|
return index + 2;
|
2019-06-01 04:46:01 +00:00
|
|
|
}
|
|
|
|
|
2019-06-01 05:06:23 +00:00
|
|
|
fn constantLongInstruction(
|
|
|
|
stdout: var,
|
|
|
|
comptime name: []const u8,
|
|
|
|
chunk: *Chunk,
|
|
|
|
offset: usize,
|
|
|
|
) !usize {
|
2019-06-01 17:18:44 +00:00
|
|
|
// 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];
|
2019-06-01 05:06:23 +00:00
|
|
|
|
2019-06-01 17:18:44 +00:00
|
|
|
var idx: u24 = (@intCast(u24, v3) << 16) | (@intCast(u24, v2) << 8) | v1;
|
2019-06-01 05:06:23 +00:00
|
|
|
|
|
|
|
try stdout.print("\t{}\t{} '", name, idx);
|
|
|
|
try value.printValue(stdout, chunk.constants.values[idx]);
|
|
|
|
try stdout.print("'\n");
|
|
|
|
|
2019-06-01 17:18:44 +00:00
|
|
|
return offset + 4;
|
2019-06-01 05:06:23 +00:00
|
|
|
}
|
|
|
|
|
2019-06-01 04:20:06 +00:00
|
|
|
pub const Chunk = struct {
|
|
|
|
count: usize,
|
2019-06-01 04:46:01 +00:00
|
|
|
lines: []usize,
|
2019-06-01 04:20:06 +00:00
|
|
|
code: []u8,
|
2019-06-01 04:46:01 +00:00
|
|
|
|
2019-06-01 04:20:06 +00:00
|
|
|
allocator: *Allocator,
|
2019-06-01 04:46:01 +00:00
|
|
|
constants: value.ValueList,
|
2019-06-01 04:20:06 +00:00
|
|
|
|
|
|
|
pub fn init(allocator: *Allocator) !Chunk {
|
|
|
|
return Chunk{
|
|
|
|
.count = 0,
|
|
|
|
.allocator = allocator,
|
|
|
|
.code = try allocator.alloc(u8, 0),
|
2019-06-01 04:46:01 +00:00
|
|
|
.lines = try allocator.alloc(usize, 0),
|
|
|
|
.constants = try value.ValueList.init(allocator),
|
2019-06-01 04:20:06 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-06-01 04:46:01 +00:00
|
|
|
pub fn write(self: *Chunk, byte: u8, line: usize) !void {
|
2019-06-01 04:20:06 +00:00
|
|
|
if (self.code.len < self.count + 1) {
|
|
|
|
self.code = try self.allocator.realloc(
|
|
|
|
self.code,
|
|
|
|
self.count + 1,
|
|
|
|
);
|
2019-06-01 04:46:01 +00:00
|
|
|
|
|
|
|
self.lines = try self.allocator.realloc(
|
|
|
|
self.lines,
|
|
|
|
self.count + 1,
|
|
|
|
);
|
2019-06-01 04:20:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
self.code[self.count] = byte;
|
2019-06-01 04:46:01 +00:00
|
|
|
self.lines[self.count] = line;
|
2019-06-01 04:20:06 +00:00
|
|
|
self.count += 1;
|
|
|
|
}
|
|
|
|
|
2019-06-01 04:46:01 +00:00
|
|
|
pub fn addConstant(self: *Chunk, val: value.Value) !u8 {
|
|
|
|
try self.constants.write(val);
|
|
|
|
return self.constants.count - 1;
|
|
|
|
}
|
|
|
|
|
2019-06-01 05:06:23 +00:00
|
|
|
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 {
|
2019-06-01 17:18:44 +00:00
|
|
|
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);
|
2019-06-01 05:06:23 +00:00
|
|
|
|
2019-06-01 17:18:44 +00:00
|
|
|
try self.write(OpCode.ConstantLong, line);
|
|
|
|
try self.write(v3, line);
|
|
|
|
try self.write(v2, line);
|
|
|
|
try self.write(v1, line);
|
2019-06-01 05:06:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-01 04:20:06 +00:00
|
|
|
pub fn disassembleInstruction(
|
|
|
|
self: *Chunk,
|
|
|
|
stdout: var,
|
|
|
|
index: usize,
|
|
|
|
) !usize {
|
|
|
|
try stdout.print("{} ", index);
|
|
|
|
|
2019-06-01 04:46:01 +00:00
|
|
|
if (index > 0 and self.lines[index] == self.lines[index - 1]) {
|
|
|
|
try stdout.print(" | ");
|
|
|
|
} else {
|
|
|
|
try stdout.print("{} ", self.lines[index]);
|
|
|
|
}
|
|
|
|
|
2019-06-01 04:20:06 +00:00
|
|
|
var instruction = self.code[index];
|
|
|
|
|
2019-06-01 04:46:01 +00:00
|
|
|
if (instruction == OpCode.Return) {
|
2019-06-01 04:20:06 +00:00
|
|
|
return try simpleInstruction(stdout, "OP_RETURN", index);
|
2019-06-01 04:46:01 +00:00
|
|
|
} else if (instruction == OpCode.Constant) {
|
|
|
|
return try constantInstruction(stdout, "OP_CONSTANT", self, index);
|
2019-06-01 05:06:23 +00:00
|
|
|
} else if (instruction == OpCode.ConstantLong) {
|
|
|
|
return try constantLongInstruction(
|
|
|
|
stdout,
|
|
|
|
"OP_CONSTANT_LONG",
|
|
|
|
self,
|
|
|
|
index,
|
|
|
|
);
|
2019-06-01 18:27:19 +00:00
|
|
|
} else if (instruction == OpCode.Negate) {
|
|
|
|
return try simpleInstruction(stdout, "OP_NEGATE", index);
|
2019-06-01 18:40:18 +00:00
|
|
|
} 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);
|
2019-06-02 02:44:59 +00:00
|
|
|
} 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);
|
2019-06-02 03:03:54 +00:00
|
|
|
} else if (instruction == OpCode.Not) {
|
|
|
|
return try simpleInstruction(stdout, "OP_NOT", index);
|
2019-06-02 03:23:50 +00:00
|
|
|
} 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);
|
2019-06-02 20:28:54 +00:00
|
|
|
} else if (instruction == OpCode.Print) {
|
|
|
|
return try simpleInstruction(stdout, "OP_PRINT", index);
|
2019-06-01 04:20:06 +00:00
|
|
|
} 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;
|
2019-06-01 04:46:01 +00:00
|
|
|
while (i < self.count) {
|
2019-06-01 04:20:06 +00:00
|
|
|
i = try self.disassembleInstruction(stdout, i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|