stage2 迁移ast解析器 stage1 防止错误死循环
This commit is contained in:
1 parent
bb9d0a9f18
commit
b5a7a811b3
7 files changed
+3000
-71
No files matched your search
+642
@@ -0,0 +1,642 @@
|
||||
// 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();
|
||||
}
|
||||
Reference in new issue
Block a user