feat: cheaper runtime value type, float and int arithmetic rules

This commit is contained in:
Brett Broadhurst 2026-03-29 02:24:02 -06:00
parent 9b5cd4038f
commit 92e8bcd866
Failed to generate hash of commit
13 changed files with 630 additions and 448 deletions

View file

@ -1,5 +1,6 @@
const std = @import("std");
const Story = @import("../Story.zig");
const Value = Story.Value;
const Object = Story.Object;
const Opcode = Story.Opcode;
const assert = std.debug.assert;
@ -21,6 +22,8 @@ fn dumpByteInst(
op: Opcode,
) !usize {
const code = knot.code;
const constants_pool = &self.story.constants_pool;
assert(code.bytecode.len > offset + 1);
const arg = code.bytecode[offset + 1];
if (op == .load_const) {
@ -28,9 +31,9 @@ fn dumpByteInst(
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);
if (constants_pool.items.len > constant_index) {
const global_constant = &constants_pool.items[constant_index];
try self.dumpValue(w, global_constant);
} else {
try w.writeAll("invalid!");
}
@ -52,15 +55,24 @@ fn dumpGlobalInst(
op: Opcode,
) !usize {
const code = knot.code;
const constants_pool = &self.story.constants_pool;
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 w.print("{s} {x} '{s}'\n", .{ @tagName(op), arg, name_bytes });
const global_constant = constants_pool.items[constant_index];
switch (global_constant) {
.object => |object| switch (object.tag) {
.string => {
const global_name: *Object.String = @ptrCast(object);
const name_bytes = global_name.bytes[0..global_name.length];
try w.print("{s} {x} '{s}'\n", .{ @tagName(op), arg, name_bytes });
},
else => unreachable,
},
else => unreachable,
}
return offset + 2;
}
@ -265,40 +277,108 @@ pub fn dumpKnot(self: *Dumper, w: *std.Io.Writer, knot: *const Object.Knot) !voi
return w.flush();
}
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(obj);
switch (typed_object.data) {
.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(obj);
const string_bytes = typed_object.bytes[0..typed_object.length];
try w.print("<type={s} value=\"{s}\", address={*}>", .{
type_string,
string_bytes,
obj,
});
},
.code, .knot => {
try w.print("<type={s} address={*}>", .{ type_string, obj });
pub fn dumpValue(_: Dumper, w: *std.Io.Writer, value: *const Value) !void {
switch (value.*) {
.bool => |boolean| try w.print("<{s} value={s}>", .{
value.tagBytes(),
if (boolean) "true" else "false",
}),
.int => |int| try w.print("<{s} value={d}, address={*}>", .{
value.tagBytes(),
int,
value,
}),
.float => |float| try w.print("<{s} value={d}, address={*}>", .{
value.tagBytes(),
float,
value,
}),
.object => |object| switch (object.tag) {
.string => {
const typed_object: *const Object.String = @ptrCast(object);
const string_bytes = typed_object.bytes[0..typed_object.length];
try w.print("<{s} value=\"{s}\", address={*}>", .{
object.tag.tagBytes(),
string_bytes,
object,
});
},
else => try w.print("<{s} address={*}>", .{ object.tag.tagBytes(), object }),
},
}
}
pub fn dump(story: *Story, writer: *std.Io.Writer) !void {
var story_dumper: Dumper = .{ .story = story };
try writer.writeAll("=== Constants ===\n");
var i: usize = 0;
while (i < story.constants_pool.items.len) : (i += 1) {
const global_constant = &story.constants_pool.items[i];
try story_dumper.dumpValue(writer, global_constant);
try writer.writeAll("\n");
}
try writer.writeAll("\n");
try writer.writeAll("=== Globals ===\n");
var global_iter = story.globals.iterator();
while (global_iter.next()) |entry| {
try writer.print("{s} => ...\n", .{entry.key_ptr.*});
}
try writer.writeAll("\n");
try writer.writeAll("=== Knots ===\n");
try writer.flush();
var knots_iter = story.globals.iterator();
while (knots_iter.next()) |entry| {
if (entry.value_ptr.*) |global| {
switch (global) {
.object => |object| switch (object.tag) {
.knot => {
try writer.writeAll("*");
story_dumper.indent_level += 2;
try story_dumper.dumpKnot(writer, @ptrCast(object));
story_dumper.indent_level -= 2;
},
else => {},
},
else => {},
}
}
}
}
pub fn trace(story: *Story, writer: *std.Io.Writer, frame: *Story.CallFrame) !void {
var dumper: Dumper = .{ .story = story };
const stack = &story.stack;
const stack_top = story.stack.items.len;
try writer.print("\tStack => stack_pointer={d}, objects=[", .{frame.sp});
if (stack_top > 0) {
// FIXME: There has to be a better way to do this.
if (stack_top > 1) {
var i: usize = frame.sp;
while (i < stack.items.len - 1) : (i += 1) {
if (stack.items[i]) |*value| {
try dumper.dumpValue(writer, value);
} else {
try writer.writeAll("null");
}
try writer.writeAll(", ");
}
}
if (stack.items[stack.items.len - 1]) |*object| {
try dumper.dumpValue(writer, object);
} else {
try writer.writeAll("null");
}
}
try writer.writeAll("]\n");
_ = try dumper.dumpInst(writer, frame.callee, frame.ip, true);
return writer.flush();
}