refactor: make room for loading and outputting pre-compiled files

This commit is contained in:
Brett Broadhurst 2026-04-13 18:40:04 -06:00
parent 3afbbb6ec2
commit 96866ba9ae
Failed to generate hash of commit
11 changed files with 537 additions and 433 deletions

View file

@ -1,48 +1,14 @@
const std = @import("std");
const ink = @import("ink");
const Story = ink.Story;
const Module = ink.Module;
const Compilation = ink.Compilation;
const Ast = ink.Ast;
const AstGen = ink.AstGen;
const Ir = ink.Ir;
var global_allocator: std.heap.DebugAllocator(.{}) = .init;
var stdout_buffer: [4096]u8 align(std.heap.page_size_min) = undefined;
pub export fn ink_open() callconv(.c) ?*Story {
const gpa = global_allocator.allocator();
const story = gpa.create(Story) catch |err| switch (err) {
error.OutOfMemory => return null,
};
story.* = .{
.gpa = gpa,
.arena = .init(gpa),
.is_exited = false,
.can_advance = false,
.stack_top = 0,
.call_stack_top = 0,
.output_marker = 0,
.choice_selected = null,
.output_buffer = .empty,
.output_scratch = .empty,
.current_choices = .empty,
.variable_observers = .empty,
.globals = .empty,
.stack = &.{},
.call_stack = &.{},
.code_chunks = .empty,
.gc_objects = .{},
.constants_pool = &.{},
.string_bytes = &.{},
.dump_writer = null,
};
return story;
}
pub export fn ink_close(story: *Story) callconv(.c) void {
defer _ = global_allocator.deinit();
const gpa = story.gpa;
story.deinit();
gpa.destroy(story);
}
pub const InkLoadOpts = extern struct {
filename: [*]const u8,
filename_length: usize,
@ -51,61 +17,90 @@ pub const InkLoadOpts = extern struct {
flags: i32,
};
fn loadStory(story: *Story, options: *const InkLoadOpts) !void {
const gpa = story.gpa;
const LoadStoryOptions = struct {
filename: []const u8,
source_bytes: [:0]const u8,
flags: i32,
stack_size: usize,
error_writer: *std.Io.Writer,
};
fn loadStory(gpa: std.mem.Allocator, options: LoadStoryOptions) !*Story {
var arena_allocator = std.heap.ArenaAllocator.init(gpa);
defer arena_allocator.deinit();
const arena = arena_allocator.allocator();
const stderr = std.fs.File.stderr();
var stderr_writer = stderr.writer(&stdout_buffer);
var comp = try Module.compile(gpa, arena, .{
.source_bytes = options.source_bytes[0..options.source_length :0],
.filename = options.filename[0..options.filename_length],
.dump_writer = null,
.dump_use_color = false,
.dump_ast = false,
.dump_ir = false,
});
defer comp.deinit();
var tree = try Ast.parse(
gpa,
arena,
options.source_bytes,
options.filename,
@intCast(options.flags),
);
defer tree.deinit(gpa);
if (comp.errors.items.len > 0) {
for (comp.errors.items) |err| {
try comp.renderError(&stderr_writer.interface, err);
var ir = try AstGen.generate(gpa, &tree);
defer ir.deinit(gpa);
var errors: std.ArrayListUnmanaged(Compilation.Error) = .empty;
defer errors.deinit(gpa);
var cu = Compilation.build(gpa, tree, ir, &errors) catch |err| switch (err) {
else => |e| return e,
};
defer cu.deinit();
if (cu.hasCompileErrors()) {
for (cu.errors.items) |err| {
try cu.renderError(options.error_writer, err);
}
return error.LoadFailed;
return error.Failed;
}
// TODO: Make this configureable.
const stack_size = 128;
const eval_stack_ptr = try gpa.alloc(Story.Value, stack_size);
errdefer gpa.free(eval_stack_ptr);
const call_stack_ptr = try gpa.alloc(Story.CallFrame, stack_size);
errdefer gpa.free(call_stack_ptr);
story.can_advance = false;
story.stack = eval_stack_ptr;
story.call_stack = call_stack_ptr;
const story = try gpa.create(Story);
errdefer gpa.destroy(story);
story.* = .{
.gpa = gpa,
.arena = .init(gpa),
};
errdefer story.deinit();
try comp.setupStoryRuntime(gpa, story);
if (story.getKnot(Story.default_knot_name)) |knot| {
try story.pushStack(.{ .object = &knot.base });
try story.divert(knot, 0);
}
try Story.Loader.fromCompilationCompat(gpa, &cu, story, .{
.stack_size = options.stack_size,
});
return story;
}
pub export fn ink_load_story_options(
story: *Story,
options: *const InkLoadOpts,
) callconv(.c) c_int {
loadStory(story, options) catch |err| {
) callconv(.c) ?*Story {
const gpa = global_allocator.allocator();
const source_bytes = options.source_bytes[0..options.source_length :0];
const filename = options.filename[0..options.filename_length :0];
const stack_size = 128;
const stderr = std.fs.File.stderr();
var stderr_writer = stderr.writer(&stdout_buffer);
return loadStory(gpa, .{
.filename = filename,
.source_bytes = source_bytes,
.flags = options.flags,
.error_writer = &stderr_writer.interface,
.stack_size = stack_size,
}) catch |err| {
std.debug.print("{any}\n", .{@errorName(err)});
return -1;
return null;
};
return 0;
}
pub export fn ink_close(optional_story: ?*Story) callconv(.c) void {
defer _ = global_allocator.deinit();
if (optional_story) |story| {
const gpa = story.gpa;
story.deinit();
gpa.destroy(story);
}
}
pub export fn ink_story_can_continue(story: *Story) callconv(.c) bool {