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,
|
gpa: std.mem.Allocator,
|
||||||
bytes: []const u8,
|
bytes: []const u8,
|
||||||
) error{OutOfMemory}!NullTerminatedString {
|
) error{OutOfMemory}!NullTerminatedString {
|
||||||
const str_index: u32 = @intCast(ip.string_bytes.items.len);
|
const string_bytes = &ip.string_bytes;
|
||||||
try ip.string_bytes.ensureUnusedCapacity(gpa, bytes.len + 1);
|
const str_index: u32 = @intCast(string_bytes.items.len);
|
||||||
ip.string_bytes.appendSliceAssumeCapacity(bytes);
|
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{
|
const gop = try ip.string_table.getOrPutContextAdapted(gpa, key, StringIndexAdapter{
|
||||||
.bytes = &ip.string_bytes,
|
.bytes = string_bytes,
|
||||||
}, StringIndexContext{
|
}, StringIndexContext{
|
||||||
.bytes = &ip.string_bytes,
|
.bytes = string_bytes,
|
||||||
});
|
});
|
||||||
if (gop.found_existing) {
|
if (gop.found_existing) {
|
||||||
ip.string_bytes.shrinkRetainingCapacity(str_index);
|
string_bytes.shrinkRetainingCapacity(str_index);
|
||||||
return @enumFromInt(gop.key_ptr.*);
|
return @enumFromInt(gop.key_ptr.*);
|
||||||
} else {
|
} else {
|
||||||
gop.key_ptr.* = str_index;
|
gop.key_ptr.* = str_index;
|
||||||
try ip.string_bytes.append(gpa, 0);
|
try string_bytes.append(gpa, 0);
|
||||||
return @enumFromInt(gop.key_ptr.*);
|
return @enumFromInt(str_index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,15 @@ pub const CallFrame = struct {
|
||||||
bp: usize,
|
bp: usize,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Value = union(enum) {
|
pub const ValueType = enum(u8) {
|
||||||
|
nil,
|
||||||
|
bool,
|
||||||
|
int,
|
||||||
|
float,
|
||||||
|
object,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Value = union(ValueType) {
|
||||||
nil,
|
nil,
|
||||||
bool: bool,
|
bool: bool,
|
||||||
int: i64,
|
int: i64,
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,31 @@ const Dumper = @This();
|
||||||
story: *const Story,
|
story: *const Story,
|
||||||
indent_level: usize = 0,
|
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 {
|
fn dumpSimpleInst(_: *Dumper, w: *std.Io.Writer, offset: usize, op: Opcode) !usize {
|
||||||
try w.print("{s}\n", .{@tagName(op)});
|
try w.print("{s}\n", .{@tagName(op)});
|
||||||
return offset + 1;
|
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 {
|
pub fn dump(story: *Story, writer: *std.Io.Writer) !void {
|
||||||
var story_dumper: Dumper = .{ .story = story };
|
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;
|
try writer.writeAll("=== Constants ===\n");
|
||||||
while (i < story.constants_pool.len) : (i += 1) {
|
try story_dumper.dumpConstants(writer);
|
||||||
const global_constant = &story.constants_pool[i];
|
|
||||||
try story_dumper.dumpValue(writer, global_constant);
|
|
||||||
try writer.writeAll("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
try writer.writeAll("\n");
|
try writer.writeAll("\n");
|
||||||
try writer.writeAll("=== Globals ===\n");
|
try writer.writeAll("=== Globals ===\n");
|
||||||
|
|
|
||||||
|
|
@ -2,63 +2,237 @@ const std = @import("std");
|
||||||
const InternPool = @import("../InternPool.zig");
|
const InternPool = @import("../InternPool.zig");
|
||||||
const Compilation = @import("../compile.zig").Compilation;
|
const Compilation = @import("../compile.zig").Compilation;
|
||||||
const Story = @import("../Story.zig");
|
const Story = @import("../Story.zig");
|
||||||
const Value = Story.Value;
|
|
||||||
const Object = Story.Object;
|
const Object = Story.Object;
|
||||||
|
const Loader = @This();
|
||||||
|
|
||||||
fn makeValueFromInterned(story: *Story, value: InternPool.Key) !Story.Value {
|
gpa: std.mem.Allocator,
|
||||||
return switch (value) {
|
story: *Story,
|
||||||
.bool => |boolean| .{ .bool = boolean },
|
endian: std.builtin.Endian = .big,
|
||||||
.int => |int| .{ .int = @intCast(int) },
|
|
||||||
.float => |float| .{ .float = @bitCast(float) },
|
pub const FileHeader = extern struct {
|
||||||
.str => |index| blk: {
|
magic: [4]u8 = .{ 'I', 'N', 'K', 'C' },
|
||||||
const str_object = try Object.String.create(story, .{
|
version: [2]u8 = .{ 0, 0 },
|
||||||
.bytes = stringBytes(story, index),
|
__padding: u16 = 0,
|
||||||
});
|
sections_count: u32 = 0,
|
||||||
break :blk .{ .object = &str_object.base };
|
};
|
||||||
},
|
|
||||||
|
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(
|
pub const CodeHeader = extern struct {
|
||||||
story: *Story,
|
args_count: u8,
|
||||||
name: []const u8,
|
locals_count: u8,
|
||||||
code: *InternPool.CodeChunk,
|
stack_size: u16,
|
||||||
) !*Object.Knot {
|
constants_len: u16,
|
||||||
return Object.Knot.create(story, .{
|
bytecode_len: u16,
|
||||||
.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),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn makeConstantsPool(mod: *Compilation, story: *Story) ![]const Story.Value {
|
pub const Constant = enum(u8) {
|
||||||
const ip = &mod.intern_pool;
|
bool,
|
||||||
const gpa = mod.gpa;
|
int,
|
||||||
var constants_pool: std.ArrayListUnmanaged(Value) = .empty;
|
float,
|
||||||
defer constants_pool.deinit(mod.gpa);
|
str_index,
|
||||||
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 Options = struct {
|
pub const Options = struct {
|
||||||
stack_size: usize,
|
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(
|
pub fn fromCompilationCompat(
|
||||||
gpa: std.mem.Allocator,
|
gpa: std.mem.Allocator,
|
||||||
mod: *Compilation,
|
mod: *Compilation,
|
||||||
|
|
@ -111,26 +285,202 @@ pub fn fromCompilationCompat(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fromCompilation(
|
pub const Builder = struct {
|
||||||
gpa: std.mem.Allocator,
|
gpa: std.mem.Allocator,
|
||||||
mod: *Compilation,
|
intern_pool: *const InternPool,
|
||||||
options: Options,
|
endian: std.builtin.Endian = .big,
|
||||||
) !Story {
|
|
||||||
var story: Story = .{
|
pub const Section = struct {
|
||||||
.gpa = gpa,
|
builder: *Builder,
|
||||||
.arena = .init(gpa),
|
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),
|
||||||
|
};
|
||||||
|
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);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
try fromCompilationCompat(gpa, mod, &story, options);
|
|
||||||
return story;
|
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(
|
fn makeValueFromInterned(story: *Story, value: InternPool.Key) !Story.Value {
|
||||||
gpa: std.mem.Allocator,
|
return switch (value) {
|
||||||
bytes: []const u8,
|
.bool => |boolean| .{ .bool = boolean },
|
||||||
options: Options,
|
.int => |int| .{ .int = @intCast(int) },
|
||||||
) !Story {
|
.float => |float| .{ .float = @bitCast(float) },
|
||||||
_ = gpa;
|
.str => |index| blk: {
|
||||||
_ = bytes;
|
const str_object = try Object.String.create(story, .{
|
||||||
_ = options;
|
.bytes = stringBytes(story, index),
|
||||||
return error.NotImplemented;
|
});
|
||||||
|
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,
|
arena: std.heap.ArenaAllocator,
|
||||||
tree: Ast,
|
tree: Ast,
|
||||||
ir: Ir,
|
ir: Ir,
|
||||||
globals: std.ArrayListUnmanaged(InternPool.Global) = .empty,
|
globals: std.ArrayList(InternPool.Global) = .empty,
|
||||||
knots: std.ArrayListUnmanaged(InternPool.Knot) = .empty,
|
knots: std.ArrayList(InternPool.Knot) = .empty,
|
||||||
stitches: std.ArrayListUnmanaged(InternPool.Stitch) = .empty,
|
stitches: std.ArrayList(InternPool.Stitch) = .empty,
|
||||||
errors: *std.ArrayListUnmanaged(Error),
|
code_chunks: std.ArrayList(*InternPool.CodeChunk) = .empty,
|
||||||
|
errors: *std.ArrayList(Error),
|
||||||
intern_pool: InternPool = .{},
|
intern_pool: InternPool = .{},
|
||||||
work_queue: WorkQueue = .{},
|
work_queue: WorkQueue = .{},
|
||||||
|
|
||||||
|
|
@ -291,9 +292,9 @@ pub const Compilation = struct {
|
||||||
cu: *Compilation,
|
cu: *Compilation,
|
||||||
arena: std.mem.Allocator,
|
arena: std.mem.Allocator,
|
||||||
) error{OutOfMemory}!*InternPool.CodeChunk {
|
) error{OutOfMemory}!*InternPool.CodeChunk {
|
||||||
_ = cu;
|
|
||||||
const chunk = try arena.create(InternPool.CodeChunk);
|
const chunk = try arena.create(InternPool.CodeChunk);
|
||||||
chunk.* = .{};
|
chunk.* = .{};
|
||||||
|
try cu.code_chunks.append(cu.gpa, chunk);
|
||||||
return chunk;
|
return chunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -338,11 +339,20 @@ pub const Compilation = struct {
|
||||||
|
|
||||||
pub fn deinit(mod: *Compilation) void {
|
pub fn deinit(mod: *Compilation) void {
|
||||||
const gpa = mod.gpa;
|
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.intern_pool.deinit(gpa);
|
||||||
mod.globals.deinit(gpa);
|
mod.globals.deinit(gpa);
|
||||||
mod.knots.deinit(gpa);
|
mod.knots.deinit(gpa);
|
||||||
mod.stitches.deinit(gpa);
|
mod.stitches.deinit(gpa);
|
||||||
|
mod.code_chunks.deinit(gpa);
|
||||||
|
mod.arena.deinit();
|
||||||
mod.* = undefined;
|
mod.* = undefined;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
129
src/main.zig
129
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 stdin_buffer: [4096]u8 align(std.heap.page_size_min) = undefined;
|
||||||
var stdout_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;
|
var debug_allocator: std.heap.DebugAllocator(.{}) = .init;
|
||||||
|
|
||||||
const ArgsIterator = struct {
|
const ArgsIterator = struct {
|
||||||
|
|
@ -64,6 +63,7 @@ fn mainArgs(
|
||||||
var dump_trace: bool = false;
|
var dump_trace: bool = false;
|
||||||
var use_stdin: bool = false;
|
var use_stdin: bool = false;
|
||||||
var use_color: bool = false;
|
var use_color: bool = false;
|
||||||
|
const stack_size = 128;
|
||||||
|
|
||||||
var args_iter: ArgsIterator = .{
|
var args_iter: ArgsIterator = .{
|
||||||
.args = args_list[1..],
|
.args = args_list[1..],
|
||||||
|
|
@ -98,34 +98,33 @@ 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 filename = source_path orelse "<STDIN>";
|
||||||
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) });
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
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 file_ext: FileExtension = f: {
|
||||||
const bytes = std.fs.path.extension(filename);
|
const bytes = std.fs.path.extension(filename);
|
||||||
if (std.mem.eql(u8, bytes, ".ink")) break :f .ink;
|
if (std.mem.eql(u8, bytes, ".ink")) break :f .ink;
|
||||||
if (std.mem.eql(u8, bytes, ".inkc")) break :f .inkc;
|
if (std.mem.eql(u8, bytes, ".inkc")) break :f .inkc;
|
||||||
return error.InvalidFileExtension;
|
return error.InvalidFileExtension;
|
||||||
};
|
};
|
||||||
switch (file_ext) {
|
var story: Story = story: switch (file_ext) {
|
||||||
.ink => {
|
.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| {
|
||||||
|
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) });
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
var tree = try Ast.parse(gpa, arena, source_bytes, filename, 0);
|
var tree = try Ast.parse(gpa, arena, source_bytes, filename, 0);
|
||||||
defer tree.deinit(gpa);
|
defer tree.deinit(gpa);
|
||||||
|
|
||||||
|
|
@ -136,16 +135,17 @@ fn mainArgs(
|
||||||
defer errors.deinit(gpa);
|
defer errors.deinit(gpa);
|
||||||
|
|
||||||
if (dump_ast) {
|
if (dump_ast) {
|
||||||
try io_w.writeAll("=== AST ===\n");
|
try log_writer.writeAll("=== AST ===\n");
|
||||||
try tree.render(gpa, io_w, .{
|
try tree.render(gpa, log_writer, .{
|
||||||
.use_color = use_color,
|
.use_color = use_color,
|
||||||
});
|
});
|
||||||
try io_w.flush();
|
try log_writer.flush();
|
||||||
}
|
}
|
||||||
if (dump_ir) {
|
if (dump_ir) {
|
||||||
try io_w.writeAll("=== IR ===\n");
|
try log_writer.writeAll("=== IR ===\n");
|
||||||
try ir.render(io_w);
|
try ir.dumpInfo(log_writer);
|
||||||
try io_w.flush();
|
try ir.render(log_writer);
|
||||||
|
try log_writer.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
var cu = Compilation.build(gpa, tree, ir, &errors) catch |err| switch (err) {
|
var cu = Compilation.build(gpa, tree, ir, &errors) catch |err| switch (err) {
|
||||||
|
|
@ -155,41 +155,62 @@ fn mainArgs(
|
||||||
|
|
||||||
if (cu.hasCompileErrors()) {
|
if (cu.hasCompileErrors()) {
|
||||||
for (cu.errors.items) |err| {
|
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, .{
|
|
||||||
.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);
|
|
||||||
}
|
}
|
||||||
},
|
if (output_path) |path| {
|
||||||
.inkc => {
|
const file = try std.Io.Dir.cwd().createFile(io, path, .{});
|
||||||
var story: Story = try .fromCachedCompilation(gpa, source_bytes, .{
|
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,
|
.stack_size = stack_size,
|
||||||
});
|
});
|
||||||
defer story.deinit();
|
|
||||||
return if (!compile_only) run(io, gpa, &story);
|
|
||||||
},
|
},
|
||||||
}
|
.inkc => {
|
||||||
}
|
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();
|
||||||
|
|
||||||
|
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();
|
const stdin = std.Io.File.stdin();
|
||||||
var stdin_reader = stdin.reader(io, &stdin_buffer);
|
var stdin_reader = stdin.reader(io, &stdin_buffer);
|
||||||
const stdout = std.Io.File.stdin();
|
return if (!compile_only) run(gpa, &story, .{
|
||||||
var stdout_writer = stdout.writer(io, &stdout_buffer);
|
.reader = &stdin_reader.interface,
|
||||||
const reader = &stdin_reader.interface;
|
.writer = log_writer,
|
||||||
const writer = &stdout_writer.interface;
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (!story.is_exited and story.can_advance) {
|
||||||
while (try story.advance()) |content| {
|
while (try story.advance()) |content| {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue