refactor: redefining assumptions about declarations

This commit is contained in:
Brett Broadhurst 2026-03-23 22:45:55 -06:00
parent be297047d1
commit 33e3c1c271
Failed to generate hash of commit
4 changed files with 210 additions and 202 deletions

View file

@ -11,9 +11,8 @@ gpa: std.mem.Allocator,
tree: *const Ast, tree: *const Ast,
string_table: std.HashMapUnmanaged(u32, void, StringIndexContext, std.hash_map.default_max_load_percentage) = .empty, string_table: std.HashMapUnmanaged(u32, void, StringIndexContext, std.hash_map.default_max_load_percentage) = .empty,
string_bytes: std.ArrayListUnmanaged(u8) = .empty, string_bytes: std.ArrayListUnmanaged(u8) = .empty,
globals: std.ArrayListUnmanaged(Ir.Inst.Index) = .empty,
instructions: std.ArrayListUnmanaged(Ir.Inst) = .empty, instructions: std.ArrayListUnmanaged(Ir.Inst) = .empty,
globals: std.ArrayListUnmanaged(Ir.Global) = .empty,
global_ref_table: std.AutoHashMapUnmanaged(Ir.NullTerminatedString, usize) = .empty,
extra: std.ArrayListUnmanaged(u32) = .empty, extra: std.ArrayListUnmanaged(u32) = .empty,
scratch: std.ArrayListUnmanaged(u32) = .empty, scratch: std.ArrayListUnmanaged(u32) = .empty,
compile_errors: std.ArrayListUnmanaged(Ir.Inst.CompileErrors.Item) = .empty, compile_errors: std.ArrayListUnmanaged(Ir.Inst.CompileErrors.Item) = .empty,
@ -166,41 +165,6 @@ fn strFromNode(astgen: *AstGen, node: *const Ast.Node) !IndexSlice {
return astgen.strFromSlice(name_bytes); return astgen.strFromSlice(name_bytes);
} }
fn qualifiedString(
astgen: *AstGen,
prefix: Ir.NullTerminatedString,
relative: Ir.NullTerminatedString,
) !Ir.NullTerminatedString {
const gpa = astgen.gpa;
const string_bytes = &astgen.string_bytes;
const string_table = &astgen.string_table;
const str_index: u32 = @intCast(string_bytes.items.len);
switch (prefix) {
.empty => return relative,
else => |prev| {
try string_bytes.appendSlice(gpa, nullTerminatedString(astgen, prev));
try string_bytes.append(gpa, '.');
try string_bytes.appendSlice(gpa, nullTerminatedString(astgen, relative));
const key: []const u8 = string_bytes.items[str_index..];
const gop = try string_table.getOrPutContextAdapted(gpa, key, StringIndexAdapter{
.bytes = string_bytes,
}, StringIndexContext{
.bytes = string_bytes,
});
if (gop.found_existing) {
string_bytes.shrinkRetainingCapacity(str_index);
return @enumFromInt(gop.key_ptr.*);
} else {
gop.key_ptr.* = str_index;
try string_bytes.append(gpa, 0);
return @enumFromInt(str_index);
}
},
}
}
/// Perform IR code generation via tree-walk. /// Perform IR code generation via tree-walk.
pub fn generate(gpa: std.mem.Allocator, tree: *const Ast) !Ir { pub fn generate(gpa: std.mem.Allocator, tree: *const Ast) !Ir {
var astgen: AstGen = .{ var astgen: AstGen = .{
@ -217,7 +181,6 @@ pub fn generate(gpa: std.mem.Allocator, tree: *const Ast) !Ir {
var file_scope: Scope = .{ var file_scope: Scope = .{
.parent = null, .parent = null,
.decls = .empty, .decls = .empty,
.namespace_prefix = .empty,
.astgen = &astgen, .astgen = &astgen,
}; };
var block: GenIr = .{ var block: GenIr = .{
@ -263,7 +226,6 @@ pub fn generate(gpa: std.mem.Allocator, tree: *const Ast) !Ir {
return .{ return .{
.instructions = if (fatal) &.{} else try astgen.instructions.toOwnedSlice(gpa), .instructions = if (fatal) &.{} else try astgen.instructions.toOwnedSlice(gpa),
.string_bytes = try astgen.string_bytes.toOwnedSlice(gpa), .string_bytes = try astgen.string_bytes.toOwnedSlice(gpa),
.globals = try astgen.globals.toOwnedSlice(gpa),
.extra = try astgen.extra.toOwnedSlice(gpa), .extra = try astgen.extra.toOwnedSlice(gpa),
}; };
} }
@ -273,7 +235,6 @@ fn deinit(astgen: *AstGen) void {
astgen.string_table.deinit(gpa); astgen.string_table.deinit(gpa);
astgen.string_bytes.deinit(gpa); astgen.string_bytes.deinit(gpa);
astgen.globals.deinit(gpa); astgen.globals.deinit(gpa);
astgen.global_ref_table.deinit(gpa);
astgen.instructions.deinit(gpa); astgen.instructions.deinit(gpa);
astgen.extra.deinit(gpa); astgen.extra.deinit(gpa);
astgen.scratch.deinit(gpa); astgen.scratch.deinit(gpa);
@ -429,56 +390,6 @@ const GenIr = struct {
} }); } });
} }
fn makeBlockInst(gi: *GenIr, tag: Ir.Inst.Tag) !Ir.Inst.Index {
const inst_index: Ir.Inst.Index = @enumFromInt(gi.astgen.instructions.items.len);
const gpa = gi.astgen.gpa;
try gi.astgen.instructions.append(gpa, .{
.tag = tag,
.data = .{
.payload = undefined,
},
});
return inst_index;
}
fn addKnot(self: *GenIr) !Ir.Inst.Index {
const gpa = self.astgen.gpa;
const body = self.instructionsSlice();
const extra_len = @typeInfo(Ir.Inst.Knot).@"struct".fields.len + body.len;
try self.astgen.extra.ensureUnusedCapacity(gpa, extra_len);
const new_index: Ir.Inst.Index = @enumFromInt(self.astgen.instructions.items.len);
try self.astgen.instructions.append(gpa, .{
.tag = .decl_knot,
.data = .{
.payload = undefined,
},
});
const inst_data = &self.astgen.instructions.items[@intFromEnum(new_index)].data;
inst_data.payload.extra_index = self.astgen.addExtraAssumeCapacity(
Ir.Inst.Knot{ .body_len = @intCast(body.len) },
);
self.astgen.appendBlockBody(body);
return new_index;
}
fn addVar(gen: *GenIr, node: *const Ast.Node) !Ir.Inst.Index {
const gpa = gen.astgen.gpa;
const body = gen.instructionsSlice();
const extra_len = @typeInfo(Ir.Inst.Var).@"struct".fields.len + body.len;
try gen.astgen.extra.ensureUnusedCapacity(gpa, extra_len);
try gen.astgen.instructions.ensureUnusedCapacity(gpa, 1);
const extra_index = gen.astgen.addExtraAssumeCapacity(Ir.Inst.Var{
.body_len = @intCast(body.len),
});
const new_index = try gen.addPayloadNodeWithIndex(.decl_var, node, extra_index);
gen.astgen.appendBlockBody(body);
gen.instructions.appendAssumeCapacity(new_index.toIndex().?);
return new_index.toIndex().?;
}
fn addCondBr(gen: *GenIr, tag: Ir.Inst.Tag) !Ir.Inst.Index { fn addCondBr(gen: *GenIr, tag: Ir.Inst.Tag) !Ir.Inst.Index {
const gpa = gen.astgen.gpa; const gpa = gen.astgen.gpa;
try gen.instructions.ensureUnusedCapacity(gpa, 1); try gen.instructions.ensureUnusedCapacity(gpa, 1);
@ -509,6 +420,18 @@ const GenIr = struct {
return gen.addPayloadNodeWithIndex(tag, node, extra_index); return gen.addPayloadNodeWithIndex(tag, node, extra_index);
} }
fn makePayloadNode(self: *GenIr, tag: Ir.Inst.Tag) !Ir.Inst.Index {
const astgen = self.astgen;
const inst_index: Ir.Inst.Index = @enumFromInt(astgen.instructions.items.len);
try astgen.instructions.append(astgen.gpa, .{
.tag = tag,
.data = .{
.payload = undefined,
},
});
return inst_index;
}
fn setBlockBody(self: *GenIr, inst: Ir.Inst.Index) !void { fn setBlockBody(self: *GenIr, inst: Ir.Inst.Index) !void {
const gpa = self.astgen.gpa; const gpa = self.astgen.gpa;
const body = self.instructionsSlice(); const body = self.instructionsSlice();
@ -527,7 +450,6 @@ const GenIr = struct {
const Scope = struct { const Scope = struct {
parent: ?*Scope, parent: ?*Scope,
astgen: *AstGen, astgen: *AstGen,
namespace_prefix: Ir.NullTerminatedString,
decls: std.AutoHashMapUnmanaged(Ir.NullTerminatedString, Decl), decls: std.AutoHashMapUnmanaged(Ir.NullTerminatedString, Decl),
const Decl = struct { const Decl = struct {
@ -544,16 +466,10 @@ const Scope = struct {
return .{ return .{
.parent = parent_scope, .parent = parent_scope,
.astgen = parent_scope.astgen, .astgen = parent_scope.astgen,
.namespace_prefix = parent_scope.namespace_prefix,
.decls = .empty, .decls = .empty,
}; };
} }
fn setNamespacePrefix(scope: *Scope, relative: Ir.NullTerminatedString) !void {
const astgen = scope.astgen;
scope.namespace_prefix = try astgen.qualifiedString(scope.namespace_prefix, relative);
}
fn insert(self: *Scope, ref: Ir.NullTerminatedString, decl: Decl) !void { fn insert(self: *Scope, ref: Ir.NullTerminatedString, decl: Decl) !void {
const gpa = self.astgen.gpa; const gpa = self.astgen.gpa;
return self.decls.put(gpa, ref, decl); return self.decls.put(gpa, ref, decl);
@ -573,38 +489,89 @@ fn setDeclaration(
decl_index: Ir.Inst.Index, decl_index: Ir.Inst.Index,
args: struct { args: struct {
name: Ir.NullTerminatedString, name: Ir.NullTerminatedString,
tag: Ir.Global.Tag, value: Ir.Inst.Index,
ref: Ir.Inst.Index, gi: *GenIr,
decl_node: *const Ast.Node,
body_block: *GenIr,
is_constant: bool = true,
}, },
) !void { ) !void {
const astgen = args.body_block.astgen; const astgen = args.gi.astgen;
const gpa = astgen.gpa;
const extra_len = @typeInfo(Ir.Inst.Declaration).@"struct".fields.len; const extra_len = @typeInfo(Ir.Inst.Declaration).@"struct".fields.len;
const global_index = astgen.globals.items.len; try astgen.extra.ensureUnusedCapacity(astgen.gpa, extra_len);
try astgen.extra.ensureUnusedCapacity(gpa, extra_len);
try astgen.globals.ensureUnusedCapacity(gpa, 1);
try astgen.global_ref_table.ensureUnusedCapacity(gpa, 1);
if (astgen.global_ref_table.get(args.name)) |_| {
return fail(astgen, args.decl_node, "redefined identifier", .{});
}
const inst_data = &astgen.instructions.items[@intFromEnum(decl_index)].data; const inst_data = &astgen.instructions.items[@intFromEnum(decl_index)].data;
inst_data.payload.extra_index = astgen.addExtraAssumeCapacity( inst_data.payload.extra_index = astgen.addExtraAssumeCapacity(
Ir.Inst.Declaration{ .name = args.name, .value = args.ref }, Ir.Inst.Declaration{
.name = args.name,
.value = args.value,
},
);
}
fn setDeclVarPayload(
decl_index: Ir.Inst.Index,
body_block: *GenIr,
) !void {
defer body_block.unstack();
const astgen = body_block.astgen;
const body = body_block.instructionsSlice();
const extra_len = @typeInfo(Ir.Inst.Var).@"struct".fields.len + body.len;
try astgen.extra.ensureUnusedCapacity(astgen.gpa, extra_len);
const inst_data = &astgen.instructions.items[@intFromEnum(decl_index)].data;
inst_data.payload.extra_index = astgen.addExtraAssumeCapacity(
Ir.Inst.Var{
.body_len = @intCast(body.len),
},
);
astgen.appendBlockBody(body);
}
fn setDeclStitchPayload(
decl_index: Ir.Inst.Index,
body_block: *GenIr,
) !void {
defer body_block.unstack();
const astgen = body_block.astgen;
const block_body = body_block.instructionsSlice();
const extra_len = @typeInfo(Ir.Inst.Knot).@"struct".fields.len + block_body.len;
try astgen.extra.ensureUnusedCapacity(astgen.gpa, extra_len);
const inst_data = &astgen.instructions.items[@intFromEnum(decl_index)].data;
inst_data.payload.extra_index = astgen.addExtraAssumeCapacity(
Ir.Inst.Stitch{
.body_len = @intCast(block_body.len),
},
); );
astgen.globals.appendAssumeCapacity(.{ astgen.appendBlockBody(block_body);
.tag = args.tag, }
.name = args.name,
.is_constant = args.is_constant, fn setDeclKnotPayload(
}); decl_index: Ir.Inst.Index,
astgen.global_ref_table.putAssumeCapacity(args.name, global_index); body_block: *GenIr,
args.body_block.unstack(); stitches_block: *GenIr,
) !void {
defer body_block.unstack();
defer stitches_block.unstack();
const astgen = body_block.astgen;
const block_body = body_block.instructionsSliceUpto(stitches_block);
const stitches_body = stitches_block.instructionsSlice();
const extra_len =
@typeInfo(Ir.Inst.Knot).@"struct".fields.len + block_body.len + stitches_body.len;
try astgen.extra.ensureUnusedCapacity(astgen.gpa, extra_len);
const inst_data = &astgen.instructions.items[@intFromEnum(decl_index)].data;
inst_data.payload.extra_index = astgen.addExtraAssumeCapacity(
Ir.Inst.Knot{
.body_len = @intCast(block_body.len),
.stitches_len = @intCast(stitches_body.len),
},
);
astgen.appendBlockBody(block_body);
astgen.appendBlockBody(stitches_body);
} }
fn setCondBrPayload( fn setCondBrPayload(
@ -843,7 +810,7 @@ fn ifStmt(
const cond_inst = try expr(&block_scope, scope, cond_expr); const cond_inst = try expr(&block_scope, scope, cond_expr);
const condbr = try block_scope.addCondBr(.condbr); const condbr = try block_scope.addCondBr(.condbr);
const block = try parent_block.makeBlockInst(.block); const block = try parent_block.makePayloadNode(.block);
try block_scope.setBlockBody(block); // unstacks block try block_scope.setBlockBody(block); // unstacks block
try parent_block.instructions.append(astgen.gpa, block); try parent_block.instructions.append(astgen.gpa, block);
@ -888,7 +855,7 @@ fn ifChain(
const body_node = branch.data.bin.rhs.?; const body_node = branch.data.bin.rhs.?;
const cond_inst = try expr(&block_scope, scope, cond_expr); const cond_inst = try expr(&block_scope, scope, cond_expr);
const condbr = try block_scope.addCondBr(.condbr); const condbr = try block_scope.addCondBr(.condbr);
const block_inst = try parent_block.makeBlockInst(.block); const block_inst = try parent_block.makePayloadNode(.block);
try block_scope.setBlockBody(block_inst); try block_scope.setBlockBody(block_inst);
try parent_block.instructions.append(gpa, block_inst); try parent_block.instructions.append(gpa, block_inst);
@ -936,7 +903,7 @@ fn switchStmt(
try validateSwitchProngs(parent_block, stmt_node); try validateSwitchProngs(parent_block, stmt_node);
const cond_inst = try expr(parent_block, scope, switch_stmt.condition_expr); const cond_inst = try expr(parent_block, scope, switch_stmt.condition_expr);
const switch_br = try parent_block.makeBlockInst(.switch_br); const switch_br = try parent_block.makePayloadNode(.switch_br);
var case_indexes: std.ArrayListUnmanaged(u32) = .empty; var case_indexes: std.ArrayListUnmanaged(u32) = .empty;
try case_indexes.ensureUnusedCapacity(gpa, switch_stmt.cases.len); try case_indexes.ensureUnusedCapacity(gpa, switch_stmt.cases.len);
defer case_indexes.deinit(gpa); defer case_indexes.deinit(gpa);
@ -1057,7 +1024,7 @@ fn choiceStmt(
const choice_branches = stmt_node.data.list.items.?; const choice_branches = stmt_node.data.list.items.?;
assert(choice_branches.len != 0); assert(choice_branches.len != 0);
const choice_br = try parent_block.makeBlockInst(.choice_br); const choice_br = try parent_block.makePayloadNode(.choice_br);
var case_indexes: std.ArrayListUnmanaged(u32) = .empty; var case_indexes: std.ArrayListUnmanaged(u32) = .empty;
try case_indexes.ensureUnusedCapacity(gpa, choice_branches.len); try case_indexes.ensureUnusedCapacity(gpa, choice_branches.len);
defer case_indexes.deinit(gpa); defer case_indexes.deinit(gpa);
@ -1180,11 +1147,16 @@ fn callExpr(
const scratch_top = astgen.scratch.items.len; const scratch_top = astgen.scratch.items.len;
defer astgen.scratch.shrinkRetainingCapacity(scratch_top); defer astgen.scratch.shrinkRetainingCapacity(scratch_top);
const args_count = if (args_node) |n| n.data.list.items.?.len else 0; const args_count = if (args_node) |n| switch (n.tag) {
.call_expr => n.data.list.items.?.len,
else => 0,
} else 0;
try astgen.scratch.resize(gpa, scratch_top + args_count); try astgen.scratch.resize(gpa, scratch_top + args_count);
var scratch_index = scratch_top; var scratch_index = scratch_top;
if (args_node) |n| { if (args_node) |n| blk: {
if (n.tag != .call_expr) break :blk;
const args_list = n.data.list.items.?; const args_list = n.data.list.items.?;
for (args_list) |arg| { for (args_list) |arg| {
var arg_block = gi.makeSubBlock(); var arg_block = gi.makeSubBlock();
@ -1229,9 +1201,10 @@ fn callExpr(
fn divertExpr(gi: *GenIr, scope: *Scope, node: *const Ast.Node) !void { fn divertExpr(gi: *GenIr, scope: *Scope, node: *const Ast.Node) !void {
// FIXME: The AST should always have an args list for these nodes. // FIXME: The AST should always have an args list for these nodes.
// FIXME: Oh God, the AST is completely fucked for this.
const lhs = node.data.bin.lhs.?; const lhs = node.data.bin.lhs.?;
switch (lhs.tag) { switch (lhs.tag) {
.identifier => { .identifier, .selector_expr => {
const callee = try calleeExpr(gi, scope, lhs); const callee = try calleeExpr(gi, scope, lhs);
switch (callee) { switch (callee) {
.direct => |callee_obj| { .direct => |callee_obj| {
@ -1249,9 +1222,8 @@ fn divertExpr(gi: *GenIr, scope: *Scope, node: *const Ast.Node) !void {
}, },
} }
}, },
else => { .call_expr => _ = try callExpr(gi, scope, lhs, .divert),
_ = try callExpr(gi, scope, lhs, .divert); else => unreachable,
},
} }
} }
@ -1282,27 +1254,25 @@ fn tempDecl(gi: *GenIr, scope: *Scope, decl_node: *const Ast.Node) !void {
fn varDecl(gi: *GenIr, scope: *Scope, decl_node: *const Ast.Node) !void { fn varDecl(gi: *GenIr, scope: *Scope, decl_node: *const Ast.Node) !void {
const astgen = gi.astgen; const astgen = gi.astgen;
const gpa = astgen.gpa;
const identifier_node = decl_node.data.bin.lhs.?; const identifier_node = decl_node.data.bin.lhs.?;
const expr_node = decl_node.data.bin.rhs.?; const expr_node = decl_node.data.bin.rhs.?;
const decl_inst = try gi.add(.{ const decl_inst = try gi.makePayloadNode(.declaration);
.tag = .declaration,
.data = .{ .payload = undefined },
});
var decl_block = gi.makeSubBlock(); var decl_block = gi.makeSubBlock();
defer decl_block.unstack(); defer decl_block.unstack();
const var_inst = try decl_block.makePayloadNode(.decl_var);
_ = try expr(&decl_block, scope, expr_node); _ = try expr(&decl_block, scope, expr_node);
const var_inst = try decl_block.addVar(decl_node);
const name_str = try astgen.strFromNode(identifier_node); const name_str = try astgen.strFromNode(identifier_node);
try setDeclaration(decl_inst.toIndex().?, .{
.tag = .variable, try setDeclVarPayload(var_inst, &decl_block);
try setDeclaration(decl_inst, .{
.name = name_str.index, .name = name_str.index,
.ref = var_inst, .value = var_inst,
.decl_node = decl_node, .gi = gi,
.body_block = &decl_block,
.is_constant = decl_node.tag == .const_decl,
}); });
try astgen.globals.append(gpa, decl_inst);
} }
fn blockInner(gi: *GenIr, parent_scope: *Scope, stmt_list: []*Ast.Node) !void { fn blockInner(gi: *GenIr, parent_scope: *Scope, stmt_list: []*Ast.Node) !void {
@ -1341,29 +1311,30 @@ fn defaultBlock(
.tag = .declaration, .tag = .declaration,
.data = .{ .payload = undefined }, .data = .{ .payload = undefined },
}); });
var decl_scope = gi.makeSubBlock(); var decl_scope = gi.makeSubBlock();
defer decl_scope.unstack(); defer decl_scope.unstack();
const knot_inst = try decl_scope.makePayloadNode(.decl_knot);
// TODO: Make sure that this value is concrete to omit check. // TODO: Make sure that this value is concrete to omit check.
const block_stmts = body_node.data.list.items.?; const block_stmts = body_node.data.list.items.?;
try blockInner(&decl_scope, scope, block_stmts); try blockInner(&decl_scope, scope, block_stmts);
const name_str = try astgen.strFromSlice(Story.default_knot_name); var stub_scope = decl_scope.makeSubBlock();
const knot_inst = try decl_scope.addKnot(); defer stub_scope.unstack();
try setDeclKnotPayload(knot_inst, &decl_scope, &stub_scope);
const decl_str = try astgen.strFromSlice(Story.default_knot_name);
try setDeclaration(decl_inst, .{ try setDeclaration(decl_inst, .{
.tag = .knot, .name = decl_str.index,
.name = name_str.index, .value = knot_inst,
.ref = knot_inst, .gi = gi,
.decl_node = body_node,
.body_block = &decl_scope,
}); });
} }
fn stitchDeclInner( fn stitchDeclInner(
gi: *GenIr, gi: *GenIr,
scope: *Scope, scope: *Scope,
decl_node: *const Ast.Node, _: *const Ast.Node,
prototype_node: *const Ast.Node, prototype_node: *const Ast.Node,
body_node: ?*const Ast.Node, body_node: ?*const Ast.Node,
) InnerError!void { ) InnerError!void {
@ -1378,6 +1349,8 @@ fn stitchDeclInner(
var decl_block = gi.makeSubBlock(); var decl_block = gi.makeSubBlock();
defer decl_block.unstack(); defer decl_block.unstack();
const stitch_inst = try decl_block.makePayloadNode(.decl_stitch);
if (prototype_data.rhs) |args_node| { if (prototype_data.rhs) |args_node| {
const args_list = args_node.data.list.items.?; const args_list = args_node.data.list.items.?;
for (args_list) |arg| { for (args_list) |arg| {
@ -1398,13 +1371,12 @@ fn stitchDeclInner(
_ = try decl_block.addUnaryNode(.implicit_ret, .none); _ = try decl_block.addUnaryNode(.implicit_ret, .none);
} }
const name_str = try astgen.strFromNode(identifier_node); const decl_str = try astgen.strFromNode(identifier_node);
try setDeclStitchPayload(stitch_inst, &decl_block);
try setDeclaration(decl_inst, .{ try setDeclaration(decl_inst, .{
.tag = .knot, .name = decl_str.index,
.name = try astgen.qualifiedString(scope.namespace_prefix, name_str.index), .value = stitch_inst,
.ref = try decl_block.addKnot(), .gi = gi,
.decl_node = decl_node,
.body_block = &decl_block,
}); });
} }
@ -1421,33 +1393,64 @@ fn stitchDecl(gi: *GenIr, parent_scope: *Scope, decl_node: *const Ast.Node) Inne
fn functionDecl(_: *GenIr, _: *Scope, _: *const Ast.Node) InnerError!void {} fn functionDecl(_: *GenIr, _: *Scope, _: *const Ast.Node) InnerError!void {}
fn knotDecl(gi: *GenIr, parent_scope: *Scope, decl_node: *const Ast.Node) InnerError!void { fn knotDecl(gi: *GenIr, parent_scope: *Scope, decl_node: *const Ast.Node) InnerError!void {
const astgen = gi.astgen;
const knot_data = decl_node.data.knot_decl; const knot_data = decl_node.data.knot_decl;
const prototype_node = knot_data.prototype; const prototype_node = knot_data.prototype;
const identifier_node = prototype_node.data.bin.lhs.?; const identifier_node = prototype_node.data.bin.lhs.?;
const nested_decls_list = knot_data.children.?; const nested_decls_list = knot_data.children.?;
const decl_inst = try gi.addAsIndex(.{
.tag = .declaration,
.data = .{ .payload = undefined },
});
var decl_scope = parent_scope.makeChild(); var child_block = gi.makeSubBlock();
defer decl_scope.deinit(); defer child_block.unstack();
const knot_inst = try gi.makePayloadNode(.decl_knot);
var child_scope = parent_scope.makeChild();
defer child_scope.deinit();
if (prototype_node.data.bin.rhs) |args_node| {
const args_list = args_node.data.list.items.?;
for (args_list) |arg| {
assert(arg.tag == .parameter_decl);
const arg_str = try astgen.strFromNode(arg);
const arg_inst = try child_block.addStrTok(.param, arg_str.index, arg.loc.start);
// TODO: Maybe make decl accept a ref?
try child_scope.insert(arg_str.index, .{
.decl_node = arg,
.inst_index = arg_inst.toIndex().?,
});
}
}
var start_index: usize = 0; 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 stitchDeclInner(gi, &decl_scope, decl_node, prototype_node, first_child); try blockStmt(&child_block, &child_scope, first_child);
start_index += 1; start_index += 1;
} else {
try stitchDeclInner(gi, &decl_scope, decl_node, prototype_node, null);
} }
const name_str = try gi.astgen.strFromNode(identifier_node); var nested_block = child_block.makeSubBlock();
try decl_scope.setNamespacePrefix(name_str.index); defer nested_block.unstack();
for (nested_decls_list[start_index..]) |nested_decl_node| { for (nested_decls_list[start_index..]) |nested_decl_node| {
switch (nested_decl_node.tag) { switch (nested_decl_node.tag) {
.stitch_decl => try stitchDecl(gi, &decl_scope, nested_decl_node), .stitch_decl => try stitchDecl(&nested_block, &child_scope, nested_decl_node),
.function_decl => try functionDecl(gi, &decl_scope, nested_decl_node), .function_decl => try functionDecl(&nested_block, &child_scope, nested_decl_node),
else => unreachable, else => unreachable,
} }
} }
const name_str = try gi.astgen.strFromNode(identifier_node);
try setDeclKnotPayload(knot_inst, &child_block, &nested_block);
try setDeclaration(decl_inst, .{
.name = name_str.index,
.value = knot_inst,
.gi = gi,
});
} }
fn file(gi: *GenIr, scope: *Scope, file_node: *const Ast.Node) InnerError!void { fn file(gi: *GenIr, scope: *Scope, file_node: *const Ast.Node) InnerError!void {
@ -1482,5 +1485,11 @@ fn file(gi: *GenIr, scope: *Scope, file_node: *const Ast.Node) InnerError!void {
else => unreachable, else => unreachable,
} }
} }
const globals_len = gi.astgen.globals.items.len;
try gi.astgen.instructions.ensureUnusedCapacity(gi.astgen.gpa, globals_len);
for (gi.astgen.globals.items) |global| {
gi.instructions.appendAssumeCapacity(global);
}
return file_scope.setBlockBody(file_inst); return file_scope.setBlockBody(file_inst);
} }

View file

@ -12,7 +12,6 @@ string_bytes: []u8,
/// Ancillary data for instructions. The meaning of this data is determined by /// Ancillary data for instructions. The meaning of this data is determined by
/// the value of `Inst.Tag`. See `ExtraIndex` for the values of reserved indexes. /// the value of `Inst.Tag`. See `ExtraIndex` for the values of reserved indexes.
extra: []u32, extra: []u32,
globals: []Global,
pub const ExtraIndex = enum(u32) { pub const ExtraIndex = enum(u32) {
/// If this is 0, no compile errors. Otherwise there is a `CompileErrors` /// If this is 0, no compile errors. Otherwise there is a `CompileErrors`
@ -90,31 +89,16 @@ pub fn dumpInfo(ir: Ir, writer: *std.Io.Writer) !void {
try writer.print("00: {s}\n", .{str}); try writer.print("00: {s}\n", .{str});
start = end + 1; start = end + 1;
} }
for (ir.globals) |global| {
try writer.print("{any}\n", .{global});
}
return writer.flush(); return writer.flush();
} }
pub fn deinit(ir: *Ir, gpa: std.mem.Allocator) void { pub fn deinit(ir: *Ir, gpa: std.mem.Allocator) void {
gpa.free(ir.string_bytes); gpa.free(ir.string_bytes);
gpa.free(ir.instructions); gpa.free(ir.instructions);
gpa.free(ir.globals);
gpa.free(ir.extra); gpa.free(ir.extra);
ir.* = undefined; ir.* = undefined;
} }
pub const Global = struct {
tag: Tag,
name: Ir.NullTerminatedString,
is_constant: bool,
pub const Tag = enum {
knot,
variable,
};
};
pub const Inst = struct { pub const Inst = struct {
tag: Tag, tag: Tag,
data: Data, data: Data,
@ -164,8 +148,9 @@ pub const Inst = struct {
pub const Tag = enum { pub const Tag = enum {
file, file,
declaration, declaration,
decl_knot,
decl_var, decl_var,
decl_knot,
decl_stitch,
/// Uses the `str_tok` union field. /// Uses the `str_tok` union field.
decl_ref, decl_ref,
alloc, alloc,
@ -247,11 +232,19 @@ pub const Inst = struct {
value: Index, value: Index,
}; };
pub const Knot = struct { pub const Var = struct {
body_len: u32, body_len: u32,
}; };
pub const Var = struct { pub const Knot = struct {
/// Number of instructions for the knot's main block.
body_len: u32,
/// Number of nested stitches.
stitches_len: u32,
};
pub const Stitch = struct {
/// Number of instructions for the stitch's body block.
body_len: u32, body_len: u32,
}; };

View file

@ -122,12 +122,12 @@ pub const Compilation = struct {
} }
break :fatal true; break :fatal true;
} else fatal: { } else fatal: {
sema.analyzeFile(.file_inst) catch |err| switch (err) { //sema.analyzeFile(.file_inst) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory, // error.OutOfMemory => return error.OutOfMemory,
error.AnalysisFail => break :fatal true, // error.AnalysisFail => break :fatal true,
// TODO: These errors should be handled... // // TODO: These errors should be handled...
else => |e| return e, // else => |e| return e,
}; //};
break :fatal false; break :fatal false;
}; };
return .{ return .{
@ -144,10 +144,8 @@ pub const Compilation = struct {
pub fn setupStoryRuntime(cu: *Compilation, gpa: std.mem.Allocator, story: *Story) !void { pub fn setupStoryRuntime(cu: *Compilation, gpa: std.mem.Allocator, story: *Story) !void {
assert(cu.errors.len == 0); assert(cu.errors.len == 0);
const globals_len = cu.ir.globals.len;
const constants_pool = &story.constants_pool; const constants_pool = &story.constants_pool;
try constants_pool.ensureUnusedCapacity(gpa, cu.constants.len); try constants_pool.ensureUnusedCapacity(gpa, cu.constants.len);
try story.globals.ensureUnusedCapacity(gpa, @intCast(globals_len));
for (cu.constants) |constant| { for (cu.constants) |constant| {
switch (constant) { switch (constant) {
@ -164,10 +162,6 @@ pub const Compilation = struct {
}, },
} }
} }
for (cu.ir.globals) |global| {
const name_bytes = cu.ir.nullTerminatedString(global.name);
story.globals.putAssumeCapacity(name_bytes, null);
}
for (cu.knots) |*knot| { for (cu.knots) |*knot| {
const knot_name = cu.ir.nullTerminatedString(knot.name); const knot_name = cu.ir.nullTerminatedString(knot.name);
const runtime_chunk: *Object.ContentPath = try .create(story, .{ const runtime_chunk: *Object.ContentPath = try .create(story, .{
@ -177,7 +171,7 @@ pub const Compilation = struct {
.const_pool = try knot.constants.toOwnedSlice(gpa), .const_pool = try knot.constants.toOwnedSlice(gpa),
.bytes = try knot.bytecode.toOwnedSlice(gpa), .bytes = try knot.bytecode.toOwnedSlice(gpa),
}); });
story.globals.putAssumeCapacity(knot_name, &runtime_chunk.base); try story.globals.put(gpa, knot_name, &runtime_chunk.base);
} }
story.string_bytes = cu.ir.string_bytes; story.string_bytes = cu.ir.string_bytes;
cu.ir.string_bytes = &.{}; cu.ir.string_bytes = &.{};

View file

@ -204,18 +204,29 @@ pub const Writer = struct {
try self.writeIndent(w); try self.writeIndent(w);
} }
fn writeKnotDeclInst(self: *Writer, w: *std.Io.Writer, inst: Ir.Inst.Index) Error!void { fn writeStitchDeclInst(self: *Writer, w: *std.Io.Writer, inst: Ir.Inst.Index) Error!void {
const data = self.code.instructions[@intFromEnum(inst)].data.payload; const data = self.code.instructions[@intFromEnum(inst)].data.payload;
const extra = self.code.extraData(Ir.Inst.Knot, data.extra_index); const extra = self.code.extraData(Ir.Inst.Stitch, data.extra_index);
const body_slice = self.code.bodySlice(extra.end, extra.data.body_len); const body_slice = self.code.bodySlice(extra.end, extra.data.body_len);
try w.writeAll("body="); try w.writeAll("body=");
try self.writeBodyInner(w, body_slice); try self.writeBodyInner(w, body_slice);
} }
fn writeVarDeclInst(self: *Writer, w: *std.Io.Writer, inst: Ir.Inst.Index) Error!void { fn writeKnotDeclInst(self: *Writer, w: *std.Io.Writer, inst: Ir.Inst.Index) Error!void {
const data = self.code.instructions[@intFromEnum(inst)].data.payload; const data = self.code.instructions[@intFromEnum(inst)].data.payload;
const extra = self.code.extraData(Ir.Inst.Knot, data.extra_index); const extra = self.code.extraData(Ir.Inst.Knot, data.extra_index);
const body_slice = self.code.bodySlice(extra.end, extra.data.body_len); const body_slice = self.code.bodySlice(extra.end, extra.data.body_len);
const stitch_slice = self.code.bodySlice(extra.end + body_slice.len, extra.data.stitches_len);
try w.writeAll("body=");
try self.writeBodyInner(w, body_slice);
try w.writeAll(", stitches=");
try self.writeBodyInner(w, stitch_slice);
}
fn writeVarDeclInst(self: *Writer, w: *std.Io.Writer, inst: Ir.Inst.Index) Error!void {
const data = self.code.instructions[@intFromEnum(inst)].data.payload;
const extra = self.code.extraData(Ir.Inst.Var, data.extra_index);
const body_slice = self.code.bodySlice(extra.end, extra.data.body_len);
try w.writeAll("body="); try w.writeAll("body=");
try self.writeBodyInner(w, body_slice); try self.writeBodyInner(w, body_slice);
} }
@ -272,8 +283,9 @@ pub const Writer = struct {
switch (tmp.tag) { switch (tmp.tag) {
.file => try self.writeFileInst(w, inst), .file => try self.writeFileInst(w, inst),
.declaration => try self.writeDeclarationInst(w, inst), .declaration => try self.writeDeclarationInst(w, inst),
.decl_knot => try self.writeKnotDeclInst(w, inst),
.decl_var => try self.writeVarDeclInst(w, inst), .decl_var => try self.writeVarDeclInst(w, inst),
.decl_knot => try self.writeKnotDeclInst(w, inst),
.decl_stitch => try self.writeVarDeclInst(w, inst),
.decl_ref => try self.writeStrTokInst(w, inst), .decl_ref => try self.writeStrTokInst(w, inst),
.condbr => try self.writeCondbrInst(w, inst), .condbr => try self.writeCondbrInst(w, inst),
.@"break" => try self.writeBreakInst(w, inst), .@"break" => try self.writeBreakInst(w, inst),