Files

305 lines
7.9 KiB
Plaintext

// Expect retcode = 0
// 声明句柄表。DefId 是声明的稳定身份:作用域符号、节点 resolved_def_id、comptime
// 生成的声明都用它;前向/环形引用由 state 状态机表达。owner_ast 为不透明 *any。
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。
fn node(self: *DefTable, id: usize): *DefNode {
if id == 0 || id >= self.table.len { ret null; }
ret (self.table.data as *DefNode) + id;
}
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。
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;
}
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();
}