refactor: story objects and story dumper
This commit is contained in:
parent
849908f251
commit
3ab279de0f
3 changed files with 196 additions and 209 deletions
217
src/Story.zig
217
src/Story.zig
|
|
@ -1,9 +1,10 @@
|
|||
//! Virtual machine state for story execution.
|
||||
const std = @import("std");
|
||||
const tokenizer = @import("./tokenizer.zig");
|
||||
const Ast = @import("./Ast.zig");
|
||||
const AstGen = @import("./AstGen.zig");
|
||||
pub const Object = @import("./object.zig").Object;
|
||||
const tokenizer = @import("tokenizer.zig");
|
||||
const Ast = @import("Ast.zig");
|
||||
const AstGen = @import("AstGen.zig");
|
||||
pub const Object = @import("Story/object.zig").Object;
|
||||
const Dumper = @import("Story/Dumper.zig");
|
||||
const assert = std.debug.assert;
|
||||
const Story = @This();
|
||||
|
||||
|
|
@ -61,206 +62,6 @@ pub const Opcode = enum(u8) {
|
|||
_,
|
||||
};
|
||||
|
||||
fn getObjectType(object: *const Object) []const u8 {
|
||||
switch (object.tag) {
|
||||
.number => return "Number",
|
||||
.string => return "String",
|
||||
.content_path => return "ContentPath",
|
||||
}
|
||||
}
|
||||
|
||||
fn printObject(writer: *std.Io.Writer, object: *const Object) !void {
|
||||
const type_string = getObjectType(object);
|
||||
switch (object.tag) {
|
||||
.number => {
|
||||
const typed_object: *const Object.Number = @ptrCast(object);
|
||||
switch (typed_object.data) {
|
||||
.boolean => |value| {
|
||||
try writer.print("<type={s} value={s}, address={*}>", .{
|
||||
type_string,
|
||||
if (value) "true" else "false",
|
||||
object,
|
||||
});
|
||||
},
|
||||
.floating => |value| {
|
||||
try writer.print("<type={s} value={d}, address={*}>", .{
|
||||
type_string,
|
||||
value,
|
||||
object,
|
||||
});
|
||||
},
|
||||
.integer => |value| {
|
||||
try writer.print("<type={s} value={d}, address={*}>", .{
|
||||
type_string,
|
||||
value,
|
||||
object,
|
||||
});
|
||||
},
|
||||
}
|
||||
},
|
||||
.string => {
|
||||
const typed_object: *const Object.String = @ptrCast(object);
|
||||
const string_bytes = typed_object.bytes[0..typed_object.length];
|
||||
try writer.print("<type={s} value=\"{s}\", address={*}>", .{
|
||||
type_string,
|
||||
string_bytes,
|
||||
object,
|
||||
});
|
||||
},
|
||||
.content_path => {
|
||||
try writer.print("<type={s} address={*}>", .{ type_string, object });
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub const Dumper = struct {
|
||||
story: *const Story,
|
||||
|
||||
fn dumpSimpleInst(
|
||||
_: *const Dumper,
|
||||
writer: *std.Io.Writer,
|
||||
offset: usize,
|
||||
op: Opcode,
|
||||
) !usize {
|
||||
try writer.print("{s}\n", .{@tagName(op)});
|
||||
return offset + 1;
|
||||
}
|
||||
|
||||
fn dumpByteInst(
|
||||
_: *const Dumper,
|
||||
writer: *std.Io.Writer,
|
||||
context: *const Object.ContentPath,
|
||||
offset: usize,
|
||||
op: Opcode,
|
||||
) !usize {
|
||||
const arg = context.bytes[offset + 1];
|
||||
if (op == .load_const) {
|
||||
try writer.print("{s} {d} (", .{ @tagName(op), arg });
|
||||
try printObject(writer, context.const_pool[arg]);
|
||||
try writer.print(")\n", .{});
|
||||
} else {
|
||||
try writer.print("{s} {x}\n", .{ @tagName(op), arg });
|
||||
}
|
||||
return offset + 2;
|
||||
}
|
||||
|
||||
fn dumpGlobalInst(
|
||||
_: *const Dumper,
|
||||
writer: *std.Io.Writer,
|
||||
context: *const Object.ContentPath,
|
||||
offset: usize,
|
||||
op: Opcode,
|
||||
) !usize {
|
||||
const arg = context.bytes[offset + 1];
|
||||
const global_name: *Object.String = @ptrCast(context.const_pool[arg]);
|
||||
const name_bytes = global_name.bytes[0..global_name.length];
|
||||
|
||||
try writer.print("{s} {x} '{s}'\n", .{ @tagName(op), arg, name_bytes });
|
||||
return offset + 2;
|
||||
}
|
||||
|
||||
fn dumpJumpInst(
|
||||
_: *const Dumper,
|
||||
writer: *std.Io.Writer,
|
||||
context: *const Object.ContentPath,
|
||||
offset: usize,
|
||||
op: Opcode,
|
||||
) !usize {
|
||||
var jump: u16 = @as(u16, context.bytes[offset + 1]) << 8;
|
||||
jump |= context.bytes[offset + 2];
|
||||
|
||||
try writer.print("{s} 0x{x:0>4} (0x{x:0>4} -> 0x{x:0>4})\n", .{
|
||||
@tagName(op),
|
||||
jump,
|
||||
offset,
|
||||
offset + 3 + jump,
|
||||
});
|
||||
return offset + 3;
|
||||
}
|
||||
|
||||
pub fn dumpInst(
|
||||
self: *const Dumper,
|
||||
writer: *std.Io.Writer,
|
||||
path: *const Object.ContentPath,
|
||||
offset: usize,
|
||||
should_prefix: bool,
|
||||
) !usize {
|
||||
const name_object = path.name;
|
||||
const name_bytes = name_object.bytes[0..name_object.length];
|
||||
const op: Opcode = @enumFromInt(path.bytes[offset]);
|
||||
|
||||
if (should_prefix) {
|
||||
try writer.print("<{s}>:0x{x:0>4} | ", .{ name_bytes, offset });
|
||||
} else {
|
||||
try writer.print("0x{x:0>4} | ", .{offset});
|
||||
}
|
||||
switch (op) {
|
||||
.exit,
|
||||
.ret,
|
||||
.pop,
|
||||
.true,
|
||||
.false,
|
||||
.add,
|
||||
.sub,
|
||||
.mul,
|
||||
.div,
|
||||
.mod,
|
||||
.neg,
|
||||
.not,
|
||||
.cmp_eq,
|
||||
.cmp_lt,
|
||||
.cmp_lte,
|
||||
.cmp_gt,
|
||||
.cmp_gte,
|
||||
.flush,
|
||||
.load_choice_id,
|
||||
.content,
|
||||
.choice,
|
||||
.line,
|
||||
.glue,
|
||||
=> return self.dumpSimpleInst(writer, offset, op),
|
||||
.load_const,
|
||||
.load,
|
||||
.store,
|
||||
=> return self.dumpByteInst(writer, path, offset, op),
|
||||
.load_global,
|
||||
.store_global,
|
||||
.call,
|
||||
.divert,
|
||||
=> return self.dumpGlobalInst(writer, path, offset, op),
|
||||
.jmp,
|
||||
.jmp_t,
|
||||
.jmp_f,
|
||||
=> return self.dumpJumpInst(writer, path, offset, op),
|
||||
else => |code| {
|
||||
try writer.print("Unknown opcode 0x{x:0>4}\n", .{@intFromEnum(code)});
|
||||
return offset + 1;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dump(
|
||||
self: *const Dumper,
|
||||
writer: *std.Io.Writer,
|
||||
path: *const Object.ContentPath,
|
||||
) !void {
|
||||
const name_object = path.name;
|
||||
const name_bytes = name_object.bytes[0..name_object.length];
|
||||
|
||||
try writer.print("=== {s}(args: {d}, locals: {d}) ===\n", .{
|
||||
name_bytes,
|
||||
path.arity,
|
||||
path.locals_count,
|
||||
});
|
||||
|
||||
var index: usize = 0;
|
||||
while (index < path.bytes.len) {
|
||||
index = try self.dumpInst(writer, path, index, false);
|
||||
}
|
||||
return writer.flush();
|
||||
}
|
||||
};
|
||||
|
||||
pub fn deinit(story: *Story) void {
|
||||
const gpa = story.allocator;
|
||||
var next = story.gc_objects.first;
|
||||
|
|
@ -279,28 +80,28 @@ pub fn deinit(story: *Story) void {
|
|||
pub fn trace(story: *Story, writer: *std.Io.Writer, frame: *CallFrame) !void {
|
||||
try writer.print("\tStack => stack_pointer={d}, objects=[", .{frame.sp});
|
||||
|
||||
const dumper = Dumper{ .story = story, .writer = writer };
|
||||
const stack = &story.stack;
|
||||
const stack_top = story.stack.items.len;
|
||||
if (stack_top > 0) {
|
||||
const last_slot = stack.items[stack.items.len - 1];
|
||||
for (stack.items[frame.sp .. stack.items.len - 1]) |slot| {
|
||||
if (slot) |object| {
|
||||
try printObject(writer, object);
|
||||
try dumper.printObject(object);
|
||||
} else {
|
||||
try writer.writeAll("NULL");
|
||||
}
|
||||
try writer.writeAll(", ");
|
||||
}
|
||||
if (last_slot) |object| {
|
||||
try printObject(writer, object);
|
||||
try dumper.printObject(object);
|
||||
} else {
|
||||
try writer.writeAll("NULL");
|
||||
}
|
||||
}
|
||||
|
||||
try writer.writeAll("]\n");
|
||||
const dumper = Dumper{ .story = story };
|
||||
_ = try dumper.dumpInst(writer, frame.callee, frame.ip, true);
|
||||
_ = try dumper.dumpInst(frame.callee, frame.ip, true);
|
||||
return writer.flush();
|
||||
}
|
||||
|
||||
|
|
|
|||
186
src/Story/Dumper.zig
Normal file
186
src/Story/Dumper.zig
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
const std = @import("std");
|
||||
const Story = @import("../Story.zig");
|
||||
const Object = Story.Object;
|
||||
const Opcode = Story.Opcode;
|
||||
const Dumper = @This();
|
||||
|
||||
story: *const Story,
|
||||
writer: *std.Io.Writer,
|
||||
|
||||
fn dumpSimpleInst(d: Dumper, offset: usize, op: Opcode) !usize {
|
||||
try d.writer.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];
|
||||
if (op == .load_const) {
|
||||
try d.writer.print("{s} {d} (", .{ @tagName(op), arg });
|
||||
try printObject(d.writer, context.const_pool[arg]);
|
||||
try d.writer.print(")\n", .{});
|
||||
} else {
|
||||
try d.writer.print("{s} {x}\n", .{ @tagName(op), arg });
|
||||
}
|
||||
return offset + 2;
|
||||
}
|
||||
|
||||
fn dumpGlobalInst(
|
||||
d: Dumper,
|
||||
context: *const Object.ContentPath,
|
||||
offset: usize,
|
||||
op: Opcode,
|
||||
) !usize {
|
||||
const arg = context.bytes[offset + 1];
|
||||
const global_name: *Object.String = @ptrCast(context.const_pool[arg]);
|
||||
const name_bytes = global_name.bytes[0..global_name.length];
|
||||
|
||||
try d.writer.print("{s} {x} '{s}'\n", .{ @tagName(op), arg, name_bytes });
|
||||
return offset + 2;
|
||||
}
|
||||
|
||||
fn dumpJumpInst(
|
||||
d: Dumper,
|
||||
context: *const Object.ContentPath,
|
||||
offset: usize,
|
||||
op: Opcode,
|
||||
) !usize {
|
||||
var jump: u16 = @as(u16, context.bytes[offset + 1]) << 8;
|
||||
jump |= context.bytes[offset + 2];
|
||||
|
||||
try d.writer.print("{s} 0x{x:0>4} (0x{x:0>4} -> 0x{x:0>4})\n", .{
|
||||
@tagName(op),
|
||||
jump,
|
||||
offset,
|
||||
offset + 3 + jump,
|
||||
});
|
||||
return offset + 3;
|
||||
}
|
||||
|
||||
pub fn dumpInst(
|
||||
d: Dumper,
|
||||
path: *const Object.ContentPath,
|
||||
offset: usize,
|
||||
should_prefix: bool,
|
||||
) !usize {
|
||||
const name_object = path.name;
|
||||
const name_bytes = name_object.bytes[0..name_object.length];
|
||||
const op: Opcode = @enumFromInt(path.bytes[offset]);
|
||||
|
||||
if (should_prefix) {
|
||||
try d.writer.print("<{s}>:0x{x:0>4} | ", .{ name_bytes, offset });
|
||||
} else {
|
||||
try d.writer.print("0x{x:0>4} | ", .{offset});
|
||||
}
|
||||
switch (op) {
|
||||
.exit,
|
||||
.ret,
|
||||
.pop,
|
||||
.true,
|
||||
.false,
|
||||
.add,
|
||||
.sub,
|
||||
.mul,
|
||||
.div,
|
||||
.mod,
|
||||
.neg,
|
||||
.not,
|
||||
.cmp_eq,
|
||||
.cmp_lt,
|
||||
.cmp_lte,
|
||||
.cmp_gt,
|
||||
.cmp_gte,
|
||||
.flush,
|
||||
.load_choice_id,
|
||||
.content,
|
||||
.choice,
|
||||
.line,
|
||||
.glue,
|
||||
=> return d.dumpSimpleInst(offset, op),
|
||||
.load_const,
|
||||
.load,
|
||||
.store,
|
||||
=> return d.dumpByteInst(path, offset, op),
|
||||
.load_global,
|
||||
.store_global,
|
||||
.call,
|
||||
.divert,
|
||||
=> return d.dumpGlobalInst(path, offset, op),
|
||||
.jmp,
|
||||
.jmp_t,
|
||||
.jmp_f,
|
||||
=> return d.dumpJumpInst(path, offset, op),
|
||||
else => |code| {
|
||||
try d.writer.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;
|
||||
const name_bytes = name_object.bytes[0..name_object.length];
|
||||
|
||||
try d.writer.print("=== {s}(args: {d}, locals: {d}) ===\n", .{
|
||||
name_bytes,
|
||||
path.arity,
|
||||
path.locals_count,
|
||||
});
|
||||
|
||||
var index: usize = 0;
|
||||
while (index < path.bytes.len) {
|
||||
index = try d.dumpInst(path, index, false);
|
||||
}
|
||||
return d.writer.flush();
|
||||
}
|
||||
|
||||
fn getObjectType(object: *const Object) []const u8 {
|
||||
switch (object.tag) {
|
||||
.number => return "Number",
|
||||
.string => return "String",
|
||||
.content_path => return "ContentPath",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn printObject(writer: *std.Io.Writer, object: *const Object) !void {
|
||||
const type_string = getObjectType(object);
|
||||
switch (object.tag) {
|
||||
.number => {
|
||||
const typed_object: *const Object.Number = @ptrCast(object);
|
||||
switch (typed_object.data) {
|
||||
.boolean => |value| {
|
||||
try writer.print("<type={s} value={s}, address={*}>", .{
|
||||
type_string,
|
||||
if (value) "true" else "false",
|
||||
object,
|
||||
});
|
||||
},
|
||||
.floating => |value| {
|
||||
try writer.print("<type={s} value={d}, address={*}>", .{
|
||||
type_string,
|
||||
value,
|
||||
object,
|
||||
});
|
||||
},
|
||||
.integer => |value| {
|
||||
try writer.print("<type={s} value={d}, address={*}>", .{
|
||||
type_string,
|
||||
value,
|
||||
object,
|
||||
});
|
||||
},
|
||||
}
|
||||
},
|
||||
.string => {
|
||||
const typed_object: *const Object.String = @ptrCast(object);
|
||||
const string_bytes = typed_object.bytes[0..typed_object.length];
|
||||
try writer.print("<type={s} value=\"{s}\", address={*}>", .{
|
||||
type_string,
|
||||
string_bytes,
|
||||
object,
|
||||
});
|
||||
},
|
||||
.content_path => {
|
||||
try writer.print("<type={s} address={*}>", .{ type_string, object });
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
const std = @import("std");
|
||||
const Story = @import("./Story.zig");
|
||||
const Story = @import("../Story.zig");
|
||||
|
||||
/// Runtime Object, whose memory is managed through the virtual machine's
|
||||
/// garbage collector.
|
||||
Loading…
Add table
Add a link
Reference in a new issue