153 lines
4 KiB
Zig
153 lines
4 KiB
Zig
const std = @import("std");
|
|
const ink = @import("ink");
|
|
const Story = ink.Story;
|
|
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 const InkLoadOpts = extern struct {
|
|
filename: [*]const u8,
|
|
filename_length: usize,
|
|
source_bytes: [*]const u8,
|
|
source_length: usize,
|
|
flags: i32,
|
|
};
|
|
|
|
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();
|
|
|
|
var tree = try Ast.parse(
|
|
gpa,
|
|
arena,
|
|
options.source_bytes,
|
|
options.filename,
|
|
@intCast(options.flags),
|
|
);
|
|
defer tree.deinit(gpa);
|
|
|
|
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.Failed;
|
|
}
|
|
|
|
const story = try gpa.create(Story);
|
|
errdefer gpa.destroy(story);
|
|
story.* = .{
|
|
.gpa = gpa,
|
|
.arena = .init(gpa),
|
|
};
|
|
errdefer story.deinit();
|
|
|
|
try Story.Loader.fromCompilationCompat(gpa, &cu, story, .{
|
|
.stack_size = options.stack_size,
|
|
});
|
|
return story;
|
|
}
|
|
|
|
pub export fn ink_load_story_options(
|
|
options: *const InkLoadOpts,
|
|
) 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 null;
|
|
};
|
|
}
|
|
|
|
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 {
|
|
return !story.is_exited and story.can_advance;
|
|
}
|
|
|
|
pub export fn ink_story_continue(
|
|
story: *Story,
|
|
line: *?[*]const u8,
|
|
linelen: *usize,
|
|
) callconv(.c) c_int {
|
|
const result = story.advance() catch return -1;
|
|
if (result) |slice| {
|
|
line.* = slice.ptr;
|
|
linelen.* = slice.len;
|
|
return 1;
|
|
} else {
|
|
line.* = null;
|
|
linelen.* = 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// FIXME: The internal counter may be a bad idea...
|
|
pub export fn ink_story_choice_next(
|
|
story: *Story,
|
|
line: *?[*]const u8,
|
|
linelen: *usize,
|
|
) callconv(.c) c_int {
|
|
if (story.internal_counter < story.current_choices.items.len) {
|
|
const choice = &story.current_choices.items[story.internal_counter];
|
|
line.* = choice.content.ptr;
|
|
linelen.* = choice.content.len;
|
|
story.internal_counter += 1;
|
|
return 1;
|
|
} else {
|
|
line.* = null;
|
|
linelen.* = 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// FIXME: The internal counter may be a bad idea...
|
|
pub export fn ink_story_choose(story: *Story, index: usize) callconv(.c) c_int {
|
|
story.selectChoiceIndex(index) catch {
|
|
return -1;
|
|
};
|
|
story.internal_counter = 0;
|
|
return 0;
|
|
}
|