3428 lines
125 KiB
C
3428 lines
125 KiB
C
// WRITE BY AI
|
||
/* spl_ast.c SPL AST 模块:LL(1) 递归下降解析+ valid/dump/drop */
|
||
|
||
#include "spl_ast.h"
|
||
#include "spl_dumptree.h"
|
||
#include <stdarg.h>
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
|
||
/* 从 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 : "<unknown>";
|
||
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 <expr>; */
|
||
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) : "<null>")
|
||
|
||
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;
|
||
}
|
||
}
|