Files
spl/stage2/scope.spl
T

218 lines
5.6 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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();
}