diff --git a/.gitignore b/.gitignore index 378276a..088dcfa 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,11 @@ !.gitignore build/ + +*.sir + +*.o +*.obj + +*.exe +*.out diff --git a/SPL.md b/SPL.md index ee0873c..732b228 100644 --- a/SPL.md +++ b/SPL.md @@ -6,8 +6,8 @@ SPL是一个从零构建的自举编译器项目。引导链: ``` stage0/spl_vm.c — SIR 虚拟机 (C 语言实现) -stage1/splc0.c — SPL→SIR 编译器 (C 语言实现,引导用) -stage1/splc1.spl — SPL→SIR 编译器 (SPL 语言实现,自举第一版) +stage1/splc0.c — SPL->SIR 编译器 (C 语言实现,引导用) +stage1/splc1.spl — SPL->SIR 编译器 (SPL 语言实现,自举第一版) ...将来... ``` @@ -84,25 +84,45 @@ ParamDecl # ================================================================ TypeDecl - <- KEYWORD_type IDENTIFIER EQUAL TypeBody (* @packed type Vec = struct { ... } *) + <- KEYWORD_type IDENTIFIER EQUAL TypeExpr (* @packed type Vec = struct { ... } *) -TypeBody - <- KEYWORD_struct LBRACE AggregateBody RBRACE - / KEYWORD_union LBRACE AggregateBody RBRACE - / KEYWORD_enum LBRACE AggregateBody RBRACE - / TypeExpr (* 别名 *) +MemberDecl + <- IDENTIFIER (COLON TypeExpr)? (COMMA / SEMICOLON)? -AggregateBody <- AggregateItem* +# ================================================================ +# 类型表达式 (Type Expression, LL(1), 无左递归) +# ================================================================ -AggregateItem - <- ContainerDeclaration +TypeExpr <- PrefixTypeOp* AttrList? TypeBase +TypeBase + <- FnTypeExpr + / TypePath + / KEYWORD_struct LBRACE ContainerDeclaration* RBRACE + / KEYWORD_union LBRACE ContainerDeclaration* RBRACE + / KEYWORD_enum LBRACE ContainerDeclaration* RBRACE (* struct: 字段, 必须 IDENTIFIER : TypeExpr (语义层检查) union: 联合体字段, 同上 enum: 变体, IDENTIFIER : TypeExpr 或 纯 IDENTIFIER (朴素变体) *) -MemberDecl - <- IDENTIFIER (COLON TypeExpr)? (COMMA / SEMICOLON)? +PrefixTypeOp (* LL(1): * / [ *) + <- ASTERISK + / LBRACKET (RBRACKET / INTEGER RBRACKET) (* [] 或 [N] *) + +FnTypeExpr <- KEYWORD_fn LPAREN TypeExprList? RPAREN TypeExpr + +TypeExprList <- (TypeExpr COMMA)* TypeExpr? + +TypePath <- TypeAtom (DOT TypeAtom)* + +TypeAtom (* LL(1): 关键词 / IDENTIFIER / _ *) + <- KEYWORD_void / KEYWORD_bool + / KEYWORD_i8 / KEYWORD_u8 / KEYWORD_i16 / KEYWORD_u16 + / KEYWORD_i32 / KEYWORD_u32 / KEYWORD_i64 / KEYWORD_u64 + / KEYWORD_isize / KEYWORD_usize + / KEYWORD_f32 / KEYWORD_f64 / KEYWORD_ptr + / UNDERSCORE + / IDENTIFIER # ================================================================ # 变量 / 常量 @@ -140,6 +160,7 @@ Statement (* LL(1): 14 分支互 / ContinueStatement / DeferStatement / VarDecl + / ConstDecl / TypeDecl / ExprStatement @@ -175,12 +196,12 @@ ForStatement MatchStatement <- KEYWORD_match Expr LBRACE MatchArm* RBRACE MatchArm - <- MatchPat FAT_R_ARROW Statement - / UNDERSCORE FAT_R_ARROW Statement + <- MatchPat (COMMA MatchPat)* FAT_R_ARROW Block MatchPat (* LL(1): . / _ / Expr *) <- DOT IDENTIFIER (LBRACKET IDENTIFIER RBRACKET)? (* 仅允许 .Item 或 .Item[bind] *) / Expr (* 字面量或变量常量 *) + / UNDERSCORE # ---- 跳转 ---- @@ -221,7 +242,7 @@ PrefixOp <- MINUS / BANG / TILDE / AMPERSAND / ASTERISK PostfixExpr <- PrimaryExpr ( LPAREN ExprList RPAREN (* 函数调用 *) - / DOT IDENTIFIER (* 字段/方法 *) + / DOT IDENTIFIER (* 字段 *) / DOT ASTERISK (* 解引用 *) / LBRACKET Expr RBRACKET (* 索引 *) / LBRACKET Expr DOT2 Expr? RBRACKET (* 切片 *) @@ -239,7 +260,8 @@ PrimaryExpr / CHAR_LITERAL / STRING_LITERAL / TRUE / FALSE / NULL_KW - / IDENTIFIER (LBRACE StructInitList? RBRACE)? (* 变量引用 / 结构体字面量 *) + / IDENTIFIER (LBRACE StructInitList? RBRACE)? (* 变量引用 / 结构体字面量 *) + / DOT LBRACE StructInitList? RBRACE (* 匿名聚合字面量 .{ .f = e } *) / LPAREN Expr RPAREN / ArrayLiteral / BuiltinExpr @@ -254,38 +276,6 @@ StructInit <- DOT IDENTIFIER (EQUAL Expr)? BuiltinExpr <- (AT / SHARP) IDENTIFIER LPAREN ExprList? RPAREN (* @sizeof(T) / #runtime(x) *) - -# ================================================================ -# 类型表达式 (Type Expression, LL(1), 无左递归) -# ================================================================ - -TypeExpr <- PrefixTypeOp* TypeBase - -TypeBase <- FnTypeExpr / TypePath - -PrefixTypeOp (* LL(1): * / [ *) - <- ASTERISK - / LBRACKET (RBRACKET / INTEGER RBRACKET) (* [] 或 [N] *) - -FnTypeExpr <- KEYWORD_fn LPAREN TypeExprList? RPAREN TypeExpr - -TypeExprList <- (TypeExpr COMMA)* TypeExpr? - -TypePath <- TypeAtom (DOT TypeAtom)* - -TypeAtom (* LL(1): 关键词 / IDENTIFIER / _ *) - <- KEYWORD_void / KEYWORD_bool - / KEYWORD_i8 / KEYWORD_u8 / KEYWORD_i16 / KEYWORD_u16 - / KEYWORD_i32 / KEYWORD_u32 / KEYWORD_i64 / KEYWORD_u64 - / KEYWORD_isize / KEYWORD_usize - / KEYWORD_f32 / KEYWORD_f64 / KEYWORD_ptr - / UNDERSCORE - / IDENTIFIER - -(* *_ = "any pointer", 唯一支持自动类型提升和隐式转换的指针类型。 - 语义上 *_ 可赋值给任意 *T, 任意 *T 可赋值给 *_。 - - # ================================================================ # 运算符 # ================================================================ @@ -324,6 +314,11 @@ KEYWORD_enum <- 'enum' !IDENTIFIER_CHAR KEYWORD_comptime <- 'comptime' !IDENTIFIER_CHAR KEYWORD_void <- 'void' !IDENTIFIER_CHAR KEYWORD_bool <- 'bool' !IDENTIFIER_CHAR +KEYWORD_isize <- 'isize' !IDENTIFIER_CHAR +KEYWORD_usize <- 'usize' !IDENTIFIER_CHAR +KEYWORD_ptr <- 'ptr' !IDENTIFIER_CHAR + +(* desperate 未来采用 ixx uxx fxx 表达而非关键字,启动阶段使用固定关键字 *) KEYWORD_i8 <- 'i8' !IDENTIFIER_CHAR KEYWORD_u8 <- 'u8' !IDENTIFIER_CHAR KEYWORD_i16 <- 'i16' !IDENTIFIER_CHAR @@ -332,11 +327,8 @@ KEYWORD_i32 <- 'i32' !IDENTIFIER_CHAR KEYWORD_u32 <- 'u32' !IDENTIFIER_CHAR KEYWORD_i64 <- 'i64' !IDENTIFIER_CHAR KEYWORD_u64 <- 'u64' !IDENTIFIER_CHAR -KEYWORD_isize <- 'isize' !IDENTIFIER_CHAR -KEYWORD_usize <- 'usize' !IDENTIFIER_CHAR KEYWORD_f32 <- 'f32' !IDENTIFIER_CHAR KEYWORD_f64 <- 'f64' !IDENTIFIER_CHAR -KEYWORD_ptr <- 'ptr' !IDENTIFIER_CHAR TRUE <- 'true' !IDENTIFIER_CHAR FALSE <- 'false' !IDENTIFIER_CHAR @@ -355,7 +347,7 @@ COLON <- ':' DOT <- '.' DOT2 <- '..' DOT3 <- '...' AT <- '@' SHARP <- '#' FAT_R_ARROW <- '=>' FAT_D_ARROW <- '<=>' -R_ARROW <- '<-' L_ARROW <- '->' D_ARROW '<->' +THIN_R_ARROW <- '<-' THIN_L_ARROW <- '->' THIN_D_ARROW '<->' EQUAL <- '=' COLON_ASSIGN <- ':=' @@ -407,6 +399,18 @@ BLOCK_COMMENT <- '/*' (!'*/' .)* '*/' eof <- !. ``` +## TODO +```spl +// 约束关键字,语义是`类型子集形状` +type name = shape { ... } + +// 默认字段 +type name = struct { x: i32 = 0, } + +// 默认参数 +fn name(x: i32 = 0) { ... } +``` + # SPL 语义规范 ## 设计总则 @@ -442,15 +446,16 @@ DirectiveBlock @id { } / #id { } 为扩展占位,无预定义行为。未识 语义: 完全由语言版本或库注册决定。当前所有均视为未识别,产生警告 (宽松) 或错误 (严格)。 ## 函数 -text + AttrList? fn IDENTIFIER ( ParamDeclList ) TypeExpr? ( ; | Block ) + 参数: 全部不可变。需要可变时通过 *T 传递。 返回类型: 省略即 void。仅有 ; 表示外部声明。 调用: 实参与形参数量、类型必须完全匹配 (无隐式可变参数)。 -执行: 新作用域 → 形参绑定实参 → 执行 Block → 遇 ret expr 返回 (类型匹配),或 void 函数自然结束返回。 +执行: 新作用域 -> 形参绑定实参 -> 执行 Block -> 遇 ret expr 返回 (类型匹配),或 void 函数自然结束返回。 ## 类型声明与聚合体 ### 别名 @@ -573,7 +578,7 @@ defer { ... }: 退出作用域时以后进先出执行。 浮点 f64,布尔 bool。 -null → ?T 隐式转换。 +null -> ?T 隐式转换。 ### 运算符 算术/位运算: 操作数类型必须完全一致或由字面量推导,无隐式提升。 @@ -587,12 +592,12 @@ null → ?T 隐式转换。 - 数值 同类型 ! bool bool ~ 整数 同类型 -& 可寻址左值 *T -解引用仅有后缀形式 .*,无前缀 * 运算符。(*ptr) 不合法。 +& 可寻址左值 \*T +解引用仅有后缀形式 .\*,无前缀 \* 运算符。(\*ptr) 不合法。 后缀运算符 运算 约束 说明 -expr.* expr 为 *T 解引用,得 T 左值 +expr.\* expr 为 \*T 解引用,得 T 左值 expr.field struct 或指针 指针时自动解引用一层再取字段 expr[i] 数组/切片/指针,i 整数 元素左值 expr[a..b] 数组/切片/指针 切片操作,产生 []T,内部设置 ptr 和 len @@ -618,9 +623,9 @@ comptime { ... } / comptime expr;: 编译时求值,不可引用运行时变量 *_ 的特殊规则: -*T → *_: 允许,必须警告。 +*T -> *_: 允许,必须警告。 -*_ → *T: 允许,必须警告。 +*_ -> *T: 允许,必须警告。 这些警告无法全局关闭,未来提供显式抑制。 @@ -628,7 +633,7 @@ comptime { ... } / comptime expr;: 编译时求值,不可引用运行时变量 整数字面量适配更大类型。 -null → ?T。 +null -> ?T。 其余必须显式 as。 @@ -740,7 +745,7 @@ fn(参数类型列表) 返回类型 null 字面量只能出现在需要 ?T 的上下文中。 -### 自定义聚合类型 +### 聚合类型 struct: 字段连续排列 (可能有对齐填充),每个字段有自己的类型。赋值是逐字段拷贝。 union: 所有字段共享起始地址,大小等于最大字段 (加上对齐)。直接字段读取被视为不安全,需通过 match 解构。 @@ -750,16 +755,7 @@ enum: 带标签的联合体,每个变体可有载荷。大小实现定义, ## 类型分类 ### 值类型 (复制语义) 所有基本标量类型,或者说底层寄存器类型 (整数、浮点、bool) - -struct - -数组 [N]T - -切片 []T - -区间 Range - -赋值、传参会复制整个值。修改副本不影响原值。 +`聚合类型` `数组 [N]T` `切片[]T` `区间Range` 赋值、传参会复制整个值。修改副本不影响原值。 ### 引用/指针类型 \*T、\*_ @@ -802,15 +798,15 @@ bool 整数 否 — 以下用伪代码描述每个表达式类型的推断方法。若类型检查失败,则为编译错误。 ### 字面量 -INTEGER → i32 +INTEGER -> i32 -FLOAT → f64 +FLOAT -> f64 -true / false → bool +true / false -> bool -null → 必须从上下文推导出 ?T,无法推导则报错。 +null -> 必须从上下文推导出 ?T,无法推导则报错。 -字符串字面量 → []u8 (具体待定) +字符串字面量 -> []u8 (具体待定) ### 二元运算 算术 e1 + e2、-、*、/、%: @@ -953,8 +949,8 @@ ForRange 提供表达式列表 [E1, E2, ...]。as 后标识符列表 [v1, v2, .. ``` for my_slice, 0.. as elem, idx { ... } -// my_slice: []T → elem: T -// 0..: Range 无界 → idx: i64,长度由 my_slice 决定 +// my_slice: []T -> elem: T +// 0..: Range 无界 -> idx: i64,长度由 my_slice 决定 ``` ## 类型系统限制 无隐式类型提升 (除字面量整数拓宽和数组到切片外)。 diff --git a/stage1/spl_ast.c b/stage1/spl_ast.c index 006a2ab..c3503bb 100644 --- a/stage1/spl_ast.c +++ b/stage1/spl_ast.c @@ -1,2 +1,3427 @@ -// 使用 LL(1) 解析整个AST +// WRITE BY AI +/* spl_ast.c SPL AST 模块:LL(1) 递归下降解析+ valid/dump/drop */ + #include "spl_ast.h" +#include "spl_dumptree.h" +#include +#include +#include +#include + +/* 从 token 复制 lexeme(带长度,NUL 终止),供 AST 字符串字段持有 */ +static char *sdupn(const char *s, usize n) { + char *r = malloc(n + 1); + if (!r) + return NULL; + memcpy(r, s, n); + r[n] = '\0'; + return r; +} + +/* ================================================================ + * 内联节点解读上下 * + * 内联子节点(attr_item/param_decl/prefix_expr/postfix_expr/primary_expr/ + * packed_expr/aggregate_init_item/type_atom/prefix_type)不暴露独立 kind * 创建时用占位 + * kind,解读(valid/dump/drop)一律按父字段引用类型递归 * + * ================================================================ */ +typedef enum { + CTX_CONTAINER, /* container_item */ + CTX_DECL, /* fn/type/var/const/member 声明*/ + CTX_ATTR_ITEM, /* attr_item */ + CTX_PARAM, /* param_decl */ + CTX_BLOCK_ITEM, /* block_item / 尾表达式 */ + CTX_EXPR, /* expr */ + CTX_PREFIX, /* prefix_expr */ + CTX_POSTFIX, /* postfix_expr */ + CTX_PRIMARY, /* primary_expr */ + CTX_PACKED, /* packed_expr */ + CTX_AGG_ITEM, /* aggregate_init_item */ + CTX_TYPE_EXPR, /* type_expr */ + CTX_TYPE_ATOM, /* type_atom */ + CTX_PREFIX_TYPE, /* prefix_type */ +} walk_ctx_t; + +typedef struct { + spl_ast_t *ast; + usize pos; + int failed; + int stop_as; /* 0 时后缀表达式不KW_AS(for 头部序列专用*/ + int stop_agg; /* 0 IDENT '{' 不作结构体字面量(if/while/match 头部专用*/ +} parser_t; + +/* 前向声明 */ +static spl_tok_t *peek(parser_t *p); +static spl_tok_t *advance(parser_t *p); +static spl_ast_node_ref_t new_node(parser_t *p, spl_ast_node_kind_t kind, spl_tok_t *loc); +static spl_ast_node_t *node_at(spl_ast_t *ast, spl_ast_node_ref_t ref); +static spl_ast_node_ref_t parse_expr(parser_t *p); +static spl_ast_node_ref_vec_t parse_expr_list(parser_t *p); +static spl_ast_node_ref_vec_t parse_block(parser_t *p); +static spl_ast_node_ref_t parse_type_expr(parser_t *p); +static spl_ast_node_ref_t parse_statement(parser_t *p); +static spl_ast_node_ref_t parse_container_declaration(parser_t *p); +static spl_ast_node_ref_t parse_param_decl_impl(parser_t *p); +static spl_ast_node_ref_t parse_bool_or(parser_t *p); +static spl_ast_node_ref_t parse_bool_and(parser_t *p); +static spl_ast_node_ref_t parse_bit_or(parser_t *p); +static spl_ast_node_ref_t parse_bit_xor(parser_t *p); +static spl_ast_node_ref_t parse_bit_and(parser_t *p); +static spl_ast_node_ref_t parse_cmp_eq(parser_t *p); +static spl_ast_node_ref_t parse_cmp(parser_t *p); +static spl_ast_node_ref_t parse_range(parser_t *p); +static spl_ast_node_ref_t parse_shift(parser_t *p); +static spl_ast_node_ref_t parse_add(parser_t *p); +static spl_ast_node_ref_t parse_mul(parser_t *p); +static spl_ast_node_ref_t parse_prefix(parser_t *p); +static spl_ast_node_ref_t parse_postfix(parser_t *p); +static spl_ast_node_ref_t parse_primary(parser_t *p); +static spl_ast_node_ref_vec_t parse_aggregate_init_body(parser_t *p); +static spl_ast_node_ref_t parse_builtin(parser_t *p); +static spl_ast_node_ref_t parse_array_literal(parser_t *p); +static spl_ast_node_ref_t parse_if(parser_t *p); +static spl_ast_node_ref_t parse_ifvar(parser_t *p); +static spl_ast_node_ref_t parse_while(parser_t *p); +static spl_ast_node_ref_t parse_loop(parser_t *p); +static spl_ast_node_ref_t parse_for(parser_t *p); +static spl_ast_node_ref_t parse_match(parser_t *p); +static int parse_match_arm(parser_t *p, spl_ast_node_ref_vec_t *paced_vec, + spl_ast_node_ref_vec_t *block_vec); +static spl_ast_node_ref_t parse_ret(parser_t *p); +static spl_ast_node_ref_t parse_break(parser_t *p); +static spl_ast_node_ref_t parse_continue(parser_t *p); +static spl_ast_node_ref_t parse_defer(parser_t *p); +static spl_ast_node_ref_t parse_var_decl(parser_t *p, spl_ast_node_ref_vec_t attr_list); +static spl_ast_node_ref_t parse_const_decl(parser_t *p, spl_ast_node_ref_vec_t attr_list); +static spl_ast_node_ref_t parse_type_decl(parser_t *p, spl_ast_node_ref_vec_t attr_list); +static spl_ast_node_ref_t parse_fn_decl(parser_t *p, spl_ast_node_ref_vec_t attr_list); +static spl_ast_node_ref_t parse_member_decl(parser_t *p, spl_ast_node_ref_vec_t attr_list); +static spl_ast_node_ref_t parse_type_atom(parser_t *p); + +static void dump_node(spl_ast_t *ast, spl_ast_node_ref_t ref, walk_ctx_t ctx, const char *prefix, + int is_last); +static void drop_node(spl_ast_t *ast, spl_ast_node_ref_t ref, walk_ctx_t ctx); +static void drop_vec(spl_ast_t *ast, spl_ast_node_ref_vec_t *vec, walk_ctx_t ctx); +static void validate_node(spl_ast_t *ast, spl_ast_node_ref_t ref, walk_ctx_t ctx); +static void validate_vec(spl_ast_t *ast, spl_ast_node_ref_vec_t *vec, walk_ctx_t ctx); + +/* ================================================================ + * 词法辅助 + * ================================================================ */ + +static int is_trivia(spl_tok_type_t t) { return t == TOK_ENDLINE || t == TOK_LINE_COMMENT; } + +/* 返回下一个有token(跳trivia),不前*/ +static spl_tok_t *peek(parser_t *p) { + spl_tok_t *toks = p->ast->input.data; + usize n = p->ast->input.size; + usize i = p->pos; + while (i < n && is_trivia(toks[i].type)) + i++; + p->pos = i; + if (i >= n) + return NULL; + return &toks[i]; +} + +/* 返回当前有效 token 并前进(EOF 处不前进*/ +static spl_tok_t *advance(parser_t *p) { + spl_tok_t *t = peek(p); + if (t && t->type != TOK_EOF) + p->pos++; + return t; +} + +/* 当前有效 token 的下一个有token */ +static spl_tok_t *peek_next(parser_t *p) { + usize i = p->pos; + spl_tok_t *toks = p->ast->input.data; + usize n = p->ast->input.size; + while (i < n && is_trivia(toks[i].type)) + i++; + i++; + while (i < n && is_trivia(toks[i].type)) + i++; + if (i >= n) + return NULL; + return &toks[i]; +} + +static void parse_error(parser_t *p, spl_tok_t *t, const char *msg) { + (void)p; + const char *fn = (t && t->fname) ? t->fname : ""; + usize line = t ? t->line : 0; + usize col = t ? t->col : 0; + fprintf(stderr, "%s:%llu:%llu: error: %s\n", fn, (unsigned long long)line, + (unsigned long long)col, msg); + fflush(stderr); + abort(); +} + +static int expect(parser_t *p, spl_tok_type_t type, const char *what) { + spl_tok_t *t = peek(p); + if (t && t->type == type) { + advance(p); + return 1; + } + parse_error(p, t, what); + return 0; +} + +/* ================================================================ + * 节点创建 + * ================================================================ */ + +static spl_ast_node_ref_t new_node(parser_t *p, spl_ast_node_kind_t kind, spl_tok_t *loc) { + spl_ast_node_t n; + memset(&n, 0, sizeof(n)); + n.kind = kind; + if (loc) { + n.loc.fname = loc->fname; + n.loc.line = loc->line; + n.loc.col = loc->col; + } + vec_push(p->ast->buckets, n); + return p->ast->buckets.size - 1; +} + +static spl_ast_node_t *node_at(spl_ast_t *ast, spl_ast_node_ref_t ref) { + return &ast->buckets.data[ref]; +} + +static spl_ast_node_ref_t mk_bin(parser_t *p, spl_tok_t *loc, int op, spl_ast_node_ref_t left, + spl_ast_node_ref_t right) { + spl_ast_node_ref_t ref = new_node(p, SPL_AST_EXPR, loc); + node_at(p->ast, ref)->expr.op = op; + node_at(p->ast, ref)->expr.op_expr.left = left; + node_at(p->ast, ref)->expr.op_expr.right = right; + return ref; +} + +/* 创建 primary_expr 内联节点(占kind=SPL_AST_EXPR*/ +static spl_ast_node_ref_t mk_primary(parser_t *p, spl_tok_t *loc, int pk) { + spl_ast_node_ref_t ref = new_node(p, SPL_AST_EXPR, loc); + node_at(p->ast, ref)->primary_expr.kind = pk; + return ref; +} + +/* op=PRIMARY EXPR 节点包装内联 primary/prefix/postfix 节点 */ +static spl_ast_node_ref_t wrap_expr(parser_t *p, spl_ast_node_ref_t inner) { + spl_ast_node_ref_t e = new_node(p, SPL_AST_EXPR, NULL); + node_at(p->ast, e)->loc = node_at(p->ast, inner)->loc; + node_at(p->ast, e)->expr.op = SPL_AST_PRIMARY_EXPR; + node_at(p->ast, e)->expr.op_expr.left = inner; + return e; +} + +/* 用指op(PREFIX/POSTFIX/PRIMARY)包装内联节*/ +static spl_ast_node_ref_t wrap_op_expr(parser_t *p, spl_ast_node_ref_t inner, int op) { + spl_ast_node_ref_t e = new_node(p, SPL_AST_EXPR, NULL); + node_at(p->ast, e)->loc = node_at(p->ast, inner)->loc; + node_at(p->ast, e)->expr.op = op; + node_at(p->ast, e)->expr.op_expr.left = inner; + return e; +} + +/* ================================================================ + * 字面量解 * ================================================================ */ + +/* 整数字面量:去下划线后按0 解析x/0o/0b/十进制,忽略 0b 特判*/ +static isize parse_int_literal(const spl_tok_t *t) { + char buf[128]; + usize j = 0; + for (usize i = 0; i < t->len && j < sizeof(buf) - 1; i++) { + if (t->lexeme[i] != '_') + buf[j++] = t->lexeme[i]; + } + buf[j] = 0; + return (isize)strtoll(buf, NULL, 0); +} + +static double parse_float_literal(const spl_tok_t *t) { + char buf[128]; + usize j = 0; + for (usize i = 0; i < t->len && j < sizeof(buf) - 1; i++) { + if (t->lexeme[i] != '_') + buf[j++] = t->lexeme[i]; + } + buf[j] = 0; + return strtod(buf, NULL); +} + +/* 字符字面量:解码转义 */ +static char parse_char_literal(const spl_tok_t *t) { + const char *p = t->lexeme + 1; + const char *end = t->lexeme + t->len - 1; + char c = 0; + if (p < end) { + spl_decode_escape(&p, &c); + } + return c; +} + +/* 字符串字面量:解码为 C 字符串副本(去引号、展开转义,malloc 分配) * + * 所有权约定:该副本spl_ast_drop STRING_LIT 节点free*/ +static char *parse_string_literal(const spl_tok_t *t) { + const char *p = t->lexeme + 1; + const char *end = t->lexeme + t->len - 1; + char *out = (char *)malloc(t->len + 1); + if (!out) + return NULL; + char *o = out; + while (p < end) { + char c; + if (spl_decode_escape(&p, &c) == 0) { + *o++ = c; + } else { + /* 非法转义:宽松保留原字符 */ + *o++ = *p; + p++; + } + } + *o = 0; + return out; +} + +/* ================================================================ + * 起始判定 + * ================================================================ */ + +static int can_start_expr(spl_tok_type_t t) { + switch (t) { + case TOK_INT_LITERAL: + case TOK_FLOAT_LITERAL: + case TOK_CHAR_LITERAL: + case TOK_STRING_LITERAL: + case KW_TRUE: + case KW_FALSE: + case KW_NULL: + case TOK_IDENT: + case TOK_AT: + case TOK_SHARP: + case TOK_L_PAREN: + case TOK_L_BRACKET: + case TOK_L_BRACE: + case TOK_SUB: + case TOK_NOT: + case TOK_BIT_NOT: + case TOK_AND: + case TOK_MUL: + return 1; + default: + return 0; + } +} + +static int can_start_type(spl_tok_type_t t) { + switch (t) { + case TOK_MUL: + case TOK_L_BRACKET: + case KW_FN: + case KW_VOID: + case KW_BOOL: + case KW_ANY: + case TOK_IDENT: + return 1; + default: + return 0; + } +} + +static int can_start_statement(spl_tok_type_t t) { + switch (t) { + case KW_IF: + case KW_WHILE: + case KW_LOOP: + case KW_FOR: + case KW_MATCH: + case KW_RET: + case KW_BREAK: + case KW_CONTINUE: + case KW_DEFER: + case KW_VAR: + case KW_CONST: + case KW_TYPE: + case KW_COMPTIME: + return 1; + default: + return 0; + } +} + +/* ================================================================ + * 容器层:声明 + * ================================================================ */ + +/* AttrList -> 追加 attr_item 节点out(空out 不变*/ +static void parse_attr_list(parser_t *p, spl_ast_node_ref_vec_t *out) { + while (1) { + spl_tok_t *t = peek(p); + if (!t) + return; + if (t->type != TOK_AT && t->type != TOK_SHARP) + return; + advance(p); + spl_tok_t *name = peek(p); + if (!name || name->type != TOK_IDENT) { + parse_error(p, name, "expected identifier after attribute"); + return; + } + advance(p); + spl_ast_node_ref_t ref = new_node(p, SPL_AST_ATTR_LIST, t); + node_at(p->ast, ref)->attr_item.ident = sdupn(name->lexeme, name->len); + if (peek(p) && peek(p)->type == TOK_L_PAREN) { + advance(p); + spl_ast_node_ref_vec_t evec = parse_expr_list(p); + node_at(p->ast, ref)->attr_item.expr_list = evec; + if (!expect(p, TOK_R_PAREN, "expected ')' to close attribute args")) + return; + } + vec_push(*out, ref); + } +} + +/* ParamDeclList -> 追加 param_decl 节点out */ +static void parse_param_list(parser_t *p, spl_ast_node_ref_vec_t *out) { + while (1) { + spl_tok_t *t = peek(p); + if (!t) + return; + if (t->type == TOK_AT || t->type == TOK_SHARP || t->type == TOK_IDENT) { + spl_ast_node_ref_t pd = parse_param_decl_impl(p); + if (pd) + vec_push(*out, pd); + if (peek(p) && peek(p)->type == TOK_COMMA) { + advance(p); + continue; + } + return; + } else if (t->type == TOK_ELLIPSIS) { + /* 变长参数:追name=NULL、type_expr=NULL 的空 param_decl */ + advance(p); + spl_ast_node_ref_t pd = new_node(p, SPL_AST_MEMBER_DECL, t); + node_at(p->ast, pd)->param_decl.name = NULL; + node_at(p->ast, pd)->param_decl.type_expr = 0; + vec_push(*out, pd); + if (peek(p) && peek(p)->type == TOK_COMMA) + advance(p); + return; + } else if (t->type == KW_VOID) { + /* fn(void):无参写法 */ + advance(p); + return; + } + return; + } +} + +static spl_ast_node_ref_t parse_param_decl_impl(parser_t *p) { + spl_ast_node_ref_vec_t attr_list; + vec_init(attr_list); + parse_attr_list(p, &attr_list); + spl_tok_t *name = peek(p); + if (!name || name->type != TOK_IDENT) { + parse_error(p, name, "expected parameter name"); + return 0; + } + advance(p); + if (!expect(p, TOK_COLON, "expected ':' in parameter declaration")) + return 0; + spl_ast_node_ref_t type_expr = parse_type_expr(p); + spl_ast_node_ref_t ref = new_node(p, SPL_AST_MEMBER_DECL, name); + node_at(p->ast, ref)->param_decl.attr_list = attr_list; + node_at(p->ast, ref)->param_decl.name = sdupn(name->lexeme, name->len); + node_at(p->ast, ref)->param_decl.type_expr = type_expr; + return ref; +} + +static spl_ast_node_ref_t parse_fn_decl(parser_t *p, spl_ast_node_ref_vec_t attr_list) { + spl_tok_t *kw = peek(p); + advance(p); /* KW_FN */ + spl_tok_t *name = peek(p); + if (!name || name->type != TOK_IDENT) { + parse_error(p, name ? name : kw, "expected function name"); + return 0; + } + advance(p); + if (!expect(p, TOK_L_PAREN, "expected '(' in function declaration")) + return 0; + spl_ast_node_ref_vec_t param_list; + vec_init(param_list); + parse_param_list(p, ¶m_list); + if (!expect(p, TOK_R_PAREN, "expected ')' in function declaration")) + return 0; + + spl_ast_node_ref_t type_expr = 0; + spl_ast_node_ref_vec_t block; + vec_init(block); + int has_body = 0; + + spl_tok_t *t = peek(p); + if (t && t->type == TOK_SEMICOLON) { + advance(p); + } else if (t && t->type == TOK_L_BRACE) { + block = parse_block(p); + has_body = 1; + } else if (t && can_start_type(t->type)) { + type_expr = parse_type_expr(p); + t = peek(p); + if (t && t->type == TOK_SEMICOLON) { + advance(p); + } else if (t && t->type == TOK_L_BRACE) { + block = parse_block(p); + has_body = 1; + } else { + parse_error(p, t, "function declaration must end with ';' or a block"); + } + } else { + parse_error(p, t, "expected return type, ';' or a block"); + } + + /* 定义SPL_AST_FN_DEFINE,外部声明(;)用 SPL_AST_FN_DECL */ + spl_ast_node_ref_t ref = new_node(p, has_body ? SPL_AST_FN_DEFINE : SPL_AST_FN_DECL, kw); + node_at(p->ast, ref)->fn_decl.attr_list = attr_list; + node_at(p->ast, ref)->fn_decl.name = sdupn(name->lexeme, name->len); + node_at(p->ast, ref)->fn_decl.param_list = param_list; + node_at(p->ast, ref)->fn_decl.type_expr = type_expr; + node_at(p->ast, ref)->fn_decl.block = block; + return ref; +} + +/* TypeDecl <- KEYWORD_type IDENTIFIER EQUAL TypeExpr */ +static spl_ast_node_ref_t parse_type_decl(parser_t *p, spl_ast_node_ref_vec_t attr_list) { + (void)attr_list; /* type_decl 自身不承载 attr,attr 归 TypeExpr 管理 */ + spl_tok_t *kw = peek(p); + advance(p); /* KW_TYPE */ + spl_tok_t *name = peek(p); + if (!name || name->type != TOK_IDENT) { + parse_error(p, name ? name : kw, "expected type name"); + return 0; + } + advance(p); + if (!expect(p, TOK_ASSIGN, "expected '=' in type declaration")) + return 0; + spl_ast_node_ref_t ref = new_node(p, SPL_AST_TYPE_DECL, kw); + node_at(p->ast, ref)->type_decl.name = sdupn(name->lexeme, name->len); + spl_ast_node_ref_t te = parse_type_expr(p); + node_at(p->ast, ref)->type_decl.type_expr = te; + if (peek(p) && (peek(p)->type == TOK_SEMICOLON || peek(p)->type == TOK_COMMA)) + advance(p); + return ref; +} + +static spl_ast_node_ref_t parse_member_decl(parser_t *p, spl_ast_node_ref_vec_t attr_list) { + spl_tok_t *name = peek(p); + advance(p); /* TOK_IDENT */ + spl_ast_node_ref_t type_expr = 0; + if (peek(p) && peek(p)->type == TOK_COLON) { + advance(p); + type_expr = parse_type_expr(p); + } + spl_ast_node_ref_t ref = new_node(p, SPL_AST_MEMBER_DECL, name); + node_at(p->ast, ref)->member_decl.attr_list = attr_list; + node_at(p->ast, ref)->member_decl.name = sdupn(name->lexeme, name->len); + node_at(p->ast, ref)->member_decl.type_expr = type_expr; + spl_tok_t *t = peek(p); + if (t && (t->type == TOK_COMMA || t->type == TOK_SEMICOLON)) + advance(p); + return ref; +} + +static spl_ast_node_ref_t parse_var_decl(parser_t *p, spl_ast_node_ref_vec_t attr_list) { + spl_tok_t *kw = peek(p); + advance(p); /* KW_VAR */ + spl_tok_t *name = peek(p); + if (!name || name->type != TOK_IDENT) { + parse_error(p, name ? name : kw, "expected variable name"); + return 0; + } + advance(p); + spl_ast_node_ref_t type_expr = 0; + spl_ast_node_ref_t expr = 0; + spl_tok_t *t = peek(p); + if (t && t->type == TOK_COLON) { + advance(p); + type_expr = parse_type_expr(p); + } else if (t && t->type == TOK_COLON_ASSIGN) { + advance(p); + expr = parse_expr(p); + } + t = peek(p); + if (t && t->type == TOK_ASSIGN) { + advance(p); + expr = parse_expr(p); + } + if (peek(p) && peek(p)->type == TOK_SEMICOLON) + advance(p); + spl_ast_node_ref_t ref = new_node(p, SPL_AST_VAR_DECL, kw); + node_at(p->ast, ref)->var_decl.attr_list = attr_list; + node_at(p->ast, ref)->var_decl.name = sdupn(name->lexeme, name->len); + node_at(p->ast, ref)->var_decl.type_expr = type_expr; + node_at(p->ast, ref)->var_decl.expr = expr; + return ref; +} + +static spl_ast_node_ref_t parse_const_decl(parser_t *p, spl_ast_node_ref_vec_t attr_list) { + spl_tok_t *kw = peek(p); + advance(p); /* KW_CONST */ + spl_tok_t *name = peek(p); + if (!name || name->type != TOK_IDENT) { + parse_error(p, name ? name : kw, "expected constant name"); + return 0; + } + advance(p); + spl_ast_node_ref_t type_expr = 0; + spl_ast_node_ref_t expr = 0; + spl_tok_t *t = peek(p); + if (t && t->type == TOK_COLON) { + advance(p); + type_expr = parse_type_expr(p); + } else if (t && t->type == TOK_COLON_ASSIGN) { + advance(p); + expr = parse_expr(p); + } + t = peek(p); + if (t && t->type == TOK_ASSIGN) { + advance(p); + expr = parse_expr(p); + } + if (peek(p) && peek(p)->type == TOK_SEMICOLON) + advance(p); + spl_ast_node_ref_t ref = new_node(p, SPL_AST_CONST_DECL, kw); + node_at(p->ast, ref)->const_decl.attr_list = attr_list; + node_at(p->ast, ref)->const_decl.type_expr = type_expr; + node_at(p->ast, ref)->const_decl.name = sdupn(name->lexeme, name->len); + node_at(p->ast, ref)->const_decl.expr = expr; + return ref; +} + +/* ContainerDeclaration <- AttrList? DeclarationBody */ +static spl_ast_node_ref_t parse_container_declaration(parser_t *p) { + spl_ast_node_ref_vec_t attr_list; + vec_init(attr_list); + parse_attr_list(p, &attr_list); + spl_tok_t *t = peek(p); + if (!t || t->type == TOK_EOF) { + parse_error(p, t, "unexpected end of file"); + return 0; + } + if (t->type == TOK_L_BRACE) { + /* DirectiveBlock(@init {} / #test {}):不实*/ + parse_error(p, t, "not implemented: @/# directive block"); + return 0; + } + switch (t->type) { + case KW_FN: + return parse_fn_decl(p, attr_list); + case KW_TYPE: + return parse_type_decl(p, attr_list); + case KW_VAR: + return parse_var_decl(p, attr_list); + case KW_CONST: + return parse_const_decl(p, attr_list); + case KW_COMPTIME: + parse_error(p, t, "not implemented: comptime statement"); + return 0; + case TOK_IDENT: + return parse_member_decl(p, attr_list); + default: + parse_error(p, t, "unrecognized declaration"); + return 0; + } +} + +/* ================================================================ + * 语句 * ================================================================ */ + +/* Block <- LBRACE BlockItem* Expr? RBRACE */ +static spl_ast_node_ref_vec_t parse_block(parser_t *p) { + spl_ast_node_ref_vec_t vec; + vec_init(vec); + if (!expect(p, TOK_L_BRACE, "expected '{'")) + return vec; + while (1) { + spl_tok_t *t = peek(p); + if (!t || t->type == TOK_EOF) { + parse_error(p, t, "block missing '}'"); + break; + } + if (t->type == TOK_R_BRACE) { + advance(p); + break; + } + if (can_start_statement(t->type)) { + spl_ast_node_ref_t s = parse_statement(p); + if (s) + vec_push(vec, s); + } else if (can_start_expr(t->type)) { + spl_ast_node_ref_t e = parse_expr(p); + spl_tok_t *n = peek(p); + if (n && n->type == TOK_SEMICOLON) { + advance(p); + spl_ast_node_ref_t st = new_node(p, SPL_AST_BLOCK_ITEM, t); + node_at(p->ast, st)->block_item.kind = SPL_AST_EXPR_STATEMENT; + node_at(p->ast, st)->block_item.expr_statement = e; + vec_push(vec, st); + } else { + /* 尾表达式:EXPR 节点直接block vec */ + vec_push(vec, e); + } + } else { + parse_error(p, t, "unrecognized statement"); + } + } + return vec; +} + +static spl_ast_node_ref_t parse_statement(parser_t *p) { + spl_tok_t *t = peek(p); + if (!t) + return 0; + switch (t->type) { + case KW_IF: { + spl_tok_t *n = peek_next(p); + if (n && n->type == KW_VAR) + return parse_ifvar(p); + return parse_if(p); + } + case KW_WHILE: + return parse_while(p); + case KW_LOOP: + return parse_loop(p); + case KW_FOR: + return parse_for(p); + case KW_MATCH: + return parse_match(p); + case KW_RET: + return parse_ret(p); + case KW_BREAK: + return parse_break(p); + case KW_CONTINUE: + return parse_continue(p); + case KW_DEFER: + return parse_defer(p); + case KW_VAR: { + spl_ast_node_ref_vec_t al; + vec_init(al); + spl_ast_node_ref_t v = parse_var_decl(p, al); + spl_ast_node_ref_t s = new_node(p, SPL_AST_BLOCK_ITEM, t); + node_at(p->ast, s)->block_item.kind = SPL_AST_VARDECL; + node_at(p->ast, s)->block_item.var_decl = v; + return s; + } + case KW_CONST: { + spl_ast_node_ref_vec_t al; + vec_init(al); + spl_ast_node_ref_t v = parse_const_decl(p, al); + spl_ast_node_ref_t s = new_node(p, SPL_AST_BLOCK_ITEM, t); + node_at(p->ast, s)->block_item.kind = SPL_AST_CONSTDECL; + node_at(p->ast, s)->block_item.const_decl = v; + return s; + } + case KW_TYPE: { + spl_ast_node_ref_vec_t al; + vec_init(al); + spl_ast_node_ref_t v = parse_type_decl(p, al); + spl_ast_node_ref_t s = new_node(p, SPL_AST_BLOCK_ITEM, t); + node_at(p->ast, s)->block_item.kind = SPL_AST_TYPEDECL; + node_at(p->ast, s)->block_item.type_decl = v; + return s; + } + case KW_COMPTIME: + parse_error(p, t, "not implemented: comptime statement"); + return 0; + default: + parse_error(p, t, "unrecognized statement"); + return 0; + } +} + +static spl_ast_node_ref_t parse_if(parser_t *p) { + spl_tok_t *kw = peek(p); + advance(p); /* KW_IF */ + p->stop_agg++; + spl_ast_node_ref_t expr = parse_expr(p); + p->stop_agg--; + spl_ast_node_ref_vec_t if_block = parse_block(p); + spl_ast_node_ref_vec_t else_block; + vec_init(else_block); + if (peek(p) && peek(p)->type == KW_ELSE) { + advance(p); + spl_tok_t *t = peek(p); + if (t && t->type == KW_IF) { + vec_push(else_block, parse_if(p)); + } else if (t && t->type == KW_VAR) { + vec_push(else_block, parse_ifvar(p)); + } else { + else_block = parse_block(p); + } + } + spl_ast_node_ref_t s = new_node(p, SPL_AST_BLOCK_ITEM, kw); + node_at(p->ast, s)->block_item.kind = SPL_AST_IF_STATEMENT; + node_at(p->ast, s)->block_item.if_statement.expr = expr; + node_at(p->ast, s)->block_item.if_statement.if_block = if_block; + node_at(p->ast, s)->block_item.if_statement.else_block = else_block; + return s; +} + +static spl_ast_node_ref_t parse_ifvar(parser_t *p) { + spl_tok_t *kw = peek(p); + advance(p); /* KW_IF */ + advance(p); /* KW_VAR */ + if (!expect(p, TOK_DOT, "expected '.' in if var")) + return 0; + spl_tok_t *name = peek(p); + if (!name || name->type != TOK_IDENT) { + parse_error(p, name, "expected variable name in if var"); + return 0; + } + advance(p); + char *bind = NULL; + if (peek(p) && peek(p)->type == TOK_L_BRACKET) { + advance(p); + spl_tok_t *b = peek(p); + if (!b || b->type != TOK_IDENT) { + parse_error(p, b, "expected identifier in if var binding"); + return 0; + } + advance(p); + bind = sdupn(b->lexeme, b->len); + if (!expect(p, TOK_R_BRACKET, "expected ']' in if var binding")) + return 0; + } + if (!expect(p, TOK_ASSIGN, "expected '=' in if var")) + return 0; + p->stop_agg++; + spl_ast_node_ref_t expr = parse_expr(p); + p->stop_agg--; + spl_ast_node_ref_vec_t if_block = parse_block(p); + spl_ast_node_ref_vec_t else_block; + vec_init(else_block); + if (peek(p) && peek(p)->type == KW_ELSE) { + advance(p); + spl_tok_t *t = peek(p); + if (t && t->type == KW_IF) { + vec_push(else_block, parse_if(p)); + } else if (t && t->type == KW_VAR) { + vec_push(else_block, parse_ifvar(p)); + } else { + else_block = parse_block(p); + } + } + spl_ast_node_ref_t packed = new_node(p, SPL_AST_BLOCK_ITEM, kw); + node_at(p->ast, packed)->packed_expr.ident = sdupn(name->lexeme, name->len); + node_at(p->ast, packed)->packed_expr.bind_ident = bind; + node_at(p->ast, packed)->packed_expr.expr = expr; + spl_ast_node_ref_t s = new_node(p, SPL_AST_BLOCK_ITEM, kw); + node_at(p->ast, s)->block_item.kind = SPL_AST_IFVAR_STATEMENT; + node_at(p->ast, s)->block_item.ifvar_statement.packed_expr = packed; + node_at(p->ast, s)->block_item.ifvar_statement.if_block = if_block; + node_at(p->ast, s)->block_item.ifvar_statement.else_block = else_block; + return s; +} + +static spl_ast_node_ref_t parse_while(parser_t *p) { + spl_tok_t *kw = peek(p); + advance(p); /* KW_WHILE */ + p->stop_agg++; + spl_ast_node_ref_t expr = parse_expr(p); + p->stop_agg--; + spl_ast_node_ref_vec_t block = parse_block(p); + spl_ast_node_ref_t s = new_node(p, SPL_AST_BLOCK_ITEM, kw); + node_at(p->ast, s)->block_item.kind = SPL_AST_WHILE_STATEMENT; + node_at(p->ast, s)->block_item.while_statement.expr = expr; + node_at(p->ast, s)->block_item.while_statement.while_block = block; + return s; +} + +static spl_ast_node_ref_t parse_loop(parser_t *p) { + spl_tok_t *kw = peek(p); + advance(p); /* KW_LOOP */ + spl_ast_node_ref_vec_t block = parse_block(p); + spl_ast_node_ref_t s = new_node(p, SPL_AST_BLOCK_ITEM, kw); + node_at(p->ast, s)->block_item.kind = SPL_AST_LOOP_STATEMENT; + node_at(p->ast, s)->block_item.loop_statement.loop_block = block; + return s; +} + +static spl_ast_node_ref_t parse_for(parser_t *p) { + spl_tok_t *kw = peek(p); + advance(p); /* KW_FOR */ + spl_ast_node_ref_vec_t expr_vec; + vec_init(expr_vec); + p->stop_as = 1; /* for 头部序列as 属于 for,不属于后缀类型转换 */ + vec_push(expr_vec, parse_expr(p)); + while (peek(p) && peek(p)->type == TOK_COMMA) { + advance(p); + vec_push(expr_vec, parse_expr(p)); + } + p->stop_as = 0; + if (!expect(p, KW_AS, "expected 'as' in for")) + return 0; + spl_ast_node_ref_t s = new_node(p, SPL_AST_BLOCK_ITEM, kw); + node_at(p->ast, s)->block_item.kind = SPL_AST_FOR_STATEMENT; + node_at(p->ast, s)->block_item.for_statement.expr_vec = expr_vec; + spl_tok_t *v = peek(p); + if (!v || (v->type != TOK_IDENT && v->type != KW_ANY)) { + parse_error(p, v, "expected binding variable in for"); + return s; + } + advance(p); + vec_push(node_at(p->ast, s)->block_item.for_statement.ident_vec, sdupn(v->lexeme, v->len)); + while (peek(p) && peek(p)->type == TOK_COMMA) { + advance(p); + spl_tok_t *vi = peek(p); + if (!vi || (vi->type != TOK_IDENT && vi->type != KW_ANY)) { + parse_error(p, vi, "expected binding variable in for"); + return s; + } + advance(p); + vec_push(node_at(p->ast, s)->block_item.for_statement.ident_vec, + sdupn(vi->lexeme, vi->len)); + } + spl_ast_node_ref_vec_t fblock = parse_block(p); + node_at(p->ast, s)->block_item.for_statement.block = fblock; + return s; +} + +static spl_ast_node_ref_t parse_match(parser_t *p) { + spl_tok_t *kw = peek(p); + advance(p); /* KW_MATCH */ + p->stop_agg++; + spl_ast_node_ref_t expr = parse_expr(p); + p->stop_agg--; + if (!expect(p, TOK_L_BRACE, "expected '{' in match")) { + return 0; + } + spl_ast_node_ref_vec_t paced_vec, stmt_vec; + vec_init(paced_vec); + vec_init(stmt_vec); + while (1) { + spl_tok_t *t = peek(p); + if (!t || t->type == TOK_EOF) { + parse_error(p, t, "match missing '}'"); + break; + } + if (t->type == TOK_R_BRACE) { + advance(p); + break; + } + if (!parse_match_arm(p, &paced_vec, &stmt_vec)) + break; + if (peek(p) && peek(p)->type == TOK_COMMA) + advance(p); + } + spl_ast_node_ref_t s = new_node(p, SPL_AST_BLOCK_ITEM, kw); + node_at(p->ast, s)->block_item.kind = SPL_AST_MATCH_STATEMENT; + node_at(p->ast, s)->block_item.match_statement.expr = expr; + node_at(p->ast, s)->block_item.match_statement.paced_exprs = paced_vec; + node_at(p->ast, s)->block_item.match_statement.match_block = stmt_vec; + return s; +} + +/* MatchArm <- MatchPat (',' MatchPat)* '=>' Block + * 模式合并: 多模式共享同一臂体。每个模式生成一个 packed_expr, + * 臂体解析一次,其节点引用按模式个数共享写入 block_vec。 + * 这样 paced_vec 与 block_vec 一一对应(每模式一条),语义等价 + * 于展开为多个独立臂。 */ +static int parse_match_arm(parser_t *p, spl_ast_node_ref_vec_t *paced_vec, + spl_ast_node_ref_vec_t *block_vec) { + spl_tok_t *t = peek(p); + if (!t) + return 0; + spl_ast_node_ref_vec_t modes; + vec_init(modes); + while (1) { + spl_tok_t *mt = peek(p); + if (!mt) { + parse_error(p, mt, "invalid match arm pattern"); + return 0; + } + spl_ast_node_ref_t pk = new_node(p, SPL_AST_BLOCK_ITEM, mt); + node_at(p->ast, pk)->packed_expr.ident = NULL; + node_at(p->ast, pk)->packed_expr.bind_ident = NULL; + node_at(p->ast, pk)->packed_expr.expr = 0; + if (mt->type == TOK_DOT) { + advance(p); + spl_tok_t *name = peek(p); + if (!name || name->type != TOK_IDENT) { + parse_error(p, name, "expected variant name in match pattern"); + return 0; + } + advance(p); + node_at(p->ast, pk)->packed_expr.ident = sdupn(name->lexeme, name->len); + if (peek(p) && peek(p)->type == TOK_L_BRACKET) { + advance(p); + spl_tok_t *b = peek(p); + if (!b || b->type != TOK_IDENT) { + parse_error(p, b, "expected identifier in match binding"); + return 0; + } + advance(p); + node_at(p->ast, pk)->packed_expr.bind_ident = sdupn(b->lexeme, b->len); + if (!expect(p, TOK_R_BRACKET, "expected ']' in match binding")) + return 0; + } + } else if (mt->type == KW_ANY) { + /* _ 通配;_ 后跟 '{'(且非聚合抑制)按匿名聚合字面量模式解析 */ + spl_tok_t *nx = peek_next(p); + if (nx && nx->type == TOK_L_BRACE && !p->stop_agg) { + spl_ast_node_ref_t pe = parse_expr(p); + node_at(p->ast, pk)->packed_expr.expr = pe; + } else { + advance(p); /* _ 通配 */ + } + } else if (can_start_expr(mt->type)) { + spl_ast_node_ref_t pe = parse_expr(p); + node_at(p->ast, pk)->packed_expr.expr = pe; + } else { + parse_error(p, mt, "invalid match arm pattern"); + return 0; + } + vec_push(modes, pk); + if (peek(p) && peek(p)->type == TOK_COMMA) { + advance(p); + continue; + } + break; + } + if (!expect(p, TOK_FAT_ARROW, "expected '=>' in match arm")) + return 0; + if (!peek(p) || peek(p)->type != TOK_L_BRACE) { + parse_error(p, peek(p), "expected '{' block in match arm body"); + return 0; + } + spl_ast_node_ref_vec_t bvec = parse_block(p); + spl_ast_node_ref_t pr = mk_primary(p, t, SPL_AST_BLOCK_EXPR); + node_at(p->ast, pr)->primary_expr.block_expr = bvec; + spl_ast_node_ref_t body = wrap_expr(p, pr); + for (usize i = 0; i < modes.size; i++) { + vec_push(*paced_vec, modes.data[i]); + vec_push(*block_vec, body); + } + vec_free(modes); + return 1; +} + +static spl_ast_node_ref_t parse_ret(parser_t *p) { + spl_tok_t *kw = peek(p); + advance(p); /* KW_RET */ + spl_ast_node_ref_t expr = 0; + if (peek(p) && can_start_expr(peek(p)->type)) + expr = parse_expr(p); + if (peek(p) && peek(p)->type == TOK_SEMICOLON) + advance(p); + spl_ast_node_ref_t s = new_node(p, SPL_AST_BLOCK_ITEM, kw); + node_at(p->ast, s)->block_item.kind = SPL_AST_RET_STATEMENT; + node_at(p->ast, s)->block_item.ret_statement.expr = expr; + return s; +} + +static spl_ast_node_ref_t parse_break(parser_t *p) { + spl_tok_t *kw = peek(p); + advance(p); /* KW_BREAK */ + if (peek(p) && peek(p)->type == TOK_SEMICOLON) + advance(p); + spl_ast_node_ref_t s = new_node(p, SPL_AST_BLOCK_ITEM, kw); + node_at(p->ast, s)->block_item.kind = SPL_AST_BREAK_STATEMENT; + return s; +} + +static spl_ast_node_ref_t parse_continue(parser_t *p) { + spl_tok_t *kw = peek(p); + advance(p); /* KW_CONTINUE */ + if (peek(p) && peek(p)->type == TOK_SEMICOLON) + advance(p); + spl_ast_node_ref_t s = new_node(p, SPL_AST_BLOCK_ITEM, kw); + node_at(p->ast, s)->block_item.kind = SPL_AST_CONTINUE_STATEMENT; + return s; +} + +static spl_ast_node_ref_t parse_defer(parser_t *p) { + spl_tok_t *kw = peek(p); + advance(p); /* KW_DEFER */ + spl_ast_node_ref_vec_t vec; + vec_init(vec); + if (peek(p) && peek(p)->type == TOK_L_BRACE) { + vec = parse_block(p); + } else if (peek(p) && can_start_statement(peek(p)->type)) { + spl_ast_node_ref_t st = parse_statement(p); + if (st) + vec_push(vec, st); + } else { + /* ExprStatement: defer ; */ + spl_tok_t *t = peek(p); + spl_ast_node_ref_t e = parse_expr(p); + if (peek(p) && peek(p)->type == TOK_SEMICOLON) + advance(p); + spl_ast_node_ref_t st = new_node(p, SPL_AST_BLOCK_ITEM, t); + node_at(p->ast, st)->block_item.kind = SPL_AST_EXPR_STATEMENT; + node_at(p->ast, st)->block_item.expr_statement = e; + vec_push(vec, st); + } + spl_ast_node_ref_t s = new_node(p, SPL_AST_BLOCK_ITEM, kw); + node_at(p->ast, s)->block_item.kind = SPL_AST_DEFER_STATEMENT; + node_at(p->ast, s)->block_item.defer_statement.block_or_statement = vec; + return s; +} + +/* ================================================================ + * 表达式(优先级爬升) + * ================================================================ */ + +static int assign_op_of(spl_tok_type_t t) { + switch (t) { + case TOK_ASSIGN: + return SPL_AST_ASSIGN_EXPR; + case TOK_ASSIGN_ADD: + return SPL_AST_ASSIGN_ADD_EXPR; + case TOK_ASSIGN_SUB: + return SPL_AST_ASSIGN_sUB_EXPR; + case TOK_ASSIGN_MUL: + return SPL_AST_ASSIGN_MUL_EXPR; + case TOK_ASSIGN_DIV: + return SPL_AST_ASSIGN_DIV_EXPR; + case TOK_ASSIGN_MOD: + return SPL_AST_ASSIGN_MOD_EXPR; + case TOK_ASSIGN_AND: + return SPL_AST_ASSIGN_AND_EXPR; + case TOK_ASSIGN_OR: + return SPL_AST_ASSIGN_OR_EXPR; + case TOK_ASSIGN_XOR: + return SPL_AST_ASSIGN_XOR_EXPR; + case TOK_ASSIGN_L_SH: + return SPL_AST_ASSIGN_LSHIFT_EXPR; + case TOK_ASSIGN_R_SH: + return SPL_AST_ASSIGN_USHIFT_EXPR; + default: + return -1; + } +} + +static spl_ast_node_ref_t parse_assign(parser_t *p) { + spl_ast_node_ref_t left = parse_bool_or(p); + if (p->failed) + return left; + spl_tok_t *t = peek(p); + int op = t ? assign_op_of(t->type) : -1; + if (op < 0) + return left; + advance(p); + spl_ast_node_ref_t right = parse_assign(p); + return mk_bin(p, t, op, left, right); +} + +static spl_ast_node_ref_t parse_bool_or(parser_t *p) { + spl_ast_node_ref_t left = parse_bool_and(p); + while (!p->failed) { + spl_tok_t *t = peek(p); + if (!t || t->type != TOK_OR_OR) + break; + advance(p); + spl_ast_node_ref_t right = parse_bool_and(p); + left = mk_bin(p, t, SPL_AST_BOOLOR_EXPR, left, right); + } + return left; +} + +static spl_ast_node_ref_t parse_bool_and(parser_t *p) { + spl_ast_node_ref_t left = parse_bit_or(p); + while (!p->failed) { + spl_tok_t *t = peek(p); + if (!t || t->type != TOK_AND_AND) + break; + advance(p); + spl_ast_node_ref_t right = parse_bit_or(p); + left = mk_bin(p, t, SPL_AST_BOOLAND_EXPR, left, right); + } + return left; +} + +static spl_ast_node_ref_t parse_bit_or(parser_t *p) { + spl_ast_node_ref_t left = parse_bit_xor(p); + while (!p->failed) { + spl_tok_t *t = peek(p); + if (!t || t->type != TOK_OR) + break; + advance(p); + spl_ast_node_ref_t right = parse_bit_xor(p); + left = mk_bin(p, t, SPL_AST_BITOR_EXPR, left, right); + } + return left; +} + +static spl_ast_node_ref_t parse_bit_xor(parser_t *p) { + spl_ast_node_ref_t left = parse_bit_and(p); + while (!p->failed) { + spl_tok_t *t = peek(p); + if (!t || t->type != TOK_XOR) + break; + advance(p); + spl_ast_node_ref_t right = parse_bit_and(p); + left = mk_bin(p, t, SPL_AST_BITXOR_EXPR, left, right); + } + return left; +} + +static spl_ast_node_ref_t parse_bit_and(parser_t *p) { + spl_ast_node_ref_t left = parse_cmp_eq(p); + while (!p->failed) { + spl_tok_t *t = peek(p); + if (!t || t->type != TOK_AND) + break; + advance(p); + spl_ast_node_ref_t right = parse_cmp_eq(p); + left = mk_bin(p, t, SPL_AST_BITAND_EXPR, left, right); + } + return left; +} + +static spl_ast_node_ref_t parse_cmp_eq(parser_t *p) { + spl_ast_node_ref_t left = parse_cmp(p); + while (!p->failed) { + spl_tok_t *t = peek(p); + int op = -1; + if (t) { + if (t->type == TOK_EQ) + op = SPL_AST_CMPEQ_EXPR; + else if (t->type == TOK_NEQ) + op = SPL_AST_CMPNE_EXPR; + } + if (op < 0) + break; + advance(p); + spl_ast_node_ref_t right = parse_cmp(p); + left = mk_bin(p, t, op, left, right); + } + return left; +} + +static spl_ast_node_ref_t parse_cmp(parser_t *p) { + spl_ast_node_ref_t left = parse_range(p); + while (!p->failed) { + spl_tok_t *t = peek(p); + int op = -1; + if (t) { + if (t->type == TOK_LT) + op = SPL_AST_CMP_LT_EXPR; + else if (t->type == TOK_GT) + op = SPL_AST_CMP_GT_EXPR; + else if (t->type == TOK_LE) + op = SPL_AST_CMP_LE_EXPR; + else if (t->type == TOK_GE) + op = SPL_AST_CMP_GE_EXPR; + } + if (op < 0) + break; + advance(p); + spl_ast_node_ref_t right = parse_range(p); + left = mk_bin(p, t, op, left, right); + } + return left; +} + +static spl_ast_node_ref_t parse_range(parser_t *p) { + spl_ast_node_ref_t left = parse_shift(p); + if (p->failed) + return left; + spl_tok_t *t = peek(p); + if (t && t->type == TOK_RANGE) { + advance(p); + spl_ast_node_ref_t right = 0; /* a.. 无右端点 */ + if (peek(p) && can_start_expr(peek(p)->type)) + right = parse_shift(p); + left = mk_bin(p, t, SPL_AST_RANGE_EXPR, left, right); + } + return left; +} + +static spl_ast_node_ref_t parse_shift(parser_t *p) { + spl_ast_node_ref_t left = parse_add(p); + while (!p->failed) { + spl_tok_t *t = peek(p); + int op = -1; + if (t) { + if (t->type == TOK_L_SH) + op = SPL_AST_LSHIFT_EXPR; + else if (t->type == TOK_R_SH) + op = SPL_AST_RSHIFT_EXPR; + } + if (op < 0) + break; + advance(p); + spl_ast_node_ref_t right = parse_add(p); + left = mk_bin(p, t, op, left, right); + } + return left; +} + +static spl_ast_node_ref_t parse_add(parser_t *p) { + spl_ast_node_ref_t left = parse_mul(p); + while (!p->failed) { + spl_tok_t *t = peek(p); + int op = -1; + if (t) { + if (t->type == TOK_ADD) + op = SPL_AST_ADD_EXPR; + else if (t->type == TOK_SUB) + op = SPL_AST_SUB_EXPR; + } + if (op < 0) + break; + advance(p); + spl_ast_node_ref_t right = parse_mul(p); + left = mk_bin(p, t, op, left, right); + } + return left; +} + +static spl_ast_node_ref_t parse_mul(parser_t *p) { + spl_ast_node_ref_t left = parse_prefix(p); + while (!p->failed) { + spl_tok_t *t = peek(p); + int op = -1; + if (t) { + if (t->type == TOK_MUL) + op = SPL_AST_MUL_EXPR; + else if (t->type == TOK_DIV) + op = SPL_AST_DIV_EXPR; + else if (t->type == TOK_MOD) + op = SPL_AST_MOD_EXPR; + } + if (op < 0) + break; + advance(p); + spl_ast_node_ref_t right = parse_prefix(p); + left = mk_bin(p, t, op, left, right); + } + return left; +} + +/* PrefixExpr <- PrefixOp* PostfixExpr */ +static spl_ast_node_ref_t parse_prefix(parser_t *p) { + int ops[16]; + spl_tok_t *op_toks[16]; + int n = 0; + while (n < 16 && !p->failed) { + spl_tok_t *t = peek(p); + int op = -1; + if (t) { + switch (t->type) { + case TOK_SUB: + op = SPL_AST_MINUS_EXPR; + break; + case TOK_NOT: + op = SPL_AST_BANG_EXPR; + break; + case TOK_BIT_NOT: + op = SPL_AST_TILDE_EXPR; + break; + case TOK_AND: + op = SPL_AST_AMPERSAND_EXPR; + break; + case TOK_MUL: + op = SPL_AST_ASTERISK_EXPR; + break; + default: + break; + } + } + if (op < 0) + break; + ops[n] = op; + op_toks[n] = t; + n++; + advance(p); + } + spl_ast_node_ref_t inner = parse_postfix(p); + /* 从内到外包装(最内层 op 在列表末尾) */ + for (int i = n - 1; i >= 0; i--) { + spl_ast_node_ref_t pf = new_node(p, SPL_AST_EXPR, op_toks[i]); + node_at(p->ast, pf)->prefix_expr.kind = ops[i]; + node_at(p->ast, pf)->prefix_expr.postfix_expr = inner; + inner = wrap_op_expr(p, pf, SPL_AST_PREFIX_EXPR); + } + return inner; +} + +/* PostfixExpr <- PrimaryExpr (call/field/deref/index/slice/as)* */ +static spl_ast_node_ref_t parse_postfix(parser_t *p) { + spl_ast_node_ref_t expr = parse_primary(p); + while (!p->failed) { + spl_tok_t *t = peek(p); + if (!t) + break; + if (t->type == TOK_L_PAREN) { + advance(p); + spl_ast_node_ref_t pf = new_node(p, SPL_AST_EXPR, t); + node_at(p->ast, pf)->postfix_expr.kind = SPL_AST_CALL_EXPR; + node_at(p->ast, pf)->postfix_expr.primary_expr = expr; + spl_ast_node_ref_vec_t cvec = parse_expr_list(p); + node_at(p->ast, pf)->postfix_expr.call_expr = cvec; + if (!expect(p, TOK_R_PAREN, "expected ')' in call")) + return expr; + expr = wrap_op_expr(p, pf, SPL_AST_POSTFIX_EXPR); + } else if (t->type == TOK_DOT) { + advance(p); + spl_tok_t *n = peek(p); + if (n && n->type == TOK_MUL) { + advance(p); + spl_ast_node_ref_t pf = new_node(p, SPL_AST_EXPR, t); + node_at(p->ast, pf)->postfix_expr.kind = SPL_AST_DEREF_EXPR; + node_at(p->ast, pf)->postfix_expr.primary_expr = expr; + expr = wrap_op_expr(p, pf, SPL_AST_POSTFIX_EXPR); + } else if (n && n->type == TOK_IDENT) { + advance(p); + spl_ast_node_ref_t pf = new_node(p, SPL_AST_EXPR, t); + node_at(p->ast, pf)->postfix_expr.kind = SPL_AST_FIELD_EXPR; + node_at(p->ast, pf)->postfix_expr.primary_expr = expr; + node_at(p->ast, pf)->postfix_expr.field_expr = sdupn(n->lexeme, n->len); + expr = wrap_op_expr(p, pf, SPL_AST_POSTFIX_EXPR); + } else { + parse_error(p, n, "expected identifier or '*' after '.'"); + break; + } + } else if (t->type == TOK_L_BRACKET) { + advance(p); + spl_ast_node_ref_t begin = parse_expr(p); + if (peek(p) && peek(p)->type == TOK_RANGE) { + advance(p); + spl_ast_node_ref_t end = 0; + if (peek(p) && peek(p)->type != TOK_R_BRACKET) + end = parse_expr(p); + if (!expect(p, TOK_R_BRACKET, "expected ']' in slice")) + return expr; + spl_ast_node_ref_t pf = new_node(p, SPL_AST_EXPR, t); + node_at(p->ast, pf)->postfix_expr.kind = SPL_AST_SLICE_EXPR; + node_at(p->ast, pf)->postfix_expr.primary_expr = expr; + node_at(p->ast, pf)->postfix_expr.slice_expr.begin = begin; + node_at(p->ast, pf)->postfix_expr.slice_expr.end = end; + expr = wrap_op_expr(p, pf, SPL_AST_POSTFIX_EXPR); + } else { + if (!expect(p, TOK_R_BRACKET, "expected ']' in index")) + return expr; + spl_ast_node_ref_t pf = new_node(p, SPL_AST_EXPR, t); + node_at(p->ast, pf)->postfix_expr.kind = SPL_AST_INDEX_EXPR; + node_at(p->ast, pf)->postfix_expr.primary_expr = expr; + node_at(p->ast, pf)->postfix_expr.index_expr = begin; + expr = wrap_op_expr(p, pf, SPL_AST_POSTFIX_EXPR); + } + } else if (t->type == KW_AS && !p->stop_as) { + advance(p); + spl_ast_node_ref_t te = parse_type_expr(p); + spl_ast_node_ref_t pf = new_node(p, SPL_AST_EXPR, t); + node_at(p->ast, pf)->postfix_expr.kind = SPL_AST_AS_EXPR; + node_at(p->ast, pf)->postfix_expr.primary_expr = expr; + node_at(p->ast, pf)->postfix_expr.type_expr = te; + expr = wrap_op_expr(p, pf, SPL_AST_POSTFIX_EXPR); + } else { + break; + } + } + return expr; +} + +/* ExprList <- (Expr COMMA)* Expr? */ +static spl_ast_node_ref_vec_t parse_expr_list(parser_t *p) { + spl_ast_node_ref_vec_t vec; + vec_init(vec); + if (p->failed) + return vec; + spl_tok_t *t = peek(p); + if (!t || !can_start_expr(t->type)) + return vec; + vec_push(vec, parse_expr(p)); + while (!p->failed && peek(p) && peek(p)->type == TOK_COMMA) { + advance(p); + if (!peek(p) || !can_start_expr(peek(p)->type)) + break; /* 尾逗号宽容 */ + vec_push(vec, parse_expr(p)); + } + return vec; +} + +/* AggregateInitBody <- LBRACE StructInitList? RBRACE + * StructInit <- DOT (IDENTIFIER / UNDERSCORE) (EQUAL Expr)? + * UNDERSCORE 作为字段名表示匿名(positional)初始化,ident 存 "_"。 + * 返回 aggregate_init_item vec。 */ +static spl_ast_node_ref_vec_t parse_aggregate_init_body(parser_t *p) { + spl_ast_node_ref_vec_t agg_vec; + vec_init(agg_vec); + if (!expect(p, TOK_L_BRACE, "expected '{' in struct init")) + return agg_vec; + while (1) { + spl_tok_t *s = peek(p); + if (!s || s->type == TOK_R_BRACE || s->type == TOK_EOF) + break; + if (!expect(p, TOK_DOT, "expected '.' in struct init")) + return agg_vec; + spl_tok_t *f = peek(p); + if (!f || f->type != TOK_IDENT) { + parse_error(p, f, "expected field name in struct init"); + return agg_vec; + } + advance(p); + spl_ast_node_ref_t item = new_node(p, SPL_AST_VAR_DECL, f); + node_at(p->ast, item)->aggregate_init_item.ident = sdupn(f->lexeme, f->len); + if (peek(p) && peek(p)->type == TOK_ASSIGN) { + advance(p); + spl_ast_node_ref_t ae = parse_expr(p); + node_at(p->ast, item)->aggregate_init_item.expr = ae; + } + vec_push(agg_vec, item); + if (peek(p) && peek(p)->type == TOK_COMMA) { + advance(p); + continue; + } + break; + } + if (!expect(p, TOK_R_BRACE, "expected '}' in struct literal")) + return agg_vec; + return agg_vec; +} + +static spl_ast_node_ref_t parse_primary(parser_t *p) { + spl_tok_t *t = peek(p); + if (!t) { + parse_error(p, t, "unexpected expression start"); + return 0; + } + switch (t->type) { + case TOK_INT_LITERAL: { + advance(p); + spl_ast_node_ref_t pr = mk_primary(p, t, SPL_AST_INTEGER); + node_at(p->ast, pr)->primary_expr.integer_expr = parse_int_literal(t); + return wrap_expr(p, pr); + } + case TOK_FLOAT_LITERAL: { + advance(p); + spl_ast_node_ref_t pr = mk_primary(p, t, SPL_AST_FLOAT); + node_at(p->ast, pr)->primary_expr.float_expr = parse_float_literal(t); + return wrap_expr(p, pr); + } + case TOK_CHAR_LITERAL: { + advance(p); + spl_ast_node_ref_t pr = mk_primary(p, t, SPL_AST_CHAR_LIT); + node_at(p->ast, pr)->primary_expr.char_lit_expr = parse_char_literal(t); + return wrap_expr(p, pr); + } + case TOK_STRING_LITERAL: { + advance(p); + spl_ast_node_ref_t pr = mk_primary(p, t, SPL_AST_STRING_LIT); + node_at(p->ast, pr)->primary_expr.string_lit_expr = parse_string_literal(t); + return wrap_expr(p, pr); + } + case KW_TRUE: { + advance(p); + return wrap_expr(p, mk_primary(p, t, SPL_AST_TRUE)); + } + case KW_FALSE: { + advance(p); + return wrap_expr(p, mk_primary(p, t, SPL_AST_FALSE)); + } + case KW_NULL: { + advance(p); + return wrap_expr(p, mk_primary(p, t, SPL_AST_NULL)); + } + case TOK_IDENT: { + advance(p); + if (peek(p) && peek(p)->type == TOK_L_BRACE && !p->stop_agg) { + /* 结构体字面量 T { .f1 = e1, ... } */ + spl_ast_node_ref_vec_t agg_vec = parse_aggregate_init_body(p); + spl_ast_node_ref_t pr = mk_primary(p, t, SPL_AST_ARGGREGATE_INIT); + node_at(p->ast, pr)->primary_expr.aggregate_init_expr = agg_vec; + return wrap_expr(p, pr); + } + spl_ast_node_ref_t pr = mk_primary(p, t, SPL_AST_IDENT); + node_at(p->ast, pr)->primary_expr.ident = sdupn(t->lexeme, t->len); + return wrap_expr(p, pr); + } + case TOK_DOT: { + /* 匿名聚合字面量 .{ .f = e };primary 位置裸 '.' 非法 */ + spl_tok_t *nx = peek_next(p); + if (nx && nx->type == TOK_L_BRACE && !p->stop_agg) { + advance(p); + spl_ast_node_ref_vec_t agg_vec = parse_aggregate_init_body(p); + spl_ast_node_ref_t pr = mk_primary(p, t, SPL_AST_ARGGREGATE_INIT); + node_at(p->ast, pr)->primary_expr.aggregate_init_expr = agg_vec; + return wrap_expr(p, pr); + } + parse_error(p, t, "unexpected '.' in expression"); + return 0; + } + case KW_ANY: + parse_error(p, t, "unexpected '_' in expression"); + return 0; + case TOK_AT: + case TOK_SHARP: + return parse_builtin(p); + case TOK_L_PAREN: { + advance(p); + spl_ast_node_ref_t e = parse_expr(p); + if (!expect(p, TOK_R_PAREN, "expected ')' in parenthesized expression")) + return e; + spl_ast_node_ref_t pr = mk_primary(p, t, SPL_AST_EXPR_EXPR); + node_at(p->ast, pr)->primary_expr.expr = e; + return wrap_expr(p, pr); + } + case TOK_L_BRACKET: + return parse_array_literal(p); + case TOK_L_BRACE: { + spl_ast_node_ref_vec_t bvec = parse_block(p); + spl_ast_node_ref_t pr = mk_primary(p, t, SPL_AST_BLOCK_EXPR); + node_at(p->ast, pr)->primary_expr.block_expr = bvec; + return wrap_expr(p, pr); + } + default: + parse_error(p, t, "unrecognized expression"); + return 0; + } +} + +/* BuiltinExpr <- (AT / SHARP) IDENTIFIER (LPAREN ExprList? RPAREN) */ +static spl_ast_node_ref_t parse_builtin(parser_t *p) { + spl_tok_t *t = peek(p); + advance(p); /* AT / SHARP */ + spl_tok_t *name = peek(p); + if (!name || name->type != TOK_IDENT) { + parse_error(p, name, "expected builtin name"); + return 0; + } + advance(p); + if (!expect(p, TOK_L_PAREN, "expected '(' in builtin call")) + return 0; + spl_ast_node_ref_vec_t args = parse_expr_list(p); + if (!expect(p, TOK_R_PAREN, "expected ')' in builtin call")) + return 0; + spl_ast_node_ref_t pr = mk_primary(p, t, SPL_AST_BUILTIN_EXPR); + node_at(p->ast, pr)->primary_expr.builtin_expr.ident = sdupn(name->lexeme, name->len); + node_at(p->ast, pr)->primary_expr.builtin_expr.expr_list = args; + return wrap_expr(p, pr); +} + +/* ArrayLiteral <- LBRACKET INTEGER RBRACKET TypeExpr LBRACE ExprList? RBRACE */ +static spl_ast_node_ref_t parse_array_literal(parser_t *p) { + spl_tok_t *t = peek(p); + advance(p); /* L_BRACKET */ + isize integer = 0; + if (peek(p) && peek(p)->type == TOK_INT_LITERAL) { + integer = parse_int_literal(peek(p)); + advance(p); + } + if (!expect(p, TOK_R_BRACKET, "expected ']' in array literal")) + return 0; + spl_ast_node_ref_t te = parse_type_expr(p); + if (!expect(p, TOK_L_BRACE, "expected '{' in array literal")) + return 0; + spl_ast_node_ref_vec_t items = parse_expr_list(p); + if (!expect(p, TOK_R_BRACE, "expected '}' in array literal")) + return 0; + spl_ast_node_ref_t pr = mk_primary(p, t, SPL_AST_ARRAY_LIT); + node_at(p->ast, pr)->primary_expr.array_lit_expr.integer = integer; + node_at(p->ast, pr)->primary_expr.array_lit_expr.type_expr = te; + node_at(p->ast, pr)->primary_expr.array_lit_expr.expr_list = items; + return wrap_expr(p, pr); +} + +static spl_ast_node_ref_t parse_expr(parser_t *p) { return parse_assign(p); } + +/* ================================================================ + * 类型表达 * ================================================================ */ + +/* TypeExpr <- PrefixTypeOp* TypeBase */ +static spl_ast_node_ref_t parse_type_expr(parser_t *p) { + spl_tok_t *t = peek(p); + spl_ast_node_ref_vec_t type_prefixs; + vec_init(type_prefixs); + while (1) { + spl_tok_t *s = peek(p); + if (!s) + break; + if (s->type == TOK_MUL) { + advance(p); + spl_ast_node_ref_t pf = new_node(p, SPL_AST_TYPE_EXPR, s); + node_at(p->ast, pf)->prefix_type.pointer = 1; + node_at(p->ast, pf)->prefix_type.array_size = 0; + vec_push(type_prefixs, pf); + } else if (s->type == TOK_L_BRACKET) { + advance(p); + int sz = 0; + if (peek(p) && peek(p)->type == TOK_INT_LITERAL) { + sz = (int)parse_int_literal(peek(p)); + advance(p); + } + if (!expect(p, TOK_R_BRACKET, "expected ']' in array type")) + return 0; + spl_ast_node_ref_t pf = new_node(p, SPL_AST_TYPE_EXPR, s); + node_at(p->ast, pf)->prefix_type.pointer = 2; + node_at(p->ast, pf)->prefix_type.array_size = sz; + vec_push(type_prefixs, pf); + } else { + break; + } + } + spl_ast_node_ref_t ref = new_node(p, SPL_AST_TYPE_EXPR, t); + node_at(p->ast, ref)->type_expr.type_prefixs = type_prefixs; + node_at(p->ast, ref)->type_expr.spl_base_type = NULL; + spl_ast_node_ref_vec_t attr_list; + vec_init(attr_list); + parse_attr_list(p, &attr_list); + node_at(p->ast, ref)->type_expr.attr_list = attr_list; + spl_tok_t *bt = peek(p); + if (bt && bt->type == KW_FN) { + node_at(p->ast, ref)->type_expr.kind = SPL_AST_BASE_TYPE_FN; + advance(p); /* KW_FN */ + if (!expect(p, TOK_L_PAREN, "expected '(' in function type")) + return ref; + spl_ast_node_ref_vec_t param_list; + vec_init(param_list); + while (!p->failed && peek(p) && peek(p)->type != TOK_R_PAREN) { + spl_ast_node_ref_t pt = parse_type_expr(p); + spl_ast_node_ref_t pd = new_node(p, SPL_AST_MEMBER_DECL, t); + node_at(p->ast, pd)->param_decl.name = NULL; + node_at(p->ast, pd)->param_decl.type_expr = pt; + vec_push(param_list, pd); + if (peek(p) && peek(p)->type == TOK_COMMA) { + advance(p); + continue; + } + break; + } + if (!expect(p, TOK_R_PAREN, "expected ')' in function type")) + return ref; + spl_ast_node_ref_t ret = parse_type_expr(p); + node_at(p->ast, ref)->type_expr.fn_type.param_list = param_list; + node_at(p->ast, ref)->type_expr.fn_type.type_expr = ret; + } else if (bt && (bt->type == KW_STRUCT || bt->type == KW_UNION || bt->type == KW_ENUM)) { + /* 内联聚合类型: struct { ... } / union { ... } / enum { ... } */ + node_at(p->ast, ref)->type_expr.kind = (bt->type == KW_STRUCT) ? SPL_AST_TYPE_STRUCT + : (bt->type == KW_UNION) ? SPL_AST_TYPE_UNION + : SPL_AST_TYPE_ENUM; + advance(p); + if (!expect(p, TOK_L_BRACE, "expected '{' in aggregate type")) + return ref; + spl_ast_node_ref_vec_t members; + vec_init(members); + while (1) { + spl_tok_t *m = peek(p); + if (!m || m->type == TOK_R_BRACE || m->type == TOK_EOF) + break; + spl_ast_node_ref_t md = parse_container_declaration(p); + if (md) + vec_push(members, md); + } + if (!expect(p, TOK_R_BRACE, "expected '}' in aggregate type")) + return ref; + node_at(p->ast, ref)->type_expr.aggregate_list = members; + } else { + node_at(p->ast, ref)->type_expr.kind = SPL_AST_BASE_TYPE_PATH; + spl_ast_node_ref_vec_t type_path; + vec_init(type_path); + while (1) { + spl_ast_node_ref_t atom = parse_type_atom(p); + if (!atom) + break; + vec_push(type_path, atom); + if (peek(p) && peek(p)->type == TOK_DOT) { + advance(p); + continue; + } + break; + } + node_at(p->ast, ref)->type_expr.type_path = type_path; + if (type_path.size > 0) { + spl_ast_node_t *last = node_at(p->ast, type_path.data[type_path.size - 1]); + node_at(p->ast, ref)->type_expr.spl_base_type = last->type_atom.ident; + } + } + return ref; +} + +/* TypeAtom <- 内置类型关键/ 内置TOK_IDENT) / _ / 自定义标识符 */ +static spl_ast_node_ref_t parse_type_atom(parser_t *p) { + spl_tok_t *t = peek(p); + if (!t) + return 0; + spl_ast_node_ref_t ref = new_node(p, SPL_AST_TYPE_EXPR, t); + int kind = -1; + switch (t->type) { + case KW_VOID: + kind = SPL_AST_TYPE_VOID; + break; + case KW_BOOL: + kind = SPL_AST_TYPE_BOOL; + break; + case KW_ANY: + kind = SPL_AST_TYPE_ANY; + break; + case TOK_IDENT: { + const char *s = t->lexeme; + usize slen = t->len; +#define IS_TY(NAME) (slen == (usize)(sizeof(NAME) - 1) && memcmp(s, NAME, sizeof(NAME) - 1) == 0) + if (IS_TY("i8")) + kind = SPL_AST_TYPE_I8; + else if (IS_TY("u8")) + kind = SPL_AST_TYPE_U8; + else if (IS_TY("i16")) + kind = SPL_AST_TYPE_I16; + else if (IS_TY("u16")) + kind = SPL_AST_TYPE_U16; + else if (IS_TY("i32")) + kind = SPL_AST_TYPE_I32; + else if (IS_TY("u32")) + kind = SPL_AST_TYPE_U32; + else if (IS_TY("i64")) + kind = SPL_AST_TYPE_I64; + else if (IS_TY("u64")) + kind = SPL_AST_TYPE_U64; + else if (IS_TY("isize")) + kind = SPL_AST_TYPE_ISIZE; + else if (IS_TY("usize")) + kind = SPL_AST_TYPE_USIZE; + else if (IS_TY("ptr")) + kind = SPL_AST_TYPE_PTR; + else if (IS_TY("f32") || IS_TY("f64")) { + parse_error(p, t, "not implemented: f32/f64 type"); + kind = SPL_AST_TYPE_IDENT; + } else { + kind = SPL_AST_TYPE_IDENT; + } +#undef IS_TY + break; + } + default: + parse_error(p, t, "unrecognized type"); + return 0; + } + node_at(p->ast, ref)->type_atom.kind = kind; + node_at(p->ast, ref)->type_atom.ident = sdupn(t->lexeme, t->len); + advance(p); + return ref; +} + +/* ================================================================ + * 公共 API + * ================================================================ */ + +void spl_ast_init(spl_ast_t *ast, const spl_tok_vec_t *tok_vec) { + memset(ast, 0, sizeof(*ast)); + ast->input = *tok_vec; /* move:共token 数据 */ + vec_init(ast->buckets); + ast->root = 0; + ast->parsed = 0; + /* 哨兵节点:ref 0 表示空引用,永不被真正使*/ + spl_ast_node_t n; + memset(&n, 0, sizeof(n)); + vec_push(ast->buckets, n); +} + +void spl_ast_prase(spl_ast_t *ast) { + if (!ast || !ast->input.data || ast->buckets.size == 0) + return; + parser_t p; + p.ast = ast; + p.pos = 0; + p.failed = 0; + ast->parsed = 0; + + spl_ast_node_ref_vec_t members; + vec_init(members); + while (1) { + spl_tok_t *t = peek(&p); + if (!t || t->type == TOK_EOF) + break; + spl_ast_node_ref_t d = parse_container_declaration(&p); + if (d) + vec_push(members, d); + } + spl_ast_node_ref_t root = new_node(&p, SPL_AST_CONTAINER_ITEM, NULL); + node_at(ast, root)->container_item.members = members; + ast->root = root; + ast->parsed = p.failed ? -1 : 1; +} + +void spl_ast_drop(spl_ast_t *ast) { + if (!ast) + return; + if (ast->root) + drop_node(ast, ast->root, CTX_CONTAINER); + vec_free(ast->buckets); + vec_free(ast->input); + ast->parsed = 0; + ast->root = 0; +} + +static walk_ctx_t ctx_from_kind(spl_ast_node_kind_t k) { + switch (k) { + case SPL_AST_CONTAINER_ITEM: + return CTX_CONTAINER; + case SPL_AST_FN_DECL: + case SPL_AST_FN_DEFINE: + case SPL_AST_TYPE_DECL: + case SPL_AST_VAR_DECL: + case SPL_AST_CONST_DECL: + case SPL_AST_MEMBER_DECL: + return CTX_DECL; + case SPL_AST_BLOCK_ITEM: + return CTX_BLOCK_ITEM; + case SPL_AST_EXPR: + return CTX_EXPR; + case SPL_AST_TYPE_EXPR: + return CTX_TYPE_EXPR; + case SPL_AST_ATTR_LIST: + return CTX_ATTR_ITEM; + default: + return CTX_CONTAINER; + } +} + +void spl_ast_valid(spl_ast_t *ast) { + if (!ast) + return; + if (ast->parsed != 1) { + printf("valid: AST not parsed successfully (parsed=%d)\n", ast->parsed); + return; + } + validate_node(ast, ast->root, CTX_CONTAINER); +} + +void spl_ast_dump(spl_ast_t *ast, spl_ast_node_ref_t node) { + if (!ast || !node || node >= ast->buckets.size) + return; + dump_node(ast, node, ctx_from_kind(ast->buckets.data[node].kind), "", 1); +} + +/* ================================================================ + * dump + * ================================================================ */ + +#define STR(s) ((s) ? (s) : "") + +static const char *expr_op_name(int op) { + switch (op) { + case SPL_AST_ASSIGN_EXPR: + return "="; + case SPL_AST_ASSIGN_ADD_EXPR: + return "+="; + case SPL_AST_ASSIGN_sUB_EXPR: + return "-="; + case SPL_AST_ASSIGN_MUL_EXPR: + return "*="; + case SPL_AST_ASSIGN_DIV_EXPR: + return "/="; + case SPL_AST_ASSIGN_MOD_EXPR: + return "%="; + case SPL_AST_ASSIGN_AND_EXPR: + return "&="; + case SPL_AST_ASSIGN_OR_EXPR: + return "|="; + case SPL_AST_ASSIGN_XOR_EXPR: + return "^="; + case SPL_AST_ASSIGN_LSHIFT_EXPR: + return "<<="; + case SPL_AST_ASSIGN_USHIFT_EXPR: + return ">>="; + case SPL_AST_BOOLOR_EXPR: + return "||"; + case SPL_AST_BOOLAND_EXPR: + return "&&"; + case SPL_AST_BITOR_EXPR: + return "|"; + case SPL_AST_BITXOR_EXPR: + return "^"; + case SPL_AST_BITAND_EXPR: + return "&"; + case SPL_AST_CMPEQ_EXPR: + return "=="; + case SPL_AST_CMPNE_EXPR: + return "!="; + case SPL_AST_CMP_LE_EXPR: + return "<="; + case SPL_AST_CMP_GE_EXPR: + return ">="; + case SPL_AST_CMP_LT_EXPR: + return "<"; + case SPL_AST_CMP_GT_EXPR: + return ">"; + case SPL_AST_RANGE_EXPR: + return ".."; + case SPL_AST_LSHIFT_EXPR: + return "<<"; + case SPL_AST_RSHIFT_EXPR: + return ">>"; + case SPL_AST_ADD_EXPR: + return "+"; + case SPL_AST_SUB_EXPR: + return "-"; + case SPL_AST_MUL_EXPR: + return "*"; + case SPL_AST_DIV_EXPR: + return "/"; + case SPL_AST_MOD_EXPR: + return "%"; + case SPL_AST_PREFIX_EXPR: + return "prefix"; + case SPL_AST_POSTFIX_EXPR: + return "postfix"; + case SPL_AST_PRIMARY_EXPR: + return "primary"; + default: + return "?"; + } +} + +static const char *type_atom_name(int k) { + switch (k) { + case SPL_AST_TYPE_VOID: + return "void"; + case SPL_AST_TYPE_BOOL: + return "bool"; + case SPL_AST_TYPE_I8: + return "i8"; + case SPL_AST_TYPE_U8: + return "u8"; + case SPL_AST_TYPE_I16: + return "i16"; + case SPL_AST_TYPE_U16: + return "u16"; + case SPL_AST_TYPE_I32: + return "i32"; + case SPL_AST_TYPE_U32: + return "u32"; + case SPL_AST_TYPE_I64: + return "i64"; + case SPL_AST_TYPE_U64: + return "u64"; + case SPL_AST_TYPE_ISIZE: + return "isize"; + case SPL_AST_TYPE_USIZE: + return "usize"; + case SPL_AST_TYPE__F32: + return "f32"; + case SPL_AST_TYPE__F64: + return "f64"; + case SPL_AST_TYPE_PTR: + return "ptr"; + case SPL_AST_TYPE_ANY: + return "_"; + case SPL_AST_TYPE_IDENT: + return "ident"; + default: + return "?"; + } +} + +static const spl_dumptree_style_t *dt = &spl_dumptree_ascii_style; + +/* 打印一行节点标签:缩进骨架 + 分支符 + label */ +static void node_label(const char *prefix, int is_last, const char *fmt, ...) { + va_list ap; + printf("%s%s ", prefix, is_last ? dt->last_branch : dt->branch); + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + printf("\n"); +} + +/* 以每个子节点为一行递归打印 vec(父级骨架由 prefix/is_last 决定) */ +static void dump_vec_children(spl_ast_t *ast, spl_ast_node_ref_vec_t *vec, walk_ctx_t ctx, + const char *prefix, int is_last) { + char cp[512]; + spl_dumptree_child_prefix(dt, prefix, is_last, cp, sizeof cp); + for (usize i = 0; i < vec->size; i++) + dump_node(ast, vec->data[i], ctx, cp, (int)(i == vec->size - 1)); +} + +static void dump_node(spl_ast_t *ast, spl_ast_node_ref_t ref, walk_ctx_t ctx, const char *prefix, + int is_last); +static void dump_vec_children(spl_ast_t *ast, spl_ast_node_ref_vec_t *vec, walk_ctx_t ctx, + const char *prefix, int is_last); +static void dump_expr(spl_ast_t *ast, spl_ast_node_ref_t ref, const char *prefix, int is_last); +static void dump_type_expr(spl_ast_t *ast, spl_ast_node_ref_t ref, const char *prefix, int is_last); +static void dump_block(spl_ast_t *ast, spl_ast_node_ref_vec_t *block, const char *prefix, + int is_last); +static void expr_inline(spl_ast_t *ast, spl_ast_node_ref_t ref, char *out, size_t cap); +static void type_inline(spl_ast_t *ast, spl_ast_node_ref_t ref, char *out, size_t cap); + +/* ---- 内联文本渲染(供树标签使用) ---- */ + +static void expr_list_inline(spl_ast_t *ast, spl_ast_node_ref_vec_t *vec, char *out, size_t cap); +static void agg_items_inline(spl_ast_t *ast, spl_ast_node_ref_vec_t *items, char *out, size_t cap); + +/* 字符串字面量转义:写入 "..." */ +static void escape_string(char *out, size_t cap, const char *s) { + size_t w = strlen(out); + out[w++] = '"'; + for (; *s && w < cap - 2; s++) { + unsigned char c = (unsigned char)*s; + switch (c) { + case '\n': + out[w++] = '\\'; + out[w++] = 'n'; + break; + case '\t': + out[w++] = '\\'; + out[w++] = 't'; + break; + case '\r': + out[w++] = '\\'; + out[w++] = 'r'; + break; + case '"': + out[w++] = '\\'; + out[w++] = '"'; + break; + case '\\': + out[w++] = '\\'; + out[w++] = '\\'; + break; + default: + if (c < 0x20 || c == 0x7f) { + if (w + 4 >= cap) + break; + out[w++] = '\\'; + out[w++] = 'x'; + out[w++] = "0123456789abcdef"[c >> 4]; + out[w++] = "0123456789abcdef"[c & 15]; + } else if (w + 1 < cap) { + out[w++] = (char)c; + } + break; + } + } + out[w++] = '"'; + out[w] = '\0'; +} + +/* 表达式完整文本(含 primary/prefix/postfix 包装折叠) */ +static void expr_inline(spl_ast_t *ast, spl_ast_node_ref_t ref, char *out, size_t cap) { + if (!ref || ref >= ast->buckets.size) { + strncat(out, "?", cap - strlen(out) - 1); + return; + } + spl_ast_node_t *n = &ast->buckets.data[ref]; + char tmp[512]; + int op = n->expr.op; + switch (op) { + case SPL_AST_PRIMARY_EXPR: { + spl_ast_node_t *pn = &ast->buckets.data[n->expr.op_expr.left]; + switch (pn->primary_expr.kind) { + case SPL_AST_INTEGER: + snprintf(tmp, sizeof tmp, "%lld", (long long)pn->primary_expr.integer_expr); + break; + case SPL_AST_FLOAT: + snprintf(tmp, sizeof tmp, "%g", pn->primary_expr.float_expr); + break; + case SPL_AST_CHAR_LIT: + snprintf(tmp, sizeof tmp, "'%c'", (int)pn->primary_expr.char_lit_expr); + break; + case SPL_AST_STRING_LIT: + tmp[0] = '\0'; + escape_string(tmp, sizeof tmp, STR(pn->primary_expr.string_lit_expr)); + break; + case SPL_AST_TRUE: + snprintf(tmp, sizeof tmp, "true"); + break; + case SPL_AST_FALSE: + snprintf(tmp, sizeof tmp, "false"); + break; + case SPL_AST_NULL: + snprintf(tmp, sizeof tmp, "null"); + break; + case SPL_AST_IDENT: + snprintf(tmp, sizeof tmp, "%s", STR(pn->primary_expr.ident)); + break; + case SPL_AST_ARGGREGATE_INIT: + tmp[0] = '{'; + tmp[1] = ' '; + tmp[2] = '\0'; + agg_items_inline(ast, &pn->primary_expr.aggregate_init_expr, tmp, sizeof tmp); + strncat(tmp, " }", sizeof tmp - strlen(tmp) - 1); + break; + case SPL_AST_EXPR_EXPR: + tmp[0] = '('; + tmp[1] = '\0'; + expr_inline(ast, pn->primary_expr.expr, tmp, sizeof tmp); + strncat(tmp, ")", sizeof tmp - strlen(tmp) - 1); + break; + case SPL_AST_ARRAY_LIT: + snprintf(tmp, sizeof tmp, "[%lld]", (long long)pn->primary_expr.array_lit_expr.integer); + if (pn->primary_expr.array_lit_expr.type_expr) + type_inline(ast, pn->primary_expr.array_lit_expr.type_expr, tmp, sizeof tmp); + strncat(tmp, "{ ", sizeof tmp - strlen(tmp) - 1); + expr_list_inline(ast, &pn->primary_expr.array_lit_expr.expr_list, tmp, sizeof tmp); + strncat(tmp, " }", sizeof tmp - strlen(tmp) - 1); + break; + case SPL_AST_BUILTIN_EXPR: + snprintf(tmp, sizeof tmp, "@%s(", STR(pn->primary_expr.builtin_expr.ident)); + expr_list_inline(ast, &pn->primary_expr.builtin_expr.expr_list, tmp, sizeof tmp); + strncat(tmp, ")", sizeof tmp - strlen(tmp) - 1); + break; + case SPL_AST_BLOCK_EXPR: + snprintf(tmp, sizeof tmp, "{...}"); + break; + default: + snprintf(tmp, sizeof tmp, "?"); + break; + } + strncat(out, tmp, cap - strlen(out) - 1); + break; + } + case SPL_AST_PREFIX_EXPR: { + spl_ast_node_t *pn = &ast->buckets.data[n->expr.op_expr.left]; + const char *pn_txt = "?"; + switch (pn->prefix_expr.kind) { + case SPL_AST_MINUS_EXPR: + pn_txt = "-"; + break; + case SPL_AST_BANG_EXPR: + pn_txt = "!"; + break; + case SPL_AST_TILDE_EXPR: + pn_txt = "~"; + break; + case SPL_AST_AMPERSAND_EXPR: + pn_txt = "&"; + break; + case SPL_AST_ASTERISK_EXPR: + pn_txt = "*"; + break; + } + strncat(out, pn_txt, cap - strlen(out) - 1); + expr_inline(ast, pn->prefix_expr.postfix_expr, out, cap); + break; + } + case SPL_AST_POSTFIX_EXPR: { + spl_ast_node_t *pn = &ast->buckets.data[n->expr.op_expr.left]; + expr_inline(ast, pn->postfix_expr.primary_expr, out, cap); + switch (pn->postfix_expr.kind) { + case SPL_AST_CALL_EXPR: + strncat(out, "(", cap - strlen(out) - 1); + expr_list_inline(ast, &pn->postfix_expr.call_expr, out, cap); + strncat(out, ")", cap - strlen(out) - 1); + break; + case SPL_AST_FIELD_EXPR: { + char t[256]; + snprintf(t, sizeof t, ".%s", STR(pn->postfix_expr.field_expr)); + strncat(out, t, cap - strlen(out) - 1); + break; + } + case SPL_AST_DEREF_EXPR: + strncat(out, ".*", cap - strlen(out) - 1); + break; + case SPL_AST_INDEX_EXPR: + strncat(out, "[", cap - strlen(out) - 1); + expr_inline(ast, pn->postfix_expr.index_expr, out, cap); + strncat(out, "]", cap - strlen(out) - 1); + break; + case SPL_AST_SLICE_EXPR: + strncat(out, "[", cap - strlen(out) - 1); + if (pn->postfix_expr.slice_expr.begin) + expr_inline(ast, pn->postfix_expr.slice_expr.begin, out, cap); + strncat(out, "..", cap - strlen(out) - 1); + if (pn->postfix_expr.slice_expr.end) + expr_inline(ast, pn->postfix_expr.slice_expr.end, out, cap); + strncat(out, "]", cap - strlen(out) - 1); + break; + case SPL_AST_AS_EXPR: + strncat(out, " as ", cap - strlen(out) - 1); + if (pn->postfix_expr.type_expr) + type_inline(ast, pn->postfix_expr.type_expr, out, cap); + break; + default: + break; + } + break; + } + default: + expr_inline(ast, n->expr.op_expr.left, out, cap); + { + char t[64]; + snprintf(t, sizeof t, " %s ", expr_op_name(op)); + strncat(out, t, cap - strlen(out) - 1); + } + if (n->expr.op_expr.right) + expr_inline(ast, n->expr.op_expr.right, out, cap); + break; + } +} + +/* 逗号分隔的表达式文本 */ +static void expr_list_inline(spl_ast_t *ast, spl_ast_node_ref_vec_t *vec, char *out, size_t cap) { + for (usize i = 0; i < vec->size; i++) { + if (i) + strncat(out, ", ", cap - strlen(out) - 1); + expr_inline(ast, vec->data[i], out, cap); + } +} + +/* 属性文本:@name(args) */ +static void attrs_inline(spl_ast_t *ast, spl_ast_node_ref_vec_t *attrs, char *out, size_t cap) { + for (usize i = 0; i < attrs->size; i++) { + spl_ast_node_t *a = &ast->buckets.data[attrs->data[i]]; + char t[256]; + snprintf(t, sizeof t, "@%s(", STR(a->attr_item.ident)); + strncat(out, t, cap - strlen(out) - 1); + expr_list_inline(ast, &a->attr_item.expr_list, out, cap); + strncat(out, ") ", cap - strlen(out) - 1); + } +} + +/* 参数文本:a: T, b: U */ +static void params_inline(spl_ast_t *ast, spl_ast_node_ref_vec_t *params, char *out, size_t cap) { + for (usize i = 0; i < params->size; i++) { + spl_ast_node_t *p = &ast->buckets.data[params->data[i]]; + if (i) + strncat(out, ", ", cap - strlen(out) - 1); + if (!p->param_decl.name) { + strncat(out, "...", cap - strlen(out) - 1); + continue; + } + attrs_inline(ast, &p->param_decl.attr_list, out, cap); + char t[256]; + snprintf(t, sizeof t, "%s: ", STR(p->param_decl.name)); + strncat(out, t, cap - strlen(out) - 1); + if (p->param_decl.type_expr) + type_inline(ast, p->param_decl.type_expr, out, cap); + else + strncat(out, "?", cap - strlen(out) - 1); + } +} + +/* 聚合初始化项文本:.f = expr */ +static void agg_items_inline(spl_ast_t *ast, spl_ast_node_ref_vec_t *items, char *out, size_t cap) { + for (usize i = 0; i < items->size; i++) { + spl_ast_node_t *it = &ast->buckets.data[items->data[i]]; + if (i) + strncat(out, ", ", cap - strlen(out) - 1); + char t[128]; + snprintf(t, sizeof t, ".%s", STR(it->aggregate_init_item.ident)); + strncat(out, t, cap - strlen(out) - 1); + if (it->aggregate_init_item.expr) { + strncat(out, " = ", cap - strlen(out) - 1); + expr_inline(ast, it->aggregate_init_item.expr, out, cap); + } + } +} + +/* 完整类型文本(含前缀指针/数组):*u8 / []u8 / [4]i32 / fn(a: i32) void */ +static void type_inline(spl_ast_t *ast, spl_ast_node_ref_t ref, char *out, size_t cap) { + if (!ref || ref >= ast->buckets.size) { + strncat(out, "?", cap - strlen(out) - 1); + return; + } + spl_ast_node_t *n = &ast->buckets.data[ref]; + for (usize i = 0; i < n->type_expr.type_prefixs.size; i++) { + spl_ast_node_t *pt = &ast->buckets.data[n->type_expr.type_prefixs.data[i]]; + char t[32]; + if (pt->prefix_type.pointer == 1) + snprintf(t, sizeof t, "*"); + else if (pt->prefix_type.array_size != 0) + snprintf(t, sizeof t, "[%d]", pt->prefix_type.array_size); + else + snprintf(t, sizeof t, "[]"); + strncat(out, t, cap - strlen(out) - 1); + } + if (n->type_expr.attr_list.size) + attrs_inline(ast, &n->type_expr.attr_list, out, cap); + char t[256]; + switch (n->type_expr.kind) { + case SPL_AST_BASE_TYPE_FN: + snprintf(t, sizeof t, "fn("); + strncat(out, t, cap - strlen(out) - 1); + params_inline(ast, &n->type_expr.fn_type.param_list, out, cap); + strncat(out, ") ", cap - strlen(out) - 1); + type_inline(ast, n->type_expr.fn_type.type_expr, out, cap); + break; + case SPL_AST_TYPE_STRUCT: + case SPL_AST_TYPE_UNION: + case SPL_AST_TYPE_ENUM: { + const char *kw = n->type_expr.kind == SPL_AST_TYPE_STRUCT ? "struct" + : n->type_expr.kind == SPL_AST_TYPE_UNION ? "union" + : "enum"; + snprintf(t, sizeof t, "%s{ ", kw); + strncat(out, t, cap - strlen(out) - 1); + for (usize i = 0; i < n->type_expr.aggregate_list.size; i++) { + spl_ast_node_t *m = &ast->buckets.data[n->type_expr.aggregate_list.data[i]]; + if (m->kind == SPL_AST_MEMBER_DECL) { + snprintf(t, sizeof t, "%s: ", STR(m->member_decl.name)); + strncat(out, t, cap - strlen(out) - 1); + if (m->member_decl.type_expr) + type_inline(ast, m->member_decl.type_expr, out, cap); + else + strncat(out, "?", cap - strlen(out) - 1); + } else if (m->kind == SPL_AST_FN_DECL || m->kind == SPL_AST_FN_DEFINE) { + snprintf(t, sizeof t, "fn %s(", STR(m->fn_decl.name)); + strncat(out, t, cap - strlen(out) - 1); + params_inline(ast, &m->fn_decl.param_list, out, cap); + strncat(out, ") ", cap - strlen(out) - 1); + if (m->fn_decl.type_expr) + type_inline(ast, m->fn_decl.type_expr, out, cap); + } else { + strncat(out, "...", cap - strlen(out) - 1); + } + strncat(out, ", ", cap - strlen(out) - 1); + } + strncat(out, "}", cap - strlen(out) - 1); + break; + } + default: { + for (usize i = 0; i < n->type_expr.type_path.size; i++) { + spl_ast_node_t *at = &ast->buckets.data[n->type_expr.type_path.data[i]]; + if (i) + strncat(out, ".", cap - strlen(out) - 1); + if (at->type_atom.kind == SPL_AST_TYPE_IDENT) + strncat(out, STR(at->type_atom.ident), cap - strlen(out) - 1); + else + strncat(out, type_atom_name(at->type_atom.kind), cap - strlen(out) - 1); + } + if (!n->type_expr.type_path.size) + strncat(out, "?", cap - strlen(out) - 1); + break; + } + } +} + +/* 聚合初始化项文本已由 agg_items_inline 提供 */ + +/* ---- 树版表达式渲染 ---- */ + +/* 解包 BLOCK_EXPR 包装,返回 block_item 列表(match 臂体) */ +static spl_ast_node_ref_vec_t *unwrap_block_expr(spl_ast_t *ast, spl_ast_node_ref_t ref) { + spl_ast_node_t *n = &ast->buckets.data[ref]; + spl_ast_node_t *p = &ast->buckets.data[n->expr.op_expr.left]; + return &p->primary_expr.block_expr; +} + +/* 单行 packed_expr 模式文本(非表达式模式) */ +static void pattern_inline(spl_ast_t *ast, spl_ast_node_ref_t ref, char *out, size_t cap) { + out[0] = '\0'; + if (!ref || ref >= ast->buckets.size) + return; + spl_ast_node_t *n = &ast->buckets.data[ref]; + if (n->packed_expr.expr) { + expr_inline(ast, n->packed_expr.expr, out, cap); + } else if (n->packed_expr.ident) { + char t[128]; + if (n->packed_expr.bind_ident) + snprintf(t, sizeof t, ".%s[%s]", STR(n->packed_expr.ident), + STR(n->packed_expr.bind_ident)); + else + snprintf(t, sizeof t, ".%s", STR(n->packed_expr.ident)); + strncat(out, t, cap - strlen(out) - 1); + } else { + strncat(out, "_", cap - strlen(out) - 1); + } +} + +/* packed_expr 作为一行节点 */ +static void dump_packed(spl_ast_t *ast, spl_ast_node_ref_t ref, const char *prefix, int is_last) { + spl_ast_node_t *n = &ast->buckets.data[ref]; + if (n->packed_expr.expr) { + dump_expr(ast, n->packed_expr.expr, prefix, is_last); + return; + } + char t[128]; + pattern_inline(ast, ref, t, sizeof t); + node_label(prefix, is_last, "%s", t); +} + +/* match 臂:模式 + 体(BLOCK_EXPR) */ +static void dump_arm(spl_ast_t *ast, spl_ast_node_ref_t pat, spl_ast_node_ref_t body, + const char *prefix, int is_last) { + spl_ast_node_t *n = &ast->buckets.data[pat]; + char cp[512]; + if (n->packed_expr.expr) { + node_label(prefix, is_last, "Arm"); + spl_dumptree_child_prefix(dt, prefix, is_last, cp, sizeof cp); + dump_expr(ast, n->packed_expr.expr, cp, 0); + dump_block(ast, unwrap_block_expr(ast, body), cp, 1); + return; + } + char t[128]; + pattern_inline(ast, pat, t, sizeof t); + node_label(prefix, is_last, "Arm %s", t); + spl_dumptree_child_prefix(dt, prefix, is_last, cp, sizeof cp); + dump_block(ast, unwrap_block_expr(ast, body), cp, 1); +} + +/* primary_expr 树版:label + 子节点 */ +static void dump_primary_expr(spl_ast_t *ast, spl_ast_node_ref_t ref, const char *prefix, + int is_last) { + spl_ast_node_t *n = &ast->buckets.data[ref]; + char cp[512]; + switch (n->primary_expr.kind) { + case SPL_AST_INTEGER: + node_label(prefix, is_last, "Integer %lld", (long long)n->primary_expr.integer_expr); + break; + case SPL_AST_FLOAT: + node_label(prefix, is_last, "Float %g", n->primary_expr.float_expr); + break; + case SPL_AST_CHAR_LIT: + node_label(prefix, is_last, "Char '%c'", (int)n->primary_expr.char_lit_expr); + break; + case SPL_AST_STRING_LIT: { + char t[512]; + t[0] = '\0'; + escape_string(t, sizeof t, STR(n->primary_expr.string_lit_expr)); + node_label(prefix, is_last, "String %s", t); + break; + } + case SPL_AST_TRUE: + node_label(prefix, is_last, "Bool true"); + break; + case SPL_AST_FALSE: + node_label(prefix, is_last, "Bool false"); + break; + case SPL_AST_NULL: + node_label(prefix, is_last, "Null"); + break; + case SPL_AST_IDENT: + node_label(prefix, is_last, "Ident %s", STR(n->primary_expr.ident)); + break; + case SPL_AST_ARGGREGATE_INIT: + node_label(prefix, is_last, "AggInit"); + spl_dumptree_child_prefix(dt, prefix, is_last, cp, sizeof cp); + for (usize i = 0; i < n->primary_expr.aggregate_init_expr.size; i++) { + spl_ast_node_t *it = &ast->buckets.data[n->primary_expr.aggregate_init_expr.data[i]]; + int it_last = (int)(i == n->primary_expr.aggregate_init_expr.size - 1); + node_label(cp, it_last, "Field .%s", STR(it->aggregate_init_item.ident)); + if (it->aggregate_init_item.expr) { + char cq[512]; + spl_dumptree_child_prefix(dt, cp, it_last, cq, sizeof cq); + dump_expr(ast, it->aggregate_init_item.expr, cq, 1); + } + } + break; + case SPL_AST_EXPR_EXPR: + dump_expr(ast, n->primary_expr.expr, prefix, is_last); + break; + case SPL_AST_ARRAY_LIT: + node_label(prefix, is_last, "Array [%lld]", + (long long)n->primary_expr.array_lit_expr.integer); + spl_dumptree_child_prefix(dt, prefix, is_last, cp, sizeof cp); + if (n->primary_expr.array_lit_expr.type_expr) + dump_type_expr(ast, n->primary_expr.array_lit_expr.type_expr, cp, + (int)(n->primary_expr.array_lit_expr.expr_list.size == 0)); + for (usize i = 0; i < n->primary_expr.array_lit_expr.expr_list.size; i++) + dump_expr(ast, n->primary_expr.array_lit_expr.expr_list.data[i], cp, + (int)(i == n->primary_expr.array_lit_expr.expr_list.size - 1)); + break; + case SPL_AST_BUILTIN_EXPR: + node_label(prefix, is_last, "Builtin @%s", STR(n->primary_expr.builtin_expr.ident)); + spl_dumptree_child_prefix(dt, prefix, is_last, cp, sizeof cp); + for (usize i = 0; i < n->primary_expr.builtin_expr.expr_list.size; i++) + dump_expr(ast, n->primary_expr.builtin_expr.expr_list.data[i], cp, + (int)(i == n->primary_expr.builtin_expr.expr_list.size - 1)); + break; + case SPL_AST_BLOCK_EXPR: + node_label(prefix, is_last, "BlockExpr"); + dump_vec_children(ast, &n->primary_expr.block_expr, CTX_BLOCK_ITEM, prefix, is_last); + break; + default: + node_label(prefix, is_last, "?"); + break; + } +} + +/* postfix_expr 树版:label + 子节点 */ +static void dump_postfix_expr(spl_ast_t *ast, spl_ast_node_ref_t ref, const char *prefix, + int is_last) { + spl_ast_node_t *n = &ast->buckets.data[ref]; + spl_ast_node_ref_t tgt = n->postfix_expr.primary_expr; + char cp[512]; + spl_dumptree_child_prefix(dt, prefix, is_last, cp, sizeof cp); + switch (n->postfix_expr.kind) { + case SPL_AST_CALL_EXPR: { + usize nkids = 1 + n->postfix_expr.call_expr.size; + node_label(prefix, is_last, "Call"); + dump_expr(ast, tgt, cp, (int)(nkids == 1)); + for (usize i = 0; i < n->postfix_expr.call_expr.size; i++) + dump_expr(ast, n->postfix_expr.call_expr.data[i], cp, + (int)(i == n->postfix_expr.call_expr.size - 1)); + break; + } + case SPL_AST_FIELD_EXPR: + node_label(prefix, is_last, "Field .%s", STR(n->postfix_expr.field_expr)); + dump_expr(ast, tgt, cp, 1); + break; + case SPL_AST_DEREF_EXPR: + node_label(prefix, is_last, "Deref"); + dump_expr(ast, tgt, cp, 1); + break; + case SPL_AST_INDEX_EXPR: + node_label(prefix, is_last, "Index"); + dump_expr(ast, tgt, cp, 0); + dump_expr(ast, n->postfix_expr.index_expr, cp, 1); + break; + case SPL_AST_SLICE_EXPR: + node_label(prefix, is_last, "Slice"); + dump_expr(ast, tgt, cp, 0); + if (n->postfix_expr.slice_expr.begin) + dump_expr(ast, n->postfix_expr.slice_expr.begin, cp, 0); + if (n->postfix_expr.slice_expr.end) + dump_expr(ast, n->postfix_expr.slice_expr.end, cp, 1); + break; + case SPL_AST_AS_EXPR: { + char t[256]; + t[0] = '\0'; + if (n->postfix_expr.type_expr) + type_inline(ast, n->postfix_expr.type_expr, t, sizeof t); + node_label(prefix, is_last, "As %s", t); + dump_expr(ast, tgt, cp, 1); + break; + } + default: + node_label(prefix, is_last, "?"); + break; + } +} + +/* primary_expr 树版见 dump_primary_expr */ + +/* 表达式树版:EXPR 包装层折叠,直接显示具体节点类型 */ +static void dump_expr(spl_ast_t *ast, spl_ast_node_ref_t ref, const char *prefix, int is_last) { + if (!ref || ref >= ast->buckets.size) { + node_label(prefix, is_last, "?"); + return; + } + spl_ast_node_t *n = &ast->buckets.data[ref]; + char cp[512]; + int op = n->expr.op; + switch (op) { + case SPL_AST_PRIMARY_EXPR: + dump_primary_expr(ast, n->expr.op_expr.left, prefix, is_last); + break; + case SPL_AST_PREFIX_EXPR: { + spl_ast_node_t *pn = &ast->buckets.data[n->expr.op_expr.left]; + const char *nm = "?"; + switch (pn->prefix_expr.kind) { + case SPL_AST_MINUS_EXPR: + nm = "Neg"; + break; + case SPL_AST_BANG_EXPR: + nm = "Not"; + break; + case SPL_AST_TILDE_EXPR: + nm = "BitNot"; + break; + case SPL_AST_AMPERSAND_EXPR: + nm = "Addr"; + break; + case SPL_AST_ASTERISK_EXPR: + nm = "Star"; + break; + } + node_label(prefix, is_last, "%s", nm); + spl_dumptree_child_prefix(dt, prefix, is_last, cp, sizeof cp); + dump_expr(ast, pn->prefix_expr.postfix_expr, cp, 1); + break; + } + case SPL_AST_POSTFIX_EXPR: + dump_postfix_expr(ast, n->expr.op_expr.left, prefix, is_last); + break; + default: + node_label(prefix, is_last, "%s", expr_op_name(op)); + spl_dumptree_child_prefix(dt, prefix, is_last, cp, sizeof cp); + dump_expr(ast, n->expr.op_expr.left, cp, 0); + if (n->expr.op_expr.right) + dump_expr(ast, n->expr.op_expr.right, cp, 1); + break; + } +} + +/* 代码块树版 */ +static void dump_block(spl_ast_t *ast, spl_ast_node_ref_vec_t *block, const char *prefix, + int is_last) { + node_label(prefix, is_last, "Block"); + dump_vec_children(ast, block, CTX_BLOCK_ITEM, prefix, is_last); +} + +/* 模式文本见 pattern_inline;vec 递归见 dump_vec_children */ + +/* 类型节点树版:聚合(struct/union/enum)展开为子树,其余内联为 Type 标签 */ +static void dump_type_expr(spl_ast_t *ast, spl_ast_node_ref_t ref, const char *prefix, + int is_last) { + if (!ref || ref >= ast->buckets.size) { + node_label(prefix, is_last, "?"); + return; + } + spl_ast_node_t *n = &ast->buckets.data[ref]; + int agg = n->type_expr.kind == SPL_AST_TYPE_STRUCT || n->type_expr.kind == SPL_AST_TYPE_UNION || + n->type_expr.kind == SPL_AST_TYPE_ENUM; + if (!agg && n->type_expr.kind == SPL_AST_BASE_TYPE_PATH) { + char t[512]; + t[0] = '\0'; + type_inline(ast, ref, t, sizeof t); + node_label(prefix, is_last, "Type %s", t); + return; + } + const char *nm = n->type_expr.kind == SPL_AST_TYPE_STRUCT ? "Struct" + : n->type_expr.kind == SPL_AST_TYPE_UNION ? "Union" + : n->type_expr.kind == SPL_AST_TYPE_ENUM ? "Enum" + : "FnType"; + node_label(prefix, is_last, "%s", nm); + char cp[512]; + spl_dumptree_child_prefix(dt, prefix, is_last, cp, sizeof cp); + if (agg) { + for (usize i = 0; i < n->type_expr.aggregate_list.size; i++) + dump_node(ast, n->type_expr.aggregate_list.data[i], CTX_DECL, cp, + (int)(i == n->type_expr.aggregate_list.size - 1)); + } else { + usize np = n->type_expr.fn_type.param_list.size; + int has_ret = n->type_expr.fn_type.type_expr != 0; + for (usize i = 0; i < np; i++) + dump_node(ast, n->type_expr.fn_type.param_list.data[i], CTX_PARAM, cp, + (int)(i == np - 1 && !has_ret)); + if (has_ret) + dump_type_expr(ast, n->type_expr.fn_type.type_expr, cp, 1); + } +} + +static void dump_node(spl_ast_t *ast, spl_ast_node_ref_t ref, walk_ctx_t ctx, const char *prefix, + int is_last) { + if (!ref || ref >= ast->buckets.size) + return; + spl_ast_node_t *n = &ast->buckets.data[ref]; + char cp[512]; + switch (ctx) { + case CTX_CONTAINER: + printf("Program\n"); + dump_vec_children(ast, &n->container_item.members, CTX_DECL, "", 1); + break; + case CTX_DECL: + switch (n->kind) { + case SPL_AST_FN_DECL: + case SPL_AST_FN_DEFINE: { + int is_define = (n->kind == SPL_AST_FN_DEFINE); + node_label(prefix, is_last, "%s %s", is_define ? "FnDefine" : "FnDecl", + STR(n->fn_decl.name)); + usize total = n->fn_decl.attr_list.size + n->fn_decl.param_list.size + + (n->fn_decl.type_expr ? 1 : 0) + + (is_define && n->fn_decl.block.size ? 1 : 0); + if (total) { + spl_dumptree_child_prefix(dt, prefix, is_last, cp, sizeof cp); + usize k = 0; + for (usize i = 0; i < n->fn_decl.attr_list.size; i++) + dump_node(ast, n->fn_decl.attr_list.data[i], CTX_ATTR_ITEM, cp, + (int)(++k == total)); + for (usize i = 0; i < n->fn_decl.param_list.size; i++) + dump_node(ast, n->fn_decl.param_list.data[i], CTX_PARAM, cp, + (int)(++k == total)); + if (n->fn_decl.type_expr) + dump_type_expr(ast, n->fn_decl.type_expr, cp, (int)(++k == total)); + if (is_define && n->fn_decl.block.size) + dump_block(ast, &n->fn_decl.block, cp, (int)(++k == total)); + } + break; + } + case SPL_AST_TYPE_DECL: + node_label(prefix, is_last, "TypeDecl %s", STR(n->type_decl.name)); + if (n->type_decl.type_expr) { + spl_dumptree_child_prefix(dt, prefix, is_last, cp, sizeof cp); + dump_type_expr(ast, n->type_decl.type_expr, cp, 1); + } + break; + case SPL_AST_VAR_DECL: { + node_label(prefix, is_last, "VarDecl %s", STR(n->var_decl.name)); + usize total = n->var_decl.attr_list.size + (n->var_decl.type_expr ? 1 : 0) + + (n->var_decl.expr ? 1 : 0); + if (total) { + spl_dumptree_child_prefix(dt, prefix, is_last, cp, sizeof cp); + usize k = 0; + for (usize i = 0; i < n->var_decl.attr_list.size; i++) + dump_node(ast, n->var_decl.attr_list.data[i], CTX_ATTR_ITEM, cp, + (int)(++k == total)); + if (n->var_decl.type_expr) + dump_type_expr(ast, n->var_decl.type_expr, cp, (int)(++k == total)); + if (n->var_decl.expr) + dump_expr(ast, n->var_decl.expr, cp, (int)(++k == total)); + } + break; + } + case SPL_AST_CONST_DECL: { + node_label(prefix, is_last, "ConstDecl %s", STR(n->const_decl.name)); + usize total = n->const_decl.attr_list.size + (n->const_decl.type_expr ? 1 : 0) + + (n->const_decl.expr ? 1 : 0); + if (total) { + spl_dumptree_child_prefix(dt, prefix, is_last, cp, sizeof cp); + usize k = 0; + for (usize i = 0; i < n->const_decl.attr_list.size; i++) + dump_node(ast, n->const_decl.attr_list.data[i], CTX_ATTR_ITEM, cp, + (int)(++k == total)); + if (n->const_decl.type_expr) + dump_type_expr(ast, n->const_decl.type_expr, cp, (int)(++k == total)); + if (n->const_decl.expr) + dump_expr(ast, n->const_decl.expr, cp, (int)(++k == total)); + } + break; + } + case SPL_AST_MEMBER_DECL: { + node_label(prefix, is_last, "Member %s", STR(n->member_decl.name)); + usize total = n->member_decl.attr_list.size + (n->member_decl.type_expr ? 1 : 0); + if (total) { + spl_dumptree_child_prefix(dt, prefix, is_last, cp, sizeof cp); + usize k = 0; + for (usize i = 0; i < n->member_decl.attr_list.size; i++) + dump_node(ast, n->member_decl.attr_list.data[i], CTX_ATTR_ITEM, cp, + (int)(++k == total)); + if (n->member_decl.type_expr) + dump_type_expr(ast, n->member_decl.type_expr, cp, (int)(++k == total)); + } + break; + } + default: + node_label(prefix, is_last, "Decl ?"); + break; + } + break; + case CTX_ATTR_ITEM: + node_label(prefix, is_last, "Attr @%s", STR(n->attr_item.ident)); + if (n->attr_item.expr_list.size) { + spl_dumptree_child_prefix(dt, prefix, is_last, cp, sizeof cp); + for (usize i = 0; i < n->attr_item.expr_list.size; i++) + dump_expr(ast, n->attr_item.expr_list.data[i], cp, + (int)(i == n->attr_item.expr_list.size - 1)); + } + break; + case CTX_PARAM: + if (n->param_decl.name) { + node_label(prefix, is_last, "Param %s", STR(n->param_decl.name)); + if (n->param_decl.attr_list.size || n->param_decl.type_expr) { + spl_dumptree_child_prefix(dt, prefix, is_last, cp, sizeof cp); + usize total = n->param_decl.attr_list.size + (n->param_decl.type_expr ? 1 : 0); + usize k = 0; + for (usize i = 0; i < n->param_decl.attr_list.size; i++) + dump_node(ast, n->param_decl.attr_list.data[i], CTX_ATTR_ITEM, cp, + (int)(++k == total)); + if (n->param_decl.type_expr) + dump_type_expr(ast, n->param_decl.type_expr, cp, (int)(++k == total)); + } + } else { + node_label(prefix, is_last, "Param ..."); + } + break; + case CTX_BLOCK_ITEM: + if (n->kind == SPL_AST_EXPR) { + dump_expr(ast, ref, prefix, is_last); + break; + } + switch (n->block_item.kind) { + case SPL_AST_IF_STATEMENT: { + node_label(prefix, is_last, "If"); + usize nkids = 1 + (n->block_item.if_statement.if_block.size ? 1 : 0) + + (n->block_item.if_statement.else_block.size ? 1 : 0); + spl_dumptree_child_prefix(dt, prefix, is_last, cp, sizeof cp); + usize k = 0; + dump_expr(ast, n->block_item.if_statement.expr, cp, (int)(++k == nkids)); + if (n->block_item.if_statement.if_block.size) + dump_block(ast, &n->block_item.if_statement.if_block, cp, (int)(++k == nkids)); + if (n->block_item.if_statement.else_block.size) + dump_block(ast, &n->block_item.if_statement.else_block, cp, (int)(++k == nkids)); + break; + } + case SPL_AST_IFVAR_STATEMENT: { + node_label(prefix, is_last, "IfVar"); + usize nkids = 1 + (n->block_item.ifvar_statement.if_block.size ? 1 : 0) + + (n->block_item.ifvar_statement.else_block.size ? 1 : 0); + spl_dumptree_child_prefix(dt, prefix, is_last, cp, sizeof cp); + usize k = 0; + dump_packed(ast, n->block_item.ifvar_statement.packed_expr, cp, (int)(++k == nkids)); + if (n->block_item.ifvar_statement.if_block.size) + dump_block(ast, &n->block_item.ifvar_statement.if_block, cp, (int)(++k == nkids)); + if (n->block_item.ifvar_statement.else_block.size) + dump_block(ast, &n->block_item.ifvar_statement.else_block, cp, (int)(++k == nkids)); + break; + } + case SPL_AST_WHILE_STATEMENT: { + node_label(prefix, is_last, "While"); + usize nkids = 1 + (n->block_item.while_statement.while_block.size ? 1 : 0); + spl_dumptree_child_prefix(dt, prefix, is_last, cp, sizeof cp); + usize k = 0; + dump_expr(ast, n->block_item.while_statement.expr, cp, (int)(++k == nkids)); + if (n->block_item.while_statement.while_block.size) + dump_block(ast, &n->block_item.while_statement.while_block, cp, + (int)(++k == nkids)); + break; + } + case SPL_AST_LOOP_STATEMENT: + node_label(prefix, is_last, "Loop"); + if (n->block_item.loop_statement.loop_block.size) { + spl_dumptree_child_prefix(dt, prefix, is_last, cp, sizeof cp); + dump_block(ast, &n->block_item.loop_statement.loop_block, cp, 1); + } + break; + case SPL_AST_FOR_STATEMENT: { + node_label(prefix, is_last, "For"); + usize nkids = n->block_item.for_statement.expr_vec.size + + n->block_item.for_statement.ident_vec.size + + (n->block_item.for_statement.block.size ? 1 : 0); + if (nkids) { + spl_dumptree_child_prefix(dt, prefix, is_last, cp, sizeof cp); + usize k = 0; + for (usize i = 0; i < n->block_item.for_statement.expr_vec.size; i++) + dump_expr(ast, n->block_item.for_statement.expr_vec.data[i], cp, + (int)(++k == nkids)); + for (usize i = 0; i < n->block_item.for_statement.ident_vec.size; i++) { + const char *bi = STR(n->block_item.for_statement.ident_vec.data[i]); + node_label(cp, (int)(++k == nkids), "Bind %s", bi); + } + if (n->block_item.for_statement.block.size) + dump_block(ast, &n->block_item.for_statement.block, cp, (int)(++k == nkids)); + } + break; + } + case SPL_AST_MATCH_STATEMENT: { + node_label(prefix, is_last, "Match"); + spl_ast_node_ref_vec_t *pp = &n->block_item.match_statement.paced_exprs; + spl_ast_node_ref_vec_t *mb = &n->block_item.match_statement.match_block; + usize nkids = 1 + pp->size; + spl_dumptree_child_prefix(dt, prefix, is_last, cp, sizeof cp); + usize k = 0; + dump_expr(ast, n->block_item.match_statement.expr, cp, (int)(++k == nkids)); + for (usize i = 0; i < pp->size && i < mb->size; i++) + dump_arm(ast, pp->data[i], mb->data[i], cp, (int)(++k == nkids)); + break; + } + case SPL_AST_RET_STATEMENT: + node_label(prefix, is_last, "Ret"); + if (n->block_item.ret_statement.expr) { + spl_dumptree_child_prefix(dt, prefix, is_last, cp, sizeof cp); + dump_expr(ast, n->block_item.ret_statement.expr, cp, 1); + } + break; + case SPL_AST_BREAK_STATEMENT: + node_label(prefix, is_last, "Break"); + break; + case SPL_AST_CONTINUE_STATEMENT: + node_label(prefix, is_last, "Continue"); + break; + case SPL_AST_DEFER_STATEMENT: + node_label(prefix, is_last, "Defer"); + if (n->block_item.defer_statement.block_or_statement.size) { + spl_dumptree_child_prefix(dt, prefix, is_last, cp, sizeof cp); + dump_vec_children(ast, &n->block_item.defer_statement.block_or_statement, + CTX_BLOCK_ITEM, cp, 1); + } + break; + case SPL_AST_VARDECL: + dump_node(ast, n->block_item.var_decl, CTX_DECL, prefix, is_last); + break; + case SPL_AST_CONSTDECL: + dump_node(ast, n->block_item.const_decl, CTX_DECL, prefix, is_last); + break; + case SPL_AST_TYPEDECL: + dump_node(ast, n->block_item.type_decl, CTX_DECL, prefix, is_last); + break; + case SPL_AST_EXPR_STATEMENT: + node_label(prefix, is_last, "ExprStmt"); + if (n->block_item.expr_statement) { + spl_dumptree_child_prefix(dt, prefix, is_last, cp, sizeof cp); + dump_expr(ast, n->block_item.expr_statement, cp, 1); + } + break; + default: + node_label(prefix, is_last, "BlockItem ?"); + break; + } + break; + case CTX_EXPR: + dump_expr(ast, ref, prefix, is_last); + break; + case CTX_PREFIX: + case CTX_POSTFIX: + case CTX_PRIMARY: + node_label(prefix, is_last, "?"); + break; + case CTX_PACKED: + dump_packed(ast, ref, prefix, is_last); + break; + case CTX_AGG_ITEM: + node_label(prefix, is_last, "Field .%s", STR(n->aggregate_init_item.ident)); + if (n->aggregate_init_item.expr) { + spl_dumptree_child_prefix(dt, prefix, is_last, cp, sizeof cp); + dump_expr(ast, n->aggregate_init_item.expr, cp, 1); + } + break; + case CTX_TYPE_EXPR: + dump_type_expr(ast, ref, prefix, is_last); + break; + case CTX_TYPE_ATOM: { + char t[128]; + t[0] = '\0'; + if (n->type_atom.kind == SPL_AST_TYPE_IDENT) + strncat(t, STR(n->type_atom.ident), sizeof t - strlen(t) - 1); + else + strncat(t, type_atom_name(n->type_atom.kind), sizeof t - strlen(t) - 1); + node_label(prefix, is_last, "%s", t); + break; + } + case CTX_PREFIX_TYPE: + if (n->prefix_type.pointer == 1) + node_label(prefix, is_last, "*"); + else if (n->prefix_type.array_size != 0) + node_label(prefix, is_last, "[%d]", n->prefix_type.array_size); + else + node_label(prefix, is_last, "[]"); + break; + default: + node_label(prefix, is_last, "?"); + break; + } +} + +static void drop_vec(spl_ast_t *ast, spl_ast_node_ref_vec_t *vec, walk_ctx_t ctx) { + for (usize i = 0; i < vec->size; i++) { + drop_node(ast, vec->data[i], ctx); + } + vec_free(*vec); +} + +static void drop_node(spl_ast_t *ast, spl_ast_node_ref_t ref, walk_ctx_t ctx) { + if (!ref || ref >= ast->buckets.size) + return; + spl_ast_node_t *n = &ast->buckets.data[ref]; + switch (ctx) { + case CTX_CONTAINER: + drop_vec(ast, &n->container_item.members, CTX_DECL); + break; + case CTX_DECL: + switch (n->kind) { + case SPL_AST_FN_DECL: + case SPL_AST_FN_DEFINE: + free((void *)n->fn_decl.name); + drop_vec(ast, &n->fn_decl.attr_list, CTX_ATTR_ITEM); + drop_vec(ast, &n->fn_decl.param_list, CTX_PARAM); + drop_node(ast, n->fn_decl.type_expr, CTX_TYPE_EXPR); + drop_vec(ast, &n->fn_decl.block, CTX_BLOCK_ITEM); + break; + case SPL_AST_TYPE_DECL: + free((void *)n->type_decl.name); + drop_node(ast, n->type_decl.type_expr, CTX_TYPE_EXPR); + break; + case SPL_AST_VAR_DECL: + free((void *)n->var_decl.name); + drop_vec(ast, &n->var_decl.attr_list, CTX_ATTR_ITEM); + drop_node(ast, n->var_decl.type_expr, CTX_TYPE_EXPR); + drop_node(ast, n->var_decl.expr, CTX_EXPR); + break; + case SPL_AST_CONST_DECL: + free((void *)n->const_decl.name); + drop_vec(ast, &n->const_decl.attr_list, CTX_ATTR_ITEM); + drop_node(ast, n->const_decl.type_expr, CTX_TYPE_EXPR); + drop_node(ast, n->const_decl.expr, CTX_EXPR); + break; + case SPL_AST_MEMBER_DECL: + free((void *)n->member_decl.name); + drop_vec(ast, &n->member_decl.attr_list, CTX_ATTR_ITEM); + drop_node(ast, n->member_decl.type_expr, CTX_TYPE_EXPR); + break; + default: + break; + } + break; + case CTX_ATTR_ITEM: + free((void *)n->attr_item.ident); + drop_vec(ast, &n->attr_item.expr_list, CTX_EXPR); + break; + case CTX_PARAM: + free((void *)n->param_decl.name); + drop_vec(ast, &n->param_decl.attr_list, CTX_ATTR_ITEM); + drop_node(ast, n->param_decl.type_expr, CTX_TYPE_EXPR); + break; + case CTX_BLOCK_ITEM: + if (n->kind == SPL_AST_EXPR) { + drop_node(ast, ref, CTX_EXPR); + break; + } + switch (n->block_item.kind) { + case SPL_AST_IF_STATEMENT: + drop_node(ast, n->block_item.if_statement.expr, CTX_EXPR); + drop_vec(ast, &n->block_item.if_statement.if_block, CTX_BLOCK_ITEM); + drop_vec(ast, &n->block_item.if_statement.else_block, CTX_BLOCK_ITEM); + break; + case SPL_AST_IFVAR_STATEMENT: + drop_node(ast, n->block_item.ifvar_statement.packed_expr, CTX_PACKED); + drop_vec(ast, &n->block_item.ifvar_statement.if_block, CTX_BLOCK_ITEM); + drop_vec(ast, &n->block_item.ifvar_statement.else_block, CTX_BLOCK_ITEM); + break; + case SPL_AST_WHILE_STATEMENT: + drop_node(ast, n->block_item.while_statement.expr, CTX_EXPR); + drop_vec(ast, &n->block_item.while_statement.while_block, CTX_BLOCK_ITEM); + break; + case SPL_AST_LOOP_STATEMENT: + drop_vec(ast, &n->block_item.loop_statement.loop_block, CTX_BLOCK_ITEM); + break; + case SPL_AST_FOR_STATEMENT: + drop_vec(ast, &n->block_item.for_statement.expr_vec, CTX_EXPR); + for (usize fi = 0; fi < n->block_item.for_statement.ident_vec.size; fi++) + free(n->block_item.for_statement.ident_vec.data[fi]); + vec_free(n->block_item.for_statement.ident_vec); + drop_vec(ast, &n->block_item.for_statement.block, CTX_BLOCK_ITEM); + break; + case SPL_AST_MATCH_STATEMENT: + drop_node(ast, n->block_item.match_statement.expr, CTX_EXPR); + drop_vec(ast, &n->block_item.match_statement.paced_exprs, CTX_PACKED); + { + /* match_block 含共享 body 引用(模式合并),去重后 drop 防重复释放 */ + spl_ast_node_ref_vec_t dedup; + vec_init(dedup); + for (usize di = 0; di < n->block_item.match_statement.match_block.size; di++) { + spl_ast_node_ref_t r = n->block_item.match_statement.match_block.data[di]; + int found = 0; + for (usize dj = 0; dj < dedup.size; dj++) { + if (dedup.data[dj] == r) { + found = 1; + break; + } + } + if (!found) + vec_push(dedup, r); + } + drop_vec(ast, &dedup, CTX_BLOCK_ITEM); + } + vec_free(n->block_item.match_statement.match_block); + break; + case SPL_AST_RET_STATEMENT: + drop_node(ast, n->block_item.ret_statement.expr, CTX_EXPR); + break; + case SPL_AST_DEFER_STATEMENT: + drop_vec(ast, &n->block_item.defer_statement.block_or_statement, CTX_BLOCK_ITEM); + break; + case SPL_AST_VARDECL: + drop_node(ast, n->block_item.var_decl, CTX_DECL); + break; + case SPL_AST_CONSTDECL: + drop_node(ast, n->block_item.const_decl, CTX_DECL); + break; + case SPL_AST_TYPEDECL: + drop_node(ast, n->block_item.type_decl, CTX_DECL); + break; + case SPL_AST_EXPR_STATEMENT: + drop_node(ast, n->block_item.expr_statement, CTX_EXPR); + break; + default: + break; + } + break; + case CTX_EXPR: + switch (n->expr.op) { + case SPL_AST_PREFIX_EXPR: + drop_node(ast, n->expr.op_expr.left, CTX_PREFIX); + break; + case SPL_AST_POSTFIX_EXPR: + drop_node(ast, n->expr.op_expr.left, CTX_POSTFIX); + break; + case SPL_AST_PRIMARY_EXPR: + drop_node(ast, n->expr.op_expr.left, CTX_PRIMARY); + break; + default: + drop_node(ast, n->expr.op_expr.left, CTX_EXPR); + drop_node(ast, n->expr.op_expr.right, CTX_EXPR); + break; + } + break; + case CTX_PREFIX: + drop_node(ast, n->prefix_expr.postfix_expr, CTX_EXPR); + break; + case CTX_POSTFIX: + drop_node(ast, n->postfix_expr.primary_expr, CTX_PRIMARY); + if (n->postfix_expr.kind == SPL_AST_FIELD_EXPR) + free((void *)n->postfix_expr.field_expr); + switch (n->postfix_expr.kind) { + case SPL_AST_CALL_EXPR: + drop_vec(ast, &n->postfix_expr.call_expr, CTX_EXPR); + break; + case SPL_AST_INDEX_EXPR: + drop_node(ast, n->postfix_expr.index_expr, CTX_EXPR); + break; + case SPL_AST_SLICE_EXPR: + drop_node(ast, n->postfix_expr.slice_expr.begin, CTX_EXPR); + drop_node(ast, n->postfix_expr.slice_expr.end, CTX_EXPR); + break; + case SPL_AST_AS_EXPR: + drop_node(ast, n->postfix_expr.type_expr, CTX_TYPE_EXPR); + break; + default: + break; + } + break; + case CTX_PRIMARY: + switch (n->primary_expr.kind) { + case SPL_AST_STRING_LIT: + /* string_lit_expr 为解析出C 字符串副本,须释*/ + free((void *)n->primary_expr.string_lit_expr); + break; + case SPL_AST_ARGGREGATE_INIT: + drop_vec(ast, &n->primary_expr.aggregate_init_expr, CTX_AGG_ITEM); + break; + case SPL_AST_EXPR_EXPR: + drop_node(ast, n->primary_expr.expr, CTX_EXPR); + break; + case SPL_AST_ARRAY_LIT: + drop_node(ast, n->primary_expr.array_lit_expr.type_expr, CTX_TYPE_EXPR); + drop_vec(ast, &n->primary_expr.array_lit_expr.expr_list, CTX_EXPR); + break; + case SPL_AST_BUILTIN_EXPR: + free((void *)n->primary_expr.builtin_expr.ident); + drop_vec(ast, &n->primary_expr.builtin_expr.expr_list, CTX_EXPR); + break; + case SPL_AST_BLOCK_EXPR: + drop_vec(ast, &n->primary_expr.block_expr, CTX_BLOCK_ITEM); + break; + case SPL_AST_IDENT: + free((void *)n->primary_expr.ident); + break; + default: + break; + } + break; + case CTX_PACKED: + free((void *)n->packed_expr.ident); + free((void *)n->packed_expr.bind_ident); + drop_node(ast, n->packed_expr.expr, CTX_EXPR); + break; + case CTX_AGG_ITEM: + free((void *)n->aggregate_init_item.ident); + drop_node(ast, n->aggregate_init_item.expr, CTX_EXPR); + break; + case CTX_TYPE_EXPR: + drop_vec(ast, &n->type_expr.type_prefixs, CTX_PREFIX_TYPE); + drop_vec(ast, &n->type_expr.attr_list, CTX_ATTR_ITEM); + if (n->type_expr.kind == SPL_AST_BASE_TYPE_FN) { + drop_vec(ast, &n->type_expr.fn_type.param_list, CTX_PARAM); + drop_node(ast, n->type_expr.fn_type.type_expr, CTX_TYPE_EXPR); + } else if (n->type_expr.kind == SPL_AST_TYPE_STRUCT || + n->type_expr.kind == SPL_AST_TYPE_UNION || + n->type_expr.kind == SPL_AST_TYPE_ENUM) { + drop_vec(ast, &n->type_expr.aggregate_list, CTX_DECL); + } else { + drop_vec(ast, &n->type_expr.type_path, CTX_TYPE_ATOM); + } + break; + case CTX_TYPE_ATOM: + free((void *)n->type_atom.ident); + break; + default: + break; + } +} + +/* ================================================================ + * valid(结构校验) + * ================================================================ */ + +static void validate_vec(spl_ast_t *ast, spl_ast_node_ref_vec_t *vec, walk_ctx_t ctx) { + for (usize i = 0; i < vec->size; i++) { + validate_node(ast, vec->data[i], ctx); + } +} + +static void validate_node(spl_ast_t *ast, spl_ast_node_ref_t ref, walk_ctx_t ctx) { + if (!ref) + return; + if (ref >= ast->buckets.size) { + printf("valid: invalid node reference %llu\n", (unsigned long long)ref); + return; + } + spl_ast_node_t *n = &ast->buckets.data[ref]; + switch (ctx) { + case CTX_CONTAINER: + validate_vec(ast, &n->container_item.members, CTX_DECL); + break; + case CTX_DECL: + switch (n->kind) { + case SPL_AST_FN_DECL: + case SPL_AST_FN_DEFINE: + if (!n->fn_decl.name) + printf("valid: function missing name\n"); + validate_vec(ast, &n->fn_decl.attr_list, CTX_ATTR_ITEM); + validate_vec(ast, &n->fn_decl.param_list, CTX_PARAM); + validate_node(ast, n->fn_decl.type_expr, CTX_TYPE_EXPR); + validate_vec(ast, &n->fn_decl.block, CTX_BLOCK_ITEM); + break; + case SPL_AST_TYPE_DECL: + if (!n->type_decl.name) + printf("valid: type declaration missing name\n"); + validate_node(ast, n->type_decl.type_expr, CTX_TYPE_EXPR); + break; + case SPL_AST_VAR_DECL: + if (!n->var_decl.name) + printf("valid: variable declaration missing name\n"); + validate_vec(ast, &n->var_decl.attr_list, CTX_ATTR_ITEM); + validate_node(ast, n->var_decl.type_expr, CTX_TYPE_EXPR); + validate_node(ast, n->var_decl.expr, CTX_EXPR); + break; + case SPL_AST_CONST_DECL: + if (!n->const_decl.name) + printf("valid: constant declaration missing name\n"); + validate_vec(ast, &n->const_decl.attr_list, CTX_ATTR_ITEM); + validate_node(ast, n->const_decl.type_expr, CTX_TYPE_EXPR); + validate_node(ast, n->const_decl.expr, CTX_EXPR); + break; + case SPL_AST_MEMBER_DECL: + if (!n->member_decl.name) + printf("valid: member declaration missing name\n"); + validate_vec(ast, &n->member_decl.attr_list, CTX_ATTR_ITEM); + validate_node(ast, n->member_decl.type_expr, CTX_TYPE_EXPR); + break; + default: + break; + } + break; + case CTX_ATTR_ITEM: + if (!n->attr_item.ident) + printf("valid: attribute missing name\n"); + validate_vec(ast, &n->attr_item.expr_list, CTX_EXPR); + break; + case CTX_PARAM: + validate_vec(ast, &n->param_decl.attr_list, CTX_ATTR_ITEM); + validate_node(ast, n->param_decl.type_expr, CTX_TYPE_EXPR); + break; + case CTX_BLOCK_ITEM: + if (n->kind == SPL_AST_EXPR) { + validate_node(ast, ref, CTX_EXPR); + break; + } + switch (n->block_item.kind) { + case SPL_AST_IF_STATEMENT: + validate_node(ast, n->block_item.if_statement.expr, CTX_EXPR); + validate_vec(ast, &n->block_item.if_statement.if_block, CTX_BLOCK_ITEM); + validate_vec(ast, &n->block_item.if_statement.else_block, CTX_BLOCK_ITEM); + break; + case SPL_AST_IFVAR_STATEMENT: + validate_node(ast, n->block_item.ifvar_statement.packed_expr, CTX_PACKED); + validate_vec(ast, &n->block_item.ifvar_statement.if_block, CTX_BLOCK_ITEM); + validate_vec(ast, &n->block_item.ifvar_statement.else_block, CTX_BLOCK_ITEM); + break; + case SPL_AST_WHILE_STATEMENT: + validate_node(ast, n->block_item.while_statement.expr, CTX_EXPR); + validate_vec(ast, &n->block_item.while_statement.while_block, CTX_BLOCK_ITEM); + break; + case SPL_AST_LOOP_STATEMENT: + validate_vec(ast, &n->block_item.loop_statement.loop_block, CTX_BLOCK_ITEM); + break; + case SPL_AST_FOR_STATEMENT: + validate_vec(ast, &n->block_item.for_statement.expr_vec, CTX_EXPR); + validate_vec(ast, &n->block_item.for_statement.block, CTX_BLOCK_ITEM); + break; + case SPL_AST_MATCH_STATEMENT: + validate_node(ast, n->block_item.match_statement.expr, CTX_EXPR); + validate_vec(ast, &n->block_item.match_statement.paced_exprs, CTX_PACKED); + validate_vec(ast, &n->block_item.match_statement.match_block, CTX_BLOCK_ITEM); + if (n->block_item.match_statement.paced_exprs.size != + n->block_item.match_statement.match_block.size) + printf("valid: match arm pattern/statement count mismatch\n"); + break; + case SPL_AST_RET_STATEMENT: + validate_node(ast, n->block_item.ret_statement.expr, CTX_EXPR); + break; + case SPL_AST_DEFER_STATEMENT: + validate_vec(ast, &n->block_item.defer_statement.block_or_statement, CTX_BLOCK_ITEM); + break; + case SPL_AST_VARDECL: + validate_node(ast, n->block_item.var_decl, CTX_DECL); + break; + case SPL_AST_CONSTDECL: + validate_node(ast, n->block_item.const_decl, CTX_DECL); + break; + case SPL_AST_TYPEDECL: + validate_node(ast, n->block_item.type_decl, CTX_DECL); + break; + case SPL_AST_EXPR_STATEMENT: + validate_node(ast, n->block_item.expr_statement, CTX_EXPR); + break; + default: + break; + } + break; + case CTX_EXPR: + switch (n->expr.op) { + case SPL_AST_PREFIX_EXPR: + validate_node(ast, n->expr.op_expr.left, CTX_PREFIX); + break; + case SPL_AST_POSTFIX_EXPR: + validate_node(ast, n->expr.op_expr.left, CTX_POSTFIX); + break; + case SPL_AST_PRIMARY_EXPR: + validate_node(ast, n->expr.op_expr.left, CTX_PRIMARY); + break; + default: + validate_node(ast, n->expr.op_expr.left, CTX_EXPR); + validate_node(ast, n->expr.op_expr.right, CTX_EXPR); + break; + } + break; + case CTX_PREFIX: + validate_node(ast, n->prefix_expr.postfix_expr, CTX_EXPR); + break; + case CTX_POSTFIX: + validate_node(ast, n->postfix_expr.primary_expr, CTX_PRIMARY); + switch (n->postfix_expr.kind) { + case SPL_AST_CALL_EXPR: + validate_vec(ast, &n->postfix_expr.call_expr, CTX_EXPR); + break; + case SPL_AST_INDEX_EXPR: + validate_node(ast, n->postfix_expr.index_expr, CTX_EXPR); + break; + case SPL_AST_SLICE_EXPR: + validate_node(ast, n->postfix_expr.slice_expr.begin, CTX_EXPR); + validate_node(ast, n->postfix_expr.slice_expr.end, CTX_EXPR); + break; + case SPL_AST_AS_EXPR: + validate_node(ast, n->postfix_expr.type_expr, CTX_TYPE_EXPR); + break; + default: + break; + } + break; + case CTX_PRIMARY: + switch (n->primary_expr.kind) { + case SPL_AST_IDENT: + if (!n->primary_expr.ident) + printf("valid: identifier missing name\n"); + break; + case SPL_AST_ARGGREGATE_INIT: + validate_vec(ast, &n->primary_expr.aggregate_init_expr, CTX_AGG_ITEM); + break; + case SPL_AST_EXPR_EXPR: + validate_node(ast, n->primary_expr.expr, CTX_EXPR); + break; + case SPL_AST_ARRAY_LIT: + validate_node(ast, n->primary_expr.array_lit_expr.type_expr, CTX_TYPE_EXPR); + validate_vec(ast, &n->primary_expr.array_lit_expr.expr_list, CTX_EXPR); + break; + case SPL_AST_BUILTIN_EXPR: + validate_vec(ast, &n->primary_expr.builtin_expr.expr_list, CTX_EXPR); + break; + case SPL_AST_BLOCK_EXPR: + validate_vec(ast, &n->primary_expr.block_expr, CTX_BLOCK_ITEM); + break; + default: + break; + } + break; + case CTX_PACKED: + validate_node(ast, n->packed_expr.expr, CTX_EXPR); + break; + case CTX_AGG_ITEM: + validate_node(ast, n->aggregate_init_item.expr, CTX_EXPR); + break; + case CTX_TYPE_EXPR: + validate_vec(ast, &n->type_expr.type_prefixs, CTX_PREFIX_TYPE); + validate_vec(ast, &n->type_expr.attr_list, CTX_ATTR_ITEM); + if (n->type_expr.kind == SPL_AST_BASE_TYPE_FN) { + validate_vec(ast, &n->type_expr.fn_type.param_list, CTX_PARAM); + validate_node(ast, n->type_expr.fn_type.type_expr, CTX_TYPE_EXPR); + } else if (n->type_expr.kind == SPL_AST_TYPE_STRUCT || + n->type_expr.kind == SPL_AST_TYPE_UNION || + n->type_expr.kind == SPL_AST_TYPE_ENUM) { + validate_vec(ast, &n->type_expr.aggregate_list, CTX_DECL); + } else { + validate_vec(ast, &n->type_expr.type_path, CTX_TYPE_ATOM); + } + break; + default: + break; + } +} diff --git a/stage1/spl_ast.h b/stage1/spl_ast.h index bf8dd0e..b90c8d4 100644 --- a/stage1/spl_ast.h +++ b/stage1/spl_ast.h @@ -4,10 +4,9 @@ #include "../stage0/include/utils.h" #include "spl_lexer.h" #include "spl_tok.h" -#include typedef enum { - SPL_AST_CONTAINER_MEMBER, + SPL_AST_CONTAINER_ITEM, SPL_AST_FN_DECL, SPL_AST_FN_DEFINE, SPL_AST_TYPE_DECL, @@ -37,7 +36,11 @@ struct spl_ast_node { spl_ast_node_kind_t kind; spl_ast_loc_t loc; union { - spl_ast_node_ref_vec_t container_member; + struct { + spl_ast_node_ref_vec_t attr_list; /* attr_item */ + spl_ast_node_ref_t self; /* self */ + spl_ast_node_ref_vec_t members; /* container_decl 列表 */ + } container_item; struct { const char *ident; @@ -58,18 +61,8 @@ struct spl_ast_node { } param_decl; struct { - spl_ast_node_ref_vec_t attr_list; /* attr_item */ const char *name; - enum { - SPL_AST_TYPE_STRUCT, - SPL_AST_TYPE_UNION, - SPL_AST_TYPE_ENUM, - SPL_AST_TYPE_TYPE_EXPR, - } kind; - union { - spl_ast_node_ref_t type_expr; - spl_ast_node_ref_t aggregate_list; - }; + spl_ast_node_ref_t type_expr; } type_decl; struct { spl_ast_node_ref_vec_t attr_list; /* attr_item */ @@ -103,6 +96,7 @@ struct spl_ast_node { SPL_AST_CONTINUE_STATEMENT, SPL_AST_DEFER_STATEMENT, SPL_AST_VARDECL, + SPL_AST_CONSTDECL, SPL_AST_TYPEDECL, SPL_AST_EXPR_STATEMENT, } kind; @@ -131,8 +125,8 @@ struct spl_ast_node { } for_statement; struct { spl_ast_node_ref_t expr; - spl_ast_node_ref_vec_t paced_exprs; - spl_ast_node_ref_vec_t statements; + spl_ast_node_ref_vec_t paced_exprs; /* packed_expr */ + spl_ast_node_ref_vec_t match_block; /* block_item */ } match_statement; struct { spl_ast_node_ref_t expr; @@ -145,6 +139,7 @@ struct spl_ast_node { spl_ast_node_ref_vec_t block_or_statement; /* block_item/statement */ } defer_statement; spl_ast_node_ref_t var_decl; + spl_ast_node_ref_t const_decl; spl_ast_node_ref_t type_decl; spl_ast_node_ref_t expr_statement; }; @@ -280,10 +275,13 @@ struct spl_ast_node { struct { spl_ast_node_ref_vec_t type_prefixs; /* prefix_type */ - + spl_ast_node_ref_vec_t attr_list; /* attr_item */ enum { SPL_AST_BASE_TYPE_FN, SPL_AST_BASE_TYPE_PATH, + SPL_AST_TYPE_STRUCT, + SPL_AST_TYPE_UNION, + SPL_AST_TYPE_ENUM, } kind; const char *spl_base_type; @@ -293,6 +291,7 @@ struct spl_ast_node { spl_ast_node_ref_vec_t param_list; /* param_decl */ spl_ast_node_ref_t type_expr; } fn_type; + spl_ast_node_ref_vec_t aggregate_list; /* container_decl */ }; } type_expr; struct { diff --git a/stage1/spl_ast2ir.c b/stage1/spl_ast2ir.c new file mode 100644 index 0000000..f34279c --- /dev/null +++ b/stage1/spl_ast2ir.c @@ -0,0 +1 @@ +#include "spl_ast2ir.h" diff --git a/stage1/spl_ast2ir.h b/stage1/spl_ast2ir.h new file mode 100644 index 0000000..ed8ef87 --- /dev/null +++ b/stage1/spl_ast2ir.h @@ -0,0 +1,4 @@ +#ifndef __SPL_AST2IR_H__ +#define __SPL_AST2IR_H__ + +#endif /* __SPL_AST2IR_H__ */ diff --git a/stage1/spl_dumptree.c b/stage1/spl_dumptree.c new file mode 100644 index 0000000..702bb89 --- /dev/null +++ b/stage1/spl_dumptree.c @@ -0,0 +1,26 @@ +/* spl_dumptree.c 可配置的树形打印模块(只用基本 ASCII)*/ + +#include "spl_dumptree.h" +#include + +const spl_dumptree_style_t spl_dumptree_ascii_style = { + "| ", + "|-", + "`-", + " ", +}; + +void spl_dumptree_print(const spl_dumptree_style_t *st, const char *prefix, int is_last, + const char *fmt, ...) { + va_list ap; + printf("%s%s ", prefix, is_last ? st->last_branch : st->branch); + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + printf("\n"); +} + +void spl_dumptree_child_prefix(const spl_dumptree_style_t *st, const char *prefix, int is_last, + char *out, size_t cap) { + snprintf(out, cap, "%s%s", prefix, is_last ? st->space : st->vertical); +} diff --git a/stage1/spl_dumptree.h b/stage1/spl_dumptree.h new file mode 100644 index 0000000..5033efc --- /dev/null +++ b/stage1/spl_dumptree.h @@ -0,0 +1,31 @@ +/* spl_dumptree.h 可配置的树形打印模块(只用基本 ASCII)*/ + +#ifndef __SPL_DUMPTREE_H__ +#define __SPL_DUMPTREE_H__ + +#include +#include + +/* 可配置的缩进字符串 */ +typedef struct { + const char *vertical; /* "| " */ + const char *branch; /* "|-" */ + const char *last_branch; /* "`-" */ + const char *space; /* " " */ +} spl_dumptree_style_t; + +/* 默认 ASCII 风格 */ +extern const spl_dumptree_style_t spl_dumptree_ascii_style; + +/* 打印一行节点标签:prefix + 分支符 + label + * prefix 已累积的缩进骨架(不含分支符) + * is_last 本节点是否为同级最后一个子节点 + * fmt printf 风格 label */ +void spl_dumptree_print(const spl_dumptree_style_t *st, const char *prefix, int is_last, + const char *fmt, ...); + +/* 生成子节点的缩进骨架:parent_prefix + (parent_is_last ? space : vertical) */ +void spl_dumptree_child_prefix(const spl_dumptree_style_t *st, const char *prefix, int is_last, + char *out, size_t cap); + +#endif /* __SPL_DUMPTREE_H__ */ diff --git a/stage1/spl_ir.h b/stage1/spl_ir.h index 4ca5079..38c2b0b 100644 --- a/stage1/spl_ir.h +++ b/stage1/spl_ir.h @@ -69,20 +69,40 @@ X(control.trap, V0, SPL_IR_CONTROL_TRAP) \ X(dbg.breakpoint, V0, SPL_IR_DBG_BREAKPOINT) \ X(dbg.declare, V0, SPL_IR_DBG_DECLARE) - + +typedef enum { +#ifdef X +#undef X +#endif +#define X(a, b, c) c, + SPL_IR_FN_TABLE +#undef X +} spl_ir_kind_t; /* clang-format on */ typedef struct { - enum { - - } kind; + spl_ir_kind_t kind; } spl_ir_node_t; typedef VEC(spl_ir_node_t) spl_ir_node_vec_t; typedef usize spl_ir_node_ref_t; typedef VEC(spl_ir_node_ref_t) spl_ir_node_ref_vec_t; + +typedef struct { + enum { + SPL_IR_ATTR_NONE, + SPL_IR_ATTR_LINK, /* 不实现 */ + SPL_IR_ATTR_ABI, /* 只有 C ABI 支持 */ + SPL_IR_ATTR_SYMBOL, /* 不实现 */ + SPL_IR_ATTR_NAKED, /* 不实现 */ + SPL_IR_ATTR_NOINLINE, /* 不实现 */ + SPL_IR_ATTR_ALWAYSINLINE, /* 不实现 */ + }; +} spl_ir_attr_t; +typedef VEC(spl_ir_attr_t) spl_ir_attr_vec_t; typedef struct { const char *name; + spl_ir_attr_t attr; spl_ir_node_vec_t nodes; spl_ir_node_ref_vec_t labels; } spl_ir_func_t; diff --git a/stage1/spl_sema.c b/stage1/spl_sema.c new file mode 100644 index 0000000..95249de --- /dev/null +++ b/stage1/spl_sema.c @@ -0,0 +1 @@ +#include "spl_sema.h" \ No newline at end of file diff --git a/stage1/spl_sema.h b/stage1/spl_sema.h new file mode 100644 index 0000000..e8ffe5d --- /dev/null +++ b/stage1/spl_sema.h @@ -0,0 +1,33 @@ +#ifndef __SPL_SEMA_H__ +#define __SPL_SEMA_H__ + +#include "spl_ast.h" +#include "spl_type.h" + +typedef usize spl_scope_id_t; /* 0 is error */ +typedef struct { + spl_scope_id_t parent; + MAP(const char *, spl_type_id_t) symbols; +} spl_scope_node_t; +typedef VEC(spl_scope_node_t) spl_scope_node_vec_t; + +typedef struct { + spl_ast_t *ast; + spl_type_t type; + spl_scope_node_vec_t scopes; + spl_scope_id_t root_scope; + spl_scope_id_t current_scope; + int error_count; +} spl_sema_t; + +void spl_sema_init(spl_sema_t *sema); +void spl_sema_drop(spl_sema_t *sema); +void spl_sema_run(spl_sema_t *sema); + +spl_scope_id_t spl_sema_scope_alloc(spl_sema_t *sema); +bool spl_sema_scope_insert(spl_sema_t *sema, spl_scope_id_t id, const char *symbol_name, + spl_type_id_t symbol_val); +typedef VEC(const char *) spl_symbol_path_t; +spl_type_id_t spl_sema_scope_find(spl_sema_t *sema, spl_symbol_path_t path); + +#endif /* __SPL_SEMA_H__ */ diff --git a/stage1/spl_type.c b/stage1/spl_type.c new file mode 100644 index 0000000..52ac773 --- /dev/null +++ b/stage1/spl_type.c @@ -0,0 +1 @@ +#include "spl_type.h" \ No newline at end of file diff --git a/stage1/spl_type.h b/stage1/spl_type.h new file mode 100644 index 0000000..e9373b9 --- /dev/null +++ b/stage1/spl_type.h @@ -0,0 +1,116 @@ +#ifndef __SPL_TYPE_H__ +#define __SPL_TYPE_H__ + +#include "../stage0/include/utils.h" + +typedef usize spl_type_id_t; /* 0 is error */ +typedef VEC(spl_type_id_t) spl_type_id_vec_t; + +typedef struct { + enum { + SPL_TYPE_VOID, + SPL_TYPE_BOOL, + SPL_TYPE_INT, + SPL_TYPE_FLOAT, + SPL_TYPE_PTR, + SPL_TYPE_SLICE, + SPL_TYPE_ARRAY, + SPL_TYPE_STRUCT, + SPL_TYPE_UNION, + SPL_TYPE_ENUM, + SPL_TYPE_FN, + SPL_TYPE_ID, + } kind; + union { + struct { + usize bits; + int is_signed; + } int_type; + struct { + usize bits; + } float_type; + spl_type_id_t ptr_pointee; + spl_type_id_t slice_element; + struct { + spl_type_id_t element; + usize len; + } array_type; + spl_type_id_vec_t agg_field_types; + struct { + spl_type_id_vec_t variants; + spl_type_id_t tag_type; + } enum_type; // ADT + struct { + spl_type_id_vec_t params; + spl_type_id_t ret; + } fn_type; + spl_type_id_t type_id; + }; + usize byte_size; /* 布局缓存 */ +} spl_type_node_t; +typedef VEC(spl_type_node_t) spl_type_node_vec_t; + +typedef struct { + const char *name; + spl_type_id_t type; + usize scope_id; +} spl_var_def_t; +typedef VEC(spl_var_def_t) spl_var_def_vec_t; + +typedef struct { + enum { + SPL_DEF_NONE, + SPL_DEF_BUILTIN, + SPL_DEF_VAR, + SPL_DEF_FN_PARAMS, + SPL_DEF_AGG, // include enum variants + SPL_DEF_DISTINCT, // newtype + SPL_DEF_ALIAS, // sametypes + } kind; + union { + spl_var_def_t var_def; + spl_var_def_vec_t agg_def; // include enum variants + spl_var_def_vec_t fn_params_def; + spl_var_def_t type_def; + }; + int source_loc; + enum { + SPL_FLAG_NONE, + } flag; +} spl_def_node_t; +typedef VEC(spl_def_node_t) spl_def_node_vec_t; + +/* +SPL 设计是严格区分类型做到类型和名称无关即 +type (类型名) = (匿名类型) +好处是递归使用可以直接操作类型名的映射的提前分配的匿名类型的id +*/ +typedef struct { + spl_type_node_vec_t type_table; + spl_def_node_vec_t def_table; /* 使用type_id作为索引 */ +} spl_type_t; + +void spl_type_init(spl_type_t *type); +void spl_type_drop(spl_type_t *type); + +void spl_type_def_dump(spl_type_t *type, spl_type_id_t id); +void spl_type_pure_dump(spl_type_t *type, spl_type_id_t id); + +// alloc 时两表平行 push, 保证索引同步 +spl_type_id_t spl_type_alloc(spl_type_t *type); +spl_type_node_t *spl_type_node(spl_type_t *type, spl_type_id_t id); +spl_def_node_t *spl_type_def(spl_type_t *type, spl_type_id_t id); + +spl_type_id_t spl_type_void(spl_type_t *type); +spl_type_id_t spl_type_bool(spl_type_t *type); +spl_type_id_t spl_type_int(spl_type_t *type, usize bits, int is_signed); +spl_type_id_t spl_type_float(spl_type_t *type, usize bits); +spl_type_id_t spl_type_ptr(spl_type_t *type, spl_type_id_t val); +spl_type_id_t spl_type_slice(spl_type_t *type, spl_type_id_t val); +spl_type_id_t spl_type_array(spl_type_t *type, spl_type_id_t val, usize len); +spl_type_id_t spl_type_tid(spl_type_t *type, spl_type_id_t val); +spl_type_id_t spl_type_agg(spl_type_t *type, spl_type_id_vec_t fields); +spl_type_id_t spl_type_enum(spl_type_t *type, spl_type_id_vec_t variants, spl_type_id_t tag); +spl_type_id_t spl_type_fn(spl_type_t *type, spl_type_id_vec_t params, spl_type_id_t ret); + +#endif /* __SPL_TYPE_H__ */ diff --git a/stage1/splc0.c b/stage1/splc0.c index c0487e1..ec49081 100644 --- a/stage1/splc0.c +++ b/stage1/splc0.c @@ -1,8 +1,19 @@ -/* splc0.c — SPL compiler CLI */ +/* splc0.c — SPL compiler CLI (stage 1, 引导用) + * + * splc0 --dump tokens|ast|all dump 前端产物 + * splc0 编译 (阶段 B 实现) + */ +#define __SCC_LOG_IMPL_IMPORT_SRC__ +#include "../stage0/include/utils.h" + #include #include #include +#include "spl_ast.h" +#include "spl_lexer.h" +#include "spl_tok.h" + static char *read_file(const char *path, long *out_len) { FILE *f = fopen(path, "rb"); if (!f) { @@ -24,19 +35,68 @@ static char *read_file(const char *path, long *out_len) { return buf; } -int main(int argc, char **argv) { - if (argc < 2) { - fprintf(stderr, "Usage: splc0 [--dump ] [out]\n"); +static const char *const tok_type_names[] = { +#define X(name, enum_name, dummy) #enum_name, + KEYWORD_TABLE +#undef X +#define X(name, enum_name, dummy) #enum_name, + TOKEN_TABLE +#undef X +}; + +static void dump_tokens(const char *src, const char *fname) { + spl_tok_vec_t toks = spl_lex(src, fname); + printf("tokens got (%zu)\n", toks.size); + for (usize i = 0; i < toks.size; i++) { + const spl_tok_t *t = &toks.data[i]; + printf("[%s] %.*s (%zu:%zu)\n", tok_type_names[t->type], (int)t->len, t->lexeme, t->line, + t->col); + } + vec_free(toks); +} + +static void dump_ast(const char *src, const char *fname) { + spl_tok_vec_t toks = spl_lex(src, fname); + spl_ast_t ast; + spl_ast_init(&ast, &toks); + spl_ast_prase(&ast); + spl_ast_valid(&ast); + spl_ast_dump(&ast, ast.root); + spl_ast_drop(&ast); +} + +static int cmd_dump(const char *flags, const char *path) { + long len; + char *src = read_file(path, &len); + if (!src) return 1; - } - if (strcmp(argv[1], "--dump") == 0) - // return cmd_dump(argc - 2, argv + 2); - return 0; - if (strcmp(argv[1], "--help") == 0) { - printf("splc0 compile\n"); - printf("splc0 --dump dump: tokens,cst,ast,ir,mcode,all\n"); - return 0; - } - // return cmd_compile(argc - 1, argv + 1); + int do_tokens = strstr(flags, "tokens") != NULL || strcmp(flags, "all") == 0; + int do_ast = strstr(flags, "ast") != NULL || strcmp(flags, "all") == 0; + if (do_tokens) + dump_tokens(src, path); + if (do_ast) + dump_ast(src, path); + free(src); return 0; } + +int main(int argc, char **argv) { + if (argc < 2) { + LOG_FATAL("Usage: splc0 [--dump ] [out]\n"); + return 1; + } + if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0) { + LOG_INFO("splc0 compile (.spl -> .sir, 阶段 B)\n"); + LOG_INFO("splc0 --dump dump: tokens,ast,all\n"); + return 0; + } + if (strcmp(argv[1], "--dump") == 0) { + if (argc < 4) { + LOG_INFO("splc0: --dump need \n"); + return 1; + } + return cmd_dump(argv[2], argv[3]); + } + LOG_FATAL("splc0: compile todo\n"); + return 1; +} diff --git a/stage1/splc_cli.c b/stage1/splc_cli.c index 40433ce..1efbc2f 100644 --- a/stage1/splc_cli.c +++ b/stage1/splc_cli.c @@ -1,4 +1,4 @@ -/* spc_vm.c 鈥?VM launcher for stage 1 */ +/* spc_vm.c launcher for stage 1 */ #include "../stage0/spl_mcode.h" #include "../stage0/spl_syscall.h" @@ -57,13 +57,12 @@ int main(int argc, const char **argv) { return 1; } spl_vm_set_trace(&vm, trace); - if (debug_addr) spl_vm_set_debug(&vm, 1); + if (debug_addr) + spl_vm_set_debug(&vm, 1); int ret = spl_vm_run_until(&vm, 0); spl_vm_drop(&vm); spl_prog_drop(&prog); - if (ret < 0) - return (int)vm.exit_code; return (int)vm.exit_code; } diff --git a/stage1/test16_methods.spl b/stage1/test16_methods.spl index 0ad1560..47e9fe6 100644 --- a/stage1/test16_methods.spl +++ b/stage1/test16_methods.spl @@ -24,8 +24,8 @@ type Expr = enum { fn eval(self: *Expr) i32 { match self { - .Int[val] => ret val, - .Add[.left = left, .right = right] => ret eval(left) + eval(right), + .Int[val] => { ret val; }, + .Add[sub] => { ret eval(sub.left) + eval(sub.right); } } ret 0; } @@ -39,7 +39,7 @@ fn main() i32 { /* enum 方法 + match */ var expr_l := Expr { .Int = 3 }; var expr_r := Expr { .Int = 4 }; - var expr := Expr { .Add = { .left = expr_l, .right = expr_r } }; + var expr := Expr { .Add = .{ .left = expr_l, .right = expr_r } }; var result := expr.eval(&expr); vm_printf("eval result: %d\n", result); if result != 7 { ret 1; } diff --git a/stage1/test18_match.spl b/stage1/test18_match.spl index f177e67..a7e211d 100644 --- a/stage1/test18_match.spl +++ b/stage1/test18_match.spl @@ -118,7 +118,7 @@ fn test_shape_match() i32 { .Circle[r] => { if r != 10 { ret 1; } }, - .Rect[.x = w, .y = h] => { + .Rect[p] => { ret 2; } } @@ -127,9 +127,9 @@ fn test_shape_match() i32 { s = Shape { .Rect = Point { .x = 3, .y = 4 } }; match s { .Circle[r] => { ret 3; }, - .Rect[.x = w, .y = h] => { - if w != 3 { ret 4; } - if h != 4 { ret 5; } + .Rect[p] => { + if p.x != 3 { ret 4; } + if p.y != 4 { ret 5; } } } diff --git a/stage1/test19_hardarray.spl b/stage1/test19_hardarray.spl index 5313b1a..bc5505c 100644 --- a/stage1/test19_hardarray.spl +++ b/stage1/test19_hardarray.spl @@ -8,7 +8,7 @@ fn test_string() i32 { * String test: str is []u8 * ============================ */ var data: *u8 = "hello"; - var s: []u8 = { .ptr = data, .len = 5 }; + var s: []u8 = .{ .ptr = data, .len = 5 }; if s.len != 5 { ret 100; } if s[0] != 104 { ret 101; } /* 'h' */ @@ -147,7 +147,7 @@ fn main() i32 { var data: [5]i32; data[0] = 10; data[1] = 20; data[2] = 30; data[3] = 40; data[4] = 50; var p: *i32 = &data[2]; - var from_ptr: []i32 = { .ptr = p, .len = 2 }; + var from_ptr: []i32 = .{ .ptr = p, .len = 2 }; var from_ptr2: []i32; from_ptr2.ptr = p; from_ptr2.len = 2; diff --git a/stage1/test20_complex.spl b/stage1/test20_complex.spl index d459353..fa15889 100644 --- a/stage1/test20_complex.spl +++ b/stage1/test20_complex.spl @@ -78,7 +78,7 @@ fn test_slice_in_struct() i32 { raw[0] = 65; raw[1] = 66; raw[2] = 67; raw[3] = 68; /* Bug fix: { .ptr = ..., .len = ... } inside struct literal */ - var b: Buffer = Buffer { .data = { .ptr = &raw[0], .len = 4 }, .len = 4 }; + var b: Buffer = Buffer { .data = .{ .ptr = &raw[0], .len = 4 }, .len = 4 }; if b.len != 4 { ret 1; } if b.data[0] != 65 { ret 2; } @@ -90,7 +90,7 @@ fn test_slice_in_struct() i32 { if raw[0] != 90 { ret 5; } /* Initialize with shorter slice */ - var b2: Buffer = Buffer { .data = { .ptr = &raw[2], .len = 2 }, .len = 2 }; + var b2: Buffer = Buffer { .data = .{ .ptr = &raw[2], .len = 2 }, .len = 2 }; if b2.len != 2 { ret 6; } if b2.data[0] != 67 { ret 7; } @@ -211,7 +211,7 @@ fn test_complex_nesting() i32 { var bundle: Bundle = Bundle { .name = &str_data[0], - .buf = Buffer { .data = { .ptr = &str_data[1], .len = 3 }, .len = 3 }, + .buf = Buffer { .data = .{ .ptr = &str_data[1], .len = 3 }, .len = 3 }, .row = MatrixRow { .items = [4]i32{1, 2, 3, 4} }, .pt = Point { .x = -5, .y = 15 } }; @@ -259,7 +259,7 @@ fn test_enum_complex() i32 { .Inactive => { ret 2; }, - .Pending[.x = px, .y = py] => { + .Pending[p] => { ret 3; } } @@ -270,7 +270,7 @@ fn test_enum_complex() i32 { match s2 { .Active[val] => {}, .Inactive => { is_inactive = 1; }, - .Pending[.x = px, .y = py] => {} + .Pending[p] => {} } if is_inactive != 1 { ret 4; } @@ -279,9 +279,9 @@ fn test_enum_complex() i32 { match s3 { .Active[val] => { ret 5; }, .Inactive => { ret 6; }, - .Pending[.x = px, .y = py] => { - if px != 7 { ret 7; } - if py != 8 { ret 8; } + .Pending[p] => { + if p.x != 7 { ret 7; } + if p.y != 8 { ret 8; } } }