feat: loading precompiled sections, strings/constants/knots
This commit is contained in:
parent
55ddd2b7cf
commit
127db83041
6 changed files with 553 additions and 142 deletions
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
fn makeKnotObject(
|
||||
gpa: std.mem.Allocator,
|
||||
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),
|
||||
}),
|
||||
});
|
||||
}
|
||||
endian: std.builtin.Endian = .big,
|
||||
|
||||
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);
|
||||
pub const FileHeader = extern struct {
|
||||
magic: [4]u8 = .{ 'I', 'N', 'K', 'C' },
|
||||
version: [2]u8 = .{ 0, 0 },
|
||||
__padding: u16 = 0,
|
||||
sections_count: u32 = 0,
|
||||
};
|
||||
|
||||
for (ip.values.items) |value| {
|
||||
const obj = try makeValueFromInterned(story, value);
|
||||
constants_pool.appendAssumeCapacity(obj);
|
||||
}
|
||||
return constants_pool.toOwnedSlice(gpa);
|
||||
}
|
||||
pub const SectionHeader = extern struct {
|
||||
kind: Tag,
|
||||
flags: u8 = 0,
|
||||
__padding: u16 = 0,
|
||||
size: u32,
|
||||
|
||||
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 Tag = enum(u8) {
|
||||
strings,
|
||||
constants,
|
||||
globals,
|
||||
knot,
|
||||
_,
|
||||
};
|
||||
};
|
||||
|
||||
pub const CodeHeader = extern struct {
|
||||
args_count: u8,
|
||||
locals_count: u8,
|
||||
stack_size: u16,
|
||||
constants_len: u16,
|
||||
bytecode_len: u16,
|
||||
};
|
||||
|
||||
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),
|
||||
};
|
||||
try fromCompilationCompat(gpa, mod, &story, options);
|
||||
return story;
|
||||
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)))),
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fromCachedCompilation(
|
||||
gpa: std.mem.Allocator,
|
||||
bytes: []const u8,
|
||||
options: Options,
|
||||
) !Story {
|
||||
_ = gpa;
|
||||
_ = bytes;
|
||||
_ = options;
|
||||
return error.NotImplemented;
|
||||
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),
|
||||
};
|
||||
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();
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
109
src/main.zig
109
src/main.zig
|
|
@ -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| {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue