feat: infrastructure to support qualified lookups

This commit is contained in:
Brett Broadhurst 2026-03-25 03:18:24 -06:00
parent e8ad1cd5b5
commit 440ec68481
Failed to generate hash of commit
6 changed files with 1118 additions and 575 deletions

View file

@ -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 });
},
}
}