feat: ink variable semantics, global ref table for astgen

This commit is contained in:
Brett Broadhurst 2026-03-09 09:21:42 -06:00
parent 197a37ebe7
commit 9658c8a308
Failed to generate hash of commit
7 changed files with 298 additions and 141 deletions

View file

@ -12,6 +12,8 @@ tree: *const Ast,
string_table: std.HashMapUnmanaged(u32, void, StringIndexContext, std.hash_map.default_max_load_percentage) = .empty,
string_bytes: std.ArrayListUnmanaged(u8) = .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,
errors: std.ArrayListUnmanaged(Ast.Error) = .empty,
@ -22,6 +24,17 @@ pub const InnerError = error{
Overflow,
};
pub fn deinit(astgen: *AstGen) void {
const gpa = astgen.gpa;
astgen.string_table.deinit(gpa);
astgen.string_bytes.deinit(gpa);
astgen.globals.deinit(gpa);
astgen.global_ref_table.deinit(gpa);
astgen.instructions.deinit(gpa);
astgen.extra.deinit(gpa);
astgen.errors.deinit(gpa);
}
const Scope = struct {
parent: ?*Scope,
astgen: *AstGen,
@ -238,32 +251,41 @@ const GenIr = struct {
}
};
pub fn deinit(astgen: *AstGen) void {
const gpa = astgen.gpa;
astgen.string_table.deinit(gpa);
astgen.string_bytes.deinit(gpa);
astgen.instructions.deinit(gpa);
astgen.errors.deinit(gpa);
}
fn setDeclaration(
decl_index: Ir.Inst.Index,
args: struct {
name: Ir.NullTerminatedString,
tag: Ir.Global.Tag,
ref: Ir.Inst.Index,
decl_node: *const Ast.Node,
body_gi: *GenIr,
is_constant: bool = true,
},
) !void {
const astgen = args.body_gi.astgen;
const gpa = astgen.gpa;
const extra_len = @typeInfo(Ir.Inst.Declaration).@"struct".fields.len;
const global_index = astgen.globals.items.len;
try astgen.extra.ensureUnusedCapacity(gpa, extra_len);
try astgen.globals.ensureUnusedCapacity(gpa, 1);
try astgen.global_ref_table.ensureUnusedCapacity(gpa, 1);
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 },
);
if (astgen.global_ref_table.get(args.name)) |_| {
return astgen.fail(.redefined_identifier, args.decl_node);
}
astgen.globals.appendAssumeCapacity(.{
.tag = args.tag,
.name = args.name,
.is_constant = args.is_constant,
});
astgen.global_ref_table.putAssumeCapacity(args.name, global_index);
args.body_gi.unstack();
}
@ -423,10 +445,13 @@ fn stringExpr(gen: *GenIr, expr_node: *const Ast.Node) InnerError!Ir.Inst.Index
return stringLiteral(gen, first_node);
}
fn identifier(gen: *GenIr, _: *Scope, node: *const Ast.Node) InnerError!Ir.Inst.Index {
const name_str = try gen.astgen.stringFromNode(node);
return gen.addDeclRef(name_str);
//return gen.fail(.unknown_identifier, node);
fn identifier(gi: *GenIr, scope: *Scope, node: *const Ast.Node) InnerError!Ir.Inst.Index {
const astgen = gi.astgen;
const str = try astgen.stringFromNode(node);
if (scope.lookup(str)) |decl| {
return gi.addUnaryNode(.load_local, decl.inst_index);
}
return gi.addDeclRef(str);
}
fn expr(gen: *GenIr, scope: *Scope, optional_expr: ?*const Ast.Node) InnerError!Ir.Inst.Index {
@ -736,30 +761,18 @@ fn contentStmt(gen: *GenIr, scope: *Scope, stmt_node: *const Ast.Node) InnerErro
return gen.addUnaryNode(.content, expr_ref);
}
fn assignStmt(gen: *GenIr, scope: *Scope, stmt_node: *const Ast.Node) InnerError!void {
fn assignStmt(gi: *GenIr, scope: *Scope, stmt_node: *const Ast.Node) InnerError!void {
const astgen = gi.astgen;
const identifier_node = stmt_node.data.bin.lhs orelse unreachable;
const expr_node = stmt_node.data.bin.rhs orelse unreachable;
const name_ref = try gen.astgen.stringFromNode(identifier_node);
const name_ref = try astgen.stringFromNode(identifier_node);
if (scope.lookup(name_ref)) |symbol| switch (symbol) {
.global => |data| {
if (data.is_constant) {
return gen.fail(.assignment_to_const, identifier_node);
}
try expr(gen, scope, expr_node);
try gen.emitConstInst(.store_global, data.constant_slot);
try gen.emitSimpleInst(.pop);
return;
},
.local => |data| {
try expr(gen, scope, expr_node);
try gen.emitConstInst(.store, data.stack_slot);
try gen.emitSimpleInst(.pop);
return;
},
else => unreachable,
};
return gen.fail(.unknown_identifier, identifier_node);
if (scope.lookup(name_ref)) |decl| {
const expr_result = try expr(gi, scope, expr_node);
_ = try gi.addBinaryNode(.store_local, decl.inst_index, expr_result);
return;
}
return gi.fail(.unknown_identifier, identifier_node);
}
fn choiceStmt(gen: *GenIr, scope: *Scope, stmt_node: *const Ast.Node) InnerError!void {
@ -836,8 +849,7 @@ fn choiceStmt(gen: *GenIr, scope: *Scope, stmt_node: *const Ast.Node) InnerError
}
}
fn varDecl(gi: *GenIr, scope: *Scope, decl_node: *const Ast.Node) !void {
const gpa = gi.astgen.gpa;
fn tempDecl(gi: *GenIr, scope: *Scope, decl_node: *const Ast.Node) !void {
const identifier_node = decl_node.data.bin.lhs.?;
const expr_node = decl_node.data.bin.rhs.?;
const name_ref = try gi.astgen.stringFromNode(identifier_node);
@ -846,6 +858,21 @@ fn varDecl(gi: *GenIr, scope: *Scope, decl_node: *const Ast.Node) !void {
return gi.fail(.redefined_identifier, decl_node);
}
const alloc_inst = try gi.add(.{ .tag = .alloc_local, .data = undefined });
const expr_result = try expr(gi, scope, expr_node);
_ = try gi.addBinaryNode(.store_local, alloc_inst, expr_result);
return scope.insert(name_ref, .{
.decl_node = decl_node,
.inst_index = alloc_inst,
});
}
fn varDecl(gi: *GenIr, scope: *Scope, decl_node: *const Ast.Node) !void {
const astgen = gi.astgen;
const gpa = astgen.gpa;
const identifier_node = decl_node.data.bin.lhs.?;
const expr_node = decl_node.data.bin.rhs.?;
const decl_inst = try gi.makeDeclaration();
try gi.instructions.append(gpa, decl_inst);
@ -853,15 +880,14 @@ fn varDecl(gi: *GenIr, scope: *Scope, decl_node: *const Ast.Node) !void {
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,
.tag = .variable,
.name = try astgen.stringFromNode(identifier_node),
.ref = var_inst,
.decl_node = decl_node,
.body_gi = &decl_block,
.is_constant = decl_node.tag == .const_decl,
});
}
@ -873,8 +899,8 @@ fn blockInner(gi: *GenIr, parent_scope: *Scope, stmt_list: []*Ast.Node) !void {
_ = switch (inner_node.tag) {
.var_decl => try varDecl(gi, &child_scope, inner_node),
.const_decl => try varDecl(gi, &child_scope, inner_node),
//.temp_decl => try varDecl(gen, scope, inner_node),
//.assign_stmt => try assignStmt(gen, scope, inner_node),
.temp_decl => try tempDecl(gi, &child_scope, inner_node),
.assign_stmt => try assignStmt(gi, &child_scope, inner_node),
.content_stmt => try contentStmt(gi, &child_scope, inner_node),
//.choice_stmt => try choiceStmt(gen, scope, inner_node),
.expr_stmt => try exprStmt(gi, &child_scope, inner_node),
@ -922,6 +948,8 @@ fn defaultBlock(
const knot_inst = try decl_scope.addKnot();
try setDeclaration(decl_inst, .{
.tag = .knot,
.decl_node = body_node,
.name = try astgen.stringFromBytes("$__main__$"),
.ref = knot_inst,
.body_gi = &decl_scope,
@ -992,6 +1020,7 @@ fn file(root_gi: *GenIr, scope: *Scope, file_node: *const Ast.Node) InnerError!v
pub const Decl = struct {
decl_node: *const Ast.Node,
inst_index: Ir.Inst.Index,
};
/// Perform code generation via tree-walk.
@ -1021,12 +1050,16 @@ pub fn generate(gpa: std.mem.Allocator, tree: *const Ast) !Ir {
defer gen.unstack();
// TODO: Make sure this is never null.
const root_node = tree.root orelse unreachable;
try file(&gen, &file_scope, root_node);
const root_node = tree.root.?;
file(&gen, &file_scope, root_node) catch |err| switch (err) {
error.SemanticError => {},
else => |e| return e,
};
return .{
.string_bytes = try astgen.string_bytes.toOwnedSlice(gpa),
.instructions = try astgen.instructions.toOwnedSlice(gpa),
.globals = try astgen.globals.toOwnedSlice(gpa),
.extra = try astgen.extra.toOwnedSlice(gpa),
.errors = try astgen.errors.toOwnedSlice(gpa),
};