2600 lines
78 KiB
C
2600 lines
78 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";
|
||
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_type_id = 0;
|
||
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) {
|
||
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)
|
||
return NULL;
|
||
char *dst = tmp;
|
||
while (src < end) {
|
||
char c;
|
||
if (spl_decode_escape(&src, &c) == 0) {
|
||
*dst++ = c;
|
||
} else {
|
||
*dst++ = *src;
|
||
src++;
|
||
}
|
||
}
|
||
*dst = '\0';
|
||
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_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) {
|
||
while (!p->failed) {
|
||
const spl_tok_t *t = peek(p);
|
||
if (!t || (t->type != TOK_AT && t->type != TOK_SHARP))
|
||
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);
|
||
tag->tag_item.args = parse_expr_list(p);
|
||
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);
|
||
|
||
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);
|
||
|
||
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, ¶ms);
|
||
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);
|
||
td->type_decl.type_expr = parse_type_expr(p);
|
||
|
||
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);
|
||
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); /* 只解析普通 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);
|
||
pat->pattern_dot.sub_pattern = parse_var_pattern(p);
|
||
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) {
|
||
spl_ast_node_ref_vec_t tag_list;
|
||
vec_init(tag_list);
|
||
parse_tag_list(p, &tag_list, 0);
|
||
|
||
const spl_tok_t *bt = peek(p);
|
||
if (!bt) {
|
||
vec_free(tag_list);
|
||
SPL_FATAL(bt, "expected type");
|
||
return 0;
|
||
}
|
||
|
||
/* 函数类型 */
|
||
if (bt->type == KW_FN) {
|
||
vec_free(tag_list);
|
||
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);
|
||
fn_type = node_at(p->ast, ref);
|
||
fn_type->type_fn.ret_type = parse_type_expr(p);
|
||
}
|
||
return ref;
|
||
}
|
||
|
||
/* 聚合类型:struct / union / enum / shape */
|
||
if (bt->type == KW_STRUCT || bt->type == KW_UNION || bt->type == KW_ENUM ||
|
||
bt->type == KW_SHAPE) {
|
||
vec_free(tag_list); /* 聚合类型不使用 tag_list */
|
||
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) {
|
||
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);
|
||
vec_free(tag_list);
|
||
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);
|
||
}
|
||
vec_free(tag_list);
|
||
return ref;
|
||
}
|
||
|
||
if (bt->type == TOK_AT || bt->type == TOK_SHARP) {
|
||
vec_free(tag_list);
|
||
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);
|
||
vec_free(tag_list);
|
||
return ref;
|
||
}
|
||
|
||
vec_free(tag_list);
|
||
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 call name");
|
||
return 0;
|
||
}
|
||
advance(p);
|
||
|
||
const spl_tok_t *t = peek(p);
|
||
if (!t || t->type != TOK_L_PAREN) {
|
||
SPL_FATAL(t, "expected '(' in comptime call");
|
||
return 0;
|
||
}
|
||
advance(p);
|
||
|
||
spl_ast_node_ref_vec_t args = parse_expr_list(p);
|
||
|
||
t = peek(p);
|
||
if (!t || t->type != TOK_R_PAREN) {
|
||
SPL_FATAL(t, "expected ')' in comptime call");
|
||
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);
|
||
node_at(p->ast, ref)->type_pointer.pointee = parse_type_expr(p);
|
||
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;
|
||
|
||
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;
|
||
node_at(p->ast, ref)->call.args = parse_expr_list(p);
|
||
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_ADDRESS, 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.val = parse_int_literal(t);
|
||
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.val = parse_float_literal(t);
|
||
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);
|
||
node_at(p->ast, ref)->string_lit.data = parse_string_literal(p, t);
|
||
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) {
|
||
// 具名聚合字面量:Identifier { fields }
|
||
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);
|
||
agg->aggregate_literal.fields = parse_aggregate_fields(p);
|
||
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) {
|
||
// 匿名聚合字面量:. { fields }
|
||
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 = NULL;
|
||
agg->aggregate_literal.fields = parse_aggregate_fields(p);
|
||
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 call name");
|
||
return 0;
|
||
}
|
||
advance(p);
|
||
|
||
const spl_tok_t *t = peek(p);
|
||
if (!t || t->type != TOK_L_PAREN) {
|
||
SPL_FATAL(t, "expected '(' in comptime call");
|
||
return 0;
|
||
}
|
||
advance(p);
|
||
|
||
spl_ast_node_ref_vec_t args = parse_expr_list(p);
|
||
|
||
t = peek(p);
|
||
if (!t || t->type != TOK_R_PAREN) {
|
||
SPL_FATAL(t, "expected ')' in comptime call");
|
||
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;
|
||
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);
|
||
|
||
spl_ast_node_ref_t label = parse_label(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 = label;
|
||
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 label = parse_label(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 = label;
|
||
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);
|
||
|
||
spl_ast_node_ref_t label = parse_label(p);
|
||
|
||
// 解析迭代表达式列表(用 stop_as 阻止 'as' 被误解析为类型转换)
|
||
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;
|
||
|
||
// 必须有关键字 as
|
||
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;
|
||
|
||
// 可选的 else 分支
|
||
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 = label;
|
||
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];
|
||
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); // 解析声明前的普通 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 if (expr && node_at(p->ast, expr)->kind == SPL_AST_EXPR_BLOCK) {
|
||
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);
|
||
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_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);
|
||
}
|