diff --git a/src/Story.zig b/src/Story.zig index afe9d5d..88db9cd 100644 --- a/src/Story.zig +++ b/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_string, - if (value) "true" else "false", - object, - }); - }, - .floating => |value| { - try writer.print("", .{ - type_string, - value, - object, - }); - }, - .integer => |value| { - try writer.print("", .{ - 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_string, - string_bytes, - object, - }); - }, - .content_path => { - try writer.print("", .{ 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(); } diff --git a/src/Story/Dumper.zig b/src/Story/Dumper.zig new file mode 100644 index 0000000..41b8bc2 --- /dev/null +++ b/src/Story/Dumper.zig @@ -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_string, + if (value) "true" else "false", + object, + }); + }, + .floating => |value| { + try writer.print("", .{ + type_string, + value, + object, + }); + }, + .integer => |value| { + try writer.print("", .{ + 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_string, + string_bytes, + object, + }); + }, + .content_path => { + try writer.print("", .{ type_string, object }); + }, + } +} diff --git a/src/object.zig b/src/Story/object.zig similarity index 99% rename from src/object.zig rename to src/Story/object.zig index 8d54876..2824bc8 100644 --- a/src/object.zig +++ b/src/Story/object.zig @@ -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.