// Expect retcode = 0 // 声明句柄表(移植自 stage1/spl_lang_type.h 的 spl_def_node_t 部分)。 // DefId 是「声明」的稳定身份:作用域符号、AST 节点 resolved_def_id、以及将来 // comptime 生成的声明都用它。前向引用/环形引用由 state 状态机表达。 // // 与 stage1 的差异:owner_ast 改为不透明 `*any`(借用),使本表不依赖具体 AST // 表示,便于将来 comptime/序列化复用;sema 侧自行 cast 回 *Ast。 // 名称是 ByteView(借用源/AST),本表不做拥有。 type .{ .Error } = @import("utils/error.spl"); type .{ .Vec } = @import("utils/vec.spl"); type .{ .ByteView } = @import("utils/byte_view.spl"); type .{ .ByteBuffer } = @import("utils/byte_buffer.spl"); type .{ .append_usize } = @import("utils/fmt.spl"); type .{ .Span, .span_none } = @import("./diag.spl"); type DefKind = enum { Error, Scalar, Member, Var, Fn, Agg, Distinct, Alias, Builtin, } type DeclState = enum { Unresolved, Resolving, Resolved, Error, } type DefNode = struct { kind: DefKind, name: ByteView, span: Span, lang_id: usize, owner: *any, // 借用;不透明,sema 侧 cast 回 *Ast ast_node_ref: usize, state: DeclState, owner_def: usize, } type DefTable = @self; table: Vec, // DefNode type DefTableResult = enum { Ok: DefTable, Err: Error, } fn new_node(): DefNode { ret DefNode { .kind = .Error, .name = ByteView.empty(), .span = span_none(), .lang_id = 0, .owner = null, .ast_node_ref = 0, .state = .Unresolved, .owner_def = 0, }; } fn new(): DefTableResult { var dt = DefTable { .table = Vec.blank(@sizeof(DefNode), @alignof(DefNode)) }; var sentinel = new_node(); match Vec.push(&dt.table, &sentinel as *u8) { .Ok => {}, .Err(error) => { Vec.drop(&dt.table); ret .Err(error); }, } ret .Ok(dt); } fn drop(self: *DefTable) { Vec.drop(&self.table); } // id 0 与越界均返回 null(对应 stage1 spl_def_node)。 fn node(self: *DefTable, id: usize): *DefNode { if id == 0 || id >= self.table.len { ret null; } match Vec.get(&self.table, id) { .Ok(ptr) => { ret ptr as *DefNode; }, .Err(error) => { ret null; }, } ret null; } fn count(self: *DefTable): usize { ret self.table.len; } // 追加一条声明,返回其 DefId(失败返回 0)。 fn alloc(self: *DefTable, kind: DefKind, name: ByteView, span: Span, owner: *any, ast_node_ref: usize): usize { var n = new_node(); n.kind = kind; n.name = name; n.span = span; n.owner = owner; n.ast_node_ref = ast_node_ref; match Vec.push(&self.table, &n as *u8) { .Ok => {}, .Err(error) => { ret 0; }, } ret self.table.len - 1; } fn resolve(self: *DefTable, id: usize, lang_id: usize) { var d = node(self, id); if d == null { ret; } d.lang_id = lang_id; d.state = .Resolved; } fn set_state(self: *DefTable, id: usize, state: DeclState) { var d = node(self, id); if d == null { ret; } d.state = state; } fn get_lang_id(self: *DefTable, id: usize): usize { var d = node(self, id); if d == null { ret 0; } ret d.lang_id; } // ---- 分类 ---- fn kind_name(kind: DefKind): []u8 { match kind { .Error => { ret "error"; }, .Scalar => { ret "scalar"; }, .Member => { ret "member"; }, .Var => { ret "var"; }, .Fn => { ret "fn"; }, .Agg => { ret "agg"; }, .Distinct => { ret "distinct"; }, .Alias => { ret "alias"; }, .Builtin => { ret "builtin"; }, } ret "unknown"; } fn state_name(state: DeclState): []u8 { match state { .Unresolved => { ret "unresolved"; }, .Resolving => { ret "resolving"; }, .Resolved => { ret "resolved"; }, .Error => { ret "error"; }, } ret "unknown"; } // 能作为类型使用的 def(对应 stage1 sema_def_is_type)。 fn is_type_kind(kind: DefKind): bool { match kind { .Scalar => { ret true; }, .Agg => { ret true; }, .Distinct => { ret true; }, .Alias => { ret true; }, .Builtin => { ret true; }, _ => { ret false; }, } ret false; } // ---- dump(镜像 stage1 spl_type_def_dump 的文本)---- fn push_out(buf: *ByteBuffer, bytes: []u8): bool { match ByteBuffer.append(buf, bytes) { .Ok => { ret true; }, .Err(error) => { ret false; }, } ret false; } fn push_usize(buf: *ByteBuffer, value: usize): bool { match append_usize(buf, value, 10, false) { .Ok => { ret true; }, .Err(error) => { ret false; }, } ret false; } fn dump(self: *DefTable, id: usize, out: *ByteBuffer): bool { var d = node(self, id); if d == null { ret push_out(out, "none"); } if !push_out(out, "kind=") { ret false; } if !push_out(out, kind_name(d.kind)) { ret false; } if !push_out(out, " lang_id=") { ret false; } if !push_usize(out, d.lang_id) { ret false; } if !push_out(out, " name=") { ret false; } if d.name.len == 0 { ret push_out(out, "?"); } ret push_out(out, ByteView.as_slice(d.name)); } // ---- 测试 ---- fn test_alloc_and_state() { var dt: DefTable = undefined; match new() { .Ok(v) => { dt = v; }, .Err(error) => { @assert(false, "def table new failed"); }, } defer { drop(&dt); } @assert(count(&dt) == 1, "sentinel only"); @assert(node(&dt, 0) == null, "id 0 is null"); var span = span_none(); var a = alloc(&dt, .Fn, ByteView.from_slice("foo"), span, null, 7); var b = alloc(&dt, .Agg, ByteView.from_slice("Bar"), span, null, 9); @assert(a == 1 && b == 2, "ids increase from 1"); var na = node(&dt, a); @assert(na != null, "node a"); @assert(ByteView.eq(na.name, ByteView.from_slice("foo")), "name stored"); @assert(na.ast_node_ref == 7, "ast_node_ref stored"); @assert(na.owner == null, "owner stored"); match na.state { .Unresolved => {}, _ => { @assert(false, "initial state unresolved"); }, } match na.kind { .Fn => {}, _ => { @assert(false, "kind fn"); }, } resolve(&dt, a, 42); @assert(get_lang_id(&dt, a) == 42, "lang id resolved"); var na2 = node(&dt, a); match na2.state { .Resolved => {}, _ => { @assert(false, "state resolved"); }, } set_state(&dt, b, .Resolving); var nb = node(&dt, b); match nb.state { .Resolving => {}, _ => { @assert(false, "state resolving"); }, } @assert(get_lang_id(&dt, 999) == 0, "missing id"); } fn test_kind_helpers() { @assert(is_type_kind(.Scalar), "scalar is type"); @assert(is_type_kind(.Agg), "agg is type"); @assert(is_type_kind(.Alias), "alias is type"); @assert(!is_type_kind(.Fn), "fn not type"); @assert(!is_type_kind(.Var), "var not type"); @assert(!is_type_kind(.Member), "member not type"); @assert(ByteView.eq(ByteView.from_slice(kind_name(.Agg)), ByteView.from_slice("agg")), "agg name"); @assert(ByteView.eq(ByteView.from_slice(state_name(.Resolving)), ByteView.from_slice("resolving")), "resolving name"); } fn test_dump() { var dt: DefTable = undefined; match new() { .Ok(v) => { dt = v; }, .Err(error) => { @assert(false, "def table new failed"); }, } defer { drop(&dt); } var a = alloc(&dt, .Var, ByteView.from_slice("x"), span_none(), null, 0); resolve(&dt, a, 5); var out = ByteBuffer.new(); defer { ByteBuffer.drop(&out); } if !dump(&dt, a, &out) { @assert(false, "dump ok"); } expect_text(out, "kind=var lang_id=5 name=x"); var out2 = ByteBuffer.new(); defer { ByteBuffer.drop(&out2); } if !dump(&dt, 0, &out2) { @assert(false, "dump none ok"); } expect_text(out2, "none"); var out3 = ByteBuffer.new(); defer { ByteBuffer.drop(&out3); } var c = alloc(&dt, .Alias, ByteView.empty(), span_none(), null, 0); if !dump(&dt, c, &out3) { @assert(false, "dump empty name ok"); } expect_text(out3, "kind=alias lang_id=0 name=?"); } fn expect_text(buf: ByteBuffer, expected: []u8) { var got = ByteBuffer.as_slice(buf); @assert(got.len == expected.len, "dump length"); var i: usize = 0; while i < expected.len { @assert(got.ptr[i] == expected.ptr[i], "dump byte"); i = i + 1; } } fn main() { test_alloc_and_state(); test_kind_helpers(); test_dump(); }