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.
|
//! Virtual machine state for story execution.
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const tokenizer = @import("./tokenizer.zig");
|
const tokenizer = @import("tokenizer.zig");
|
||||||
const Ast = @import("./Ast.zig");
|
const Ast = @import("Ast.zig");
|
||||||
const AstGen = @import("./AstGen.zig");
|
const AstGen = @import("AstGen.zig");
|
||||||
pub const Object = @import("./object.zig").Object;
|
pub const Object = @import("Story/object.zig").Object;
|
||||||
|
const Dumper = @import("Story/Dumper.zig");
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
const Story = @This();
|
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 {
|
pub fn deinit(story: *Story) void {
|
||||||
const gpa = story.allocator;
|
const gpa = story.allocator;
|
||||||
var next = story.gc_objects.first;
|
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 {
|
pub fn trace(story: *Story, writer: *std.Io.Writer, frame: *CallFrame) !void {
|
||||||
try writer.print("\tStack => stack_pointer={d}, objects=[", .{frame.sp});
|
try writer.print("\tStack => stack_pointer={d}, objects=[", .{frame.sp});
|
||||||
|
|
||||||
|
const dumper = Dumper{ .story = story, .writer = writer };
|
||||||
const stack = &story.stack;
|
const stack = &story.stack;
|
||||||
const stack_top = story.stack.items.len;
|
const stack_top = story.stack.items.len;
|
||||||
if (stack_top > 0) {
|
if (stack_top > 0) {
|
||||||
const last_slot = stack.items[stack.items.len - 1];
|
const last_slot = stack.items[stack.items.len - 1];
|
||||||
for (stack.items[frame.sp .. stack.items.len - 1]) |slot| {
|
for (stack.items[frame.sp .. stack.items.len - 1]) |slot| {
|
||||||
if (slot) |object| {
|
if (slot) |object| {
|
||||||
try printObject(writer, object);
|
try dumper.printObject(object);
|
||||||
} else {
|
} else {
|
||||||
try writer.writeAll("NULL");
|
try writer.writeAll("NULL");
|
||||||
}
|
}
|
||||||
try writer.writeAll(", ");
|
try writer.writeAll(", ");
|
||||||
}
|
}
|
||||||
if (last_slot) |object| {
|
if (last_slot) |object| {
|
||||||
try printObject(writer, object);
|
try dumper.printObject(object);
|
||||||
} else {
|
} else {
|
||||||
try writer.writeAll("NULL");
|
try writer.writeAll("NULL");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try writer.writeAll("]\n");
|
try writer.writeAll("]\n");
|
||||||
const dumper = Dumper{ .story = story };
|
_ = try dumper.dumpInst(frame.callee, frame.ip, true);
|
||||||
_ = try dumper.dumpInst(writer, frame.callee, frame.ip, true);
|
|
||||||
return writer.flush();
|
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 std = @import("std");
|
||||||
const Story = @import("./Story.zig");
|
const Story = @import("../Story.zig");
|
||||||
|
|
||||||
/// Runtime Object, whose memory is managed through the virtual machine's
|
/// Runtime Object, whose memory is managed through the virtual machine's
|
||||||
/// garbage collector.
|
/// garbage collector.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue