// Expect retcode = 0 // AST:用 ADT(enum 带载荷)表达节点,节点引用是 arena 序号 usize。 // stage1 的 C 结构只是「手写 union 模拟 ADT」;这里直接用 SPL 的 enum 载荷, // 不照抄 union 布局,只要求最终 dump / 语义与 stage1 一致。 // 索引 0 为 NONE 哨兵(与 stage1 一致)。 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 .{ .Punct } = @import("token.spl"); type BigInt = @import("utils/mp.spl"); type .{ .Span, .span_none, .span_make, .span_is_none } = @import("./diag.spl"); type .{ .Sources, .Source, .set_at } = @import("utils/source.spl"); // 算子不另立 enum:AST 直接存词法 Punct。一元/二元由节点 kind 区分 // (一元:Sub=负 / Not=! / BitNot=~ / And=& / Mul=.*;二元即对应 Punct)。 // ---- 节点体(ADT;variant 名 = stage1 的 kind 名;载荷用内联匿名 struct)---- type Body = enum { None, Root: struct { members: usize }, ContainerMembers: struct { external_tags: Vec, declarations: Vec }, ExternalTagItem: struct { is_at: bool, is_external: bool, name: ByteView, args: Vec }, TagItem: struct { is_at: bool, is_external: bool, name: ByteView, args: Vec }, ComptimeDecl: struct { body: usize }, TopLevelBlock: struct { tag_list: Vec, block: usize }, FnDecl: struct { tag_list: Vec, is_comptime: bool, name: ByteView, params: Vec, ret_type: usize, body: usize, }, VarDecl: struct { attr_list: Vec, pattern: usize, type_expr: usize, init_expr: usize }, ConstDecl: struct { attr_list: Vec, pattern: usize, type_expr: usize, init_expr: usize }, TypeDecl: struct { attr_list: Vec, pattern: usize, type_expr: usize }, MemberDecl: struct { attr_list: Vec, name: ByteView, type_expr: usize, init_expr: usize, }, ParamDecl: struct { attr_list: Vec, name: ByteView, type_expr: usize }, StatementRet: struct { expr: usize }, StatementBreak: struct { label: usize, expr: usize }, StatementContinue: struct { label: usize }, StatementDefer: struct { body: usize }, StatementErrdefer: struct { body: usize }, // errdefer:仅错误退出时执行 StatementComptime: struct { body: usize }, StatementDecl: struct { decl: usize }, StatementExpr: struct { expr: usize }, AggregateMember: struct { attr_list: Vec, name: ByteView, type_expr: usize, init_expr: usize, }, StructInitItem: struct { name: ByteView, expr: usize }, MatchArm: struct { pattern: usize, body: usize }, Label: struct { name: ByteView }, ExprIntLit: struct { value: BigInt }, // 任意宽度(mp),解析后由 Ast 拥有 ExprFloatLit: struct { text: ByteView }, ExprCharLit: struct { value: u8 }, ExprStringLit: struct { data: ByteView }, ExprBoolLit: struct { value: bool }, ExprNullLit, ExprUndefinedLit, ExprIdent: struct { name: ByteView }, ExprUnary: struct { op: Punct, operand: usize }, ExprBinary: struct { op: Punct, left: usize, right: usize }, ExprCall: struct { callee: usize, args: Vec }, ExprField: struct { obj: usize, name: ByteView }, ExprIndex: struct { obj: usize, index: usize }, ExprSlice: struct { obj: usize, begin: usize, end: usize }, ExprCast: struct { expr: usize, type_expr: usize }, ExprTuple: struct { items: Vec }, ExprArrayLit: struct { has_len: bool, len: usize, is_underscore_len: bool, elem_type: usize, elems: Vec, }, ExprAggregateLit: struct { name: ByteView, fields: Vec }, ExprComptimeCall: struct { name: ByteView, args: Vec }, ExprTry: struct { value: usize }, // `try expr` ExprCatch: struct { value: usize, pattern: usize, handler: usize }, // `expr catch { }` ExprBlock: struct { stmts: Vec, tail_expr: usize }, ExprIf: struct { cond: usize, then_block: usize, else_branch: usize }, ExprIfvar: struct { var_decl: usize, then_block: usize, else_branch: usize }, ExprWhile: struct { label: usize, cond: usize, body: usize, else_branch: usize }, ExprLoop: struct { label: usize, body: usize }, ExprFor: struct { label: usize, iter_exprs: Vec, pattern: usize, body: usize, else_branch: usize, }, ExprMatch: struct { matched_expr: usize, arms: Vec }, ExprFnType: struct { is_comptime: bool, params: Vec, ret_type: usize }, TypePath: struct { name: ByteView, generic_args: Vec }, TypePointer: struct { pointee: usize }, TypeArray: struct { has_len: bool, len: isize, is_underscore_len: bool, elem_type: usize, }, TypeSlice: struct { elem_type: usize }, TypeFn: struct { is_comptime: bool, params: Vec, ret_type: usize }, TypeParen: struct { types: Vec }, TypeAggregate: struct { kind_name: ByteView, members: Vec }, TypeComptimeCall: struct { name: ByteView, args: Vec }, PatternIdent: struct { name: ByteView }, PatternUnderscore, PatternDot: struct { name: ByteView, sub_pattern: usize }, PatternTuple: struct { items: Vec }, PatternList: struct { items: Vec }, PatternAggregate: struct { items: Vec }, PatternField: struct { name: ByteView, pattern: usize }, ExprComptime: struct { body: usize }, ExprComptimeInit: struct { name: ByteView, args: Vec, fields: Vec }, } fn kind_name(body: Body): []u8 { match body { .None => { ret "none"; }, .Root(_) => { ret "root"; }, .ContainerMembers(_) => { ret "container_members"; }, .ExternalTagItem(_) => { ret "external_tag_item"; }, .TagItem(_) => { ret "tag_item"; }, .ComptimeDecl(_) => { ret "comptime_decl"; }, .TopLevelBlock(_) => { ret "top_level_block"; }, .FnDecl(_) => { ret "fn_decl"; }, .VarDecl(_) => { ret "var_decl"; }, .ConstDecl(_) => { ret "const_decl"; }, .TypeDecl(_) => { ret "type_decl"; }, .MemberDecl(_) => { ret "member_decl"; }, .ParamDecl(_) => { ret "param_decl"; }, .StatementRet(_) => { ret "statement_ret"; }, .StatementBreak(_) => { ret "statement_break"; }, .StatementContinue(_) => { ret "statement_continue"; }, .StatementDefer(_) => { ret "statement_defer"; }, .StatementErrdefer(_) => { ret "statement_errdefer"; }, .StatementComptime(_) => { ret "statement_comptime"; }, .ExprComptime(_) => { ret "expr_comptime"; }, .StatementDecl(_) => { ret "statement_decl"; }, .StatementExpr(_) => { ret "statement_expr"; }, .AggregateMember(_) => { ret "aggregate_member"; }, .StructInitItem(_) => { ret "struct_init_item"; }, .MatchArm(_) => { ret "match_arm"; }, .Label(_) => { ret "label"; }, .ExprIntLit(_) => { ret "int_lit"; }, .ExprFloatLit(_) => { ret "float_lit"; }, .ExprCharLit(_) => { ret "char_lit"; }, .ExprStringLit(_) => { ret "string_lit"; }, .ExprBoolLit(_) => { ret "bool_lit"; }, .ExprNullLit => { ret "null_lit"; }, .ExprUndefinedLit => { ret "undefined_lit"; }, .ExprIdent(_) => { ret "ident"; }, .ExprUnary(_) => { ret "unary"; }, .ExprBinary(_) => { ret "binary"; }, .ExprCall(_) => { ret "call"; }, .ExprField(_) => { ret "field"; }, .ExprIndex(_) => { ret "index"; }, .ExprSlice(_) => { ret "slice"; }, .ExprCast(_) => { ret "cast"; }, .ExprTuple(_) => { ret "tuple"; }, .ExprArrayLit(_) => { ret "array_literal"; }, .ExprAggregateLit(_) => { ret "aggregate_literal"; }, .ExprComptimeCall(_) => { ret "comptime_call"; }, .ExprTry(_) => { ret "try_expr"; }, .ExprCatch(_) => { ret "catch_expr"; }, .ExprComptimeInit(_) => { ret "comptime_init"; }, .ExprBlock(_) => { ret "block"; }, .ExprIf(_) => { ret "if_expr"; }, .ExprIfvar(_) => { ret "ifvar_expr"; }, .ExprWhile(_) => { ret "while_expr"; }, .ExprLoop(_) => { ret "loop_expr"; }, .ExprFor(_) => { ret "for_expr"; }, .ExprMatch(_) => { ret "match_expr"; }, .ExprFnType(_) => { ret "fn_type"; }, .TypePath(_) => { ret "type_path"; }, .TypePointer(_) => { ret "type_pointer"; }, .TypeArray(_) => { ret "type_array"; }, .TypeSlice(_) => { ret "type_slice"; }, .TypeFn(_) => { ret "type_fn"; }, .TypeParen(_) => { ret "type_paren"; }, .TypeAggregate(_) => { ret "type_aggregate"; }, .TypeComptimeCall(_) => { ret "type_comptime_call"; }, .PatternIdent(_) => { ret "pattern_ident"; }, .PatternUnderscore => { ret "pattern_underscore"; }, .PatternDot(_) => { ret "pattern_dot"; }, .PatternTuple(_) => { ret "pattern_tuple"; }, .PatternList(_) => { ret "pattern_list"; }, .PatternAggregate(_) => { ret "pattern_aggregate"; }, .PatternField(_) => { ret "pattern_field"; }, } ret "?"; } type Node = struct { span: Span, resolved_def_id: usize, resolved_lang_tid: usize, expected_lang_tid: usize, attrs: Vec, body: Body, } type Ast = @self; nodes: Vec, // Node root: usize, sources: *Sources, // 借用;可为 null(dump 时不解析行列) strings: Vec, // ByteBuffer:已解码的字符串字面量字节(ByteView 指向其中) type AstResult = enum { Ok: Ast, Err: Error, } fn new_node(body: Body, span: Span): Node { ret Node { .span = span, .resolved_def_id = 0, .resolved_lang_tid = 0, .expected_lang_tid = 0, .attrs = Vec.blank(@sizeof(usize), @alignof(usize)), .body = body, }; } fn new(sources: *Sources): AstResult { var ast = Ast { .nodes = Vec.blank(@sizeof(Node), @alignof(Node)), .root = 0, .sources = sources, .strings = Vec.blank(@sizeof(ByteBuffer), @alignof(ByteBuffer)), }; var sentinel = new_node(.None, span_none()); match Vec.push(&ast.nodes, &sentinel as *u8) { .Ok => {}, .Err(error) => { Vec.drop(&ast.nodes); ret .Err(error); }, } ret .Ok(ast); } fn drop(self: *Ast) { var i: usize = 0; while i < self.nodes.len { var n = node(self, i); if n != null { Vec.drop(&n.attrs); match n.body { .ExprIntLit(x) => { BigInt.drop(&x.value); }, _ => {}, } } i = i + 1; } Vec.drop(&self.nodes); var k: usize = 0; while k < self.strings.len { match Vec.get(&self.strings, k) { .Ok(ptr) => { ByteBuffer.drop(ptr as *ByteBuffer); }, .Err(error) => {}, } k = k + 1; } Vec.drop(&self.strings); self.root = 0; } fn node(self: *Ast, ref: usize): *Node { if ref >= self.nodes.len { ret null; } ret (self.nodes.data as *Node) + ref; } // 追加节点,返回其引用(失败返回 0)。 fn alloc(self: *Ast, body: Body, span: Span): usize { var n = new_node(body, span); match Vec.push(&self.nodes, &n as *u8) { .Ok => {}, .Err(error) => { Vec.drop(&n.attrs); ret 0; }, } ret self.nodes.len - 1; } // 注册一段已解码的字符串字节(Ast 拥有;ByteView 指向其中,生命周期同 Ast)。 // 空字符串也保证非空 data 指针(镜像 stage1:空字面量仍有稳定的非空字节地址)。 fn push_string(self: *Ast, bytes: ByteBuffer): ByteView { var b = bytes; if b.storage.len == 0 { match ByteBuffer.reserve(&b, 1) { .Ok => {}, .Err(error) => {} } } @assert(b.storage.data != null, "ast string pool must be non-null"); // 直接构造,避免 from_slice 把空串规范成 null 视图。 var view = ByteView { .ptr = b.storage.data, .len = b.storage.len }; match Vec.push(&self.strings, &b as *u8) { .Ok => { ret view; }, .Err(error) => { ByteBuffer.drop(&b); ret ByteView.empty(); }, } ret ByteView.empty(); } fn count(self: *Ast): usize { ret self.nodes.len; } fn ref_vec(): Vec { ret Vec.blank(@sizeof(usize), @alignof(usize)); } // ---- dump(镜像 stage1 spl_ast_dump)---- // // AST: // `-/|-kind #ref [line:col] // indent:栈上每层 last 为真输出 " ",否则 "| "。 fn push(buf: *ByteBuffer, bytes: []u8): bool { match ByteBuffer.append(buf, bytes) { .Ok => { ret true; }, .Err(error) => { ret false; }, } ret false; } fn push_byte(buf: *ByteBuffer, byte: u8): bool { match ByteBuffer.append_byte(buf, byte) { .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 ref_at(list: Vec, index: usize): usize { match Vec.get(&list, index) { .Ok(ptr) => { ret (ptr as *usize).*; }, .Err(error) => { ret 0; }, } ret 0; } fn dump_opt(ast: *Ast, buf: *ByteBuffer, stack: *Vec, ref: usize, last: bool): bool { if ref == 0 { ret true; } ret dump_node(ast, buf, stack, ref, last); } fn dump_list(ast: *Ast, buf: *ByteBuffer, stack: *Vec, list: Vec): bool { var i: usize = 0; while i < list.len { var last = (i + 1 == list.len); if !dump_node(ast, buf, stack, ref_at(list, i), last) { ret false; } i = i + 1; } ret true; } fn dump_node(ast: *Ast, buf: *ByteBuffer, stack: *Vec, ref: usize, last: bool): bool { var n = node(ast, ref); if n == null { ret false; } var i: usize = 0; while i < stack.len { var flag: u8 = 0; match Vec.get(stack, i) { .Ok(ptr) => { flag = ptr.*; }, .Err(error) => { ret false; }, } if !push(buf, if flag != 0 { " " } else { "| " }) { ret false; } i = i + 1; } if !push(buf, if last { "`-" } else { "|-" }) { ret false; } if !push(buf, kind_name(n.body)) { ret false; } if !push(buf, " #") { ret false; } if !push_usize(buf, ref) { ret false; } if !span_is_none(n.span) && ast.sources != null { var src = set_at(ast.sources, n.span.file); if src != null { var pos = Source.line_col(src.*, n.span.start); if !push(buf, " [") { ret false; } if !push_usize(buf, pos.line) { ret false; } if !push_byte(buf, ':') { ret false; } if !push_usize(buf, pos.col) { ret false; } if !push_byte(buf, ']') { ret false; } } } if !push_byte(buf, '\n') { ret false; } var l: u8 = 0; if last { l = 1; } match Vec.push(stack, &l as *u8) { .Ok => {}, .Err(error) => { ret false; }, } var ok = true; match n.body { .None => {}, .Root(r) => { ok = dump_opt(ast, buf, stack, r.members, true); }, .ContainerMembers(c) => { ok = dump_list(ast, buf, stack, c.external_tags) && dump_list(ast, buf, stack, c.declarations); }, .ExternalTagItem(t) => { ok = dump_list(ast, buf, stack, t.args); }, .TagItem(t) => { ok = dump_list(ast, buf, stack, t.args); }, .ComptimeDecl(x) => { ok = dump_opt(ast, buf, stack, x.body, true); }, .TopLevelBlock(t) => { ok = dump_list(ast, buf, stack, t.tag_list) && dump_opt(ast, buf, stack, t.block, true); }, .FnDecl(f) => { ok = dump_list(ast, buf, stack, f.tag_list) && dump_list(ast, buf, stack, f.params) && dump_opt(ast, buf, stack, f.ret_type, false) && dump_opt(ast, buf, stack, f.body, true); }, .VarDecl(d) => { ok = dump_list(ast, buf, stack, d.attr_list) && dump_opt(ast, buf, stack, d.pattern, false) && dump_opt(ast, buf, stack, d.type_expr, false) && dump_opt(ast, buf, stack, d.init_expr, true); }, .ConstDecl(d) => { ok = dump_list(ast, buf, stack, d.attr_list) && dump_opt(ast, buf, stack, d.pattern, false) && dump_opt(ast, buf, stack, d.type_expr, false) && dump_opt(ast, buf, stack, d.init_expr, true); }, .TypeDecl(d) => { ok = dump_list(ast, buf, stack, d.attr_list) && dump_opt(ast, buf, stack, d.pattern, false) && dump_opt(ast, buf, stack, d.type_expr, true); }, .MemberDecl(d) => { ok = dump_list(ast, buf, stack, d.attr_list) && dump_opt(ast, buf, stack, d.type_expr, false) && dump_opt(ast, buf, stack, d.init_expr, true); }, .ParamDecl(p) => { ok = dump_list(ast, buf, stack, p.attr_list) && dump_opt(ast, buf, stack, p.type_expr, true); }, .StatementRet(e) => { ok = dump_opt(ast, buf, stack, e.expr, true); }, .StatementBreak(b) => { ok = dump_opt(ast, buf, stack, b.label, false) && dump_opt(ast, buf, stack, b.expr, true); }, .StatementContinue(x) => { ok = dump_opt(ast, buf, stack, x.label, true); }, .StatementDefer(x) => { ok = dump_opt(ast, buf, stack, x.body, true); }, .StatementErrdefer(x) => { ok = dump_opt(ast, buf, stack, x.body, true); }, .StatementComptime(x) => { ok = dump_opt(ast, buf, stack, x.body, true); }, .ExprComptime(x) => { ok = dump_opt(ast, buf, stack, x.body, true); }, .StatementDecl(d) => { ok = dump_opt(ast, buf, stack, d.decl, true); }, .StatementExpr(e) => { ok = dump_opt(ast, buf, stack, e.expr, true); }, .AggregateMember(d) => { ok = dump_list(ast, buf, stack, d.attr_list) && dump_opt(ast, buf, stack, d.type_expr, false) && dump_opt(ast, buf, stack, d.init_expr, true); }, .StructInitItem(s) => { ok = dump_opt(ast, buf, stack, s.expr, true); }, .MatchArm(m) => { ok = dump_opt(ast, buf, stack, m.pattern, false) && dump_opt(ast, buf, stack, m.body, true); }, .Label(x) => {}, .ExprIntLit(x) => {}, .ExprFloatLit(x) => {}, .ExprCharLit(x) => {}, .ExprStringLit(x) => {}, .ExprBoolLit(x) => {}, .ExprNullLit => {}, .ExprUndefinedLit => {}, .ExprIdent(x) => {}, .ExprUnary(u) => { ok = dump_opt(ast, buf, stack, u.operand, true); }, .ExprBinary(b) => { ok = dump_opt(ast, buf, stack, b.left, false) && dump_opt(ast, buf, stack, b.right, true); }, .ExprCall(c) => { ok = dump_opt(ast, buf, stack, c.callee, false) && dump_list(ast, buf, stack, c.args); }, .ExprField(f) => { ok = dump_opt(ast, buf, stack, f.obj, true); }, .ExprIndex(x) => { ok = dump_opt(ast, buf, stack, x.obj, false) && dump_opt(ast, buf, stack, x.index, true); }, .ExprSlice(s) => { ok = dump_opt(ast, buf, stack, s.obj, false) && dump_opt(ast, buf, stack, s.begin, false) && dump_opt(ast, buf, stack, s.end, true); }, .ExprCast(c) => { ok = dump_opt(ast, buf, stack, c.expr, false) && dump_opt(ast, buf, stack, c.type_expr, true); }, .ExprTuple(v) => { ok = dump_list(ast, buf, stack, v.items); }, .ExprArrayLit(a) => { ok = dump_opt(ast, buf, stack, a.elem_type, false) && dump_list(ast, buf, stack, a.elems); }, .ExprAggregateLit(a) => { ok = dump_list(ast, buf, stack, a.fields); }, .ExprComptimeCall(c) => { ok = dump_list(ast, buf, stack, c.args); }, .ExprTry(t2) => { ok = dump_opt(ast, buf, stack, t2.value, true); }, .ExprCatch(c2) => { ok = dump_opt(ast, buf, stack, c2.value, false) && dump_opt(ast, buf, stack, c2.pattern, false) && dump_opt(ast, buf, stack, c2.handler, true); }, .ExprComptimeInit(x) => { ok = dump_list(ast, buf, stack, x.fields); }, .ExprBlock(b) => { ok = dump_list(ast, buf, stack, b.stmts) && dump_opt(ast, buf, stack, b.tail_expr, true); }, .ExprIf(x) => { ok = dump_opt(ast, buf, stack, x.cond, false) && dump_opt(ast, buf, stack, x.then_block, false) && dump_opt(ast, buf, stack, x.else_branch, true); }, .ExprIfvar(x) => { ok = dump_opt(ast, buf, stack, x.var_decl, false) && dump_opt(ast, buf, stack, x.then_block, false) && dump_opt(ast, buf, stack, x.else_branch, true); }, .ExprWhile(w) => { ok = dump_opt(ast, buf, stack, w.label, false) && dump_opt(ast, buf, stack, w.cond, false) && dump_opt(ast, buf, stack, w.body, false) && dump_opt(ast, buf, stack, w.else_branch, true); }, .ExprLoop(x) => { ok = dump_opt(ast, buf, stack, x.label, false) && dump_opt(ast, buf, stack, x.body, true); }, .ExprFor(f) => { ok = dump_opt(ast, buf, stack, f.label, false) && dump_list(ast, buf, stack, f.iter_exprs) && dump_opt(ast, buf, stack, f.pattern, false) && dump_opt(ast, buf, stack, f.body, false) && dump_opt(ast, buf, stack, f.else_branch, true); }, .ExprMatch(m) => { ok = dump_opt(ast, buf, stack, m.matched_expr, false) && dump_list(ast, buf, stack, m.arms); }, .ExprFnType(f) => { ok = dump_list(ast, buf, stack, f.params) && dump_opt(ast, buf, stack, f.ret_type, true); }, .TypePath(p) => {}, .TypePointer(p) => { ok = dump_opt(ast, buf, stack, p.pointee, true); }, .TypeArray(a) => { ok = dump_opt(ast, buf, stack, a.elem_type, true); }, .TypeSlice(s) => { ok = dump_opt(ast, buf, stack, s.elem_type, true); }, .TypeFn(f) => { ok = dump_list(ast, buf, stack, f.params) && dump_opt(ast, buf, stack, f.ret_type, true); }, .TypeParen(p) => { ok = dump_list(ast, buf, stack, p.types); }, .TypeAggregate(a) => { ok = dump_list(ast, buf, stack, a.members); }, .TypeComptimeCall(c) => { ok = dump_list(ast, buf, stack, c.args); }, .PatternIdent(x) => {}, .PatternUnderscore => {}, .PatternDot(p) => { ok = dump_opt(ast, buf, stack, p.sub_pattern, true); }, .PatternTuple(v) => { ok = dump_list(ast, buf, stack, v.items); }, .PatternList(v) => { ok = dump_list(ast, buf, stack, v.items); }, .PatternAggregate(v) => { ok = dump_list(ast, buf, stack, v.items); }, .PatternField(p) => { ok = dump_opt(ast, buf, stack, p.pattern, true); }, } stack.len = stack.len - 1; ret ok; } // 把整棵树 dump 到 out(追加,不 clear)。 fn dump(ast: *Ast, out: *ByteBuffer): bool { if !push(out, "AST:\n") { ret false; } var stack = Vec.blank(1, 1); defer { Vec.drop(&stack); } ret dump_node(ast, out, &stack, ast.root, true); } // ---- 测试 ---- fn test_kind_names() { @assert(ByteView.eq(ByteView.from_slice(kind_name(.None)), ByteView.from_slice("none")), "none name"); @assert(ByteView.eq(ByteView.from_slice(kind_name(.ExprBinary(.{ .op = .Add, .left = 0, .right = 0, }))), ByteView.from_slice("binary")), "binary name"); } fn test_alloc_and_payload() { var ast: Ast = undefined; match new(null) { .Ok(v) => { ast = v; }, .Err(error) => { @assert(false, "ast new failed"); }, } defer { drop(&ast); } @assert(count(&ast) == 1, "sentinel only"); var r = alloc(&ast, .ExprIdent(.{ .name = ByteView.from_slice("main") }), span_make(0, 5, 9)); @assert(r == 1, "first alloc is 1"); var n = node(&ast, r); n.resolved_def_id = 99; @assert(n.span.file == 0 && n.span.start == 5 && n.span.end == 9, "span stored"); @assert(n.resolved_def_id == 99, "header field"); match n.body { .ExprIdent(i) => { @assert(ByteView.eq(i.name, ByteView.from_slice("main")), "payload name"); }, _ => { @assert(false, "expected ident"); }, } } fn expect_bytes(buf: *ByteBuffer, expected: []u8) { var actual = ByteBuffer.as_slice(buf.*); @assert(actual.len == expected.len, "dump length"); var i: usize = 0; while i < expected.len { @assert(actual.ptr[i] == expected.ptr[i], "dump byte"); i = i + 1; } } fn test_dump() { var ast: Ast = undefined; match new(null) { .Ok(v) => { ast = v; }, .Err(error) => { @assert(false, "ast new failed"); }, } defer { drop(&ast); } var id_ref = alloc(&ast, .ExprIdent(.{ .name = ByteView.from_slice("main") }), span_none()); // #1 var decls = ref_vec(); match Vec.push(&decls, &id_ref as *u8) { .Ok => {}, .Err(error) => { @assert(false, "push decl"); }, } var cm_ref = alloc(&ast, .ContainerMembers(.{ .external_tags = ref_vec(), .declarations = decls, }), span_none()); // #2 var root_ref = alloc(&ast, .Root(.{ .members = cm_ref }), span_none()); // #3 ast.root = root_ref; var buf = ByteBuffer.new(); defer { ByteBuffer.drop(&buf); } @assert(dump(&ast, &buf), "dump ok"); // sources=null → 不解析行列;无 span 时也不打印。 expect_bytes(&buf, "AST:\n`-root #3\n `-container_members #2\n `-ident #1\n"); } fn main() { test_kind_names(); test_alloc_and_payload(); test_dump(); }