feat: loading precompiled sections, strings/constants/knots

This commit is contained in:
Brett Broadhurst 2026-04-20 23:02:32 -06:00
parent 55ddd2b7cf
commit 127db83041
Failed to generate hash of commit
6 changed files with 553 additions and 142 deletions

View file

@ -103,23 +103,23 @@ pub fn getOrPutString(
gpa: std.mem.Allocator,
bytes: []const u8,
) error{OutOfMemory}!NullTerminatedString {
const str_index: u32 = @intCast(ip.string_bytes.items.len);
try ip.string_bytes.ensureUnusedCapacity(gpa, bytes.len + 1);
ip.string_bytes.appendSliceAssumeCapacity(bytes);
const string_bytes = &ip.string_bytes;
const str_index: u32 = @intCast(string_bytes.items.len);
try string_bytes.appendSlice(gpa, bytes);
const key: []const u8 = ip.string_bytes.items[str_index..];
const key: []const u8 = string_bytes.items[str_index..];
const gop = try ip.string_table.getOrPutContextAdapted(gpa, key, StringIndexAdapter{
.bytes = &ip.string_bytes,
.bytes = string_bytes,
}, StringIndexContext{
.bytes = &ip.string_bytes,
.bytes = string_bytes,
});
if (gop.found_existing) {
ip.string_bytes.shrinkRetainingCapacity(str_index);
string_bytes.shrinkRetainingCapacity(str_index);
return @enumFromInt(gop.key_ptr.*);
} else {
gop.key_ptr.* = str_index;
try ip.string_bytes.append(gpa, 0);
return @enumFromInt(gop.key_ptr.*);
try string_bytes.append(gpa, 0);
return @enumFromInt(str_index);
}
}

View file

