// Expect retcode = 0 // 作用域树:名字 -> Symbol,节点经 parent 上溯查找(支持遮蔽)。名字借用源字节, // 存活由调用方保证;node 为 def.spl 的 DefId。 type .{ .Error } = @import("utils/error.spl"); type .{ .Vec } = @import("utils/vec.spl"); type .{ .Map, .hash_kind_view } = @import("utils/map.spl"); type .{ .ByteView } = @import("utils/byte_view.spl"); type .{ .hash_view, .eq_view } = @import("utils/hash.spl"); type SymbolKind = enum { Error, Var, Member, Fn, Type, } type Symbol = struct { name: ByteView, kind: SymbolKind, node: usize, // DefId } type ScopeNode = struct { parent: usize, symbols: Map, } type Scope = @self; scopes: Vec, // ScopeNode root_scope: usize, current_scope: usize, type ScopeResult = enum { Ok: Scope, Err: Error, } fn new_map(): Map { ret Map.new(@sizeof(ByteView), @alignof(ByteView), @sizeof(Symbol), @alignof(Symbol), hash_kind_view()); } fn new(): ScopeResult { var sc = Scope { .scopes = Vec.blank(@sizeof(ScopeNode), @alignof(ScopeNode)), .root_scope = 0, .current_scope = 0, }; var sentinel = ScopeNode { .parent = 0, .symbols = new_map() }; match Vec.push(&sc.scopes, &sentinel as *u8) { .Ok => {}, .Err(error) => { Vec.drop(&sc.scopes); Map.drop(&sentinel.symbols); ret .Err(error); }, } ret .Ok(sc); } fn drop(self: *Scope) { var i: usize = 0; while i < self.scopes.len { var n = node(self, i); if n != null { Map.drop(&n.symbols); } i = i + 1; } Vec.drop(&self.scopes); self.root_scope = 0; self.current_scope = 0; } // id 0 与越界返回 null。 fn node(self: *Scope, id: usize): *ScopeNode { if id == 0 || id >= self.scopes.len { ret null; } ret (self.scopes.data as *ScopeNode) + id; } // 新作用域 parent = 当前作用域;不自动切换 current(调用方负责,见 sema)。 fn alloc(self: *Scope): usize { var n = ScopeNode { .parent = self.current_scope, .symbols = new_map() }; match Vec.push(&self.scopes, &n as *u8) { .Ok => {}, .Err(error) => { Map.drop(&n.symbols); ret 0; }, } ret self.scopes.len - 1; } // 插入符号;同名已存在返回 false(不改动)。 fn insert(self: *Scope, id: usize, symbol: Symbol): bool { if id == 0 || id >= self.scopes.len { ret false; } var n = node(self, id); if n == null { ret false; } var old = Symbol { .name = ByteView.empty(), .kind = .Error, .node = 0, }; match Map.get(&n.symbols, &symbol.name as *u8, &old as *u8) { .Found => { ret false; }, .NotFound => {}, } match Map.put(&n.symbols, &symbol.name as *u8, &symbol as *u8) { .Ok => { ret true; }, .Err(error) => { ret false; }, } ret false; } // 从 current_scope 沿父链查找;找到填 out 返回 true。 fn find(self: *Scope, name: ByteView, out: *Symbol): bool { if out == null { ret false; } var id = self.current_scope; while id != 0 { var n = node(self, id); if n == null { ret false; } match Map.get(&n.symbols, &name as *u8, out as *u8) { .Found => { ret true; }, .NotFound => {}, } id = n.parent; } ret false; } fn sym(name: []u8, kind: SymbolKind, node: usize): Symbol { ret Symbol { .name = ByteView.from_slice(name), .kind = kind, .node = node, }; } fn test_alloc_and_find() { var sc: Scope = undefined; match new() { .Ok(v) => { sc = v; }, .Err(error) => { @assert(false, "scope new failed"); }, } defer { drop(&sc); } @assert(sc.root_scope == 0 && sc.current_scope == 0, "initial"); // root var root = alloc(&sc); sc.root_scope = root; sc.current_scope = root; // module scope(child of root) var mod = alloc(&sc); sc.current_scope = mod; @assert(insert(&sc, mod, sym("x", .Var, 11)), "insert x"); @assert(!insert(&sc, mod, sym("x", .Var, 12)), "duplicate x"); // 函数作用域 var fn_scope = alloc(&sc); sc.current_scope = fn_scope; var out = sym("", .Error, 0); @assert(find(&sc, ByteView.from_slice("x"), &out), "find x from child"); @assert(out.node == 11, "x def id"); // 遮蔽:函数内层再定义 x @assert(insert(&sc, fn_scope, sym("x", .Fn, 99)), "shadow x"); @assert(find(&sc, ByteView.from_slice("x"), &out), "find shadow"); @assert(out.node == 99, "shadow def id"); // 未定义 @assert(!find(&sc, ByteView.from_slice("nope"), &out), "missing"); // 父链结构 var m = node(&sc, mod); var f = node(&sc, fn_scope); @assert(m != null && m.parent == root, "mod parent"); @assert(f != null && f.parent == mod, "fn parent"); } fn test_kinds_isolated() { var sc: Scope = undefined; match new() { .Ok(v) => { sc = v; }, .Err(error) => { @assert(false, "scope new failed"); }, } defer { drop(&sc); } var a = alloc(&sc); sc.current_scope = a; @assert(insert(&sc, a, sym("T", .Type, 1)), "type T"); @assert(insert(&sc, a, sym("f", .Fn, 2)), "fn f"); var out = sym("", .Error, 0); @assert(find(&sc, ByteView.from_slice("T"), &out), "find T"); var kind_matches = false; match out.kind { .Type => { kind_matches = true; }, _ => {}, } @assert(kind_matches, "T kind"); @assert(find(&sc, ByteView.from_slice("f"), &out), "find f"); @assert(out.node == 2, "f def id"); } fn main() { test_alloc_and_find(); test_kinds_isolated(); }