feat: infrastructure to support qualified lookups
This commit is contained in:
parent
e8ad1cd5b5
commit
440ec68481
6 changed files with 1118 additions and 575 deletions
|
|
@ -6,41 +6,61 @@ const assert = std.debug.assert;
|
|||
const Dumper = @This();
|
||||
|
||||
story: *const Story,
|
||||
writer: *std.Io.Writer,
|
||||
indent_level: usize = 0,
|
||||
|
||||
fn dumpSimpleInst(d: Dumper, offset: usize, op: Opcode) !usize {
|
||||
try d.writer.print("{s}\n", .{@tagName(op)});
|
||||
fn dumpSimpleInst(_: *Dumper, w: *std.Io.Writer, offset: usize, op: Opcode) !usize {
|
||||
try w.print("{s}\n", .{@tagName(op)});
|
||||
return offset + 1;
|
||||
}
|
||||
|
||||
fn dumpByteInst(d: Dumper, context: *const Object.ContentPath, offset: usize, op: Opcode) !usize {
|
||||
const arg = context.bytes[offset + 1];
|
||||
fn dumpByteInst(
|
||||
self: *Dumper,
|
||||
w: *std.Io.Writer,
|
||||
knot: *const Object.Knot,
|
||||
offset: usize,
|
||||
op: Opcode,
|
||||
) !usize {
|
||||
const code = knot.code;
|
||||
assert(code.bytecode.len > offset + 1);
|
||||
const arg = code.bytecode[offset + 1];
|
||||
if (op == .load_const) {
|
||||
const constant_index = context.const_pool[arg];
|
||||
const global_constant = d.story.constants_pool.items[constant_index];
|
||||
try d.writer.print("{s} {d} (", .{ @tagName(op), arg });
|
||||
try d.dumpObject(global_constant);
|
||||
try d.writer.print(")\n", .{});
|
||||
try w.print("{s} {d}", .{ @tagName(op), arg });
|
||||
try w.writeAll(" (");
|
||||
if (code.constants.len > arg) {
|
||||
const constant_index = code.constants[arg];
|
||||
if (self.story.constants_pool.items.len > constant_index) {
|
||||
const global_constant = self.story.constants_pool.items[constant_index];
|
||||
try self.dumpObject(w, global_constant);
|
||||
} else {
|
||||
try w.writeAll("invalid!");
|
||||
}
|
||||
} else {
|
||||
try w.writeAll("invalid!");
|
||||
}
|
||||
try w.writeAll(")\n");
|
||||
} else {
|
||||
try d.writer.print("{s} {x}\n", .{ @tagName(op), arg });
|
||||
try w.print("{s} {x}\n", .{ @tagName(op), arg });
|
||||
}
|
||||
return offset + 2;
|
||||
}
|
||||
|
||||
fn dumpGlobalInst(
|
||||
d: Dumper,
|
||||
context: *const Object.ContentPath,
|
||||
self: *Dumper,
|
||||
w: *std.Io.Writer,
|
||||
knot: *const Object.Knot,
|
||||
offset: usize,
|
||||
op: Opcode,
|
||||
) !usize {
|
||||
const arg = context.bytes[offset + 1];
|
||||
const constant_index = context.const_pool[arg];
|
||||
const global_constant = d.story.constants_pool.items[constant_index];
|
||||
const code = knot.code;
|
||||
assert(code.bytecode.len > offset + 1);
|
||||
const arg = code.bytecode[offset + 1];
|
||||
assert(code.constants.len > arg);
|
||||
const constant_index = code.constants[arg];
|
||||
const global_constant = self.story.constants_pool.items[constant_index];
|
||||
assert(global_constant.tag == .string);
|
||||
|
||||
const global_name: *Object.String = @ptrCast(global_constant);
|
||||
const name_bytes = global_name.bytes[0..global_name.length];
|
||||
try d.writer.print("{s} {x} '{s}'\n", .{ @tagName(op), arg, name_bytes });
|
||||
try w.print("{s} {x} '{s}'\n", .{ @tagName(op), arg, name_bytes });
|
||||
return offset + 2;
|
||||
}
|
||||
|
||||
|
|
@ -50,23 +70,26 @@ const Jump = enum {
|
|||
};
|
||||
|
||||
fn dumpJumpInst(
|
||||
d: Dumper,
|
||||
context: *const Object.ContentPath,
|
||||
_: *Dumper,
|
||||
w: *std.Io.Writer,
|
||||
knot: *const Object.Knot,
|
||||
offset: usize,
|
||||
op: Opcode,
|
||||
mode: Jump,
|
||||
) !usize {
|
||||
var jump: u16 = @as(u16, context.bytes[offset + 1]) << 8;
|
||||
jump |= context.bytes[offset + 2];
|
||||
const code = knot.code;
|
||||
assert(code.bytecode.len > offset + 2);
|
||||
var jump: u16 = @as(u16, code.bytecode[offset + 1]) << 8;
|
||||
jump |= code.bytecode[offset + 2];
|
||||
|
||||
switch (mode) {
|
||||
.relative => try d.writer.print("{s} 0x{x:0>4} (0x{x:0>4} -> 0x{x:0>4})\n", .{
|
||||
.relative => try w.print("{s} 0x{x:0>4} (0x{x:0>4} -> 0x{x:0>4})\n", .{
|
||||
@tagName(op),
|
||||
jump,
|
||||
offset,
|
||||
offset + 3 + jump,
|
||||
}),
|
||||
.absolute => try d.writer.print("{s} 0x{x:0>4}\n", .{
|
||||
.absolute => try w.print("{s} 0x{x:0>4}\n", .{
|
||||
@tagName(op),
|
||||
jump,
|
||||
}),
|
||||
|
|
@ -75,130 +98,204 @@ fn dumpJumpInst(
|
|||
}
|
||||
|
||||
pub fn dumpInst(
|
||||
d: Dumper,
|
||||
path: *const Object.ContentPath,
|
||||
self: *Dumper,
|
||||
w: *std.Io.Writer,
|
||||
knot: *const Object.Knot,
|
||||
offset: usize,
|
||||
should_prefix: bool,
|
||||
) !usize {
|
||||
const name_object = path.name;
|
||||
const name_object = knot.name;
|
||||
const name_bytes = name_object.bytes[0..name_object.length];
|
||||
const op: Opcode = @enumFromInt(path.bytes[offset]);
|
||||
const op: Opcode = @enumFromInt(knot.code.bytecode[offset]);
|
||||
|
||||
try w.splatByteAll(' ', self.indent_level);
|
||||
|
||||
if (should_prefix) {
|
||||
try d.writer.print("<{s}>:0x{x:0>4} | ", .{ name_bytes, offset });
|
||||
try w.print("<{s}>:0x{x:0>4} | ", .{ name_bytes, offset });
|
||||
} else {
|
||||
try d.writer.print("0x{x:0>4} | ", .{offset});
|
||||
try w.print("0x{x:0>4} | ", .{offset});
|
||||
}
|
||||
switch (op) {
|
||||
.exit => return d.dumpSimpleInst(offset, op),
|
||||
.ret => return d.dumpSimpleInst(offset, op),
|
||||
.pop => return d.dumpSimpleInst(offset, op),
|
||||
.true => return d.dumpSimpleInst(offset, op),
|
||||
.false => return d.dumpSimpleInst(offset, op),
|
||||
.add => return d.dumpSimpleInst(offset, op),
|
||||
.sub => return d.dumpSimpleInst(offset, op),
|
||||
.mul => return d.dumpSimpleInst(offset, op),
|
||||
.div => return d.dumpSimpleInst(offset, op),
|
||||
.mod => return d.dumpSimpleInst(offset, op),
|
||||
.neg => return d.dumpSimpleInst(offset, op),
|
||||
.not => return d.dumpSimpleInst(offset, op),
|
||||
.cmp_eq => return d.dumpSimpleInst(offset, op),
|
||||
.cmp_lt => return d.dumpSimpleInst(offset, op),
|
||||
.cmp_lte => return d.dumpSimpleInst(offset, op),
|
||||
.cmp_gt => return d.dumpSimpleInst(offset, op),
|
||||
.cmp_gte => return d.dumpSimpleInst(offset, op),
|
||||
.load_const => return d.dumpByteInst(path, offset, op),
|
||||
.load => return d.dumpByteInst(path, offset, op),
|
||||
.store => return d.dumpByteInst(path, offset, op),
|
||||
.load_global => return d.dumpGlobalInst(path, offset, op),
|
||||
.store_global => return d.dumpGlobalInst(path, offset, op),
|
||||
.call => return d.dumpByteInst(path, offset, op),
|
||||
.divert => return d.dumpByteInst(path, offset, op),
|
||||
.jmp => return d.dumpJumpInst(path, offset, op, .relative),
|
||||
.jmp_t => return d.dumpJumpInst(path, offset, op, .relative),
|
||||
.jmp_f => return d.dumpJumpInst(path, offset, op, .relative),
|
||||
.stream_push => return d.dumpSimpleInst(offset, op),
|
||||
.stream_flush => return d.dumpSimpleInst(offset, op),
|
||||
.stream_line => return d.dumpSimpleInst(offset, op),
|
||||
.stream_glue => return d.dumpSimpleInst(offset, op),
|
||||
.br_push => return d.dumpJumpInst(path, offset, op, .absolute),
|
||||
.br_table => return d.dumpSimpleInst(offset, op),
|
||||
.br_dispatch => return d.dumpSimpleInst(offset, op),
|
||||
.br_select_index => return d.dumpSimpleInst(offset, op),
|
||||
.exit => return self.dumpSimpleInst(w, offset, op),
|
||||
.ret => return self.dumpSimpleInst(w, offset, op),
|
||||
.pop => return self.dumpSimpleInst(w, offset, op),
|
||||
.true => return self.dumpSimpleInst(w, offset, op),
|
||||
.false => return self.dumpSimpleInst(w, offset, op),
|
||||
.add => return self.dumpSimpleInst(w, offset, op),
|
||||
.sub => return self.dumpSimpleInst(w, offset, op),
|
||||
.mul => return self.dumpSimpleInst(w, offset, op),
|
||||
.div => return self.dumpSimpleInst(w, offset, op),
|
||||
.mod => return self.dumpSimpleInst(w, offset, op),
|
||||
.neg => return self.dumpSimpleInst(w, offset, op),
|
||||
.not => return self.dumpSimpleInst(w, offset, op),
|
||||
.cmp_eq => return self.dumpSimpleInst(w, offset, op),
|
||||
.cmp_lt => return self.dumpSimpleInst(w, offset, op),
|
||||
.cmp_lte => return self.dumpSimpleInst(w, offset, op),
|
||||
.cmp_gt => return self.dumpSimpleInst(w, offset, op),
|
||||
.cmp_gte => return self.dumpSimpleInst(w, offset, op),
|
||||
.load_const => return self.dumpByteInst(w, knot, offset, op),
|
||||
.load => return self.dumpByteInst(w, knot, offset, op),
|
||||
.store => return self.dumpByteInst(w, knot, offset, op),
|
||||
.load_global => return self.dumpGlobalInst(w, knot, offset, op),
|
||||
.store_global => return self.dumpGlobalInst(w, knot, offset, op),
|
||||
.call => return self.dumpByteInst(w, knot, offset, op),
|
||||
.divert => return self.dumpByteInst(w, knot, offset, op),
|
||||
.jmp => return self.dumpJumpInst(w, knot, offset, op, .relative),
|
||||
.jmp_t => return self.dumpJumpInst(w, knot, offset, op, .relative),
|
||||
.jmp_f => return self.dumpJumpInst(w, knot, offset, op, .relative),
|
||||
.load_attr => return self.dumpGlobalInst(w, knot, offset, op),
|
||||
.store_attr => return self.dumpGlobalInst(w, knot, offset, op),
|
||||
.stream_push => return self.dumpSimpleInst(w, offset, op),
|
||||
.stream_flush => return self.dumpSimpleInst(w, offset, op),
|
||||
.stream_line => return self.dumpSimpleInst(w, offset, op),
|
||||
.stream_glue => return self.dumpSimpleInst(w, offset, op),
|
||||
.br_push => return self.dumpJumpInst(w, knot, offset, op, .absolute),
|
||||
.br_table => return self.dumpSimpleInst(w, offset, op),
|
||||
.br_dispatch => return self.dumpSimpleInst(w, offset, op),
|
||||
.br_select_index => return self.dumpSimpleInst(w, offset, op),
|
||||
else => |code| {
|
||||
try d.writer.print("Unknown opcode 0x{x:0>4}\n", .{@intFromEnum(code)});
|
||||
try w.print("Unknown opcode 0x{x:0>4}\n", .{@intFromEnum(code)});
|
||||
return offset + 1;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dump(d: Dumper, path: *const Object.ContentPath) !void {
|
||||
const name_object = path.name;
|
||||
fn dumpKnotConstants(w: *std.Io.Writer, knot: *const Object.Knot) !void {
|
||||
try w.writeAll("[");
|
||||
for (knot.code.constants) |index| {
|
||||
try w.print("{d},", .{index});
|
||||
}
|
||||
try w.writeAll("]");
|
||||
try w.flush();
|
||||
}
|
||||
|
||||
fn dumpKnotChildren(w: *std.Io.Writer, knot: *const Object.Knot) !void {
|
||||
try w.writeAll("[");
|
||||
var stitch_iter = knot.members.iterator();
|
||||
while (stitch_iter.next()) |entry| {
|
||||
try w.print("Stitch: \"{s}\",", .{entry.key_ptr.*});
|
||||
}
|
||||
try w.writeAll("]");
|
||||
try w.flush();
|
||||
}
|
||||
|
||||
fn dumpKnotBytecode(self: *Dumper, w: *std.Io.Writer, knot: *const Object.Knot) !void {
|
||||
var index: usize = 0;
|
||||
while (index < knot.code.bytecode.len) {
|
||||
index = try self.dumpInst(w, knot, index, false);
|
||||
try w.flush();
|
||||
}
|
||||
return w.flush();
|
||||
}
|
||||
|
||||
pub fn dumpKnot(self: *Dumper, w: *std.Io.Writer, knot: *const Object.Knot) !void {
|
||||
const name_object = knot.name;
|
||||
const name_bytes = name_object.bytes[0..name_object.length];
|
||||
|
||||
try d.writer.print("=== {s}(args: {d}, constants: {d}, locals: {d}) ===\n", .{
|
||||
name_bytes,
|
||||
path.arity,
|
||||
path.const_pool.len,
|
||||
path.locals_count,
|
||||
});
|
||||
try w.splatByteAll(' ', self.indent_level);
|
||||
try w.print("Name: \"{s}\"\n", .{name_bytes});
|
||||
|
||||
var index: usize = 0;
|
||||
while (index < path.bytes.len) {
|
||||
index = try d.dumpInst(path, index, false);
|
||||
try d.writer.flush();
|
||||
self.indent_level += 1;
|
||||
|
||||
try w.splatByteAll(' ', self.indent_level);
|
||||
try w.print("Arguments: {d}\n", .{knot.code.args_count});
|
||||
|
||||
try w.splatByteAll(' ', self.indent_level);
|
||||
try w.print("Locals: {d}\n", .{knot.code.locals_count});
|
||||
|
||||
try w.splatByteAll(' ', self.indent_level);
|
||||
try w.print("Stack Size: {d}\n", .{knot.code.stack_size});
|
||||
|
||||
try w.splatByteAll(' ', self.indent_level);
|
||||
try w.writeAll("Constants: ");
|
||||
try dumpKnotConstants(w, knot);
|
||||
try w.writeAll("\n");
|
||||
|
||||
try w.splatByteAll(' ', self.indent_level);
|
||||
try w.writeAll("Children: ");
|
||||
try dumpKnotChildren(w, knot);
|
||||
try w.writeAll("\n");
|
||||
|
||||
try w.splatByteAll(' ', self.indent_level);
|
||||
try w.writeAll("Bytecode: \n");
|
||||
self.indent_level += 2;
|
||||
try self.dumpKnotBytecode(w, knot);
|
||||
try w.writeAll("\n");
|
||||
self.indent_level -= 2;
|
||||
|
||||
var stitch_iter = knot.members.iterator();
|
||||
var count: usize = 0;
|
||||
while (stitch_iter.next()) |entry| : (count += 1) {
|
||||
try w.splatByteAll(' ', self.indent_level);
|
||||
try w.print("Stitch #{d}: \"{s}\"\n", .{ count, entry.key_ptr.* });
|
||||
|
||||
self.indent_level += 2;
|
||||
try w.splatByteAll(' ', self.indent_level);
|
||||
try w.print("Arguments: {d}\n", .{knot.code.args_count});
|
||||
|
||||
try w.splatByteAll(' ', self.indent_level);
|
||||
try w.print("Locals: {d}\n", .{knot.code.locals_count});
|
||||
|
||||
try w.splatByteAll(' ', self.indent_level);
|
||||
try w.print("Stack Size: {d}\n", .{knot.code.stack_size});
|
||||
|
||||
try w.splatByteAll(' ', self.indent_level);
|
||||
try w.writeAll("Constants: ");
|
||||
try dumpKnotConstants(w, knot);
|
||||
try w.writeAll("\n");
|
||||
|
||||
try w.splatByteAll(' ', self.indent_level);
|
||||
try w.writeAll("Children: ");
|
||||
try dumpKnotChildren(w, knot);
|
||||
try w.writeAll("\n");
|
||||
|
||||
try w.splatByteAll(' ', self.indent_level);
|
||||
try w.writeAll("Bytecode: \n");
|
||||
self.indent_level += 2;
|
||||
try self.dumpKnotBytecode(w, @ptrCast(entry.value_ptr.*));
|
||||
try w.writeAll("\n");
|
||||
self.indent_level -= 4;
|
||||
|
||||
try w.flush();
|
||||
}
|
||||
return d.writer.flush();
|
||||
self.indent_level = 2;
|
||||
return w.flush();
|
||||
}
|
||||
|
||||
fn getObjectType(object: *const Object) []const u8 {
|
||||
switch (object.tag) {
|
||||
.number => return "Number",
|
||||
.string => return "String",
|
||||
.content_path => return "ContentPath",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dumpObject(d: Dumper, object: *const Object) !void {
|
||||
const type_string = getObjectType(object);
|
||||
switch (object.tag) {
|
||||
pub fn dumpObject(_: Dumper, w: *std.Io.Writer, obj: *const Object) !void {
|
||||
const type_string = obj.tag.toStr();
|
||||
switch (obj.tag) {
|
||||
.number => {
|
||||
const typed_object: *const Object.Number = @ptrCast(object);
|
||||
const typed_object: *const Object.Number = @ptrCast(obj);
|
||||
switch (typed_object.data) {
|
||||
.boolean => |value| {
|
||||
try d.writer.print("<type={s} value={s}, address={*}>", .{
|
||||
type_string,
|
||||
if (value) "true" else "false",
|
||||
object,
|
||||
});
|
||||
},
|
||||
.floating => |value| {
|
||||
try d.writer.print("<type={s} value={d}, address={*}>", .{
|
||||
type_string,
|
||||
value,
|
||||
object,
|
||||
});
|
||||
},
|
||||
.integer => |value| {
|
||||
try d.writer.print("<type={s} value={d}, address={*}>", .{
|
||||
type_string,
|
||||
value,
|
||||
object,
|
||||
});
|
||||
},
|
||||
.boolean => |value| try w.print("<type={s} value={s}, address={*}>", .{
|
||||
type_string,
|
||||
if (value) "true" else "false",
|
||||
obj,
|
||||
}),
|
||||
.floating => |value| try w.print("<type={s} value={d}, address={*}>", .{
|
||||
type_string,
|
||||
value,
|
||||
obj,
|
||||
}),
|
||||
.integer => |value| try w.print("<type={s} value={d}, address={*}>", .{
|
||||
type_string,
|
||||
value,
|
||||
obj,
|
||||
}),
|
||||
}
|
||||
},
|
||||
.string => {
|
||||
const typed_object: *const Object.String = @ptrCast(object);
|
||||
const typed_object: *const Object.String = @ptrCast(obj);
|
||||
const string_bytes = typed_object.bytes[0..typed_object.length];
|
||||
try d.writer.print("<type={s} value=\"{s}\", address={*}>", .{
|
||||
try w.print("<type={s} value=\"{s}\", address={*}>", .{
|
||||
type_string,
|
||||
string_bytes,
|
||||
object,
|
||||
obj,
|
||||
});
|
||||
},
|
||||
.content_path => {
|
||||
try d.writer.print("<type={s} address={*}>", .{ type_string, object });
|
||||
.code, .knot => {
|
||||
try w.print("<type={s} address={*}>", .{ type_string, obj });
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue