2292 lines
70 KiB
C
2292 lines
70 KiB
C
#include "spl_parser.h"
|
|
#include "spl_lexer.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;
|
|
}
|
|
|
|
static spl_dbg_node_t tok_to_dbg(const spl_tok_t *t) {
|
|
spl_dbg_node_t d = {0};
|
|
if (t) {
|
|
d.fname = t->fname;
|
|
d.line = (int)t->line;
|
|
d.col = (int)t->col;
|
|
}
|
|
return d;
|
|
}
|
|
|
|
static void tok_desc(const spl_tok_t *t, char *buf, usize cap) {
|
|
if (!t) {
|
|
snprintf(buf, cap, "<no token>");
|
|
return;
|
|
}
|
|
if (t->type == TOK_EOF) {
|
|
snprintf(buf, cap, "EOF");
|
|
return;
|
|
}
|
|
const char *kind = spl_tok_type_name(t->type);
|
|
if (t->type == TOK_ENDLINE) {
|
|
snprintf(buf, cap, "ENDLINE at %zu:%zu", t->line, t->col);
|
|
return;
|
|
}
|
|
if (!t->lexeme || t->len == 0) {
|
|
snprintf(buf, cap, "%s at %zu:%zu", kind, t->line, t->col);
|
|
return;
|
|
}
|
|
int n = (int)(t->len > 32 ? 32 : t->len);
|
|
snprintf(buf, cap, "%s `%.*s` at %s:%zu:%zu", kind, n, t->lexeme, t->fname, t->line, t->col);
|
|
}
|
|
|
|
#define PARSE_ERROR(p, kind, tok, fmt, ...) \
|
|
do { \
|
|
(p)->error_count++; \
|
|
if ((p)->diag) { \
|
|
char _tb[80]; \
|
|
tok_desc((tok), _tb, sizeof _tb); \
|
|
spl_dbg_node_t _pd = tok_to_dbg(tok); \
|
|
spl_diag_error((p)->diag, (kind), &_pd, fmt " (got %s)", ##__VA_ARGS__, _tb); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define PARSE_FATAL(p, kind, tok, fmt, ...) \
|
|
do { \
|
|
(p)->failed = 1; \
|
|
(p)->error_count++; \
|
|
if ((p)->diag) { \
|
|
char _tb[80]; \
|
|
tok_desc((tok), _tb, sizeof _tb); \
|
|
spl_dbg_node_t _pd = tok_to_dbg(tok); \
|
|
spl_diag_fatal((p)->diag, (kind), &_pd, fmt " (got %s)", ##__VA_ARGS__, _tb); \
|
|
} \
|
|
} while (0)
|
|
|
|
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(spl_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(spl_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(spl_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.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 int expect(spl_parser_t *p, spl_tok_type_t tt, const char *what) {
|
|
const spl_tok_t *t = peek(p);
|
|
if (!t || t->type != tt) {
|
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_EXPECTED, t, "expected %s", what);
|
|
return 0;
|
|
}
|
|
advance(p);
|
|
return 1;
|
|
}
|
|
|
|
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)strtoull(buf + 2, NULL, 2);
|
|
if (j >= 3 && buf[0] == '0' && (buf[1] == 'o' || buf[1] == 'O'))
|
|
return (isize)strtoull(buf + 2, NULL, 8);
|
|
if (j >= 3 && buf[0] == '0' && (buf[1] == 'x' || buf[1] == 'X'))
|
|
return (isize)strtoull(buf, NULL, 16);
|
|
return (isize)strtoull(buf, NULL, 10);
|
|
}
|
|
|
|
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 *s = t->lexeme + 1;
|
|
const char *end = t->lexeme + t->len - 1;
|
|
char c = 0;
|
|
if (s < end)
|
|
spl_decode_escape(&s, &c);
|
|
return c;
|
|
}
|
|
|
|
static char *parse_string_literal(spl_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 int is_builtin_type_kw(spl_tok_type_t t) {
|
|
switch (t) {
|
|
// case KW_I8:
|
|
// case KW_U8:
|
|
// case KW_I16:
|
|
// case KW_U16:
|
|
// case KW_I32:
|
|
// case KW_U32:
|
|
// case KW_I64:
|
|
// case KW_U64:
|
|
// case KW_F32:
|
|
// case KW_F64:
|
|
// case KW_ISIZE:
|
|
// case KW_USIZE:
|
|
case KW_BOOL:
|
|
// case KW_VOID:
|
|
// case KW_PTR:
|
|
case KW_ANY:
|
|
return 1;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static spl_ast_node_ref_t parse_expr(spl_parser_t *p);
|
|
static spl_ast_node_ref_vec_t parse_expr_list(spl_parser_t *p);
|
|
static spl_ast_node_ref_t parse_block(spl_parser_t *p);
|
|
static spl_ast_node_ref_t parse_type_expr(spl_parser_t *p);
|
|
static spl_ast_node_ref_t parse_type_comptime_call(spl_parser_t *p);
|
|
static spl_ast_node_ref_t parse_statement(spl_parser_t *p);
|
|
static spl_ast_node_ref_t parse_container_declaration(spl_parser_t *p);
|
|
static spl_ast_node_ref_t parse_param_decl_impl(spl_parser_t *p);
|
|
static spl_ast_node_ref_t parse_pattern(spl_parser_t *p);
|
|
static spl_ast_node_ref_t parse_pattern_atom(spl_parser_t *p);
|
|
static spl_ast_node_ref_t parse_pattern_aggregate(spl_parser_t *p, const spl_tok_t *start);
|
|
|
|
static spl_ast_node_ref_t parse_cast(spl_parser_t *p);
|
|
static spl_ast_node_ref_t parse_prefix(spl_parser_t *p);
|
|
static spl_ast_node_ref_t parse_postfix(spl_parser_t *p);
|
|
static spl_ast_node_ref_t parse_primary(spl_parser_t *p);
|
|
static spl_ast_node_ref_vec_t parse_aggregate_fields(spl_parser_t *p);
|
|
static spl_ast_node_ref_t parse_array_literal(spl_parser_t *p);
|
|
static spl_ast_node_ref_t parse_comptime_call(spl_parser_t *p);
|
|
static spl_ast_node_ref_t parse_if_expr(spl_parser_t *p);
|
|
static spl_ast_node_ref_t parse_while_expr(spl_parser_t *p);
|
|
static spl_ast_node_ref_t parse_loop_expr(spl_parser_t *p);
|
|
static spl_ast_node_ref_t parse_for_expr(spl_parser_t *p);
|
|
static spl_ast_node_ref_t parse_match_expr(spl_parser_t *p);
|
|
static spl_ast_node_ref_t parse_ret_stmt(spl_parser_t *p);
|
|
static spl_ast_node_ref_t parse_break_stmt(spl_parser_t *p);
|
|
static spl_ast_node_ref_t parse_continue_stmt(spl_parser_t *p);
|
|
static spl_ast_node_ref_t parse_defer_stmt(spl_parser_t *p);
|
|
static spl_ast_node_ref_t parse_comptime_stmt(spl_parser_t *p);
|
|
static spl_ast_node_ref_t parse_label(spl_parser_t *p);
|
|
|
|
/* ---------- Pattern ---------- */
|
|
|
|
static spl_ast_node_ref_t parse_pattern_atom(spl_parser_t *p) {
|
|
const spl_tok_t *t = peek(p);
|
|
if (!t)
|
|
return 0;
|
|
|
|
switch (t->type) {
|
|
case KW_UNDERSCORE:
|
|
advance(p);
|
|
return new_node(p, SPL_AST_PATTERN_UNDERSCORE, t);
|
|
|
|
case TOK_DOT: {
|
|
advance(p);
|
|
const spl_tok_t *nx = peek(p);
|
|
if (nx && nx->type == TOK_L_BRACE)
|
|
return parse_pattern_aggregate(p, t);
|
|
|
|
if (!nx || nx->type != TOK_IDENT) {
|
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_EXPECTED, nx, "expected variant name after '.'");
|
|
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, nx);
|
|
pat->pattern_dot.sub_pattern = 0;
|
|
|
|
if (peek(p) && peek(p)->type == TOK_L_PAREN) {
|
|
advance(p);
|
|
if (peek(p) && peek(p)->type != TOK_R_PAREN) {
|
|
spl_ast_node_ref_t sub = parse_pattern(p);
|
|
node_at(p->ast, ref)->pattern_dot.sub_pattern = sub;
|
|
}
|
|
expect(p, TOK_R_PAREN, "')' after variant payload");
|
|
}
|
|
return ref;
|
|
}
|
|
|
|
case TOK_L_PAREN: {
|
|
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 (peek(p) && peek(p)->type == TOK_R_PAREN) {
|
|
advance(p);
|
|
return ref;
|
|
}
|
|
|
|
spl_ast_node_ref_t inner = parse_pattern(p);
|
|
expect(p, TOK_R_PAREN, "')' in tuple pattern");
|
|
|
|
tuple = node_at(p->ast, ref);
|
|
if (inner && node_at(p->ast, inner)->kind == SPL_AST_PATTERN_LIST) {
|
|
spl_ast_node_t *inner_node = node_at(p->ast, inner);
|
|
MOVE_VEC(tuple->pattern_tuple.patterns, inner_node->pattern_list.patterns);
|
|
} else if (inner) {
|
|
vec_push(tuple->pattern_tuple.patterns, inner);
|
|
}
|
|
return ref;
|
|
}
|
|
|
|
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 = (uint64_t)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.bits = 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);
|
|
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 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;
|
|
}
|
|
|
|
default:
|
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_UNRECOGNIZED, t, "unexpected token in pattern");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static spl_ast_node_ref_t parse_pattern_aggregate(spl_parser_t *p, const spl_tok_t *start) {
|
|
/* 已消费 '.',当前应为 '{' */
|
|
if (!expect(p, TOK_L_BRACE, "'{' after '.'"))
|
|
return 0;
|
|
|
|
spl_ast_node_ref_t ref = new_node(p, SPL_AST_PATTERN_AGGREGATE, start);
|
|
spl_ast_node_t *agg = node_at(p->ast, ref);
|
|
vec_init(agg->pattern_aggregate.fields);
|
|
|
|
while (!p->failed) {
|
|
const spl_tok_t *t = peek(p);
|
|
if (!t || t->type == TOK_R_BRACE || t->type == TOK_EOF)
|
|
break;
|
|
|
|
if (t->type != TOK_DOT) {
|
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_EXPECTED, t, "expected '.' in aggregate pattern");
|
|
break;
|
|
}
|
|
advance(p);
|
|
|
|
const spl_tok_t *name = peek(p);
|
|
if (!name || name->type != TOK_IDENT) {
|
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_EXPECTED, name, "expected field name");
|
|
break;
|
|
}
|
|
advance(p);
|
|
|
|
spl_ast_node_ref_t field = new_node(p, SPL_AST_PATTERN_FIELD, name);
|
|
spl_ast_node_t *f = node_at(p->ast, field);
|
|
SET_STR_FIELD(p->ast, f->pattern_field.name, name);
|
|
f->pattern_field.pattern = 0;
|
|
|
|
if (peek(p) && peek(p)->type == TOK_ASSIGN) {
|
|
advance(p);
|
|
spl_ast_node_ref_t sub = parse_pattern_atom(p);
|
|
f = node_at(p->ast, field);
|
|
f->pattern_field.pattern = sub;
|
|
}
|
|
|
|
agg = node_at(p->ast, ref);
|
|
vec_push(agg->pattern_aggregate.fields, field);
|
|
|
|
t = peek(p);
|
|
if (t && (t->type == TOK_COMMA || t->type == TOK_SEMICOLON)) {
|
|
advance(p);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
expect(p, TOK_R_BRACE, "'}' in aggregate pattern");
|
|
return ref;
|
|
}
|
|
|
|
static spl_ast_node_ref_t parse_pattern(spl_parser_t *p) {
|
|
spl_ast_node_ref_vec_t atoms;
|
|
vec_init(atoms);
|
|
|
|
while (!p->failed) {
|
|
spl_ast_node_ref_t atom = parse_pattern_atom(p);
|
|
if (!atom)
|
|
break;
|
|
vec_push(atoms, atom);
|
|
|
|
const spl_tok_t *t = peek(p);
|
|
if (t && t->type == TOK_COMMA) {
|
|
advance(p);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (atoms.size == 0)
|
|
return 0;
|
|
if (atoms.size == 1) {
|
|
spl_ast_node_ref_t r = atoms.data[0];
|
|
vec_free(atoms);
|
|
return r;
|
|
}
|
|
|
|
spl_ast_node_ref_t ref = new_node(p, SPL_AST_PATTERN_LIST, peek(p));
|
|
node_at(p->ast, ref)->pattern_list.patterns = atoms;
|
|
return ref;
|
|
}
|
|
|
|
/* ---------- 声明 ---------- */
|
|
|
|
static void parse_tag_list(spl_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) {
|
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_EXPECTED, t, "expected '!' in external tag");
|
|
return;
|
|
}
|
|
advance(p);
|
|
}
|
|
|
|
t = peek(p);
|
|
if (!t || t->type != TOK_IDENT) {
|
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_EXPECTED, 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;
|
|
if (!expect(p, TOK_R_PAREN, "')' in tag args"))
|
|
return;
|
|
}
|
|
vec_push(*out, ref);
|
|
}
|
|
}
|
|
|
|
static void parse_param_list(spl_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 {
|
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_UNRECOGNIZED, t,
|
|
"unexpected token in parameter list");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static spl_ast_node_ref_t parse_param_decl_impl(spl_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)) {
|
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_EXPECTED, name, "expected parameter name or '_'");
|
|
vec_free(attr_list);
|
|
return 0;
|
|
}
|
|
advance(p);
|
|
|
|
if (!expect(p, TOK_COLON, "':' in parameter declaration")) {
|
|
vec_free(attr_list);
|
|
return 0;
|
|
}
|
|
|
|
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(spl_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) {
|
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_EXPECTED, name ? name : kw, "expected function name");
|
|
vec_free(tag_list);
|
|
return 0;
|
|
}
|
|
advance(p);
|
|
|
|
if (!expect(p, TOK_L_PAREN, "'(' in function declaration")) {
|
|
vec_free(tag_list);
|
|
return 0;
|
|
}
|
|
|
|
spl_ast_node_ref_vec_t params;
|
|
vec_init(params);
|
|
parse_param_list(p, ¶ms);
|
|
if (!expect(p, TOK_R_PAREN, "')' in function declaration")) {
|
|
vec_free(tag_list);
|
|
vec_free(params);
|
|
return 0;
|
|
}
|
|
|
|
spl_ast_node_ref_t ret_type = 0;
|
|
const spl_tok_t *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 {
|
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_UNRECOGNIZED, t,
|
|
"function declaration must end with ';' or block");
|
|
vec_free(tag_list);
|
|
vec_free(params);
|
|
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;
|
|
fn->fn_decl.ret_type = ret_type;
|
|
fn->fn_decl.body = body;
|
|
return ref;
|
|
}
|
|
|
|
static spl_ast_node_ref_t parse_type_decl(spl_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);
|
|
|
|
spl_ast_node_ref_t pattern = parse_pattern(p);
|
|
if (!pattern) {
|
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_EXPECTED, peek(p), "expected pattern after 'type'");
|
|
vec_free(tag_list);
|
|
return 0;
|
|
}
|
|
|
|
if (!expect(p, TOK_ASSIGN, "'=' in type declaration")) {
|
|
vec_free(tag_list);
|
|
return 0;
|
|
}
|
|
|
|
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);
|
|
td->type_decl.pattern = pattern;
|
|
|
|
spl_ast_node_ref_t type_expr = parse_type_expr(p);
|
|
td = node_at(p->ast, ref); /* parse_type_expr 可能 push node_buckets,重新取 */
|
|
td->type_decl.type_expr = type_expr;
|
|
|
|
const spl_tok_t *t = peek(p);
|
|
if (t && (t->type == TOK_COMMA || t->type == TOK_SEMICOLON))
|
|
advance(p);
|
|
return ref;
|
|
}
|
|
|
|
static spl_ast_node_ref_t parse_var_const_decl(spl_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_pattern(p);
|
|
if (!pattern) {
|
|
vec_free(tag_list);
|
|
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(spl_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(spl_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(spl_parser_t *p) {
|
|
spl_ast_node_ref_vec_t tag_list;
|
|
vec_init(tag_list);
|
|
parse_tag_list(p, &tag_list, 0, 0);
|
|
|
|
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);
|
|
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:
|
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_UNRECOGNIZED, t, "unrecognized declaration");
|
|
advance(p); /* 消费该 token,避免调用方在错误恢复时原地死循环 */
|
|
vec_free(tag_list);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* ---------- 类型 ---------- */
|
|
|
|
static spl_ast_node_ref_t parse_type_base(spl_parser_t *p) {
|
|
const spl_tok_t *bt = peek(p);
|
|
if (!bt) {
|
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_EXPECTED, bt, "expected type");
|
|
return 0;
|
|
}
|
|
|
|
if (bt->type == KW_FN) {
|
|
advance(p);
|
|
if (!expect(p, TOK_L_PAREN, "'(' in function type"))
|
|
return 0;
|
|
|
|
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;
|
|
}
|
|
|
|
if (!expect(p, TOK_R_PAREN, "')' in function type"))
|
|
return ref;
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
if (!expect(p, TOK_L_BRACE, "'{' in aggregate type"))
|
|
return 0;
|
|
|
|
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) {
|
|
const spl_tok_t *t = peek(p);
|
|
if (!t || t->type == TOK_EOF) {
|
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_EXPECTED, 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;
|
|
}
|
|
|
|
/* 内建类型 keyword + 普通标识符类型路径 */
|
|
if (bt->type == TOK_IDENT || is_builtin_type_kw(bt->type)) {
|
|
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;
|
|
}
|
|
|
|
if (bt->type == TOK_AT || bt->type == TOK_SHARP)
|
|
return parse_type_comptime_call(p);
|
|
|
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_UNRECOGNIZED, bt, "unrecognized type");
|
|
return 0;
|
|
}
|
|
|
|
static spl_ast_node_ref_t parse_type_comptime_call(spl_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) {
|
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_EXPECTED, 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);
|
|
if (!expect(p, TOK_R_PAREN, "')' in comptime type")) {
|
|
vec_free(args);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
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(spl_parser_t *p) {
|
|
const spl_tok_t *t = peek(p);
|
|
if (!t) {
|
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_EXPECTED, 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);
|
|
}
|
|
|
|
if (!expect(p, TOK_R_BRACKET, "']' in array type"))
|
|
return 0;
|
|
|
|
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);
|
|
}
|
|
|
|
if (!expect(p, TOK_R_PAREN, "')' in parenthesized type"))
|
|
return 0;
|
|
|
|
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;
|
|
}
|
|
|
|
spl_ast_node_ref_t base = parse_type_base(p);
|
|
if (p->failed || !base)
|
|
return base;
|
|
|
|
while (peek(p) && peek(p)->type == TOK_DOT) {
|
|
const spl_tok_t *dot = peek(p);
|
|
advance(p);
|
|
const spl_tok_t *seg = peek(p);
|
|
if (!seg || seg->type != TOK_IDENT) {
|
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_EXPECTED, seg ? seg : dot,
|
|
"type: expected identifier after '.'");
|
|
return base;
|
|
}
|
|
advance(p);
|
|
spl_ast_node_ref_t sel = new_node(p, SPL_AST_EXPR_FIELD, dot);
|
|
node_at(p->ast, sel)->field.obj = base;
|
|
SET_STR_FIELD(p->ast, node_at(p->ast, sel)->field.field, seg);
|
|
base = sel;
|
|
}
|
|
return base;
|
|
}
|
|
|
|
/* ---------- 表达式 ---------- */
|
|
|
|
static spl_ast_node_ref_vec_t parse_expr_list(spl_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(spl_parser_t *p) {
|
|
spl_ast_node_ref_vec_t fields;
|
|
vec_init(fields);
|
|
|
|
if (!expect(p, TOK_L_BRACE, "'{' in aggregate literal"))
|
|
return fields;
|
|
|
|
while (!p->failed) {
|
|
const spl_tok_t *t = peek(p);
|
|
if (!t || t->type == TOK_R_BRACE || t->type == TOK_EOF)
|
|
break;
|
|
|
|
if (t->type != TOK_DOT) {
|
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_EXPECTED, t, "expected '.' in aggregate field");
|
|
break;
|
|
}
|
|
advance(p);
|
|
|
|
t = peek(p);
|
|
if (!t || t->type != TOK_IDENT) {
|
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_EXPECTED, t, "expected field name after '.'");
|
|
break;
|
|
}
|
|
advance(p);
|
|
|
|
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;
|
|
}
|
|
|
|
expect(p, TOK_R_BRACE, "'}' in aggregate literal");
|
|
return fields;
|
|
}
|
|
|
|
typedef struct {
|
|
int prec;
|
|
char assoc;
|
|
} 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 mk_bin(spl_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(spl_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_prec(spl_parser_t *p, int min_prec) {
|
|
spl_ast_node_ref_t left = parse_cast(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);
|
|
left = mk_bin(p, t, op, left, right);
|
|
continue;
|
|
}
|
|
|
|
int next_min = (info.assoc == 'R') ? info.prec : 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(spl_parser_t *p) { return parse_expr_prec(p, 1); }
|
|
|
|
static spl_ast_node_ref_t parse_cast(spl_parser_t *p) {
|
|
spl_ast_node_ref_t expr = parse_prefix(p);
|
|
while (!p->failed) {
|
|
const spl_tok_t *t = peek(p);
|
|
if (!t || t->type != KW_AS)
|
|
break;
|
|
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;
|
|
}
|
|
return expr;
|
|
}
|
|
|
|
static spl_ast_node_ref_t parse_prefix(spl_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 {
|
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_EXPECTED, 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(spl_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;
|
|
|
|
if (!expect(p, TOK_R_PAREN, "')' in call"))
|
|
return expr;
|
|
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);
|
|
expr = mk_unary(p, t, SPL_UNARY_OP_DEREF, expr);
|
|
} else {
|
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_EXPECTED, 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);
|
|
if (!expect(p, TOK_R_BRACKET, "']' in slice"))
|
|
return expr;
|
|
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 {
|
|
if (!expect(p, TOK_R_BRACKET, "']' in index"))
|
|
return expr;
|
|
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 {
|
|
break;
|
|
}
|
|
}
|
|
return expr;
|
|
}
|
|
|
|
static spl_ast_node_ref_t parse_variant_construct(spl_parser_t *p, const spl_tok_t *dot) {
|
|
/* 已消费 '.',当前 token 是 IDENT */
|
|
const spl_tok_t *name = advance(p);
|
|
spl_ast_node_ref_vec_t fields;
|
|
vec_init(fields);
|
|
|
|
spl_ast_node_ref_t field = new_node(p, SPL_AST_STRUCT_INIT_ITEM, name);
|
|
node_at(p->ast, field)->struct_init_item.name = intern_string(p->ast, name->lexeme, name->len);
|
|
node_at(p->ast, field)->struct_init_item.expr = 0;
|
|
|
|
if (peek(p) && peek(p)->type == TOK_L_PAREN) {
|
|
advance(p);
|
|
spl_ast_node_ref_vec_t args;
|
|
vec_init(args);
|
|
if (peek(p) && peek(p)->type != TOK_R_PAREN)
|
|
args = parse_expr_list(p);
|
|
expect(p, TOK_R_PAREN, "')' after variant payload");
|
|
|
|
spl_ast_node_ref_t expr = 0;
|
|
if (args.size == 1) {
|
|
expr = args.data[0];
|
|
} else {
|
|
expr = new_node(p, SPL_AST_EXPR_TUPLE, name);
|
|
node_at(p->ast, expr)->tuple.elems = args;
|
|
vec_init(args);
|
|
}
|
|
vec_free(args);
|
|
node_at(p->ast, field)->struct_init_item.expr = expr;
|
|
}
|
|
|
|
vec_push(fields, field);
|
|
|
|
spl_ast_node_ref_t ref = new_node(p, SPL_AST_EXPR_AGGREGATE_LIT, dot);
|
|
spl_ast_node_t *agg = node_at(p->ast, ref);
|
|
agg->aggregate_literal.name = NULL;
|
|
agg->aggregate_literal.fields = fields;
|
|
return ref;
|
|
}
|
|
|
|
static spl_ast_node_ref_t parse_primary(spl_parser_t *p) {
|
|
const spl_tok_t *t = peek(p);
|
|
if (!t) {
|
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_UNRECOGNIZED, 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 = (uint64_t)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.bits = 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);
|
|
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) {
|
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_UNRECOGNIZED, 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) {
|
|
advance(p);
|
|
spl_ast_node_ref_vec_t elems = parse_expr_list(p);
|
|
if (!expect(p, TOK_R_BRACKET, "']' in array literal"))
|
|
return 0;
|
|
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 if (nx->type == TOK_IDENT) {
|
|
return parse_variant_construct(p, t);
|
|
} else {
|
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_EXPECTED, nx,
|
|
"expected '{', '[' or identifier 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));
|
|
}
|
|
if (!expect(p, TOK_R_PAREN, "')' in tuple literal"))
|
|
return 0;
|
|
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 {
|
|
if (!expect(p, TOK_R_PAREN, "')' in paren expression"))
|
|
return 0;
|
|
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:
|
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_UNRECOGNIZED, t, "unrecognized expression");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static spl_ast_node_ref_t parse_array_literal(spl_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);
|
|
}
|
|
|
|
if (!expect(p, TOK_R_BRACKET, "']' in array literal"))
|
|
return 0;
|
|
|
|
spl_ast_node_ref_t elem_type = parse_type_expr(p);
|
|
if (!expect(p, TOK_L_BRACKET, "'[' in array literal"))
|
|
return 0;
|
|
|
|
spl_ast_node_ref_vec_t elems = parse_expr_list(p);
|
|
if (!expect(p, TOK_R_BRACKET, "']' in array literal"))
|
|
return 0;
|
|
|
|
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 = (usize)len;
|
|
arr->array_literal.is_underscore_len = is_underscore;
|
|
arr->array_literal.elem_type = elem_type;
|
|
arr->array_literal.elems = elems;
|
|
return ref;
|
|
}
|
|
|
|
static spl_ast_node_ref_t parse_comptime_call(spl_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) {
|
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_EXPECTED, 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);
|
|
if (!expect(p, TOK_R_PAREN, "')' in comptime call")) {
|
|
vec_free(args);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
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(spl_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) {
|
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_EXPECTED, 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(spl_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_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);
|
|
p->stop_agg++;
|
|
init_expr = parse_expr(p);
|
|
p->stop_agg--;
|
|
} else {
|
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_EXPECTED, t, "expected '=' in if var");
|
|
return 0;
|
|
}
|
|
} else if (t && t->type == TOK_ASSIGN) {
|
|
advance(p);
|
|
p->stop_agg++;
|
|
init_expr = parse_expr(p);
|
|
p->stop_agg--;
|
|
} else {
|
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_EXPECTED, 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(spl_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(spl_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(spl_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 pattern = parse_pattern(p);
|
|
if (!pattern) {
|
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_EXPECTED, peek(p), "expected pattern in for");
|
|
return 0;
|
|
}
|
|
|
|
if (!expect(p, TOK_COLON, "':' after for pattern"))
|
|
return 0;
|
|
|
|
spl_ast_node_ref_vec_t iter_exprs;
|
|
vec_init(iter_exprs);
|
|
p->stop_agg++;
|
|
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_agg--;
|
|
|
|
spl_ast_node_ref_t body = parse_block(p);
|
|
if (!body) {
|
|
vec_free(iter_exprs);
|
|
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.pattern = pattern;
|
|
fe->for_expr.body = body;
|
|
fe->for_expr.else_branch = else_branch;
|
|
return ref;
|
|
}
|
|
|
|
static spl_ast_node_ref_t parse_match_expr(spl_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--;
|
|
|
|
if (!expect(p, TOK_L_BRACE, "'{' in match"))
|
|
return 0;
|
|
|
|
spl_ast_node_ref_vec_t patterns, bodies;
|
|
vec_init(patterns);
|
|
vec_init(bodies);
|
|
|
|
while (!p->failed) {
|
|
const spl_tok_t *t = peek(p);
|
|
if (!t || t->type == TOK_R_BRACE || t->type == TOK_EOF)
|
|
break;
|
|
|
|
spl_ast_node_ref_t pat = parse_pattern(p);
|
|
if (!pat)
|
|
break;
|
|
|
|
if (!expect(p, TOK_RIGHT_FAT_ARROW, "'=>' in match arm"))
|
|
break;
|
|
|
|
spl_ast_node_ref_t body = parse_block(p);
|
|
|
|
vec_push(patterns, pat);
|
|
vec_push(bodies, body);
|
|
|
|
if (peek(p) && peek(p)->type == TOK_COMMA)
|
|
advance(p);
|
|
}
|
|
|
|
if (!expect(p, TOK_R_BRACE, "'}' in match"))
|
|
return 0;
|
|
|
|
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);
|
|
vec_push(me->match_expr.arms, arm);
|
|
}
|
|
vec_free(patterns);
|
|
vec_free(bodies);
|
|
return ref;
|
|
}
|
|
|
|
static spl_ast_node_ref_t parse_ret_stmt(spl_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(spl_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(spl_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(spl_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(spl_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(spl_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);
|
|
|
|
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);
|
|
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;
|
|
}
|
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_UNRECOGNIZED, t, "unrecognized statement");
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
static spl_ast_node_ref_t parse_block(spl_parser_t *p) {
|
|
const spl_tok_t *start = peek(p);
|
|
if (!start || start->type != TOK_L_BRACE) {
|
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_EXPECTED, 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) {
|
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_EXPECTED, 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
|
|
break;
|
|
} else if (can_start_expr(t->type)) {
|
|
spl_ast_node_ref_t expr = parse_expr(p);
|
|
if (!expr)
|
|
break;
|
|
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) {
|
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_UNRECOGNIZED, 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 {
|
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_EXPECTED, t,
|
|
"expected ';' or '}' after expression");
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_UNRECOGNIZED, t, "unrecognized token in block");
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ref;
|
|
}
|
|
|
|
/* ---------- 入口 ---------- */
|
|
|
|
void spl_parser_init(spl_parser_t *parser) {
|
|
parser->ast = NULL;
|
|
parser->diag = NULL;
|
|
parser->pos = 0;
|
|
parser->failed = 0;
|
|
parser->stop_agg = 0;
|
|
parser->error_count = 0;
|
|
}
|
|
|
|
void spl_parser_drop(spl_parser_t *parser) { (void)parser; }
|
|
|
|
static void skip_to_sync(spl_parser_t *p) {
|
|
while (!p->failed) {
|
|
const spl_tok_t *t = peek(p);
|
|
if (!t || t->type == TOK_EOF)
|
|
return;
|
|
if (t->type == TOK_SEMICOLON || t->type == TOK_R_BRACE) {
|
|
advance(p);
|
|
return;
|
|
}
|
|
advance(p);
|
|
}
|
|
}
|
|
|
|
void spl_ast_parse(spl_parser_t *parser, spl_ast_t *ast) {
|
|
if (!ast || !ast->input.data || ast->node_buckets.size == 0)
|
|
return;
|
|
|
|
parser->ast = ast;
|
|
parser->pos = 0;
|
|
parser->failed = 0;
|
|
parser->stop_agg = 0;
|
|
|
|
spl_ast_node_ref_vec_t external_tags;
|
|
vec_init(external_tags);
|
|
spl_ast_node_ref_vec_t declarations;
|
|
vec_init(declarations);
|
|
|
|
while (!parser->failed) {
|
|
const spl_tok_t *t = peek(parser);
|
|
if (!t || t->type == TOK_EOF)
|
|
break;
|
|
|
|
if (t->type == TOK_AT || t->type == TOK_SHARP) {
|
|
usize save_pos = parser->pos;
|
|
const spl_tok_t *t2 = NULL;
|
|
parser->pos++;
|
|
t2 = peek(parser);
|
|
parser->pos = save_pos;
|
|
if (t2 && t2->type == TOK_NOT) {
|
|
parse_tag_list(parser, &external_tags, 1, 1);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
spl_ast_node_ref_t decl = parse_container_declaration(parser);
|
|
if (decl) {
|
|
vec_push(declarations, decl);
|
|
} else if (!parser->failed) {
|
|
const spl_tok_t *at = peek(parser);
|
|
if (at && at->type != TOK_EOF) {
|
|
skip_to_sync(parser);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
spl_ast_node_ref_t container_members = new_node(parser, 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(parser, SPL_AST_ROOT, NULL);
|
|
node_at(ast, root)->root.members = container_members;
|
|
|
|
ast->root = root;
|
|
}
|