@ -127,7 +127,15 @@ pub const CallFrame = struct {
bp: usize,
};
pub const Value = union(enum) {
pub const ValueType = enum(u8) {
nil,
bool,
int,
float,
object,
};
pub const Value = union(ValueType) {
nil,
bool: bool,
int: i64,

View file

@ -9,6 +9,31 @@ const Dumper = @This();
story: *const Story,
indent_level: usize = 0,
fn dumpStringBytes(dumper: *Dumper, writer: *std.Io.Writer) !void {
const bytes = dumper.story.string_bytes;
var start: usize = 0;
while (start < bytes.len) {
const end = std.mem.indexOfScalarPos(u8, bytes, start, 0) orelse break;
const str = bytes[start..end];
try writer.print("[{d:04}] ", .{start});
for (str) |b| try writer.print("{x:02} ", .{b});
try writer.print("00: {s}\n", .{str});
start = end + 1;
}
return writer.flush();
}
fn dumpConstants(dumper: *Dumper, writer: *std.Io.Writer) !void {
const story = dumper.story;
var i: usize = 0;
while (i < story.constants_pool.len) : (i += 1) {
const global_constant = &story.constants_pool[i];
try dumper.dumpValue(writer, global_constant);
try writer.writeAll("\n");
}
}
fn dumpSimpleInst(_: *Dumper, w: *std.Io.Writer, offset: usize, op: Opcode) !usize {
try w.print("{s}\n", .{@tagName(op)});
return offset + 1;
@ -312,14 +337,11 @@ pub fn dumpValue(_: Dumper, w: *std.Io.Writer, value: *const Value) !void {
pub fn dump(story: *Story, writer: *std.Io.Writer) !void {
var story_dumper: Dumper = .{ .story = story };
try writer.writeAll("=== Constants ===\n");
try writer.writeAll("=== Strings ===\n");
try story_dumper.dumpStringBytes(writer);
var i: usize = 0;
while (i < story.constants_pool.len) : (i += 1) {
const global_constant = &story.constants_pool[i];
try story_dumper.dumpValue(writer, global_constant);
try writer.writeAll("\n");
}
try writer.writeAll("=== Constants ===\n");
try story_dumper.dumpConstants(writer);
try writer.writeAll("\n");
try writer.writeAll("=== Globals ===\n");

View file

@ -2,63 +2,237 @@ const std = @import("std");
const InternPool = @import("../InternPool.zig");
const Compilation = @import("../compile.zig").Compilation;
const Story = @import("../Story.zig");
const Value = Story.Value;
const Object = Story.Object;
const Loader = @This();
fn makeValueFromInterned(story: *Story, value: InternPool.Key) !Story.Value {
return switch (value) {
.bool => |boolean| .{ .bool = boolean },
.int => |int| .{ .int = @intCast(int) },
.float => |float| .{ .float = @bitCast(float) },
.str => |index| blk: {
const str_object = try Object.String.create(story, .{
.bytes = stringBytes(story, index),
});
break :blk .{ .object = &str_object.base };
},
gpa: std.mem.Allocator,
story: *Story,
endian: std.builtin.Endian = .big,
pub const FileHeader = extern struct {
magic: [4]u8 = .{ 'I', 'N', 'K', 'C' },
version: [2]u8 = .{ 0, 0 },
__padding: u16 = 0,
sections_count: u32 = 0,
};
pub const SectionHeader = extern struct {
kind: Tag,
flags: u8 = 0,
__padding: u16 = 0,
size: u32,
pub const Tag = enum(u8) {
strings,
constants,
globals,
knot,
_,
};
}
};
fn makeKnotObject(
story: *Story,
name: []const u8,
code: *InternPool.CodeChunk,
) !*Object.Knot {
return Object.Knot.create(story, .{
.name = name,
.code = try Object.Code.create(story, .{
.args_count = @intCast(code.args_count),
.locals_count = @intCast(code.locals_count),
.stack_size = @intCast(code.stack_size),
.constants = try code.constants.toOwnedSlice(story.gpa),
.code_bytes = try code.bytecode.toOwnedSlice(story.gpa),
}),
});
}
pub const CodeHeader = extern struct {
args_count: u8,
locals_count: u8,
stack_size: u16,
constants_len: u16,
bytecode_len: u16,
};
fn makeConstantsPool(mod: *Compilation, story: *Story) ![]const Story.Value {
const ip = &mod.intern_pool;
const gpa = mod.gpa;
var constants_pool: std.ArrayListUnmanaged(Value) = .empty;
defer constants_pool.deinit(mod.gpa);
try constants_pool.ensureUnusedCapacity(mod.gpa, ip.values.items.len);
for (ip.values.items) |value| {
const obj = try makeValueFromInterned(story, value);
constants_pool.appendAssumeCapacity(obj);
}
return constants_pool.toOwnedSlice(gpa);
}
fn stringBytes(story: *Story, index: InternPool.NullTerminatedString) [:0]const u8 {
const slice = story.string_bytes[@intFromEnum(index)..];
return slice[0..std.mem.indexOfScalar(u8, slice, 0).? :0];
}
pub const Constant = enum(u8) {
bool,
int,
float,
str_index,
_,
};
pub const Options = struct {
stack_size: usize,
};
pub const SectionIterator = struct {
bytes: []const u8,
index: usize,
pub const Entry = struct {
header: SectionHeader,
bytes: []const u8,
};
pub fn next(iter: *SectionIterator) !?Entry {
if (iter.index >= iter.bytes.len)
return null;
if (iter.index + @sizeOf(SectionHeader) > iter.bytes.len)
return error.Corrupted;
const header: SectionHeader = .{
.kind = @enumFromInt(iter.bytes[iter.index]),
.flags = iter.bytes[iter.index + 1],
.__padding = 0,
.size = std.mem.readInt(u32, iter.bytes[iter.index + 4 ..][0..4], .big),
};
const payload_start = iter.index + @sizeOf(SectionHeader);
const payload_end = payload_start + header.size;
if (payload_end > iter.bytes.len)
return error.Corrupted;
const entry: Entry = .{
.header = header,
.bytes = iter.bytes[payload_start..payload_end],
};
iter.index = payload_end;
return entry;
}
};
pub fn fromCachedCompilation(
gpa: std.mem.Allocator,
bytes: []const u8,
options: Options,
) !Story {
var story: Story = .{
.gpa = gpa,
.arena = .init(gpa),
};
errdefer story.deinit();
story.stack = try gpa.alloc(Story.Value, options.stack_size);
story.call_stack = try gpa.alloc(Story.CallFrame, options.stack_size);
var loader: Loader = .{
.gpa = gpa,
.story = &story,
};
var section_iter: SectionIterator = .{
.index = @sizeOf(FileHeader),
.bytes = bytes,
};
var string_bytes: std.ArrayList(u8) = .empty;
defer string_bytes.deinit(gpa);
var constants_pool: std.ArrayList(Story.Value) = .empty;
defer constants_pool.deinit(gpa);
if (try section_iter.next()) |section| {
try string_bytes.appendSlice(gpa, section.bytes);
story.string_bytes = try string_bytes.toOwnedSlice(gpa);
}
if (try section_iter.next()) |section| {
var remaining = section.bytes;
while (remaining.len > 0) {
const tag: Constant = @enumFromInt(remaining[0]);
remaining = remaining[1..];
const value: Story.Value = v: switch (tag) {
.bool => {
const v = remaining[0];
remaining = remaining[1..];
break :v .{ .bool = if (v == 1) true else false };
},
.int => {
const v = std.mem.readInt(i64, remaining[0..8], .big);
remaining = remaining[8..];
break :v .{ .int = @bitCast(v) };
},
.float => {
const v = std.mem.readInt(i64, remaining[0..8], .big);
remaining = remaining[8..];
break :v .{ .float = @bitCast(v) };
},
.str_index => {
const str_index = std.mem.readInt(u16, remaining[2..4], .big);
const slice = story.string_bytes[str_index..];
const str_bytes = slice[0..std.mem.indexOfScalar(u8, slice, 0).? :0];
const str_object = try Object.String.create(loader.story, .{
.bytes = str_bytes,
});
remaining = remaining[@sizeOf(u16) + @sizeOf(u16) ..];
break :v .{ .object = &str_object.base };
},
else => return error.MalformedValueTag,
};
try constants_pool.append(gpa, value);
}
story.constants_pool = try constants_pool.toOwnedSlice(gpa);
}
while (try section_iter.next()) |section| {
switch (section.header.kind) {
.strings => {},
.constants => {},
.knot => {
const knot = try readKnot(&loader, section.bytes);
const knot_value: Story.Value = .{ .object = &knot.base };
try story.globals.put(gpa, knot.name.toSlice(), knot_value);
},
else => {},
}
}
if (story.getKnot(Story.default_knot_name)) |knot| {
try story.pushStack(.{ .object = &knot.base });
try story.divert(knot, 0);
}
return story;
}
fn nullTerminatedString(loader: *const Loader, index: u32) []const u8 {
const slice = loader.story.string_bytes[index..];
return slice[0..std.mem.indexOfScalar(u8, slice, 0).? :0];
}
fn readKnot(loader: *Loader, bytes: []const u8) !*Object.Knot {
const gpa = loader.gpa;
const name_index = std.mem.readInt(u32, bytes[0..4], loader.endian);
const code_header: CodeHeader = .{
.args_count = bytes[4],
.locals_count = bytes[5],
.stack_size = std.mem.readInt(u16, bytes[6..8], loader.endian),
.constants_len = std.mem.readInt(u16, bytes[8..10], loader.endian),
.bytecode_len = std.mem.readInt(u16, bytes[10..12], loader.endian),
};
const constants_start = 12;
const constants_end = constants_start + code_header.constants_len;
const bytecode_end = constants_end + code_header.bytecode_len;
if (bytecode_end > bytes.len)
return error.InvalidKnotSection;
var constants: std.ArrayList(u8) = .empty;
defer constants.deinit(gpa);
var bytecode: std.ArrayList(u8) = .empty;
defer bytecode.deinit(gpa);
try constants.appendSlice(gpa, bytes[constants_start..constants_end]);
try bytecode.appendSlice(gpa, bytes[constants_end..bytecode_end]);
return .create(loader.story, .{
.name = loader.nullTerminatedString(name_index),
.code = try Object.Code.create(loader.story, .{
.args_count = code_header.args_count,
.locals_count = code_header.locals_count,
.stack_size = code_header.stack_size,
.constants = try constants.toOwnedSlice(gpa),
.code_bytes = try bytecode.toOwnedSlice(gpa),
}),
});
}
pub fn fromCompilation(
gpa: std.mem.Allocator,
mod: *Compilation,
options: Options,
) !Story {
var story: Story = .{
.gpa = gpa,
.arena = .init(gpa),
};
try fromCompilationCompat(gpa, mod, &story, options);
return story;
}
pub fn fromCompilationCompat(
gpa: std.mem.Allocator,
mod: *Compilation,
@ -111,26 +285,202 @@ pub fn fromCompilationCompat(
}
}
pub fn fromCompilation(
pub const Builder = struct {
gpa: std.mem.Allocator,
mod: *Compilation,
options: Options,
) !Story {
var story: Story = .{
.gpa = gpa,
.arena = .init(gpa),
intern_pool: *const InternPool,
endian: std.builtin.Endian = .big,
pub const Section = struct {
builder: *Builder,
header: SectionHeader,
bytes: std.ArrayList(u8) = .empty,
fn addConstant(self: *Section, value: InternPool.Key) !void {
const gpa = self.builder.gpa;
const bytes = &self.bytes;
switch (value) {
.bool => |v| {
const size: u32 = 1 + @sizeOf(bool);
self.header.size += size;
try bytes.ensureUnusedCapacity(gpa, size);
bytes.appendAssumeCapacity(@intFromEnum(Constant.bool));
bytes.appendAssumeCapacity(@intFromBool(v));
},
.int => |v| {
const size: u32 = 1 + @sizeOf(i64);
self.header.size += size;
try bytes.ensureUnusedCapacity(gpa, size);
bytes.appendAssumeCapacity(@intFromEnum(Constant.int));
bytes.appendSliceAssumeCapacity(
&std.mem.toBytes(std.mem.nativeToBig(i64, v)),
);
},
.float => |v| {
const size: u32 = 1 + @sizeOf(u64);
self.header.size += size;
try bytes.ensureUnusedCapacity(gpa, size);
bytes.appendAssumeCapacity(@intFromEnum(Constant.float));
bytes.appendSliceAssumeCapacity(
&std.mem.toBytes(std.mem.nativeToBig(u64, v)),
);
},
.str => |index| {
const slice = self.builder.intern_pool.string_bytes.items[@intFromEnum(index)..];
const str_bytes = slice[0..std.mem.indexOfScalar(u8, slice, 0).? :0];
const str_len: u16 = @intCast(str_bytes.len);
const size: u32 = 1 + @sizeOf(u16) + @sizeOf(u16);
self.header.size += size;
try bytes.ensureUnusedCapacity(gpa, size);
bytes.appendAssumeCapacity(@intFromEnum(Constant.str_index));
bytes.appendSliceAssumeCapacity(
&std.mem.toBytes(std.mem.nativeToBig(u16, str_len)),
);
bytes.appendSliceAssumeCapacity(
&std.mem.toBytes(std.mem.nativeToBig(u16, @intCast(@intFromEnum(index)))),
);
},
}
}
fn addKnot(self: *Section, knot: InternPool.Knot) !void {
const builder = self.builder;
const gpa = builder.gpa;
const bytes = &self.bytes;
const section_size = @sizeOf(u32) + @sizeOf(CodeHeader) +
knot.code_chunk.constants.items.len +
knot.code_chunk.bytecode.items.len;
self.header.size += @intCast(section_size);
const string_const = builder.intern_pool.values.items[@intFromEnum(knot.name_index)];
const string_index = string_const.str;
try bytes.ensureUnusedCapacity(gpa, section_size);
const code_header: CodeHeader = .{
.args_count = @intCast(knot.code_chunk.args_count),
.locals_count = @intCast(knot.code_chunk.locals_count),
.stack_size = @intCast(knot.code_chunk.stack_size),
.constants_len = @intCast(knot.code_chunk.constants.items.len),
.bytecode_len = @intCast(knot.code_chunk.bytecode.items.len),
};
try fromCompilationCompat(gpa, mod, &story, options);
return story;
bytes.appendSliceAssumeCapacity(
&std.mem.toBytes(std.mem.nativeToBig(u32, @intFromEnum(string_index))),
);
bytes.appendAssumeCapacity(code_header.args_count);
bytes.appendAssumeCapacity(code_header.locals_count);
bytes.appendSliceAssumeCapacity(
&std.mem.toBytes(std.mem.nativeToBig(u16, code_header.stack_size)),
);
bytes.appendSliceAssumeCapacity(
&std.mem.toBytes(std.mem.nativeToBig(u16, code_header.constants_len)),
);
bytes.appendSliceAssumeCapacity(
&std.mem.toBytes(std.mem.nativeToBig(u16, code_header.bytecode_len)),
);
bytes.appendSliceAssumeCapacity(knot.code_chunk.constants.items);
bytes.appendSliceAssumeCapacity(knot.code_chunk.bytecode.items);
}
};
fn makeSection(self: *Builder, tag: SectionHeader.Tag) Section {
return .{
.builder = self,
.header = .{ .kind = tag, .size = 0 },
};
}
};
pub fn writeFile(comp: *Compilation, writer: *std.Io.Writer) !void {
const endian: std.builtin.Endian = .big;
const gpa = comp.gpa;
const ip = &comp.intern_pool;
var bytes: std.ArrayList(u8) = .empty;
defer bytes.deinit(comp.gpa);
var builder: Builder = .{
.gpa = gpa,
.intern_pool = ip,
.endian = endian,
};
const file_header: FileHeader = .{
.sections_count = @intCast(2 + comp.knots.items.len),
};
try writer.writeStruct(file_header, endian);
var strings_section = builder.makeSection(.strings);
defer strings_section.bytes.deinit(gpa);
try strings_section.bytes.appendSlice(gpa, ip.string_bytes.items);
strings_section.header.size += @intCast(ip.string_bytes.items.len);
try writer.writeStruct(strings_section.header, endian);
try writer.writeAll(strings_section.bytes.items);
var const_section = builder.makeSection(.constants);
defer const_section.bytes.deinit(gpa);
for (ip.values.items) |value| {
try const_section.addConstant(value);
}
try writer.writeStruct(const_section.header, endian);
try writer.writeAll(const_section.bytes.items);
for (comp.knots.items) |knot| {
var knot_section = builder.makeSection(.knot);
try knot_section.addKnot(knot);
try writer.writeStruct(knot_section.header, endian);
try writer.writeAll(knot_section.bytes.items);
}
try writer.flush();
}
pub fn fromCachedCompilation(
gpa: std.mem.Allocator,
bytes: []const u8,
options: Options,
) !Story {
_ = gpa;
_ = bytes;
_ = options;
return error.NotImplemented;
fn makeValueFromInterned(story: *Story, value: InternPool.Key) !Story.Value {
return switch (value) {
.bool => |boolean| .{ .bool = boolean },
.int => |int| .{ .int = @intCast(int) },
.float => |float| .{ .float = @bitCast(float) },
.str => |index| blk: {
const str_object = try Object.String.create(story, .{
.bytes = stringBytes(story, index),
});
break :blk .{ .object = &str_object.base };
},
};
}
fn makeConstantsPool(mod: *Compilation, story: *Story) ![]const Story.Value {
const ip = &mod.intern_pool;
const gpa = mod.gpa;
var constants_pool: std.ArrayList(Story.Value) = .empty;
defer constants_pool.deinit(mod.gpa);
try constants_pool.ensureUnusedCapacity(mod.gpa, ip.values.items.len);
for (ip.values.items) |value| {
const obj = try makeValueFromInterned(story, value);
constants_pool.appendAssumeCapacity(obj);
}
return constants_pool.toOwnedSlice(gpa);
}
fn makeKnotObject(
story: *Story,
name: []const u8,
code: *InternPool.CodeChunk,
) !*Object.Knot {
return .create(story, .{
.name = name,
.code = try .create(story, .{
.args_count = @intCast(code.args_count),
.locals_count = @intCast(code.locals_count),
.stack_size = @intCast(code.stack_size),
.constants = try code.constants.toOwnedSlice(story.gpa),
.code_bytes = try code.bytecode.toOwnedSlice(story.gpa),
}),
});
}
fn stringBytes(story: *Story, index: InternPool.NullTerminatedString) [:0]const u8 {
const slice = story.string_bytes[@intFromEnum(index)..];
return slice[0..std.mem.indexOfScalar(u8, slice, 0).? :0];
}

View file

@ -81,10 +81,11 @@ pub const Compilation = struct {
arena: std.heap.ArenaAllocator,
tree: Ast,
ir: Ir,
globals: std.ArrayListUnmanaged(InternPool.Global) = .empty,
knots: std.ArrayListUnmanaged(InternPool.Knot) = .empty,
stitches: std.ArrayListUnmanaged(InternPool.Stitch) = .empty,
errors: *std.ArrayListUnmanaged(Error),
globals: std.ArrayList(InternPool.Global) = .empty,
knots: std.ArrayList(InternPool.Knot) = .empty,
stitches: std.ArrayList(InternPool.Stitch) = .empty,
code_chunks: std.ArrayList(*InternPool.CodeChunk) = .empty,
errors: *std.ArrayList(Error),
intern_pool: InternPool = .{},
work_queue: WorkQueue = .{},
@ -291,9 +292,9 @@ pub const Compilation = struct {
cu: *Compilation,
arena: std.mem.Allocator,
) error{OutOfMemory}!*InternPool.CodeChunk {
_ = cu;
const chunk = try arena.create(InternPool.CodeChunk);
chunk.* = .{};
try cu.code_chunks.append(cu.gpa, chunk);
return chunk;
}
@ -338,11 +339,20 @@ pub const Compilation = struct {
pub fn deinit(mod: *Compilation) void {
const gpa = mod.gpa;
mod.arena.deinit();
var i: usize = 0;
while (i < mod.code_chunks.items.len) : (i += 1) {
const chunk = mod.code_chunks.items[i];
chunk.bytecode.deinit(gpa);
chunk.constants.deinit(gpa);
}
mod.intern_pool.deinit(gpa);
mod.globals.deinit(gpa);
mod.knots.deinit(gpa);
mod.stitches.deinit(gpa);
mod.code_chunks.deinit(gpa);
mod.arena.deinit();
mod.* = undefined;
}
};

View file

@ -9,7 +9,6 @@ const fatal = std.process.fatal;
var stdin_buffer: [4096]u8 align(std.heap.page_size_min) = undefined;
var stdout_buffer: [4096]u8 align(std.heap.page_size_min) = undefined;
var stderr_buffer: [4096]u8 align(std.heap.page_size_min) = undefined;
var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
const ArgsIterator = struct {
@ -64,6 +63,7 @@ fn mainArgs(
var dump_trace: bool = false;
var use_stdin: bool = false;
var use_color: bool = false;
const stack_size = 128;
var args_iter: ArgsIterator = .{
.args = args_list[1..],
@ -98,7 +98,19 @@ fn mainArgs(
}
}
const stdout = std.Io.File.stdout();
var stdout_writer = stdout.writer(io, &stdout_buffer);
const log_writer = &stdout_writer.interface;
const filename = source_path orelse "<STDIN>";
const file_ext: FileExtension = f: {
const bytes = std.fs.path.extension(filename);
if (std.mem.eql(u8, bytes, ".ink")) break :f .ink;
if (std.mem.eql(u8, bytes, ".inkc")) break :f .inkc;
return error.InvalidFileExtension;
};
var story: Story = story: switch (file_ext) {
.ink => {
const source_bytes: [:0]const u8 = s: {
var f = if (source_path) |p| file: {
break :file std.Io.Dir.cwd().openFile(io, p, .{}) catch |err| {
@ -113,19 +125,6 @@ fn mainArgs(
};
};
const stderr = std.Io.File.stderr();
var stderr_writer = stderr.writer(io, &stderr_buffer);
const io_w = &stderr_writer.interface;
const stack_size = 128;
const file_ext: FileExtension = f: {
const bytes = std.fs.path.extension(filename);
if (std.mem.eql(u8, bytes, ".ink")) break :f .ink;
if (std.mem.eql(u8, bytes, ".inkc")) break :f .inkc;
return error.InvalidFileExtension;
};
switch (file_ext) {
.ink => {
var tree = try Ast.parse(gpa, arena, source_bytes, filename, 0);
defer tree.deinit(gpa);
@ -136,16 +135,17 @@ fn mainArgs(
defer errors.deinit(gpa);
if (dump_ast) {
try io_w.writeAll("=== AST ===\n");
try tree.render(gpa, io_w, .{
try log_writer.writeAll("=== AST ===\n");
try tree.render(gpa, log_writer, .{
.use_color = use_color,
});
try io_w.flush();
try log_writer.flush();
}
if (dump_ir) {
try io_w.writeAll("=== IR ===\n");
try ir.render(io_w);
try io_w.flush();
try log_writer.writeAll("=== IR ===\n");
try ir.dumpInfo(log_writer);
try ir.render(log_writer);
try log_writer.flush();
}
var cu = Compilation.build(gpa, tree, ir, &errors) catch |err| switch (err) {
@ -155,41 +155,62 @@ fn mainArgs(
if (cu.hasCompileErrors()) {
for (cu.errors.items) |err| {
try cu.renderError(io_w, err);
try cu.renderError(log_writer, err);
}
} else if (output_path) |_| {
return error.NotImplemented;
} else {
var story: Story = try .fromCompilation(gpa, &cu, .{
}
if (output_path) |path| {
const file = try std.Io.Dir.cwd().createFile(io, path, .{});
defer file.close(io);
var file_writer = file.writer(io, &.{});
try Story.Loader.writeFile(&cu, &file_writer.interface);
try file_writer.interface.flush();
return;
}
break :story try .fromCompilation(gpa, &cu, .{
.stack_size = stack_size,
});
defer story.deinit();
if (dump_story) {
try story.dump(io_w);
}
if (dump_trace) {
story.dump_writer = io_w;
}
return if (!compile_only) run(io, gpa, &story);
}
},
.inkc => {
var story: Story = try .fromCachedCompilation(gpa, source_bytes, .{
const source_bytes: [:0]const u8 = s: {
var f = if (source_path) |p| file: {
break :file std.Io.Dir.cwd().openFile(io, p, .{}) catch |err| {
fatal("unable to open file '{s}': {s}", .{ p, @errorName(err) });
};
} else std.Io.File.stdin();
defer if (source_path != null) f.close(io);
var file_reader: std.Io.File.Reader = f.reader(io, &stdin_buffer);
break :s ink.readSourceFileToEndAlloc(arena, &file_reader) catch |err| {
fatal("unable to load file '{s}': {s}", .{ filename, @errorName(err) });
};
};
break :story try .fromCachedCompilation(gpa, source_bytes, .{
.stack_size = stack_size,
});
defer story.deinit();
return if (!compile_only) run(io, gpa, &story);
},
}
}
};
defer story.deinit();
if (dump_story) try story.dump(log_writer);
if (dump_trace) story.dump_writer = log_writer;
fn run(io: std.Io, _: std.mem.Allocator, story: *Story) !void {
const stdin = std.Io.File.stdin();
var stdin_reader = stdin.reader(io, &stdin_buffer);
const stdout = std.Io.File.stdin();
var stdout_writer = stdout.writer(io, &stdout_buffer);
const reader = &stdin_reader.interface;
const writer = &stdout_writer.interface;
return if (!compile_only) run(gpa, &story, .{
.reader = &stdin_reader.interface,
.writer = log_writer,
});
}
pub const RunOptions = struct {
reader: *std.Io.Reader,
writer: *std.Io.Writer,
};
fn run(_: std.mem.Allocator, story: *Story, options: RunOptions) !void {
const reader = options.reader;
const writer = options.writer;
while (!story.is_exited and story.can_advance) {
while (try story.advance()) |content| {