feat: ir for declarations and semantic analyzer start
This commit is contained in:
parent
f16162b5bb
commit
197a37ebe7
4 changed files with 453 additions and 145 deletions
231
src/AstGen.zig
231
src/AstGen.zig
|
|
@ -18,21 +18,8 @@ errors: std.ArrayListUnmanaged(Ast.Error) = .empty,
|
||||||
pub const InnerError = error{
|
pub const InnerError = error{
|
||||||
OutOfMemory,
|
OutOfMemory,
|
||||||
SemanticError,
|
SemanticError,
|
||||||
TooManyConstants,
|
|
||||||
InvalidCharacter,
|
InvalidCharacter,
|
||||||
} || anyerror;
|
Overflow,
|
||||||
|
|
||||||
pub const Fixup = struct {
|
|
||||||
mode: enum {
|
|
||||||
relative,
|
|
||||||
absolute,
|
|
||||||
},
|
|
||||||
label_index: usize,
|
|
||||||
code_offset: usize,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Label = struct {
|
|
||||||
code_offset: usize,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const Scope = struct {
|
const Scope = struct {
|
||||||
|
|
@ -73,8 +60,20 @@ const GenIr = struct {
|
||||||
instructions: *std.ArrayListUnmanaged(Ir.Inst.Index),
|
instructions: *std.ArrayListUnmanaged(Ir.Inst.Index),
|
||||||
instructions_top: usize,
|
instructions_top: usize,
|
||||||
|
|
||||||
pub fn unstack(gi: *GenIr) void {
|
const unstacked_top = std.math.maxInt(usize);
|
||||||
gi.instructions.items.len = gi.instructions_top;
|
|
||||||
|
pub fn unstack(self: *GenIr) void {
|
||||||
|
if (self.instructions_top != unstacked_top) {
|
||||||
|
self.instructions.items.len = self.instructions_top;
|
||||||
|
self.instructions_top = unstacked_top;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn instructionsSlice(self: *const GenIr) []Ir.Inst.Index {
|
||||||
|
return if (self.instructions_top == unstacked_top)
|
||||||
|
&[0]Ir.Inst.Index{}
|
||||||
|
else
|
||||||
|
self.instructions.items[self.instructions_top..];
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fail(
|
fn fail(
|
||||||
|
|
@ -85,25 +84,14 @@ const GenIr = struct {
|
||||||
return self.astgen.fail(tag, node);
|
return self.astgen.fail(tag, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn instructionsSlice(gi: *const GenIr) []Ir.Inst.Index {
|
|
||||||
return gi.instructions.items[gi.instructions_top..];
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn makeSubBlock(self: *GenIr) GenIr {
|
pub fn makeSubBlock(self: *GenIr) GenIr {
|
||||||
return .{
|
return .{
|
||||||
.astgen = self.astgen,
|
.astgen = self.astgen,
|
||||||
.instructions = self.instructions,
|
.instructions = self.instructions,
|
||||||
.instructions_top = self.astgen.instructions.items.len,
|
.instructions_top = self.instructions.items.len,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn addGlobal(self: *GenIr, decl: Decl) error{OutOfMemory}!void {
|
|
||||||
const gpa = self.astgen.gpa;
|
|
||||||
try self.astgen.globals.append(gpa, .{
|
|
||||||
.variable = .{ .name = decl.string_index },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add(gi: *GenIr, inst: Ir.Inst) !Ir.Inst.Index {
|
pub fn add(gi: *GenIr, inst: Ir.Inst) !Ir.Inst.Index {
|
||||||
const gpa = gi.astgen.gpa;
|
const gpa = gi.astgen.gpa;
|
||||||
try gi.instructions.ensureUnusedCapacity(gpa, 1);
|
try gi.instructions.ensureUnusedCapacity(gpa, 1);
|
||||||
|
|
@ -152,6 +140,15 @@ const GenIr = struct {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn addDeclRef(gi: *GenIr, decl_ref: Ir.NullTerminatedString) !Ir.Inst.Index {
|
||||||
|
return add(gi, .{
|
||||||
|
.tag = .decl_ref,
|
||||||
|
.data = .{ .string = .{
|
||||||
|
.start = decl_ref,
|
||||||
|
} },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pub fn makePayloadNode(gi: *GenIr, tag: Ir.Inst.Tag) !Ir.Inst.Index {
|
pub fn makePayloadNode(gi: *GenIr, tag: Ir.Inst.Tag) !Ir.Inst.Index {
|
||||||
const gpa = gi.astgen.gpa;
|
const gpa = gi.astgen.gpa;
|
||||||
const inst_index: Ir.Inst.Index = @enumFromInt(gi.astgen.instructions.items.len);
|
const inst_index: Ir.Inst.Index = @enumFromInt(gi.astgen.instructions.items.len);
|
||||||
|
|
@ -164,41 +161,70 @@ const GenIr = struct {
|
||||||
return inst_index;
|
return inst_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn makeDeclNode(gi: *GenIr) !Ir.Inst.Index {
|
pub fn makeDeclaration(gi: *GenIr) !Ir.Inst.Index {
|
||||||
return makePayloadNode(gi, .decl_knot);
|
return makePayloadNode(gi, .declaration);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn makeBlockInst(gi: *GenIr) !Ir.Inst.Index {
|
pub fn makeBlockInst(gi: *GenIr) !Ir.Inst.Index {
|
||||||
return makePayloadNode(gi, .block);
|
return makePayloadNode(gi, .block);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setDecl(
|
pub fn addKnot(gi: *GenIr) !Ir.Inst.Index {
|
||||||
gi: *GenIr,
|
|
||||||
inst: Ir.Inst.Index,
|
|
||||||
name_ref: Ir.NullTerminatedString,
|
|
||||||
) !void {
|
|
||||||
const astgen = gi.astgen;
|
const astgen = gi.astgen;
|
||||||
const gpa = astgen.gpa;
|
const gpa = astgen.gpa;
|
||||||
const body = gi.instructionsSlice();
|
const body = gi.instructionsSlice();
|
||||||
const extra_len = @typeInfo(Ir.Inst.Knot).@"struct".fields.len + body.len;
|
const extra_len = @typeInfo(Ir.Inst.Knot).@"struct".fields.len + body.len;
|
||||||
try astgen.extra.ensureUnusedCapacity(gpa, extra_len);
|
try astgen.extra.ensureUnusedCapacity(gpa, extra_len);
|
||||||
|
|
||||||
const inst_data = &astgen.instructions.items[@intFromEnum(inst)].data;
|
const knot_node = try makePayloadNode(gi, .decl_knot);
|
||||||
|
const inst_data = &astgen.instructions.items[@intFromEnum(knot_node)].data;
|
||||||
inst_data.payload.payload_index = astgen.addExtraAssumeCapacity(
|
inst_data.payload.payload_index = astgen.addExtraAssumeCapacity(
|
||||||
Ir.Inst.Knot{ .name_ref = name_ref, .body_len = @intCast(body.len) },
|
Ir.Inst.Knot{ .body_len = @intCast(body.len) },
|
||||||
);
|
);
|
||||||
|
|
||||||
for (body) |inst_index| {
|
for (body) |inst_index| {
|
||||||
astgen.extra.appendAssumeCapacity(@intFromEnum(inst_index));
|
astgen.extra.appendAssumeCapacity(@intFromEnum(inst_index));
|
||||||
}
|
}
|
||||||
gi.unstack();
|
return knot_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn addVar(gi: *GenIr) !Ir.Inst.Index {
|
||||||
|
const astgen = gi.astgen;
|
||||||
|
const gpa = astgen.gpa;
|
||||||
|
const new_index: Ir.Inst.Index = @enumFromInt(astgen.instructions.items.len);
|
||||||
|
|
||||||
|
try astgen.instructions.ensureUnusedCapacity(gpa, 1);
|
||||||
|
try gi.instructions.ensureUnusedCapacity(gpa, 1);
|
||||||
|
|
||||||
|
gi.astgen.instructions.appendAssumeCapacity(.{
|
||||||
|
.tag = .decl_var,
|
||||||
|
.data = .{
|
||||||
|
.payload = .{ .payload_index = undefined },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const body = gi.instructionsSlice();
|
||||||
|
const extra_len = @typeInfo(Ir.Inst.Var).@"struct".fields.len + body.len;
|
||||||
|
try astgen.extra.ensureUnusedCapacity(gpa, extra_len);
|
||||||
|
|
||||||
|
const inst_data = &astgen.instructions.items[@intFromEnum(new_index)].data;
|
||||||
|
inst_data.payload.payload_index = astgen.addExtraAssumeCapacity(
|
||||||
|
Ir.Inst.Var{ .body_len = @intCast(body.len) },
|
||||||
|
);
|
||||||
|
|
||||||
|
for (body) |inst_index| {
|
||||||
|
astgen.extra.appendAssumeCapacity(@intFromEnum(inst_index));
|
||||||
|
}
|
||||||
|
|
||||||
|
gi.instructions.appendAssumeCapacity(new_index);
|
||||||
|
return new_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setBlockBody(gi: *GenIr, inst: Ir.Inst.Index) !void {
|
pub fn setBlockBody(gi: *GenIr, inst: Ir.Inst.Index) !void {
|
||||||
const astgen = gi.astgen;
|
const astgen = gi.astgen;
|
||||||
const gpa = astgen.gpa;
|
const gpa = astgen.gpa;
|
||||||
const body = gi.instructionsSlice();
|
const body = gi.instructionsSlice();
|
||||||
const extra_len = @typeInfo(Ir.Inst.Knot).@"struct".fields.len + body.len;
|
const extra_len = @typeInfo(Ir.Inst.Block).@"struct".fields.len + body.len;
|
||||||
try astgen.extra.ensureUnusedCapacity(gpa, extra_len);
|
try astgen.extra.ensureUnusedCapacity(gpa, extra_len);
|
||||||
|
|
||||||
const inst_data = &astgen.instructions.items[@intFromEnum(inst)].data;
|
const inst_data = &astgen.instructions.items[@intFromEnum(inst)].data;
|
||||||
|
|
@ -220,6 +246,27 @@ pub fn deinit(astgen: *AstGen) void {
|
||||||
astgen.errors.deinit(gpa);
|
astgen.errors.deinit(gpa);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn setDeclaration(
|
||||||
|
decl_index: Ir.Inst.Index,
|
||||||
|
args: struct {
|
||||||
|
name: Ir.NullTerminatedString,
|
||||||
|
ref: Ir.Inst.Index,
|
||||||
|
body_gi: *GenIr,
|
||||||
|
},
|
||||||
|
) !void {
|
||||||
|
const astgen = args.body_gi.astgen;
|
||||||
|
const gpa = astgen.gpa;
|
||||||
|
const extra_len = @typeInfo(Ir.Inst.Declaration).@"struct".fields.len;
|
||||||
|
try astgen.extra.ensureUnusedCapacity(gpa, extra_len);
|
||||||
|
|
||||||
|
const inst_data = &astgen.instructions.items[@intFromEnum(decl_index)].data;
|
||||||
|
inst_data.payload.payload_index = astgen.addExtraAssumeCapacity(
|
||||||
|
Ir.Inst.Declaration{ .name = args.name, .value = args.ref },
|
||||||
|
);
|
||||||
|
|
||||||
|
args.body_gi.unstack();
|
||||||
|
}
|
||||||
|
|
||||||
fn addExtraAssumeCapacity(astgen: *AstGen, extra: anytype) u32 {
|
fn addExtraAssumeCapacity(astgen: *AstGen, extra: anytype) u32 {
|
||||||
const fields = std.meta.fields(@TypeOf(extra));
|
const fields = std.meta.fields(@TypeOf(extra));
|
||||||
const extra_index: u32 = @intCast(astgen.extra.items.len);
|
const extra_index: u32 = @intCast(astgen.extra.items.len);
|
||||||
|
|
@ -280,7 +327,7 @@ fn stringFromBytes(astgen: *AstGen, bytes: []const u8) error{OutOfMemory}!Ir.Nul
|
||||||
});
|
});
|
||||||
if (gop.found_existing) {
|
if (gop.found_existing) {
|
||||||
string_bytes.shrinkRetainingCapacity(str_index);
|
string_bytes.shrinkRetainingCapacity(str_index);
|
||||||
return @enumFromInt(str_index);
|
return @enumFromInt(gop.key_ptr.*);
|
||||||
} else {
|
} else {
|
||||||
gop.key_ptr.* = str_index;
|
gop.key_ptr.* = str_index;
|
||||||
try string_bytes.append(gpa, 0);
|
try string_bytes.append(gpa, 0);
|
||||||
|
|
@ -346,17 +393,11 @@ fn logicalOp(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trueLiteral(gi: *GenIr) InnerError!Ir.Inst.Index {
|
fn trueLiteral(gi: *GenIr) InnerError!Ir.Inst.Index {
|
||||||
return gi.add(.{
|
return gi.add(.{ .tag = .true_literal, .data = undefined });
|
||||||
.tag = .true_literal,
|
|
||||||
.data = undefined,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn falseLiteral(gi: *GenIr) InnerError!Ir.Inst.Index {
|
fn falseLiteral(gi: *GenIr) InnerError!Ir.Inst.Index {
|
||||||
return gi.add(.{
|
return gi.add(.{ .tag = .false_literal, .data = undefined });
|
||||||
.tag = .false_literal,
|
|
||||||
.data = undefined,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn numberLiteral(gen: *GenIr, node: *const Ast.Node) InnerError!Ir.Inst.Index {
|
fn numberLiteral(gen: *GenIr, node: *const Ast.Node) InnerError!Ir.Inst.Index {
|
||||||
|
|
@ -382,10 +423,10 @@ fn stringExpr(gen: *GenIr, expr_node: *const Ast.Node) InnerError!Ir.Inst.Index
|
||||||
return stringLiteral(gen, first_node);
|
return stringLiteral(gen, first_node);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn identifier(gen: *GenIr, scope: *Scope, node: *const Ast.Node) InnerError!Ir.Inst.Index {
|
fn identifier(gen: *GenIr, _: *Scope, node: *const Ast.Node) InnerError!Ir.Inst.Index {
|
||||||
const name_ref = try gen.astgen.stringFromNode(node);
|
const name_str = try gen.astgen.stringFromNode(node);
|
||||||
if (scope.lookup(name_ref)) |_| {}
|
return gen.addDeclRef(name_str);
|
||||||
return gen.fail(.unknown_identifier, node);
|
//return gen.fail(.unknown_identifier, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expr(gen: *GenIr, scope: *Scope, optional_expr: ?*const Ast.Node) InnerError!Ir.Inst.Index {
|
fn expr(gen: *GenIr, scope: *Scope, optional_expr: ?*const Ast.Node) InnerError!Ir.Inst.Index {
|
||||||
|
|
@ -795,48 +836,33 @@ fn choiceStmt(gen: *GenIr, scope: *Scope, stmt_node: *const Ast.Node) InnerError
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn varDecl(gen: *GenIr, scope: *Scope, decl_node: *const Ast.Node) !void {
|
fn varDecl(gi: *GenIr, scope: *Scope, decl_node: *const Ast.Node) !void {
|
||||||
const identifier_node = decl_node.data.bin.lhs orelse unreachable;
|
const gpa = gi.astgen.gpa;
|
||||||
const expr_node = decl_node.data.bin.rhs orelse unreachable;
|
const identifier_node = decl_node.data.bin.lhs.?;
|
||||||
const name_ref = try gen.astgen.stringFromNode(identifier_node);
|
const expr_node = decl_node.data.bin.rhs.?;
|
||||||
const decl_symbol: Decl = blk: switch (decl_node.tag) {
|
const name_ref = try gi.astgen.stringFromNode(identifier_node);
|
||||||
.temp_decl => {
|
|
||||||
const stack_slot = try gen.makeStackSlot();
|
|
||||||
break :blk .{
|
|
||||||
.local = .{
|
|
||||||
.decl_node = decl_node,
|
|
||||||
.stack_slot = stack_slot,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
.var_decl, .const_decl => |tag| {
|
|
||||||
const constant_slot = try gen.makeConstant(.{
|
|
||||||
.string = name_ref,
|
|
||||||
});
|
|
||||||
break :blk .{
|
|
||||||
.global = .{
|
|
||||||
.decl_node = decl_node,
|
|
||||||
.is_constant = tag == .const_decl,
|
|
||||||
.constant_slot = constant_slot,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
else => unreachable,
|
|
||||||
};
|
|
||||||
|
|
||||||
try scope.insert(name_ref, decl_symbol);
|
if (scope.lookup(name_ref)) |_| {
|
||||||
try expr(gen, scope, expr_node);
|
return gi.fail(.redefined_identifier, decl_node);
|
||||||
|
|
||||||
switch (decl_symbol) {
|
|
||||||
.local => |data| {
|
|
||||||
try gen.emitConstInst(.store, data.stack_slot);
|
|
||||||
},
|
|
||||||
.global => |data| {
|
|
||||||
try gen.emitConstInst(.store_global, data.constant_slot);
|
|
||||||
},
|
|
||||||
else => unreachable,
|
|
||||||
}
|
}
|
||||||
try gen.emitSimpleInst(.pop);
|
|
||||||
|
const decl_inst = try gi.makeDeclaration();
|
||||||
|
try gi.instructions.append(gpa, decl_inst);
|
||||||
|
|
||||||
|
var decl_block = gi.makeSubBlock();
|
||||||
|
defer decl_block.unstack();
|
||||||
|
|
||||||
|
_ = try expr(&decl_block, scope, expr_node);
|
||||||
|
try scope.insert(name_ref, .{
|
||||||
|
.decl_node = decl_node,
|
||||||
|
});
|
||||||
|
|
||||||
|
const var_inst = try decl_block.addVar();
|
||||||
|
try setDeclaration(decl_inst, .{
|
||||||
|
.name = name_ref,
|
||||||
|
.ref = var_inst,
|
||||||
|
.body_gi = &decl_block,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn blockInner(gi: *GenIr, parent_scope: *Scope, stmt_list: []*Ast.Node) !void {
|
fn blockInner(gi: *GenIr, parent_scope: *Scope, stmt_list: []*Ast.Node) !void {
|
||||||
|
|
@ -845,8 +871,8 @@ fn blockInner(gi: *GenIr, parent_scope: *Scope, stmt_list: []*Ast.Node) !void {
|
||||||
|
|
||||||
for (stmt_list) |inner_node| {
|
for (stmt_list) |inner_node| {
|
||||||
_ = switch (inner_node.tag) {
|
_ = switch (inner_node.tag) {
|
||||||
//.var_decl => try varDecl(gen, scope, inner_node),
|
.var_decl => try varDecl(gi, &child_scope, inner_node),
|
||||||
//.const_decl => try varDecl(gen, scope, inner_node),
|
.const_decl => try varDecl(gi, &child_scope, inner_node),
|
||||||
//.temp_decl => try varDecl(gen, scope, inner_node),
|
//.temp_decl => try varDecl(gen, scope, inner_node),
|
||||||
//.assign_stmt => try assignStmt(gen, scope, inner_node),
|
//.assign_stmt => try assignStmt(gen, scope, inner_node),
|
||||||
.content_stmt => try contentStmt(gi, &child_scope, inner_node),
|
.content_stmt => try contentStmt(gi, &child_scope, inner_node),
|
||||||
|
|
@ -884,7 +910,7 @@ fn defaultBlock(
|
||||||
) InnerError!void {
|
) InnerError!void {
|
||||||
const astgen = gi.astgen;
|
const astgen = gi.astgen;
|
||||||
const gpa = astgen.gpa;
|
const gpa = astgen.gpa;
|
||||||
const decl_inst = try gi.makeDeclNode();
|
const decl_inst = try gi.makeDeclaration();
|
||||||
try gi.instructions.append(gpa, decl_inst);
|
try gi.instructions.append(gpa, decl_inst);
|
||||||
|
|
||||||
var decl_scope = gi.makeSubBlock();
|
var decl_scope = gi.makeSubBlock();
|
||||||
|
|
@ -894,9 +920,12 @@ fn defaultBlock(
|
||||||
const block_stmts = body_node.data.list.items orelse unreachable;
|
const block_stmts = body_node.data.list.items orelse unreachable;
|
||||||
try blockInner(&decl_scope, scope, block_stmts);
|
try blockInner(&decl_scope, scope, block_stmts);
|
||||||
|
|
||||||
std.debug.print("{any}\n", .{decl_scope});
|
const knot_inst = try decl_scope.addKnot();
|
||||||
const name_ref = try decl_scope.astgen.stringFromBytes("$__main__$");
|
try setDeclaration(decl_inst, .{
|
||||||
try decl_scope.setDecl(decl_inst, name_ref);
|
.name = try astgen.stringFromBytes("$__main__$"),
|
||||||
|
.ref = knot_inst,
|
||||||
|
.body_gi = &decl_scope,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stitchDecl(_: *GenIr, _: *Scope, _: *const Ast.Node) InnerError!void {}
|
fn stitchDecl(_: *GenIr, _: *Scope, _: *const Ast.Node) InnerError!void {}
|
||||||
|
|
@ -932,18 +961,16 @@ fn knotDecl(gen: *GenIr, scope: *Scope, decl_node: *const Ast.Node) InnerError!v
|
||||||
fn file(root_gi: *GenIr, scope: *Scope, file_node: *const Ast.Node) InnerError!void {
|
fn file(root_gi: *GenIr, scope: *Scope, file_node: *const Ast.Node) InnerError!void {
|
||||||
const astgen = root_gi.astgen;
|
const astgen = root_gi.astgen;
|
||||||
const gpa = astgen.gpa;
|
const gpa = astgen.gpa;
|
||||||
|
|
||||||
const file_inst = try root_gi.makePayloadNode(.file);
|
const file_inst = try root_gi.makePayloadNode(.file);
|
||||||
try root_gi.instructions.append(gpa, file_inst);
|
try root_gi.instructions.append(gpa, file_inst);
|
||||||
|
|
||||||
|
var start_index: usize = 0;
|
||||||
var file_scope = root_gi.makeSubBlock();
|
var file_scope = root_gi.makeSubBlock();
|
||||||
defer file_scope.unstack();
|
defer file_scope.unstack();
|
||||||
// TODO: Make sure this is non-nullable.
|
// TODO: Make sure this is non-nullable.
|
||||||
const nested_decls_list = file_node.data.list.items orelse return;
|
const nested_decls_list = file_node.data.list.items orelse return;
|
||||||
if (nested_decls_list.len == 0) return;
|
if (nested_decls_list.len == 0) return;
|
||||||
|
|
||||||
var start_index: usize = 0;
|
|
||||||
|
|
||||||
const first_child = nested_decls_list[0];
|
const first_child = nested_decls_list[0];
|
||||||
if (first_child.tag == .block_stmt) {
|
if (first_child.tag == .block_stmt) {
|
||||||
try defaultBlock(&file_scope, scope, first_child);
|
try defaultBlock(&file_scope, scope, first_child);
|
||||||
|
|
@ -975,7 +1002,7 @@ pub fn generate(gpa: std.mem.Allocator, tree: *const Ast) !Ir {
|
||||||
};
|
};
|
||||||
defer astgen.deinit();
|
defer astgen.deinit();
|
||||||
|
|
||||||
// First entry is reserved for the empty string sentinel.
|
// First entry is reserved for Ir.NullTerminatedString.empty.
|
||||||
try astgen.string_bytes.append(gpa, 0);
|
try astgen.string_bytes.append(gpa, 0);
|
||||||
|
|
||||||
var instructions: std.ArrayListUnmanaged(Ir.Inst.Index) = .empty;
|
var instructions: std.ArrayListUnmanaged(Ir.Inst.Index) = .empty;
|
||||||
|
|
|
||||||
52
src/Ir.zig
52
src/Ir.zig
|
|
@ -18,7 +18,10 @@ pub const Inst = struct {
|
||||||
|
|
||||||
pub const Tag = enum {
|
pub const Tag = enum {
|
||||||
file,
|
file,
|
||||||
|
declaration,
|
||||||
decl_knot,
|
decl_knot,
|
||||||
|
decl_var,
|
||||||
|
decl_ref,
|
||||||
block,
|
block,
|
||||||
add,
|
add,
|
||||||
sub,
|
sub,
|
||||||
|
|
@ -57,8 +60,16 @@ pub const Inst = struct {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const Declaration = struct {
|
||||||
|
name: NullTerminatedString,
|
||||||
|
value: Inst.Index,
|
||||||
|
};
|
||||||
|
|
||||||
pub const Knot = struct {
|
pub const Knot = struct {
|
||||||
name_ref: NullTerminatedString,
|
body_len: u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Var = struct {
|
||||||
body_len: u32,
|
body_len: u32,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -151,12 +162,22 @@ const Render = struct {
|
||||||
try io_w.writeAll(")");
|
try io_w.writeAll(")");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn renderKnotInst(r: *Render, ir: Ir, inst: Inst) Error!void {
|
fn renderKnotDecl(r: *Render, ir: Ir, inst: Inst) Error!void {
|
||||||
const io_w = r.writer;
|
const io_w = r.writer;
|
||||||
const extra = ir.extraData(Inst.Knot, inst.data.payload.payload_index);
|
const extra = ir.extraData(Inst.Knot, inst.data.payload.payload_index);
|
||||||
const body_slice = ir.bodySlice(extra.end, extra.data.body_len);
|
const body_slice = ir.bodySlice(extra.end, extra.data.body_len);
|
||||||
const knot_ident_str = ir.nullTerminatedString(extra.data.name_ref);
|
|
||||||
try io_w.print("{s}(name=\"{s}\",body=", .{ @tagName(inst.tag), knot_ident_str });
|
try io_w.print("{s}(body=", .{@tagName(inst.tag)});
|
||||||
|
try renderBodyInner(r, ir, body_slice);
|
||||||
|
try io_w.writeAll(")");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn renderVarDecl(r: *Render, ir: Ir, inst: Inst) Error!void {
|
||||||
|
const io_w = r.writer;
|
||||||
|
const extra = ir.extraData(Inst.Knot, inst.data.payload.payload_index);
|
||||||
|
const body_slice = ir.bodySlice(extra.end, extra.data.body_len);
|
||||||
|
|
||||||
|
try io_w.print("{s}(body=", .{@tagName(inst.tag)});
|
||||||
try renderBodyInner(r, ir, body_slice);
|
try renderBodyInner(r, ir, body_slice);
|
||||||
try io_w.writeAll(")");
|
try io_w.writeAll(")");
|
||||||
}
|
}
|
||||||
|
|
@ -171,6 +192,21 @@ const Render = struct {
|
||||||
return io_w.writeAll(")");
|
return io_w.writeAll(")");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn renderDeclaration(r: *Render, ir: Ir, inst: Inst) Error!void {
|
||||||
|
const io_w = r.writer;
|
||||||
|
const extra = ir.extraData(Inst.Declaration, inst.data.payload.payload_index);
|
||||||
|
const ident_str = ir.nullTerminatedString(extra.data.name);
|
||||||
|
|
||||||
|
try io_w.print("{s}(name=\"{s}\", value={{\n", .{ @tagName(inst.tag), ident_str });
|
||||||
|
{
|
||||||
|
const old_len = try r.prefix.pushChildPrefix(r.gpa);
|
||||||
|
defer r.prefix.restore(old_len);
|
||||||
|
try renderInst(r, ir, extra.data.value);
|
||||||
|
}
|
||||||
|
try r.prefix.writeIndent(io_w);
|
||||||
|
return io_w.writeAll(")");
|
||||||
|
}
|
||||||
|
|
||||||
fn renderInst(r: *Render, ir: Ir, index: Inst.Index) Error!void {
|
fn renderInst(r: *Render, ir: Ir, index: Inst.Index) Error!void {
|
||||||
const io_w = r.writer;
|
const io_w = r.writer;
|
||||||
const inst_index: u32 = @intFromEnum(index);
|
const inst_index: u32 = @intFromEnum(index);
|
||||||
|
|
@ -180,7 +216,13 @@ const Render = struct {
|
||||||
try io_w.print("%{d} = ", .{inst_index});
|
try io_w.print("%{d} = ", .{inst_index});
|
||||||
switch (inst.tag) {
|
switch (inst.tag) {
|
||||||
.file => try r.renderFileInst(ir, inst),
|
.file => try r.renderFileInst(ir, inst),
|
||||||
.decl_knot => try r.renderKnotInst(ir, inst),
|
.declaration => try r.renderDeclaration(ir, inst),
|
||||||
|
.decl_knot => try r.renderKnotDecl(ir, inst),
|
||||||
|
.decl_var => try r.renderVarDecl(ir, inst),
|
||||||
|
.decl_ref => {
|
||||||
|
const str_bytes = inst.data.string.get(ir);
|
||||||
|
try io_w.print("{s}(\"{s}\")", .{ @tagName(inst.tag), str_bytes });
|
||||||
|
},
|
||||||
.block => try r.renderBlockInst(ir, inst),
|
.block => try r.renderBlockInst(ir, inst),
|
||||||
.add => try r.renderBinaryInst(inst),
|
.add => try r.renderBinaryInst(inst),
|
||||||
.sub => try r.renderBinaryInst(inst),
|
.sub => try r.renderBinaryInst(inst),
|
||||||
|
|
|
||||||
261
src/Sema.zig
Normal file
261
src/Sema.zig
Normal file
|
|
@ -0,0 +1,261 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const Ir = @import("Ir.zig");
|
||||||
|
const Story = @import("Story.zig");
|
||||||
|
const Object = Story.Object;
|
||||||
|
const Sema = @This();
|
||||||
|
|
||||||
|
gpa: std.mem.Allocator,
|
||||||
|
ir: *const Ir,
|
||||||
|
bytecode: std.ArrayListUnmanaged(u8) = .empty,
|
||||||
|
constants: std.ArrayListUnmanaged(CompiledStory.Constant) = .empty,
|
||||||
|
knots: std.ArrayListUnmanaged(CompiledStory.Knot) = .empty,
|
||||||
|
|
||||||
|
const InnerError = error{
|
||||||
|
OutOfMemory,
|
||||||
|
TooManyConstants,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn deinit(sema: *Sema) void {
|
||||||
|
const gpa = sema.gpa;
|
||||||
|
sema.bytecode.deinit(gpa);
|
||||||
|
sema.constants.deinit(gpa);
|
||||||
|
sema.knots.deinit(gpa);
|
||||||
|
sema.* = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolveIndex(sema: *Sema, index: Ir.Inst.Index) Ir.Inst {
|
||||||
|
return sema.ir.instructions[@intFromEnum(index)];
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emitByte(sema: *Sema, byte: u8) !void {
|
||||||
|
const gpa = sema.gpa;
|
||||||
|
return sema.bytecode.append(gpa, byte);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emitByteOp(sema: *Sema, op: Story.Opcode) !void {
|
||||||
|
return emitByte(sema, @intFromEnum(op));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emitConstOp(sema: *Sema, op: Story.Opcode, arg: usize) !void {
|
||||||
|
const gpa = sema.gpa;
|
||||||
|
if (arg >= std.math.maxInt(u8)) return error.TooManyConstants;
|
||||||
|
try sema.bytecode.ensureUnusedCapacity(gpa, 2);
|
||||||
|
sema.bytecode.appendAssumeCapacity(@intFromEnum(op));
|
||||||
|
sema.bytecode.appendAssumeCapacity(@intCast(arg));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emitJumpOp(sema: *Sema, op: Story.Opcode) error{OutOfMemory}!usize {
|
||||||
|
const gpa = sema.gpa;
|
||||||
|
try sema.bytecode.ensureUnusedCapacity(gpa, 3);
|
||||||
|
sema.bytecode.appendAssumeCapacity(@intFromEnum(op));
|
||||||
|
sema.bytecode.appendAssumeCapacity(0xff);
|
||||||
|
sema.bytecode.appendAssumeCapacity(0xff);
|
||||||
|
return sema.bytecode.items.len - 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn makeConstant(sema: *Sema, data: CompiledStory.Constant) !usize {
|
||||||
|
const gpa = sema.gpa;
|
||||||
|
const const_index = sema.constants.items.len;
|
||||||
|
try sema.constants.append(gpa, data);
|
||||||
|
return const_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unaryInst(sema: *Sema, _: Ir.Inst, op: Story.Opcode) InnerError!void {
|
||||||
|
return emitByteOp(sema, op);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn binaryInst(sema: *Sema, _: Ir.Inst, op: Story.Opcode) InnerError!void {
|
||||||
|
return emitByteOp(sema, op);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contentInst(sema: *Sema, _: Ir.Inst) InnerError!void {
|
||||||
|
return emitByteOp(sema, .stream_flush);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn blockInst(sema: *Sema, block_inst: Ir.Inst) InnerError!void {
|
||||||
|
const ir = sema.ir;
|
||||||
|
const extra = ir.extraData(Ir.Inst.Block, block_inst.data.payload.payload_index);
|
||||||
|
const body = ir.bodySlice(extra.end, extra.data.body_len);
|
||||||
|
|
||||||
|
for (body) |inst_index| {
|
||||||
|
const body_inst = resolveIndex(sema, inst_index);
|
||||||
|
try compileInst(sema, body_inst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn integerInst(sema: *Sema, inst: Ir.Inst) InnerError!void {
|
||||||
|
const int_const = try sema.makeConstant(.{
|
||||||
|
.integer = inst.data.integer.value,
|
||||||
|
});
|
||||||
|
return emitConstOp(sema, .load_const, int_const);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stringInst(sema: *Sema, inst: Ir.Inst) InnerError!void {
|
||||||
|
const str_const = try sema.makeConstant(.{
|
||||||
|
.string = inst.data.string.start,
|
||||||
|
});
|
||||||
|
return emitConstOp(sema, .load_const, str_const);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compileInst(sema: *Sema, inst: Ir.Inst) InnerError!void {
|
||||||
|
switch (inst.tag) {
|
||||||
|
.block => try blockInst(sema, inst),
|
||||||
|
.add => try binaryInst(sema, inst, .add),
|
||||||
|
.sub => try binaryInst(sema, inst, .sub),
|
||||||
|
.mul => try binaryInst(sema, inst, .mul),
|
||||||
|
.div => try binaryInst(sema, inst, .div),
|
||||||
|
.mod => try binaryInst(sema, inst, .mod),
|
||||||
|
.neg => try unaryInst(sema, inst, .neg),
|
||||||
|
.content => try contentInst(sema, inst),
|
||||||
|
.string => try stringInst(sema, inst),
|
||||||
|
.integer => try integerInst(sema, inst),
|
||||||
|
else => unreachable,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn innerDecl(sema: *Sema, inst: Ir.Inst) !void {
|
||||||
|
const ir = sema.ir;
|
||||||
|
const extra = ir.extraData(Ir.Inst.Knot, inst.data.payload.payload_index);
|
||||||
|
const body_slice = ir.bodySlice(extra.end, extra.data.body_len);
|
||||||
|
for (body_slice) |inst_index| {
|
||||||
|
const body_inst = resolveIndex(sema, inst_index);
|
||||||
|
try compileInst(sema, body_inst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn declaration(sema: *Sema, inst: Ir.Inst) !void {
|
||||||
|
const gpa = sema.gpa;
|
||||||
|
const ir = sema.ir;
|
||||||
|
const byte_index = sema.bytecode.items.len;
|
||||||
|
const const_index = sema.constants.items.len;
|
||||||
|
const extra = ir.extraData(Ir.Inst.Declaration, inst.data.payload.payload_index);
|
||||||
|
const value_inst = sema.resolveIndex(extra.data.value);
|
||||||
|
switch (value_inst.tag) {
|
||||||
|
.decl_var => try innerDecl(sema, value_inst),
|
||||||
|
.decl_knot => try innerDecl(sema, value_inst),
|
||||||
|
else => unreachable,
|
||||||
|
}
|
||||||
|
|
||||||
|
const name_ref = extra.data.name;
|
||||||
|
try sema.knots.append(gpa, .{
|
||||||
|
.name_ref = name_ref,
|
||||||
|
.arity = 0,
|
||||||
|
.stack_size = 0,
|
||||||
|
.bytecode = .{
|
||||||
|
.start = @intCast(byte_index),
|
||||||
|
.len = @intCast(sema.bytecode.items.len - byte_index),
|
||||||
|
},
|
||||||
|
.constants = .{
|
||||||
|
.start = @intCast(const_index),
|
||||||
|
.len = @intCast(sema.constants.items.len - const_index),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn file(sema: *Sema, inst: Ir.Inst) !void {
|
||||||
|
const extra = sema.ir.extraData(Ir.Inst.Block, inst.data.payload.payload_index);
|
||||||
|
const body = sema.ir.bodySlice(extra.end, extra.data.body_len);
|
||||||
|
for (body) |inst_index| {
|
||||||
|
const body_inst = sema.resolveIndex(inst_index);
|
||||||
|
switch (body_inst.tag) {
|
||||||
|
.declaration => try declaration(sema, body_inst),
|
||||||
|
else => unreachable,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const CompiledStory = struct {
|
||||||
|
knots: []Knot,
|
||||||
|
constants: []Constant,
|
||||||
|
bytecode: []u8,
|
||||||
|
|
||||||
|
pub const Knot = struct {
|
||||||
|
name_ref: Ir.NullTerminatedString,
|
||||||
|
arity: u32,
|
||||||
|
stack_size: u32,
|
||||||
|
constants: struct {
|
||||||
|
start: u32,
|
||||||
|
len: u32,
|
||||||
|
},
|
||||||
|
bytecode: struct {
|
||||||
|
start: u32,
|
||||||
|
len: u32,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Constant = union(enum) {
|
||||||
|
integer: u64,
|
||||||
|
string: Ir.NullTerminatedString,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn deinit(self: *CompiledStory, gpa: std.mem.Allocator) void {
|
||||||
|
gpa.free(self.knots);
|
||||||
|
gpa.free(self.bytecode);
|
||||||
|
gpa.free(self.constants);
|
||||||
|
self.* = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bytecodeSlice(self: CompiledStory, knot: *const Knot) []const u8 {
|
||||||
|
return self.bytecode[knot.bytecode.start..][0..knot.bytecode.len];
|
||||||
|
}
|
||||||
|
|
||||||
|
fn constantsSlice(self: CompiledStory, knot: *const Knot) []const Constant {
|
||||||
|
return self.constants[knot.constants.start..][0..knot.constants.len];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn buildRuntime(
|
||||||
|
self: *CompiledStory,
|
||||||
|
gpa: std.mem.Allocator,
|
||||||
|
ir: Ir,
|
||||||
|
story: *Story,
|
||||||
|
) !void {
|
||||||
|
for (self.knots) |compiled_knot| {
|
||||||
|
var constant_pool: std.ArrayListUnmanaged(*Object) = .empty;
|
||||||
|
try constant_pool.ensureUnusedCapacity(gpa, compiled_knot.constants.len);
|
||||||
|
defer constant_pool.deinit(gpa);
|
||||||
|
|
||||||
|
const constants_slice = self.constantsSlice(&compiled_knot);
|
||||||
|
for (constants_slice) |constant| {
|
||||||
|
switch (constant) {
|
||||||
|
.integer => |value| {
|
||||||
|
const object: *Object.Number = try .create(story, .{
|
||||||
|
.integer = @intCast(value),
|
||||||
|
});
|
||||||
|
constant_pool.appendAssumeCapacity(&object.base);
|
||||||
|
},
|
||||||
|
.string => |ref| {
|
||||||
|
const bytes = ir.nullTerminatedString(ref);
|
||||||
|
const object: *Object.String = try .create(story, bytes);
|
||||||
|
constant_pool.appendAssumeCapacity(&object.base);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const bytecode_slice = self.bytecodeSlice(&compiled_knot);
|
||||||
|
const chunk_name = ir.nullTerminatedString(compiled_knot.name_ref);
|
||||||
|
const runtime_chunk: *Object.ContentPath = try .create(story, .{
|
||||||
|
.name = try .create(story, chunk_name),
|
||||||
|
.arity = @intCast(compiled_knot.arity),
|
||||||
|
.locals_count = @intCast(compiled_knot.stack_size - compiled_knot.arity),
|
||||||
|
.const_pool = try constant_pool.toOwnedSlice(gpa),
|
||||||
|
.bytes = try gpa.dupe(u8, bytecode_slice),
|
||||||
|
});
|
||||||
|
try story.paths.append(gpa, &runtime_chunk.base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn compile(gpa: std.mem.Allocator, ir: *const Ir) !CompiledStory {
|
||||||
|
var sema: Sema = .{
|
||||||
|
.gpa = gpa,
|
||||||
|
.ir = ir,
|
||||||
|
};
|
||||||
|
defer sema.deinit();
|
||||||
|
|
||||||
|
try file(&sema, ir.instructions[0]);
|
||||||
|
return .{
|
||||||
|
.bytecode = try sema.bytecode.toOwnedSlice(gpa),
|
||||||
|
.constants = try sema.constants.toOwnedSlice(gpa),
|
||||||
|
.knots = try sema.knots.toOwnedSlice(gpa),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@ const std = @import("std");
|
||||||
const tokenizer = @import("tokenizer.zig");
|
const tokenizer = @import("tokenizer.zig");
|
||||||
const Ast = @import("Ast.zig");
|
const Ast = @import("Ast.zig");
|
||||||
const AstGen = @import("AstGen.zig");
|
const AstGen = @import("AstGen.zig");
|
||||||
|
const Sema = @import("Sema.zig");
|
||||||
pub const Object = @import("Story/object.zig").Object;
|
pub const Object = @import("Story/object.zig").Object;
|
||||||
const Dumper = @import("Story/Dumper.zig");
|
const Dumper = @import("Story/Dumper.zig");
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
|
|
@ -481,11 +482,11 @@ pub fn loadFromString(
|
||||||
) !Story {
|
) !Story {
|
||||||
var arena_allocator = std.heap.ArenaAllocator.init(gpa);
|
var arena_allocator = std.heap.ArenaAllocator.init(gpa);
|
||||||
defer arena_allocator.deinit();
|
defer arena_allocator.deinit();
|
||||||
|
|
||||||
const arena = arena_allocator.allocator();
|
const arena = arena_allocator.allocator();
|
||||||
const ast = try Ast.parse(gpa, arena, source_bytes, "<STDIN>", 0);
|
const ast = try Ast.parse(gpa, arena, source_bytes, "<STDIN>", 0);
|
||||||
|
|
||||||
if (options.dump_writer) |w| {
|
if (options.dump_writer) |w| {
|
||||||
|
try w.writeAll("=== AST ===\n");
|
||||||
try ast.render(gpa, w, .{
|
try ast.render(gpa, w, .{
|
||||||
.use_color = options.use_color,
|
.use_color = options.use_color,
|
||||||
});
|
});
|
||||||
|
|
@ -497,48 +498,25 @@ pub fn loadFromString(
|
||||||
return error.Invalid;
|
return error.Invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
const comp_unit = try AstGen.generate(gpa, &ast);
|
var sem_ir = try AstGen.generate(gpa, &ast);
|
||||||
defer comp_unit.deinit(gpa);
|
defer sem_ir.deinit(gpa);
|
||||||
comp_unit.dumpStringsWithHex();
|
|
||||||
|
if (options.dump_writer) |w| {
|
||||||
|
try w.writeAll("=== Semantic IR ===\n");
|
||||||
|
sem_ir.dumpStringsWithHex();
|
||||||
|
try sem_ir.render(gpa, w);
|
||||||
|
}
|
||||||
|
|
||||||
|
var compiled = try Sema.compile(gpa, &sem_ir);
|
||||||
|
defer compiled.deinit(gpa);
|
||||||
|
|
||||||
var story: Story = .{
|
var story: Story = .{
|
||||||
.allocator = gpa,
|
.allocator = gpa,
|
||||||
.can_advance = false,
|
.can_advance = false,
|
||||||
.dump_writer = options.dump_writer,
|
.dump_writer = options.dump_writer,
|
||||||
};
|
};
|
||||||
errdefer story.deinit();
|
try compiled.buildRuntime(gpa, sem_ir, &story);
|
||||||
|
// try story.divert("$__main__$");
|
||||||
for (comp_unit.knots) |compiled_chunk| {
|
// story.can_advance = true;
|
||||||
const chunk_name = comp_unit.resolveString(compiled_chunk.name_ref);
|
|
||||||
var constant_pool: std.ArrayList(*Object) = .empty;
|
|
||||||
try constant_pool.ensureUnusedCapacity(gpa, compiled_chunk.constants.len);
|
|
||||||
defer constant_pool.deinit(gpa);
|
|
||||||
|
|
||||||
for (comp_unit.resolveConstants(compiled_chunk.constants)) |constant| {
|
|
||||||
switch (constant) {
|
|
||||||
.number => |value| {
|
|
||||||
const object: *Object.Number = try .create(&story, .{ .integer = value });
|
|
||||||
constant_pool.appendAssumeCapacity(&object.base);
|
|
||||||
},
|
|
||||||
.string => |ref| {
|
|
||||||
const bytes = comp_unit.resolveString(ref);
|
|
||||||
const object: *Object.String = try .create(&story, bytes);
|
|
||||||
constant_pool.appendAssumeCapacity(&object.base);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const runtime_chunk: *Object.ContentPath = try .create(&story, .{
|
|
||||||
.name = try .create(&story, chunk_name),
|
|
||||||
.arity = @intCast(compiled_chunk.arity),
|
|
||||||
.locals_count = @intCast(compiled_chunk.stack_size - compiled_chunk.arity),
|
|
||||||
.const_pool = try constant_pool.toOwnedSlice(gpa),
|
|
||||||
.bytes = try gpa.dupe(u8, comp_unit.resolveInstructions(compiled_chunk.instructions)),
|
|
||||||
});
|
|
||||||
try story.paths.append(gpa, &runtime_chunk.base);
|
|
||||||
}
|
|
||||||
|
|
||||||
try story.divert("$__main__$");
|
|
||||||
story.can_advance = true;
|
|
||||||
return story;
|
return story;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue