Files
spl/stage1/spl_ast.c
T

2649 lines
79 KiB
C

#include "spl_ast.h"
#include "spl_dbg.h"
#include "spl_dumptree.h"
#include "spl_tok.h"
#define MOVE_VEC(dst, src) \
do { \
(dst) = (src); \
vec_init(src); \
} while (0)
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;
}
const char *spl_ast_kind_name(spl_ast_node_kind_t kind) {
static const char *const names[] = {
#define X(a, b, c) #c,
SPL_AST_KIND_TABLE
#undef X
};
if ((usize)kind < sizeof(names) / sizeof(names[0]))
return names[kind];
return "?";
}
const char *spl_binary_op_name(spl_binary_op_t op) {
static const char *const names[] = {
#define X(a, b, c, name) #name,
SPL_BINARY_OP_TABLE
#undef X
};
if ((usize)op < sizeof(names) / sizeof(names[0]))
return names[op];
return "?";
}
const char *spl_unary_op_name(spl_unary_op_t op) {
switch (op) {
case SPL_UNARY_OP_MINUS:
return "neg";
case SPL_UNARY_OP_NOT:
return "not";
case SPL_UNARY_OP_BIT_NOT:
return "bitnot";
case SPL_UNARY_OP_ADDRESS:
return "addr";
case SPL_UNARY_OP_DEREF:
return "deref";
default:
return "?";
}
}
typedef struct {
spl_ast_t *ast;
usize pos;
int failed;
int stop_as;
int stop_agg;
} parser_t;
static inline int is_trivia(spl_tok_type_t t) { return t == TOK_ENDLINE || t == TOK_LINE_COMMENT; }
static inline const spl_tok_t *peek(parser_t *p) {
usize n = vec_size(p->ast->input);
usize i = p->pos;
while (i < n && is_trivia(vec_at(p->ast->input, i).type))
i++;
p->pos = i;
if (i >= n)
return NULL;
return &vec_at(p->ast->input, i);
}
static inline const spl_tok_t *advance(parser_t *p) {
const spl_tok_t *t = peek(p);
if (t && t->type != TOK_EOF)
p->pos++;
return t;
}
static inline spl_ast_node_t *node_at(spl_ast_t *ast, spl_ast_node_ref_t ref) {
return &ast->node_buckets.data[ref];
}
static spl_ast_node_ref_t new_node(parser_t *p, spl_ast_node_kind_t kind, const spl_tok_t *tok) {
spl_ast_node_t n = {0};
n.kind = kind;
n.resolved_def_id = 0;
n.resolved_lang_tid = 0;
vec_init(n.attrs);
if (tok) {
n.dbg.dbg_name = spl_ast_kind_name(kind);
n.dbg.fname = tok->fname;
n.dbg.line = (int)tok->line;
n.dbg.col = (int)tok->col;
}
vec_push(p->ast->node_buckets, n);
return p->ast->node_buckets.size - 1;
}
static char *intern_string(spl_ast_t *ast, const char *s, usize len) {
char *copy = sdupn(s, len);
if (!copy)
return NULL;
vec_push(ast->str_buckets, copy);
return copy;
}
#define SET_STR_FIELD(ast, field, tok) \
do { \
(field) = intern_string(ast, (tok)->lexeme, (tok)->len); \
} while (0)
static spl_ast_node_ref_t mk_bin(parser_t *p, const spl_tok_t *loc, spl_binary_op_t 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_BINARY, loc);
spl_ast_node_t *node = node_at(p->ast, ref);
node->binary.op = op;
node->binary.left = left;
node->binary.right = right;
return ref;
}
static spl_ast_node_ref_t mk_unary(parser_t *p, const spl_tok_t *loc, spl_unary_op_t op,
spl_ast_node_ref_t operand) {
spl_ast_node_ref_t ref = new_node(p, SPL_AST_EXPR_UNARY, loc);
spl_ast_node_t *node = node_at(p->ast, ref);
node->unary.op = op;
node->unary.operand = operand;
return 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_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_type_comptime_call(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_var_pattern(parser_t *p);
static spl_ast_node_ref_t parse_match_pattern(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_fields(parser_t *p);
static spl_ast_node_ref_t parse_array_literal(parser_t *p);
static spl_ast_node_ref_t parse_comptime_call(parser_t *p);
static spl_ast_node_ref_t parse_if_expr(parser_t *p);
static spl_ast_node_ref_t parse_while_expr(parser_t *p);
static spl_ast_node_ref_t parse_loop_expr(parser_t *p);
static spl_ast_node_ref_t parse_for_expr(parser_t *p);
static spl_ast_node_ref_t parse_match_expr(parser_t *p);
static spl_ast_node_ref_t parse_ret_stmt(parser_t *p);
static spl_ast_node_ref_t parse_break_stmt(parser_t *p);
static spl_ast_node_ref_t parse_continue_stmt(parser_t *p);
static spl_ast_node_ref_t parse_defer_stmt(parser_t *p);
static spl_ast_node_ref_t parse_comptime_stmt(parser_t *p);
static spl_ast_node_ref_t parse_label(parser_t *p);
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;
if (j >= 3 && buf[0] == '0' && (buf[1] == 'b' || buf[1] == 'B'))
return (isize)strtoll(buf + 2, NULL, 2);
if (j >= 3 && buf[0] == '0' && (buf[1] == 'o' || buf[1] == 'O'))
return (isize)strtoll(buf + 2, NULL, 8);
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;
}
static char *parse_string_literal(parser_t *p, const spl_tok_t *t, usize *out_len) {
const char *src = t->lexeme + 1;
const char *end = t->lexeme + t->len - 1;
usize cap = t->len + 1;
char *tmp = malloc(cap);
if (!tmp) {
*out_len = 0;
return NULL;
}
char *dst = tmp;
while (src < end) {
char c;
if (spl_decode_escape(&src, &c) == 0) {
*dst++ = c;
} else {
*dst++ = *src;
src++;
}
}
*out_len = (usize)(dst - tmp);
char *interned = intern_string(p->ast, tmp, dst - tmp);
free(tmp);
return interned;
}
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 KW_UNDEFINED:
case TOK_IDENT:
case TOK_AT:
case TOK_SHARP:
case TOK_DOLLAR:
case TOK_L_PAREN:
case TOK_L_BRACKET:
case TOK_L_BRACE:
case TOK_DOT:
case TOK_SUB:
case TOK_NOT:
case TOK_BIT_NOT:
case TOK_AND:
case TOK_MUL:
case KW_IF:
case KW_WHILE:
case KW_LOOP:
case KW_FOR:
case KW_MATCH:
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;
}
}
static void parse_tag_list(parser_t *p, spl_ast_node_ref_vec_t *out, int is_external,
int allow_at) {
while (!p->failed) {
const spl_tok_t *t = peek(p);
if (!t || (t->type != TOK_AT && t->type != TOK_SHARP))
return;
if (!allow_at && t->type == TOK_AT)
return;
int is_at = (t->type == TOK_AT);
advance(p);
if (is_external) {
t = peek(p);
if (!t || t->type != TOK_NOT) {
SPL_FATAL(t, "expected '!' in external tag");
return;
}
advance(p);
}
t = peek(p);
if (!t || t->type != TOK_IDENT) {
SPL_FATAL(t, "expected tag name");
return;
}
advance(p);
spl_ast_node_ref_t ref =
new_node(p, is_external ? SPL_AST_EXTERNAL_TAG_ITEM : SPL_AST_TAG_ITEM, t);
spl_ast_node_t *tag = node_at(p->ast, ref);
tag->tag_item.is_at = is_at;
tag->tag_item.is_external = is_external;
SET_STR_FIELD(p->ast, tag->tag_item.name, t);
t = peek(p);
if (t && t->type == TOK_L_PAREN) {
advance(p);
spl_ast_node_ref_vec_t args = parse_expr_list(p);
node_at(p->ast, ref)->tag_item.args = args;
t = peek(p);
if (!t || t->type != TOK_R_PAREN) {
SPL_FATAL(t, "expected ')' in tag args");
return;
}
advance(p);
}
vec_push(*out, ref);
}
}
static void parse_param_list(parser_t *p, spl_ast_node_ref_vec_t *out) {
while (!p->failed) {
const spl_tok_t *t = peek(p);
if (!t)
break;
if (t->type == TOK_R_PAREN) {
break;
} else 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;
}
break;
} else if (t->type == TOK_ELLIPSIS) {
advance(p);
spl_ast_node_ref_t pd = new_node(p, SPL_AST_PARAM_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);
break;
// } else if (t->type == KW_VOID) {
// advance(p);
// t = peek(p);
// if (!t || t->type != TOK_R_PAREN) {
// SPL_FATAL(t, "expected ')' after void");
// }
// break;
} else {
SPL_FATAL(t, "unexpected token in parameter list");
break;
}
}
}
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_tag_list(p, &attr_list, 0, 1);
const spl_tok_t *name = peek(p);
if (!name || (name->type != TOK_IDENT && name->type != KW_UNDERSCORE)) {
SPL_FATAL(name, "expected parameter name or '_'");
return 0;
}
advance(p);
const spl_tok_t *t = peek(p);
if (!t || t->type != TOK_COLON) {
SPL_FATAL(t, "expected ':' in parameter declaration");
return 0;
}
advance(p);
parse_tag_list(p, &attr_list, 0, 0);
spl_ast_node_ref_t type_expr = parse_type_expr(p);
spl_ast_node_ref_t ref = new_node(p, SPL_AST_PARAM_DECL, name);
spl_ast_node_t *param = node_at(p->ast, ref);
MOVE_VEC(param->param_decl.attr_list, attr_list);
SET_STR_FIELD(p->ast, param->param_decl.name, name);
param->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 tag_list) {
const spl_tok_t *kw = peek(p);
if (!kw || kw->type != KW_FN)
return 0;
advance(p);
const spl_tok_t *name = peek(p);
if (!name || name->type != TOK_IDENT) {
SPL_FATAL(name ? name : kw, "expected function name");
return 0;
}
advance(p);
const spl_tok_t *t = peek(p);
if (!t || t->type != TOK_L_PAREN) {
SPL_FATAL(t, "expected '(' in function declaration");
return 0;
}
advance(p);
spl_ast_node_ref_vec_t params;
vec_init(params);
parse_param_list(p, &params);
t = peek(p);
if (!t || t->type != TOK_R_PAREN) {
SPL_FATAL(t, "expected ')' in function declaration");
return 0;
}
advance(p);
spl_ast_node_ref_t ret_type = 0;
t = peek(p);
if (t && t->type == TOK_COLON) {
advance(p);
ret_type = parse_type_expr(p);
t = peek(p);
}
spl_ast_node_ref_t body = 0;
if (t && t->type == TOK_L_BRACE) {
body = parse_block(p);
} else if (t && t->type == TOK_SEMICOLON) {
advance(p);
} else {
SPL_FATAL(t, "function declaration must end with ';' or block");
return 0;
}
spl_ast_node_ref_t ref = new_node(p, SPL_AST_FN_DECL, kw);
spl_ast_node_t *fn = node_at(p->ast, ref);
MOVE_VEC(fn->fn_decl.tag_list, tag_list);
fn->fn_decl.is_comptime = 0;
SET_STR_FIELD(p->ast, fn->fn_decl.name, name);
fn->fn_decl.params = params; /* params 局部变量,直接转移 */
fn->fn_decl.ret_type = ret_type;
fn->fn_decl.body = body;
return ref;
}
static spl_ast_node_ref_t parse_type_decl(parser_t *p, spl_ast_node_ref_vec_t tag_list) {
const spl_tok_t *kw = peek(p);
if (!kw || kw->type != KW_TYPE)
return 0;
advance(p);
const spl_tok_t *name = peek(p);
if (!name || name->type != TOK_IDENT) {
SPL_FATAL(name ? name : kw, "expected type name");
return 0;
}
advance(p);
const spl_tok_t *t = peek(p);
if (!t || t->type != TOK_ASSIGN) {
SPL_FATAL(t, "expected '=' in type declaration");
return 0;
}
advance(p);
spl_ast_node_ref_t ref = new_node(p, SPL_AST_TYPE_DECL, kw);
spl_ast_node_t *td = node_at(p->ast, ref);
MOVE_VEC(td->type_decl.attr_list, tag_list);
SET_STR_FIELD(p->ast, td->type_decl.name, name);
spl_ast_node_ref_t type_expr = parse_type_expr(p);
node_at(p->ast, ref)->type_decl.type_expr = type_expr;
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_const_decl(parser_t *p, spl_ast_node_ref_vec_t tag_list,
int is_const) {
const spl_tok_t *kw = peek(p);
if (!kw)
return 0;
if (is_const && kw->type != KW_CONST)
return 0;
if (!is_const && kw->type != KW_VAR)
return 0;
advance(p);
spl_ast_node_ref_t pattern = parse_var_pattern(p);
if (!pattern)
return 0;
spl_ast_node_ref_t type_expr = 0;
spl_ast_node_ref_t init_expr = 0;
const spl_tok_t *t = peek(p);
if (t && t->type == TOK_COLON) {
advance(p);
type_expr = parse_type_expr(p);
t = peek(p);
if (t && t->type == TOK_ASSIGN) {
advance(p);
init_expr = parse_expr(p);
}
} else if (t && t->type == TOK_ASSIGN) {
advance(p);
init_expr = parse_expr(p);
}
spl_ast_node_ref_t ref = new_node(p, is_const ? SPL_AST_CONST_DECL : SPL_AST_VAR_DECL, kw);
spl_ast_node_t *vc = node_at(p->ast, ref);
MOVE_VEC(vc->var_const_decl.attr_list, tag_list);
vc->var_const_decl.pattern = pattern;
vc->var_const_decl.type = type_expr;
vc->var_const_decl.init_expr = init_expr;
t = peek(p);
if (t && t->type == TOK_SEMICOLON) {
advance(p);
}
return ref;
}
static spl_ast_node_ref_t parse_member_decl(parser_t *p, spl_ast_node_ref_vec_t tag_list) {
const spl_tok_t *name = peek(p);
if (!name || name->type != TOK_IDENT)
return 0;
advance(p);
spl_ast_node_ref_t type_expr = 0;
spl_ast_node_ref_t init_expr = 0;
const spl_tok_t *t = peek(p);
if (t && t->type == TOK_COLON) {
advance(p);
parse_tag_list(p, &tag_list, 0, 0);
type_expr = parse_type_expr(p);
t = peek(p);
if (t && t->type == TOK_ASSIGN) {
advance(p);
init_expr = parse_expr(p);
}
} else if (t && t->type == TOK_ASSIGN) {
advance(p);
init_expr = parse_expr(p);
}
spl_ast_node_ref_t ref = new_node(p, SPL_AST_MEMBER_DECL, name);
spl_ast_node_t *md = node_at(p->ast, ref);
MOVE_VEC(md->member_decl.attr_list, tag_list);
SET_STR_FIELD(p->ast, md->member_decl.name, name);
md->member_decl.type_expr = type_expr;
md->member_decl.init_expr = init_expr;
t = peek(p);
if (t && (t->type == TOK_COMMA || t->type == TOK_SEMICOLON)) {
advance(p);
}
return ref;
}
static spl_ast_node_ref_t parse_comptime_decl(parser_t *p) {
const spl_tok_t *kw = peek(p);
if (!kw || kw->type != KW_COMPTIME)
return 0;
advance(p);
spl_ast_node_ref_t body = 0;
const spl_tok_t *t = peek(p);
if (t && t->type == TOK_L_BRACE) {
body = parse_block(p);
} else {
body = parse_expr(p);
}
spl_ast_node_ref_t ref = new_node(p, SPL_AST_COMPTIME_DECL, kw);
node_at(p->ast, ref)->comptime_decl.body = body;
return ref;
}
static spl_ast_node_ref_t parse_container_declaration(parser_t *p) {
spl_ast_node_ref_vec_t tag_list;
vec_init(tag_list);
parse_tag_list(p, &tag_list, 0, 0); /* 只解析普通 tag */
const spl_tok_t *t = peek(p);
if (!t || t->type == TOK_EOF) {
vec_free(tag_list);
return 0;
}
switch (t->type) {
case KW_FN:
return parse_fn_decl(p, tag_list);
case KW_TYPE:
return parse_type_decl(p, tag_list);
case KW_VAR:
return parse_var_const_decl(p, tag_list, 0);
case KW_CONST:
return parse_var_const_decl(p, tag_list, 1);
case TOK_IDENT:
return parse_member_decl(p, tag_list);
case KW_COMPTIME:
vec_free(tag_list); /* comptime decl 不使用 tag_list */
return parse_comptime_decl(p);
case TOK_L_BRACE: {
spl_ast_node_ref_t block = parse_block(p);
spl_ast_node_ref_t ref = new_node(p, SPL_AST_TOP_LEVEL_BLOCK, t);
spl_ast_node_t *tb = node_at(p->ast, ref);
MOVE_VEC(tb->top_level_block.tag_list, tag_list);
tb->top_level_block.block = block;
return ref;
}
default:
SPL_FATAL(t, "unrecognized declaration");
vec_free(tag_list);
return 0;
}
}
static spl_ast_node_ref_t parse_var_pattern_atom(parser_t *p) {
const spl_tok_t *t = peek(p);
if (!t)
return 0;
if (t->type == TOK_IDENT) {
advance(p);
spl_ast_node_ref_t ref = new_node(p, SPL_AST_PATTERN_IDENT, t);
SET_STR_FIELD(p->ast, node_at(p->ast, ref)->pattern_ident.name, t);
return ref;
} else if (t->type == KW_UNDERSCORE) {
advance(p);
return new_node(p, SPL_AST_PATTERN_UNDERSCORE, t);
} else if (t->type == TOK_DOT) {
advance(p);
const spl_tok_t *name = peek(p);
if (!name || name->type != TOK_IDENT) {
SPL_FATAL(name, "expected identifier after '.' in pattern");
return 0;
}
advance(p);
spl_ast_node_ref_t ref = new_node(p, SPL_AST_PATTERN_DOT, t);
spl_ast_node_t *pat = node_at(p->ast, ref);
SET_STR_FIELD(p->ast, pat->pattern_dot.name, name);
if (peek(p) && peek(p)->type == TOK_L_BRACKET) {
advance(p);
spl_ast_node_ref_t sub = parse_var_pattern(p);
node_at(p->ast, ref)->pattern_dot.sub_pattern = sub;
t = peek(p);
if (!t || t->type != TOK_R_BRACKET) {
SPL_FATAL(t, "expected ']' in pattern");
return 0;
}
advance(p);
}
return ref;
} else if (t->type == TOK_L_PAREN) {
advance(p);
spl_ast_node_ref_t inner = parse_var_pattern(p);
t = peek(p);
if (!t || t->type != TOK_R_PAREN) {
SPL_FATAL(t, "expected ')' in pattern");
return 0;
}
advance(p);
spl_ast_node_ref_t ref = new_node(p, SPL_AST_PATTERN_TUPLE, t);
spl_ast_node_t *tuple = node_at(p->ast, ref);
vec_init(tuple->pattern_tuple.patterns);
if (node_at(p->ast, inner)->kind == SPL_AST_PATTERN_LIST) {
tuple->pattern_tuple.patterns = node_at(p->ast, inner)->pattern_list.patterns;
} else {
vec_push(tuple->pattern_tuple.patterns, inner);
}
return ref;
} else {
SPL_FATAL(t, "unexpected token in pattern");
return 0;
}
}
static spl_ast_node_ref_t parse_var_pattern(parser_t *p) {
spl_ast_node_ref_vec_t patterns;
vec_init(patterns);
while (!p->failed) {
spl_ast_node_ref_t atom = parse_var_pattern_atom(p);
if (!atom)
break;
vec_push(patterns, atom);
const spl_tok_t *t = peek(p);
if (t && t->type == TOK_COMMA) {
advance(p);
continue;
}
break;
}
if (patterns.size == 0)
return 0;
if (patterns.size == 1) {
spl_ast_node_ref_t ref = patterns.data[0];
vec_free(patterns);
return ref;
}
spl_ast_node_ref_t ref = new_node(p, SPL_AST_PATTERN_LIST, peek(p));
node_at(p->ast, ref)->pattern_list.patterns = patterns;
return ref;
}
static spl_ast_node_ref_t parse_match_pattern(parser_t *p) {
const spl_tok_t *t = peek(p);
if (!t)
return 0;
/* 如果当前 token 是模式专用起始符,按变量模式解析;否则按表达式解析 */
if (t->type == KW_UNDERSCORE || t->type == TOK_DOT || t->type == TOK_L_PAREN) {
return parse_var_pattern(p);
} else {
/* 表达式模式:字面量、标识符、块、if 等 */
return parse_expr(p);
}
}
static spl_ast_node_ref_t parse_type_base(parser_t *p) {
const spl_tok_t *bt = peek(p);
if (!bt) {
SPL_FATAL(bt, "expected type");
return 0;
}
/* 函数类型 */
if (bt->type == KW_FN) {
advance(p);
const spl_tok_t *t = peek(p);
if (!t || t->type != TOK_L_PAREN) {
SPL_FATAL(t, "expected '(' in function type");
return 0;
}
advance(p);
spl_ast_node_ref_t ref = new_node(p, SPL_AST_TYPE_FN, bt);
spl_ast_node_t *fn_type = node_at(p->ast, ref);
fn_type->type_fn.is_comptime = 0;
vec_init(fn_type->type_fn.params);
while (!p->failed && peek(p) && peek(p)->type != TOK_R_PAREN) {
spl_ast_node_ref_t pt = parse_type_expr(p);
fn_type = node_at(p->ast, ref); // 防悬空
vec_push(fn_type->type_fn.params, pt);
if (peek(p) && peek(p)->type == TOK_COMMA) {
advance(p);
continue;
}
break;
}
t = peek(p);
if (!t || t->type != TOK_R_PAREN) {
SPL_FATAL(t, "expected ')' in function type");
return 0;
}
advance(p);
if (peek(p) && peek(p)->type == TOK_COLON) {
advance(p);
spl_ast_node_ref_t ret = parse_type_expr(p);
node_at(p->ast, ref)->type_fn.ret_type = ret;
}
return ref;
}
/* 聚合类型:struct / union / enum / shape */
if (bt->type == KW_STRUCT || bt->type == KW_UNION || bt->type == KW_ENUM ||
bt->type == KW_SHAPE) {
const char *kind_name = (bt->type == KW_STRUCT) ? "struct"
: (bt->type == KW_UNION) ? "union"
: (bt->type == KW_ENUM) ? "enum"
: "shape";
advance(p);
const spl_tok_t *t = peek(p);
if (!t || t->type != TOK_L_BRACE) {
SPL_FATAL(t, "expected '{' in aggregate type");
return 0;
}
advance(p);
spl_ast_node_ref_t ref = new_node(p, SPL_AST_TYPE_AGGREGATE, bt);
spl_ast_node_t *agg = node_at(p->ast, ref);
agg->type_aggregate.kind_name = kind_name;
vec_init(agg->type_aggregate.members); // 存放真正的容器声明节点
/* 解析容器成员,直到 '}' */
while (!p->failed) {
t = peek(p);
if (!t || t->type == TOK_EOF) {
SPL_FATAL(t, "aggregate type missing '}'");
return ref;
}
if (t->type == TOK_R_BRACE) {
advance(p);
break;
}
spl_ast_node_ref_t member = parse_container_declaration(p);
if (member) {
agg = node_at(p->ast, ref); // 防止节点数组扩容导致悬空
vec_push(agg->type_aggregate.members, member);
}
/* 跳过可选分隔符 */
t = peek(p);
if (t && (t->type == TOK_COMMA || t->type == TOK_SEMICOLON)) {
advance(p);
}
}
return ref;
}
/* 内建类型和普通路径 */
if (bt->type == TOK_IDENT || bt->type == KW_BOOL) {
const char *name = bt->lexeme;
usize len = bt->len;
/* 内建类型关键字 */
if ((len == 2 && strncmp(name, "i8", 2) == 0) ||
(len == 2 && strncmp(name, "u8", 2) == 0) ||
(len == 3 && strncmp(name, "i16", 3) == 0) ||
(len == 3 && strncmp(name, "u16", 3) == 0) ||
(len == 3 && strncmp(name, "i32", 3) == 0) ||
(len == 3 && strncmp(name, "u32", 3) == 0) ||
(len == 3 && strncmp(name, "i64", 3) == 0) ||
(len == 3 && strncmp(name, "u64", 3) == 0) ||
(len == 5 && strncmp(name, "isize", 5) == 0) ||
(len == 5 && strncmp(name, "usize", 5) == 0) ||
(len == 3 && strncmp(name, "f32", 3) == 0) ||
(len == 3 && strncmp(name, "f64", 3) == 0) ||
(len == 4 && strncmp(name, "bool", 4) == 0)
// (len == 4 && strncmp(name, "void", 4) == 0) ||
// (len == 3 && strncmp(name, "any", 3) == 0)
) {
advance(p);
spl_ast_node_ref_t ref = new_node(p, SPL_AST_TYPE_PATH, bt);
spl_ast_node_t *tp = node_at(p->ast, ref);
SET_STR_FIELD(p->ast, tp->type_path.name, bt);
vec_init(tp->type_path.generic_args);
return ref;
}
/* 普通类型路径 */
advance(p);
spl_ast_node_ref_t ref = new_node(p, SPL_AST_TYPE_PATH, bt);
spl_ast_node_t *tp = node_at(p->ast, ref);
SET_STR_FIELD(p->ast, tp->type_path.name, bt);
vec_init(tp->type_path.generic_args);
while (peek(p) && peek(p)->type == TOK_DOT) {
advance(p);
const spl_tok_t *seg = peek(p);
if (!seg || seg->type != TOK_IDENT) {
SPL_FATAL(seg, "expected identifier after '.' in type path");
break;
}
advance(p);
}
return ref;
}
if (bt->type == TOK_AT || bt->type == TOK_SHARP) {
return parse_type_comptime_call(p);
}
if (bt->type == KW_ANY) {
advance(p);
spl_ast_node_ref_t ref = new_node(p, SPL_AST_TYPE_PATH, bt);
spl_ast_node_t *tp = node_at(p->ast, ref);
SET_STR_FIELD(p->ast, tp->type_path.name, bt);
vec_init(tp->type_path.generic_args);
return ref;
}
SPL_FATAL(bt, "unrecognized type");
return 0;
}
static spl_ast_node_ref_t parse_type_comptime_call(parser_t *p) {
const spl_tok_t *start = peek(p);
if (!start || (start->type != TOK_AT && start->type != TOK_SHARP))
return 0;
advance(p);
const spl_tok_t *name = peek(p);
if (!name || name->type != TOK_IDENT) {
SPL_FATAL(name, "expected comptime name");
return 0;
}
advance(p);
spl_ast_node_ref_vec_t args;
vec_init(args);
const spl_tok_t *t = peek(p);
if (t && t->type == TOK_L_PAREN) {
advance(p);
args = parse_expr_list(p);
t = peek(p);
if (!t || t->type != TOK_R_PAREN) {
SPL_FATAL(t, "expected ')' in comptime type");
vec_free(args);
return 0;
}
advance(p);
}
spl_ast_node_ref_t ref = new_node(p, SPL_AST_TYPE_COMPTIME_CALL, start);
spl_ast_node_t *cc = node_at(p->ast, ref);
SET_STR_FIELD(p->ast, cc->type_comptime_call.name, name);
cc->type_comptime_call.args = args;
return ref;
}
static spl_ast_node_ref_t parse_type_expr(parser_t *p) {
const spl_tok_t *t = peek(p);
if (!t) {
SPL_FATAL(t, "expected type");
return 0;
}
if (t->type == TOK_MUL) {
advance(p);
spl_ast_node_ref_t ref = new_node(p, SPL_AST_TYPE_POINTER, t);
spl_ast_node_ref_t pointee = parse_type_expr(p);
node_at(p->ast, ref)->type_pointer.pointee = pointee;
return ref;
}
if (t->type == TOK_L_BRACKET) {
advance(p);
bool has_len = false;
isize len = 0;
bool is_underscore = false;
t = peek(p);
if (t && t->type == TOK_INT_LITERAL) {
len = parse_int_literal(t);
has_len = true;
advance(p);
} else if (t && t->type == KW_UNDERSCORE) {
has_len = true;
is_underscore = true;
advance(p);
}
t = peek(p);
if (!t || t->type != TOK_R_BRACKET) {
SPL_FATAL(t, "expected ']' in array type");
return 0;
}
advance(p);
spl_ast_node_ref_t elem_type = parse_type_expr(p);
spl_ast_node_ref_t ref;
if (has_len) {
ref = new_node(p, SPL_AST_TYPE_ARRAY, t);
spl_ast_node_t *arr = node_at(p->ast, ref);
arr->type_array.has_len = true;
arr->type_array.len = len;
arr->type_array.is_underscore_len = is_underscore;
arr->type_array.elem_type = elem_type;
} else {
ref = new_node(p, SPL_AST_TYPE_SLICE, t);
node_at(p->ast, ref)->type_slice.elem_type = elem_type;
}
return ref;
}
if (t->type == TOK_L_PAREN) {
advance(p);
spl_ast_node_ref_vec_t types;
vec_init(types);
if (peek(p) && peek(p)->type == TOK_R_PAREN) {
advance(p);
spl_ast_node_ref_t ref = new_node(p, SPL_AST_TYPE_PAREN, t);
node_at(p->ast, ref)->type_paren.types = types;
return ref;
}
spl_ast_node_ref_t first = parse_type_expr(p);
vec_push(types, first);
while (peek(p) && peek(p)->type == TOK_COMMA) {
advance(p);
if (peek(p) && peek(p)->type == TOK_R_PAREN)
break;
spl_ast_node_ref_t next = parse_type_expr(p);
vec_push(types, next);
}
t = peek(p);
if (!t || t->type != TOK_R_PAREN) {
SPL_FATAL(t, "expected ')' in parenthesized type");
return 0;
}
advance(p);
if (types.size == 1) {
vec_free(types);
return first;
}
spl_ast_node_ref_t ref = new_node(p, SPL_AST_TYPE_PAREN, t);
node_at(p->ast, ref)->type_paren.types = types;
return ref;
}
return parse_type_base(p);
}
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;
const 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;
}
static spl_ast_node_ref_vec_t parse_aggregate_fields(parser_t *p) {
spl_ast_node_ref_vec_t fields;
vec_init(fields);
const spl_tok_t *t = peek(p);
if (!t || t->type != TOK_L_BRACE) {
SPL_FATAL(t, "expected '{' in aggregate literal");
return fields; // 空 vec,调用者处理
}
advance(p); // 消费 '{'
while (!p->failed) {
t = peek(p);
if (!t || t->type == TOK_R_BRACE || t->type == TOK_EOF)
break;
// 字段必须由 '.' 开头
if (t->type != TOK_DOT) {
SPL_FATAL(t, "expected '.' in aggregate field initializer");
break;
}
advance(p);
t = peek(p);
if (!t || t->type != TOK_IDENT) {
SPL_FATAL(t, "expected field name after '.'");
break;
}
advance(p);
// 创建 struct_init_item 节点
spl_ast_node_ref_t item_ref = new_node(p, SPL_AST_STRUCT_INIT_ITEM, t);
spl_ast_node_t *item = node_at(p->ast, item_ref);
SET_STR_FIELD(p->ast, item->struct_init_item.name, t);
item->struct_init_item.expr = 0;
// 可选:= 表达式
if (peek(p) && peek(p)->type == TOK_ASSIGN) {
advance(p);
spl_ast_node_ref_t expr_ref = parse_expr(p);
item = node_at(p->ast, item_ref); // 重新获取,防扩容
item->struct_init_item.expr = expr_ref;
}
vec_push(fields, item_ref);
// 分隔符
t = peek(p);
if (t && (t->type == TOK_COMMA || t->type == TOK_SEMICOLON)) {
advance(p);
continue;
}
break;
}
t = peek(p);
if (!t || t->type != TOK_R_BRACE) {
SPL_FATAL(t, "expected '}' in aggregate literal");
} else {
advance(p); // 消费 '}'
}
return fields;
}
typedef struct {
int prec;
char assoc; // 'L', 'R', 'N'
} op_info_t;
static op_info_t binary_op_info[] = {
#define X(name, prec, assoc, str) {prec, assoc},
SPL_BINARY_OP_TABLE
#undef X
};
static spl_binary_op_t token_to_binary_op(spl_tok_type_t tt) {
switch (tt) {
case TOK_ASSIGN:
return SPL_OP_ASSIGN;
case TOK_ASSIGN_ADD:
return SPL_OP_ASSIGN_ADD;
case TOK_ASSIGN_SUB:
return SPL_OP_ASSIGN_SUB;
case TOK_ASSIGN_MUL:
return SPL_OP_ASSIGN_MUL;
case TOK_ASSIGN_DIV:
return SPL_OP_ASSIGN_DIV;
case TOK_ASSIGN_MOD:
return SPL_OP_ASSIGN_MOD;
case TOK_ASSIGN_AND:
return SPL_OP_ASSIGN_AND;
case TOK_ASSIGN_OR:
return SPL_OP_ASSIGN_OR;
case TOK_ASSIGN_XOR:
return SPL_OP_ASSIGN_XOR;
case TOK_ASSIGN_L_SH:
return SPL_OP_ASSIGN_LSHIFT;
case TOK_ASSIGN_R_SH:
return SPL_OP_ASSIGN_RSHIFT;
case TOK_OR_OR:
return SPL_OP_BOOL_OR;
case TOK_AND_AND:
return SPL_OP_BOOL_AND;
case TOK_OR:
return SPL_OP_BIT_OR;
case TOK_XOR:
return SPL_OP_BIT_XOR;
case TOK_AND:
return SPL_OP_BIT_AND;
case TOK_EQ:
return SPL_OP_CMP_EQ;
case TOK_NEQ:
return SPL_OP_CMP_NE;
case TOK_LE:
return SPL_OP_CMP_LE;
case TOK_GE:
return SPL_OP_CMP_GE;
case TOK_LT:
return SPL_OP_CMP_LT;
case TOK_GT:
return SPL_OP_CMP_GT;
case TOK_LEFT_ARROW:
return SPL_OP_ARROW_LEFT;
case TOK_RIGHT_ARROW:
return SPL_OP_ARROW_RIGHT;
case TOK_FAT_ARROW_BOTH:
return SPL_OP_ARROW_FAT_BOTH;
case TOK_ARROW_BOTH:
return SPL_OP_ARROW_THIN_BOTH;
case TOK_RIGHT_FAT_ARROW:
return SPL_OP_ARROW_FAT_RIGHT;
case TOK_RANGE:
return SPL_OP_RANGE;
case TOK_L_SH:
return SPL_OP_LSHIFT;
case TOK_R_SH:
return SPL_OP_RSHIFT;
case TOK_ADD:
return SPL_OP_ADD;
case TOK_SUB:
return SPL_OP_SUB;
case TOK_MUL:
return SPL_OP_MUL;
case TOK_DIV:
return SPL_OP_DIV;
case TOK_MOD:
return SPL_OP_MOD;
default:
return (spl_binary_op_t)-1; // 无效
}
}
static spl_ast_node_ref_t parse_expr_prec(parser_t *p, int min_prec) {
spl_ast_node_ref_t left = parse_prefix(p);
if (p->failed)
return left;
while (!p->failed) {
const spl_tok_t *t = peek(p);
if (!t)
break;
spl_binary_op_t op = token_to_binary_op(t->type);
if (op == (spl_binary_op_t)-1)
break;
op_info_t info = binary_op_info[op];
if (info.prec < min_prec)
break;
advance(p);
if (op == SPL_OP_RANGE) {
spl_ast_node_ref_t right = 0;
if (peek(p) && can_start_expr(peek(p)->type))
right = parse_expr_prec(p, info.prec + 1); // 最小优先级 = range 优先级 + 1
left = mk_bin(p, t, op, left, right);
continue;
}
int next_min = info.prec;
if (info.assoc == 'R') {
next_min = info.prec;
} else {
next_min = info.prec + 1;
}
spl_ast_node_ref_t right = parse_expr_prec(p, next_min);
left = mk_bin(p, t, op, left, right);
}
return left;
}
static spl_ast_node_ref_t parse_expr(parser_t *p) {
return parse_expr_prec(p, 1); // 最低优先级
}
static spl_ast_node_ref_t parse_prefix(parser_t *p) {
const spl_tok_t *t = peek(p);
if (!t)
return 0;
if (t->type == TOK_DOLLAR) {
spl_ast_node_ref_t label = parse_label(p);
const spl_tok_t *kw = peek(p);
spl_ast_node_ref_t ref = 0;
if (kw && kw->type == KW_WHILE)
ref = parse_while_expr(p);
else if (kw && kw->type == KW_LOOP)
ref = parse_loop_expr(p);
else if (kw && kw->type == KW_FOR)
ref = parse_for_expr(p);
else {
SPL_FATAL(kw, "expected while/loop/for after label");
return 0;
}
if (!ref)
return 0;
spl_ast_node_t *n = node_at(p->ast, ref);
switch (n->kind) {
case SPL_AST_EXPR_WHILE:
n->while_expr.label = label;
break;
case SPL_AST_EXPR_LOOP:
n->loop_expr.label = label;
break;
case SPL_AST_EXPR_FOR:
n->for_expr.label = label;
break;
default:
break;
}
return ref;
}
spl_unary_op_t uop;
switch (t->type) {
case TOK_SUB:
uop = SPL_UNARY_OP_MINUS;
break;
case TOK_NOT:
uop = SPL_UNARY_OP_NOT;
break;
case TOK_BIT_NOT:
uop = SPL_UNARY_OP_BIT_NOT;
break;
case TOK_AND:
uop = SPL_UNARY_OP_ADDRESS;
break;
default:
return parse_postfix(p);
}
advance(p);
spl_ast_node_ref_t operand = parse_prefix(p);
return mk_unary(p, t, uop, operand);
}
static spl_ast_node_ref_t parse_postfix(parser_t *p) {
spl_ast_node_ref_t expr = parse_primary(p);
while (!p->failed) {
const spl_tok_t *t = peek(p);
if (!t)
break;
if (t->type == TOK_L_PAREN) {
advance(p);
spl_ast_node_ref_t ref = new_node(p, SPL_AST_EXPR_CALL, t);
node_at(p->ast, ref)->call.callee = expr;
spl_ast_node_ref_vec_t args = parse_expr_list(p);
node_at(p->ast, ref)->call.args = args;
t = peek(p);
if (!t || t->type != TOK_R_PAREN) {
SPL_FATAL(t, "expected ')' in call");
return expr;
}
advance(p);
expr = ref;
} else if (t->type == TOK_DOT) {
advance(p);
const spl_tok_t *next = peek(p);
if (!next)
break;
if (next->type == TOK_IDENT) {
advance(p);
spl_ast_node_ref_t ref = new_node(p, SPL_AST_EXPR_FIELD, t);
node_at(p->ast, ref)->field.obj = expr;
SET_STR_FIELD(p->ast, node_at(p->ast, ref)->field.field, next);
expr = ref;
} else if (next->type == TOK_MUL) {
advance(p);
spl_ast_node_ref_t ref = mk_unary(p, t, SPL_UNARY_OP_DEREF, expr);
expr = ref;
} else {
SPL_FATAL(next, "expected identifier or '*' after '.'");
break;
}
} else if (t->type == TOK_L_BRACKET) {
advance(p);
int range_prec = binary_op_info[SPL_OP_RANGE].prec;
spl_ast_node_ref_t begin = parse_expr_prec(p, range_prec + 1);
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);
t = peek(p);
if (!t || t->type != TOK_R_BRACKET) {
SPL_FATAL(t, "expected ']' in slice");
return expr;
}
advance(p);
spl_ast_node_ref_t ref = new_node(p, SPL_AST_EXPR_SLICE, t);
node_at(p->ast, ref)->slice.obj = expr;
node_at(p->ast, ref)->slice.begin = begin;
node_at(p->ast, ref)->slice.end = end;
expr = ref;
} else {
t = peek(p);
if (!t || t->type != TOK_R_BRACKET) {
SPL_FATAL(t, "expected ']' in index");
return expr;
}
advance(p);
spl_ast_node_ref_t ref = new_node(p, SPL_AST_EXPR_INDEX, t);
node_at(p->ast, ref)->index.obj = expr;
node_at(p->ast, ref)->index.index = begin;
expr = ref;
}
} else if (t->type == KW_AS && !p->stop_as) {
advance(p);
spl_ast_node_ref_t type = parse_type_expr(p);
spl_ast_node_ref_t ref = new_node(p, SPL_AST_EXPR_CAST, t);
node_at(p->ast, ref)->cast.expr = expr;
node_at(p->ast, ref)->cast.type = type;
expr = ref;
} else {
break;
}
}
return expr;
}
static spl_ast_node_ref_t parse_primary(parser_t *p) {
const spl_tok_t *t = peek(p);
if (!t) {
SPL_FATAL(t, "unexpected end of expression");
return 0;
}
switch (t->type) {
case TOK_INT_LITERAL: {
advance(p);
spl_ast_node_ref_t ref = new_node(p, SPL_AST_EXPR_INT_LIT, t);
node_at(p->ast, ref)->int_lit.bits = parse_int_literal(t); // FIXME
return ref;
}
case TOK_FLOAT_LITERAL: {
advance(p);
spl_ast_node_ref_t ref = new_node(p, SPL_AST_EXPR_FLOAT_LIT, t);
node_at(p->ast, ref)->float_lit.bits = parse_float_literal(t); // FIXME
return ref;
}
case TOK_CHAR_LITERAL: {
advance(p);
spl_ast_node_ref_t ref = new_node(p, SPL_AST_EXPR_CHAR_LIT, t);
node_at(p->ast, ref)->char_lit.val = parse_char_literal(t);
return ref;
}
case TOK_STRING_LITERAL: {
advance(p);
spl_ast_node_ref_t ref = new_node(p, SPL_AST_EXPR_STRING_LIT, t);
usize len = 0;
const char *data = parse_string_literal(p, t, &len);
spl_ast_node_t *node = node_at(p->ast, ref);
node->string_lit.data = data;
node->string_lit.len = len;
return ref;
}
case KW_TRUE:
case KW_FALSE: {
advance(p);
spl_ast_node_ref_t ref = new_node(p, SPL_AST_EXPR_BOOL_LIT, t);
node_at(p->ast, ref)->bool_lit.val = (t->type == KW_TRUE);
return ref;
}
case KW_NULL: {
advance(p);
return new_node(p, SPL_AST_EXPR_NULL_LIT, t);
}
case KW_UNDEFINED: {
advance(p);
return new_node(p, SPL_AST_EXPR_UNDEFINED_LIT, t);
}
case TOK_IDENT: {
advance(p);
if (peek(p) && peek(p)->type == TOK_L_BRACE && !p->stop_agg) {
spl_ast_node_ref_t ref = new_node(p, SPL_AST_EXPR_AGGREGATE_LIT, t);
spl_ast_node_t *agg = node_at(p->ast, ref);
agg->aggregate_literal.name = intern_string(p->ast, t->lexeme, t->len);
spl_ast_node_ref_vec_t fields = parse_aggregate_fields(p);
node_at(p->ast, ref)->aggregate_literal.fields = fields;
return ref;
}
/* 普通标识符 */
spl_ast_node_ref_t ref = new_node(p, SPL_AST_EXPR_IDENT, t);
node_at(p->ast, ref)->ident.name = intern_string(p->ast, t->lexeme, t->len);
return ref;
}
case TOK_DOT: {
advance(p);
const spl_tok_t *nx = peek(p);
if (!nx) {
SPL_FATAL(t, "unexpected '.'");
return 0;
}
if (nx->type == TOK_L_BRACE && !p->stop_agg) {
spl_ast_node_ref_t ref = new_node(p, SPL_AST_EXPR_AGGREGATE_LIT, t);
node_at(p->ast, ref)->aggregate_literal.name = NULL;
spl_ast_node_ref_vec_t fields = parse_aggregate_fields(p);
node_at(p->ast, ref)->aggregate_literal.fields = fields;
return ref;
} else if (nx->type == TOK_L_BRACKET) {
// 匿名数组字面量:. [ elems ]
advance(p); // 消费 '['
// 先解析元素列表,避免节点扩容导致 arr 指针失效
spl_ast_node_ref_vec_t elems = parse_expr_list(p);
const spl_tok_t *close = peek(p);
if (!close || close->type != TOK_R_BRACKET) {
SPL_FATAL(close, "expected ']'");
return 0;
}
advance(p); // 消费 ']'
spl_ast_node_ref_t ref = new_node(p, SPL_AST_EXPR_ARRAY_LIT, t);
spl_ast_node_t *arr = node_at(p->ast, ref);
arr->array_literal.elems = elems;
arr->array_literal.elem_type = 0;
return ref;
} else {
SPL_FATAL(nx, "expected '{' or '[' after '.'");
return 0;
}
}
case TOK_L_BRACKET:
return parse_array_literal(p);
case TOK_L_BRACE:
return parse_block(p);
case TOK_AT:
case TOK_SHARP:
return parse_comptime_call(p);
case TOK_L_PAREN: {
advance(p);
spl_ast_node_ref_vec_t elems;
vec_init(elems);
if (peek(p) && peek(p)->type == TOK_R_PAREN) {
advance(p);
spl_ast_node_ref_t ref = new_node(p, SPL_AST_EXPR_TUPLE, t);
node_at(p->ast, ref)->tuple.elems = elems;
return ref;
}
spl_ast_node_ref_t first = parse_expr(p);
if (peek(p) && peek(p)->type == TOK_COMMA) {
vec_push(elems, first);
while (peek(p) && peek(p)->type == TOK_COMMA) {
advance(p);
if (peek(p) && peek(p)->type == TOK_R_PAREN)
break;
vec_push(elems, parse_expr(p));
}
t = peek(p);
if (!t || t->type != TOK_R_PAREN) {
SPL_FATAL(t, "expected ')'");
return 0;
}
advance(p);
spl_ast_node_ref_t ref = new_node(p, SPL_AST_EXPR_TUPLE, t);
node_at(p->ast, ref)->tuple.elems = elems;
return ref;
} else {
t = peek(p);
if (!t || t->type != TOK_R_PAREN) {
SPL_FATAL(t, "expected ')'");
return 0;
}
advance(p);
vec_free(elems);
return first;
}
}
case KW_IF:
return parse_if_expr(p);
case KW_WHILE:
return parse_while_expr(p);
case KW_LOOP:
return parse_loop_expr(p);
case KW_FOR:
return parse_for_expr(p);
case KW_MATCH:
return parse_match_expr(p);
default:
SPL_FATAL(t, "unrecognized expression");
return 0;
}
}
static spl_ast_node_ref_t parse_array_literal(parser_t *p) {
const spl_tok_t *start = peek(p);
if (!start || start->type != TOK_L_BRACKET)
return 0;
advance(p);
bool has_len = false;
isize len = 0;
bool is_underscore = false;
const spl_tok_t *t = peek(p);
if (t && t->type == TOK_INT_LITERAL) {
len = parse_int_literal(t);
has_len = true;
advance(p);
} else if (t && t->type == KW_UNDERSCORE) {
has_len = true;
is_underscore = true;
advance(p);
}
t = peek(p);
if (!t || t->type != TOK_R_BRACKET) {
SPL_FATAL(t, "expected ']' in array literal");
return 0;
}
advance(p);
spl_ast_node_ref_t elem_type = parse_type_expr(p);
t = peek(p);
if (!t || t->type != TOK_L_BRACKET) {
SPL_FATAL(t, "expected '[' in array literal");
return 0;
}
advance(p);
spl_ast_node_ref_vec_t elems = parse_expr_list(p);
t = peek(p);
if (!t || t->type != TOK_R_BRACKET) {
SPL_FATAL(t, "expected ']' in array literal");
return 0;
}
advance(p);
spl_ast_node_ref_t ref = new_node(p, SPL_AST_EXPR_ARRAY_LIT, start);
spl_ast_node_t *arr = node_at(p->ast, ref);
arr->array_literal.has_len = has_len;
arr->array_literal.len = len;
arr->array_literal.is_underscore_len = is_underscore;
arr->array_literal.elem_type = elem_type;
arr->array_literal.elems = elems; // elems 是局部 vec,直接转移
return ref;
}
static spl_ast_node_ref_t parse_comptime_call(parser_t *p) {
const spl_tok_t *start = peek(p);
if (!start || (start->type != TOK_AT && start->type != TOK_SHARP))
return 0;
advance(p);
const spl_tok_t *name = peek(p);
if (!name || name->type != TOK_IDENT) {
SPL_FATAL(name, "expected comptime name");
return 0;
}
advance(p);
spl_ast_node_ref_vec_t args;
vec_init(args);
int has_paren = 0;
const spl_tok_t *t = peek(p);
if (t && t->type == TOK_L_PAREN) {
has_paren = 1;
advance(p);
args = parse_expr_list(p);
t = peek(p);
if (!t || t->type != TOK_R_PAREN) {
SPL_FATAL(t, "expected ')' in comptime call");
vec_free(args);
return 0;
}
advance(p);
}
spl_ast_node_ref_t ref = new_node(p, SPL_AST_EXPR_COMPTIME_CALL, start);
spl_ast_node_t *cc = node_at(p->ast, ref);
SET_STR_FIELD(p->ast, cc->comptime_call.name, name);
cc->comptime_call.args = args;
(void)has_paren;
return ref;
}
static spl_ast_node_ref_t parse_label(parser_t *p) {
const spl_tok_t *t = peek(p);
if (!t || t->type != TOK_DOLLAR)
return 0;
advance(p);
const spl_tok_t *name = peek(p);
if (!name || name->type != TOK_IDENT) {
SPL_FATAL(name, "expected identifier after '$'");
return 0;
}
advance(p);
spl_ast_node_ref_t ref = new_node(p, SPL_AST_LABEL, t);
SET_STR_FIELD(p->ast, node_at(p->ast, ref)->label.name, name);
return ref;
}
static spl_ast_node_ref_t parse_if_expr(parser_t *p) {
const spl_tok_t *kw = peek(p);
if (!kw || kw->type != KW_IF)
return 0;
advance(p);
int is_ifvar = 0;
spl_ast_node_ref_t var_decl = 0;
const spl_tok_t *t = peek(p);
if (t && t->type == KW_VAR) {
advance(p);
is_ifvar = 1;
spl_ast_node_ref_t pattern = parse_var_pattern(p);
if (!pattern)
return 0;
spl_ast_node_ref_t type_expr = 0;
spl_ast_node_ref_t init_expr = 0;
t = peek(p);
if (t && t->type == TOK_COLON) {
advance(p);
type_expr = parse_type_expr(p);
t = peek(p);
if (t && t->type == TOK_ASSIGN) {
advance(p);
init_expr = parse_expr(p);
} else {
SPL_FATAL(t, "expected '=' in if var");
return 0;
}
} else if (t && t->type == TOK_ASSIGN) {
advance(p);
init_expr = parse_expr(p);
} else {
SPL_FATAL(t, "expected type annotation or '=' in if var");
return 0;
}
var_decl = new_node(p, SPL_AST_VAR_DECL, kw);
spl_ast_node_t *vd = node_at(p->ast, var_decl);
vd->var_const_decl.pattern = pattern;
vd->var_const_decl.type = type_expr;
vd->var_const_decl.init_expr = init_expr;
} else {
p->stop_agg++;
spl_ast_node_ref_t cond = parse_expr(p);
p->stop_agg--;
if (!cond)
return 0;
var_decl = cond;
}
spl_ast_node_ref_t then_block = parse_block(p);
if (!then_block)
return 0;
spl_ast_node_ref_t else_branch = 0;
t = peek(p);
if (t && t->type == KW_ELSE) {
advance(p);
t = peek(p);
if (t && t->type == KW_IF) {
else_branch = parse_if_expr(p);
} else {
else_branch = parse_block(p);
}
}
spl_ast_node_ref_t ref;
if (is_ifvar) {
ref = new_node(p, SPL_AST_EXPR_IFVAR, kw);
node_at(p->ast, ref)->ifvar_expr.var_decl = var_decl;
node_at(p->ast, ref)->ifvar_expr.then_block = then_block;
node_at(p->ast, ref)->ifvar_expr.else_branch = else_branch;
} else {
ref = new_node(p, SPL_AST_EXPR_IF, kw);
node_at(p->ast, ref)->if_expr.cond = var_decl;
node_at(p->ast, ref)->if_expr.then_block = then_block;
node_at(p->ast, ref)->if_expr.else_branch = else_branch;
}
return ref;
}
static spl_ast_node_ref_t parse_while_expr(parser_t *p) {
const spl_tok_t *kw = peek(p);
if (!kw || kw->type != KW_WHILE)
return 0;
advance(p);
p->stop_agg++;
spl_ast_node_ref_t cond = parse_expr(p);
p->stop_agg--;
if (!cond)
return 0;
spl_ast_node_ref_t body = parse_block(p);
if (!body)
return 0;
spl_ast_node_ref_t else_branch = 0;
if (peek(p) && peek(p)->type == KW_ELSE) {
advance(p);
if (peek(p) && peek(p)->type == TOK_L_BRACE)
else_branch = parse_block(p);
else
else_branch = parse_expr(p);
}
spl_ast_node_ref_t ref = new_node(p, SPL_AST_EXPR_WHILE, kw);
node_at(p->ast, ref)->while_expr.label = 0;
node_at(p->ast, ref)->while_expr.cond = cond;
node_at(p->ast, ref)->while_expr.body = body;
node_at(p->ast, ref)->while_expr.else_branch = else_branch;
return ref;
}
static spl_ast_node_ref_t parse_loop_expr(parser_t *p) {
const spl_tok_t *kw = peek(p);
if (!kw || kw->type != KW_LOOP)
return 0;
advance(p);
spl_ast_node_ref_t body = parse_block(p);
spl_ast_node_ref_t ref = new_node(p, SPL_AST_EXPR_LOOP, kw);
node_at(p->ast, ref)->loop_expr.label = 0;
node_at(p->ast, ref)->loop_expr.body = body;
return ref;
}
static spl_ast_node_ref_t parse_for_expr(parser_t *p) {
const spl_tok_t *kw = peek(p);
if (!kw || kw->type != KW_FOR)
return 0;
advance(p);
p->stop_as = 1;
spl_ast_node_ref_vec_t iter_exprs;
vec_init(iter_exprs);
vec_push(iter_exprs, parse_expr(p));
while (peek(p) && peek(p)->type == TOK_COMMA) {
advance(p);
vec_push(iter_exprs, parse_expr(p));
}
p->stop_as = 0;
const spl_tok_t *t = peek(p);
if (!t || t->type != KW_AS) {
SPL_FATAL(t, "expected 'as' in for loop");
return 0;
}
advance(p);
spl_ast_node_ref_vec_t idents;
vec_init(idents);
t = peek(p);
if (!t || (t->type != TOK_IDENT && t->type != KW_UNDERSCORE)) {
SPL_FATAL(t, "expected binding variable or '_' in for");
return 0;
}
advance(p);
spl_ast_node_ref_t id_node = new_node(p, SPL_AST_EXPR_IDENT, t);
SET_STR_FIELD(p->ast, node_at(p->ast, id_node)->ident.name, t);
vec_push(idents, id_node);
while (peek(p) && peek(p)->type == TOK_COMMA) {
advance(p);
t = peek(p);
if (!t || (t->type != TOK_IDENT && t->type != KW_UNDERSCORE)) {
SPL_FATAL(t, "expected binding variable or '_' in for");
return 0;
}
advance(p);
id_node = new_node(p, SPL_AST_EXPR_IDENT, t);
SET_STR_FIELD(p->ast, node_at(p->ast, id_node)->ident.name, t);
vec_push(idents, id_node);
}
spl_ast_node_ref_t body = parse_block(p);
if (!body)
return 0;
spl_ast_node_ref_t else_branch = 0;
if (peek(p) && peek(p)->type == KW_ELSE) {
advance(p);
if (peek(p) && peek(p)->type == TOK_L_BRACE)
else_branch = parse_block(p);
else
else_branch = parse_expr(p);
}
spl_ast_node_ref_t ref = new_node(p, SPL_AST_EXPR_FOR, kw);
spl_ast_node_t *fe = node_at(p->ast, ref);
fe->for_expr.label = 0;
fe->for_expr.iter_exprs = iter_exprs;
fe->for_expr.idents = idents;
fe->for_expr.body = body;
fe->for_expr.else_branch = else_branch;
return ref;
}
static int parse_match_arm(parser_t *p, spl_ast_node_ref_vec_t *patterns_out,
spl_ast_node_ref_vec_t *bodies_out) {
spl_ast_node_ref_vec_t pats;
vec_init(pats);
while (!p->failed) {
spl_ast_node_ref_t pat = parse_match_pattern(p);
if (!pat)
break;
vec_push(pats, pat);
if (peek(p) && peek(p)->type == TOK_COMMA) {
advance(p);
continue;
}
break;
}
const spl_tok_t *t = peek(p);
if (!t || t->type != TOK_RIGHT_FAT_ARROW) {
SPL_FATAL(t, "expected '=>' in match arm");
return 0;
}
advance(p);
spl_ast_node_ref_t body;
t = peek(p);
if (!t || t->type != TOK_L_BRACE) {
SPL_FATAL(t, "expected block after '=>' in match arm");
return 0;
}
body = parse_block(p);
for (usize i = 0; i < pats.size; i++) {
vec_push(*patterns_out, pats.data[i]);
vec_push(*bodies_out, body);
}
vec_free(pats);
return 1;
}
static spl_ast_node_ref_t parse_match_expr(parser_t *p) {
const spl_tok_t *kw = peek(p);
if (!kw || kw->type != KW_MATCH)
return 0;
advance(p);
p->stop_agg++;
spl_ast_node_ref_t matched_expr = parse_expr(p);
p->stop_agg--;
const spl_tok_t *t = peek(p);
if (!t || t->type != TOK_L_BRACE) {
SPL_FATAL(t, "expected '{' in match");
return 0;
}
advance(p);
spl_ast_node_ref_vec_t patterns, bodies;
vec_init(patterns);
vec_init(bodies);
while (!p->failed) {
t = peek(p);
if (!t || t->type == TOK_R_BRACE || t->type == TOK_EOF)
break;
if (!parse_match_arm(p, &patterns, &bodies))
break;
if (peek(p) && peek(p)->type == TOK_COMMA)
advance(p);
}
t = peek(p);
if (!t || t->type != TOK_R_BRACE) {
SPL_FATAL(t, "expected '}' in match");
return 0;
}
advance(p);
spl_ast_node_ref_t ref = new_node(p, SPL_AST_EXPR_MATCH, kw);
spl_ast_node_t *me = node_at(p->ast, ref);
me->match_expr.matched_expr = matched_expr;
vec_init(me->match_expr.arms);
for (usize i = 0; i < patterns.size; i++) {
spl_ast_node_ref_t arm = new_node(p, SPL_AST_MATCH_ARM, NULL);
spl_ast_node_t *arm_node = node_at(p->ast, arm);
arm_node->match_arm.pattern = patterns.data[i];
arm_node->match_arm.body = bodies.data[i];
me = node_at(p->ast, ref); /* 每次迭代重新获取 防止 new_node 扩容 */
vec_push(me->match_expr.arms, arm);
}
vec_free(patterns);
vec_free(bodies);
return ref;
}
static spl_ast_node_ref_t parse_ret_stmt(parser_t *p) {
const spl_tok_t *kw = peek(p);
if (!kw || kw->type != KW_RET)
return 0;
advance(p);
spl_ast_node_ref_t expr = 0;
const spl_tok_t *t = peek(p);
if (t && can_start_expr(t->type))
expr = parse_expr(p);
t = peek(p);
if (t && t->type == TOK_SEMICOLON)
advance(p);
spl_ast_node_ref_t ref = new_node(p, SPL_AST_STATEMENT_RET, kw);
node_at(p->ast, ref)->statement_ret.expr = expr;
return ref;
}
static spl_ast_node_ref_t parse_break_stmt(parser_t *p) {
const spl_tok_t *kw = peek(p);
if (!kw || kw->type != KW_BREAK)
return 0;
advance(p);
spl_ast_node_ref_t label = parse_label(p);
spl_ast_node_ref_t expr = 0;
const spl_tok_t *t = peek(p);
if (t && can_start_expr(t->type))
expr = parse_expr(p);
t = peek(p);
if (t && t->type == TOK_SEMICOLON)
advance(p);
spl_ast_node_ref_t ref = new_node(p, SPL_AST_STATEMENT_BREAK, kw);
node_at(p->ast, ref)->statement_break.label = label;
node_at(p->ast, ref)->statement_break.expr = expr;
return ref;
}
static spl_ast_node_ref_t parse_continue_stmt(parser_t *p) {
const spl_tok_t *kw = peek(p);
if (!kw || kw->type != KW_CONTINUE)
return 0;
advance(p);
spl_ast_node_ref_t label = parse_label(p);
const spl_tok_t *t = peek(p);
if (t && t->type == TOK_SEMICOLON)
advance(p);
spl_ast_node_ref_t ref = new_node(p, SPL_AST_STATEMENT_CONTINUE, kw);
node_at(p->ast, ref)->statement_continue.label = label;
return ref;
}
static spl_ast_node_ref_t parse_defer_stmt(parser_t *p) {
const spl_tok_t *kw = peek(p);
if (!kw || kw->type != KW_DEFER)
return 0;
advance(p);
spl_ast_node_ref_t body = 0;
const spl_tok_t *t = peek(p);
if (t && t->type == TOK_L_BRACE) {
body = parse_block(p);
} else if (t && can_start_statement(t->type)) {
body = parse_statement(p);
} else {
body = parse_expr(p);
t = peek(p);
if (t && t->type == TOK_SEMICOLON)
advance(p);
}
spl_ast_node_ref_t ref = new_node(p, SPL_AST_STATEMENT_DEFER, kw);
node_at(p->ast, ref)->statement_defer.body = body;
return ref;
}
static spl_ast_node_ref_t parse_comptime_stmt(parser_t *p) {
const spl_tok_t *kw = peek(p);
if (!kw || kw->type != KW_COMPTIME)
return 0;
advance(p);
spl_ast_node_ref_t body = 0;
const spl_tok_t *t = peek(p);
if (t && t->type == TOK_L_BRACE) {
body = parse_block(p);
} else {
body = parse_expr(p);
}
spl_ast_node_ref_t ref = new_node(p, SPL_AST_STATEMENT_COMPTIME, kw);
node_at(p->ast, ref)->statement_comptime.body = body;
return ref;
}
static spl_ast_node_ref_t parse_statement(parser_t *p) {
const spl_tok_t *t = peek(p);
if (!t)
return 0;
switch (t->type) {
case KW_RET:
return parse_ret_stmt(p);
case KW_BREAK:
return parse_break_stmt(p);
case KW_CONTINUE:
return parse_continue_stmt(p);
case KW_DEFER:
return parse_defer_stmt(p);
case KW_COMPTIME:
return parse_comptime_stmt(p);
case KW_VAR:
case KW_CONST:
case KW_TYPE: {
spl_ast_node_ref_vec_t tag_list;
vec_init(tag_list);
parse_tag_list(p, &tag_list, 0, 0); // 解析声明前的普通 tag
spl_ast_node_ref_t decl;
if (t->type == KW_VAR)
decl = parse_var_const_decl(p, tag_list, 0);
else if (t->type == KW_CONST)
decl = parse_var_const_decl(p, tag_list, 1);
else
decl = parse_type_decl(p, tag_list);
/* 不再 vec_free(tag_list),所有权已转移 */
if (!decl)
return 0;
spl_ast_node_ref_t ref = new_node(p, SPL_AST_STATEMENT_DECL, t);
node_at(p->ast, ref)->statement_decl.decl = decl;
return ref;
}
default: {
if (can_start_expr(t->type)) {
spl_ast_node_ref_t expr = parse_expr(p);
if (!expr)
return 0;
spl_ast_node_ref_t ref = new_node(p, SPL_AST_STATEMENT_EXPR, t);
node_at(p->ast, ref)->statement_expr.expr = expr;
return ref;
} else {
SPL_FATAL(t, "unrecognized statement");
return 0;
}
}
}
}
static spl_ast_node_ref_t parse_block(parser_t *p) {
const spl_tok_t *start = peek(p);
if (!start || start->type != TOK_L_BRACE) {
SPL_FATAL(start, "expected '{'");
return 0;
}
advance(p);
spl_ast_node_ref_t ref = new_node(p, SPL_AST_EXPR_BLOCK, start);
spl_ast_node_t *block = node_at(p->ast, ref);
vec_init(block->block.stmts);
block->block.tail_expr = 0;
while (!p->failed) {
const spl_tok_t *t = peek(p);
if (!t || t->type == TOK_EOF) {
SPL_FATAL(t, "block missing '}'");
return ref;
}
if (t->type == TOK_R_BRACE) {
advance(p);
break;
}
if (can_start_statement(t->type)) {
spl_ast_node_ref_t stmt = parse_statement(p);
block = node_at(p->ast, ref);
if (stmt)
vec_push(block->block.stmts, stmt);
} else if (can_start_expr(t->type)) {
spl_ast_node_ref_t expr = parse_expr(p);
block = node_at(p->ast, ref);
t = peek(p);
if (t && t->type == TOK_SEMICOLON) {
advance(p);
vec_push(block->block.stmts, expr);
} else if (t && t->type == TOK_R_BRACE) {
if (block->block.tail_expr != 0) {
SPL_FATAL(t, "only one tail expression allowed");
}
block->block.tail_expr = expr;
} else {
spl_ast_node_kind_t k = node_at(p->ast, expr)->kind;
bool block_like =
(k == SPL_AST_EXPR_BLOCK || k == SPL_AST_EXPR_IF || k == SPL_AST_EXPR_IFVAR ||
k == SPL_AST_EXPR_WHILE || k == SPL_AST_EXPR_LOOP || k == SPL_AST_EXPR_FOR ||
k == SPL_AST_EXPR_MATCH);
if (block_like) {
vec_push(block->block.stmts, expr);
} else {
SPL_FATAL(t, "expected ';' or '}' after expression");
break;
}
}
} else {
SPL_FATAL(t, "unrecognized token in block");
break;
}
}
return ref;
}
void spl_ast_init(spl_ast_t *ast, const spl_tok_vec_t *tok_vec) {
ast->input = *tok_vec;
vec_init(ast->node_buckets);
vec_init(ast->str_buckets);
ast->root = 0;
ast->parsed = 0;
spl_ast_node_t n = {0};
vec_push(ast->node_buckets, n);
}
void spl_ast_parse(spl_ast_t *ast) {
if (!ast || !ast->input.data || ast->node_buckets.size == 0)
return;
parser_t p;
p.ast = ast;
p.pos = 0;
p.failed = 0;
p.stop_as = 0;
p.stop_agg = 0;
ast->parsed = 0;
spl_ast_node_ref_vec_t external_tags;
vec_init(external_tags);
spl_ast_node_ref_vec_t declarations;
vec_init(declarations);
while (!p.failed) {
const spl_tok_t *t = peek(&p);
if (!t || t->type == TOK_EOF)
break;
if (t->type == TOK_AT || t->type == TOK_SHARP) {
usize save_pos = p.pos;
const spl_tok_t *t2 = NULL;
if (t) {
p.pos++;
t2 = peek(&p);
p.pos = save_pos;
}
if (t2 && t2->type == TOK_NOT) {
parse_tag_list(&p, &external_tags, 1, 1);
continue;
}
}
spl_ast_node_ref_t decl = parse_container_declaration(&p);
if (decl)
vec_push(declarations, decl);
}
spl_ast_node_ref_t container_members = new_node(&p, SPL_AST_CONTAINER_MEMBERS, NULL);
spl_ast_node_t *cm = node_at(ast, container_members);
cm->container_members.external_tags = external_tags;
cm->container_members.declarations = declarations;
spl_ast_node_ref_t root = new_node(&p, SPL_AST_ROOT, NULL);
node_at(ast, root)->root.members = container_members;
ast->root = root;
ast->parsed = p.failed ? -1 : 1;
}
void spl_ast_drop(spl_ast_t *ast) {
if (!ast)
return;
vec_for(ast->str_buckets, idx) { free(vec_at(ast->str_buckets, idx)); }
vec_free(ast->str_buckets);
vec_for(ast->node_buckets, idx) { vec_free(vec_at(ast->node_buckets, idx).attrs); }
vec_free(ast->node_buckets);
vec_free(ast->input);
ast->parsed = 0;
ast->root = 0;
}
spl_ast_node_t *spl_ast_node(spl_ast_t *ast, spl_ast_node_ref_t node) {
if (!ast || node == 0 || node >= ast->node_buckets.size)
return NULL;
return &ast->node_buckets.data[node];
}
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;
}
/* TODO */
}
typedef VEC(char) dump_stack_t;
static void dump_node(spl_ast_t *ast, spl_ast_node_ref_t node_ref, dump_stack_t *stack, bool last);
void spl_ast_dump(spl_ast_t *ast, spl_ast_node_ref_t node) {
if (!ast || !node || node >= ast->node_buckets.size)
return;
dump_stack_t stack;
vec_init(stack);
printf("AST:\n");
dump_node(ast, node, &stack, true);
vec_free(stack);
}
#define dump_vec(_ast, _vec, _stack) \
do { \
vec_for(_vec, _i) { dump_node(_ast, vec_at(_vec, _i), _stack, _i + 1 == vec_size(_vec)); } \
} while (0)
static void dump_node(spl_ast_t *ast, spl_ast_node_ref_t node_ref, dump_stack_t *stack, bool last) {
spl_ast_node_t *node = &ast->node_buckets.data[node_ref];
for (usize i = 0; i < vec_size(*stack); i++)
printf("%s", vec_at(*stack, i) ? " " : "| ");
printf("%s%s #%zu", last ? "`-" : "|-", spl_ast_kind_name(node->kind), node_ref);
if (node->dbg.fname)
printf(" [%d:%d]", node->dbg.line, node->dbg.col);
printf("\n");
vec_push(*stack, last);
switch (node->kind) {
case SPL_AST_ROOT:
if (node->root.members)
dump_node(ast, node->root.members, stack, true);
break;
case SPL_AST_CONTAINER_MEMBERS:
dump_vec(ast, node->container_members.external_tags, stack);
dump_vec(ast, node->container_members.declarations, stack);
break;
case SPL_AST_TAG_ITEM:
case SPL_AST_EXTERNAL_TAG_ITEM:
dump_vec(ast, node->tag_item.args, stack);
break;
case SPL_AST_FN_DECL:
dump_vec(ast, node->fn_decl.tag_list, stack);
dump_vec(ast, node->fn_decl.params, stack);
if (node->fn_decl.ret_type)
dump_node(ast, node->fn_decl.ret_type, stack, false);
if (node->fn_decl.body)
dump_node(ast, node->fn_decl.body, stack, true);
break;
case SPL_AST_VAR_DECL:
case SPL_AST_CONST_DECL:
dump_vec(ast, node->var_const_decl.attr_list, stack);
if (node->var_const_decl.pattern)
dump_node(ast, node->var_const_decl.pattern, stack, false);
if (node->var_const_decl.type)
dump_node(ast, node->var_const_decl.type, stack, false);
if (node->var_const_decl.init_expr)
dump_node(ast, node->var_const_decl.init_expr, stack, true);
break;
case SPL_AST_TYPE_DECL:
if (node->type_decl.type_expr)
dump_node(ast, node->type_decl.type_expr, stack, true);
break;
case SPL_AST_MEMBER_DECL:
dump_vec(ast, node->member_decl.attr_list, stack);
if (node->member_decl.type_expr)
dump_node(ast, node->member_decl.type_expr, stack, false);
if (node->member_decl.init_expr)
dump_node(ast, node->member_decl.init_expr, stack, true);
break;
case SPL_AST_PARAM_DECL:
dump_vec(ast, node->param_decl.attr_list, stack);
if (node->param_decl.type_expr)
dump_node(ast, node->param_decl.type_expr, stack, true);
break;
case SPL_AST_COMPTIME_DECL:
if (node->comptime_decl.body)
dump_node(ast, node->comptime_decl.body, stack, true);
break;
case SPL_AST_TOP_LEVEL_BLOCK:
dump_vec(ast, node->top_level_block.tag_list, stack);
if (node->top_level_block.block)
dump_node(ast, node->top_level_block.block, stack, true);
break;
case SPL_AST_STATEMENT_RET:
if (node->statement_ret.expr)
dump_node(ast, node->statement_ret.expr, stack, true);
break;
case SPL_AST_STATEMENT_BREAK:
if (node->statement_break.label)
dump_node(ast, node->statement_break.label, stack, false);
if (node->statement_break.expr)
dump_node(ast, node->statement_break.expr, stack, true);
break;
case SPL_AST_STATEMENT_CONTINUE:
if (node->statement_continue.label)
dump_node(ast, node->statement_continue.label, stack, true);
break;
case SPL_AST_STATEMENT_DEFER:
case SPL_AST_STATEMENT_COMPTIME:
if (node->statement_defer.body)
dump_node(ast, node->statement_defer.body, stack, true);
break;
case SPL_AST_STATEMENT_DECL:
if (node->statement_decl.decl)
dump_node(ast, node->statement_decl.decl, stack, true);
break;
case SPL_AST_STATEMENT_EXPR:
if (node->statement_expr.expr)
dump_node(ast, node->statement_expr.expr, stack, true);
break;
case SPL_AST_AGGREGATE_MEMBER:
dump_vec(ast, node->aggregate_member.attr_list, stack);
if (node->aggregate_member.type_expr)
dump_node(ast, node->aggregate_member.type_expr, stack, false);
if (node->aggregate_member.init_expr)
dump_node(ast, node->aggregate_member.init_expr, stack, true);
break;
case SPL_AST_STRUCT_INIT_ITEM:
if (node->struct_init_item.expr)
dump_node(ast, node->struct_init_item.expr, stack, true);
break;
case SPL_AST_MATCH_ARM:
if (node->match_arm.pattern)
dump_node(ast, node->match_arm.pattern, stack, false);
if (node->match_arm.body)
dump_node(ast, node->match_arm.body, stack, true);
break;
case SPL_AST_LABEL:
break;
case SPL_AST_EXPR_INT_LIT:
case SPL_AST_EXPR_FLOAT_LIT:
case SPL_AST_EXPR_CHAR_LIT:
case SPL_AST_EXPR_STRING_LIT:
case SPL_AST_EXPR_BOOL_LIT:
case SPL_AST_EXPR_NULL_LIT:
case SPL_AST_EXPR_UNDEFINED_LIT:
case SPL_AST_EXPR_IDENT:
break;
case SPL_AST_EXPR_UNARY:
if (node->unary.operand)
dump_node(ast, node->unary.operand, stack, true);
break;
case SPL_AST_EXPR_BINARY:
if (node->binary.left)
dump_node(ast, node->binary.left, stack, false);
if (node->binary.right)
dump_node(ast, node->binary.right, stack, true);
break;
case SPL_AST_EXPR_CALL:
if (node->call.callee)
dump_node(ast, node->call.callee, stack, false);
dump_vec(ast, node->call.args, stack);
break;
case SPL_AST_EXPR_FIELD:
if (node->field.obj)
dump_node(ast, node->field.obj, stack, true);
break;
case SPL_AST_EXPR_INDEX:
if (node->index.obj)
dump_node(ast, node->index.obj, stack, false);
if (node->index.index)
dump_node(ast, node->index.index, stack, true);
break;
case SPL_AST_EXPR_SLICE:
if (node->slice.obj)
dump_node(ast, node->slice.obj, stack, false);
if (node->slice.begin)
dump_node(ast, node->slice.begin, stack, false);
if (node->slice.end)
dump_node(ast, node->slice.end, stack, true);
break;
case SPL_AST_EXPR_CAST:
if (node->cast.expr)
dump_node(ast, node->cast.expr, stack, false);
if (node->cast.type)
dump_node(ast, node->cast.type, stack, true);
break;
case SPL_AST_EXPR_TUPLE:
dump_vec(ast, node->tuple.elems, stack);
break;
case SPL_AST_EXPR_ARRAY_LIT:
if (node->array_literal.elem_type)
dump_node(ast, node->array_literal.elem_type, stack, false);
dump_vec(ast, node->array_literal.elems, stack);
break;
case SPL_AST_EXPR_AGGREGATE_LIT:
dump_vec(ast, node->aggregate_literal.fields, stack);
break;
case SPL_AST_EXPR_COMPTIME_CALL:
dump_vec(ast, node->comptime_call.args, stack);
break;
case SPL_AST_EXPR_BLOCK:
dump_vec(ast, node->block.stmts, stack);
if (node->block.tail_expr)
dump_node(ast, node->block.tail_expr, stack, true);
break;
case SPL_AST_EXPR_IF:
if (node->if_expr.cond)
dump_node(ast, node->if_expr.cond, stack, false);
if (node->if_expr.then_block)
dump_node(ast, node->if_expr.then_block, stack, false);
if (node->if_expr.else_branch)
dump_node(ast, node->if_expr.else_branch, stack, true);
break;
case SPL_AST_EXPR_IFVAR:
if (node->ifvar_expr.var_decl)
dump_node(ast, node->ifvar_expr.var_decl, stack, false);
if (node->ifvar_expr.then_block)
dump_node(ast, node->ifvar_expr.then_block, stack, false);
if (node->ifvar_expr.else_branch)
dump_node(ast, node->ifvar_expr.else_branch, stack, true);
break;
case SPL_AST_EXPR_WHILE:
if (node->while_expr.label)
dump_node(ast, node->while_expr.label, stack, false);
if (node->while_expr.cond)
dump_node(ast, node->while_expr.cond, stack, false);
if (node->while_expr.body)
dump_node(ast, node->while_expr.body, stack, false);
if (node->while_expr.else_branch)
dump_node(ast, node->while_expr.else_branch, stack, true);
break;
case SPL_AST_EXPR_LOOP:
if (node->loop_expr.label)
dump_node(ast, node->loop_expr.label, stack, false);
if (node->loop_expr.body)
dump_node(ast, node->loop_expr.body, stack, true);
break;
case SPL_AST_EXPR_FOR:
if (node->for_expr.label)
dump_node(ast, node->for_expr.label, stack, false);
dump_vec(ast, node->for_expr.iter_exprs, stack);
dump_vec(ast, node->for_expr.idents, stack);
if (node->for_expr.body)
dump_node(ast, node->for_expr.body, stack, false);
if (node->for_expr.else_branch)
dump_node(ast, node->for_expr.else_branch, stack, true);
break;
case SPL_AST_EXPR_MATCH:
if (node->match_expr.matched_expr)
dump_node(ast, node->match_expr.matched_expr, stack, false);
dump_vec(ast, node->match_expr.arms, stack);
break;
case SPL_AST_EXPR_FN_TYPE:
dump_vec(ast, node->fn_type.params, stack);
if (node->fn_type.ret_type)
dump_node(ast, node->fn_type.ret_type, stack, true);
break;
case SPL_AST_TYPE_PATH:
break;
case SPL_AST_TYPE_POINTER:
if (node->type_pointer.pointee)
dump_node(ast, node->type_pointer.pointee, stack, true);
break;
case SPL_AST_TYPE_ARRAY:
if (node->type_array.elem_type)
dump_node(ast, node->type_array.elem_type, stack, true);
break;
case SPL_AST_TYPE_SLICE:
if (node->type_slice.elem_type)
dump_node(ast, node->type_slice.elem_type, stack, true);
break;
case SPL_AST_TYPE_FN:
dump_vec(ast, node->type_fn.params, stack);
if (node->type_fn.ret_type)
dump_node(ast, node->type_fn.ret_type, stack, true);
break;
case SPL_AST_TYPE_PAREN:
dump_vec(ast, node->type_paren.types, stack);
break;
case SPL_AST_TYPE_AGGREGATE:
dump_vec(ast, node->type_aggregate.members, stack);
break;
case SPL_AST_TYPE_COMPTIME_CALL:
dump_vec(ast, node->type_comptime_call.args, stack);
break;
case SPL_AST_PATTERN_IDENT:
case SPL_AST_PATTERN_UNDERSCORE:
break;
case SPL_AST_PATTERN_DOT:
if (node->pattern_dot.sub_pattern)
dump_node(ast, node->pattern_dot.sub_pattern, stack, true);
break;
case SPL_AST_PATTERN_TUPLE:
dump_vec(ast, node->pattern_tuple.patterns, stack);
break;
case SPL_AST_PATTERN_LIST:
dump_vec(ast, node->pattern_list.patterns, stack);
break;
default:
break;
}
(void)vec_pop(*stack);
}