643 lines
24 KiB
Plaintext
643 lines
24 KiB
Plaintext
// 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 },
|
||
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 },
|
||
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 },
|
||
}
|
||
|
||
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"; },
|
||
.StatementComptime(_) => { ret "statement_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"; },
|
||
.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 时不解析行列)
|
||
|
||
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,
|
||
};
|
||
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);
|
||
self.root = 0;
|
||
}
|
||
|
||
fn node(self: *Ast, ref: usize): *Node {
|
||
match Vec.get(&self.nodes, ref) {
|
||
.Ok(ptr) => { ret ptr as *Node; },
|
||
.Err(error) => { ret null; },
|
||
}
|
||
ret null;
|
||
}
|
||
|
||
// 追加节点,返回其引用(失败返回 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;
|
||
}
|
||
|
||
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:
|
||
// <indent>`-/|-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); },
|
||
.StatementComptime(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); },
|
||
.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();
|
||
}
|