stage1 修复错误 提供类型检查

This commit is contained in:
zzy
2026-07-25 10:28:24 +08:00
parent da9dae734c
commit 02095524b1
10 changed files with 640 additions and 153 deletions

View File

@@ -16,6 +16,7 @@ void spl_comp_init(spl_comp_t *ctx) {
map_init(ctx->const_values, MAP_HASH_STR, MAP_CMP_STR);
spl_prog_init(&ctx->prog);
ctx->error_msg[0] = '\0';
ctx->parse_context[0] = '\0';
spl_emit_init(&ctx->emit, &ctx->prog);
spl_type_ctx_init(&ctx->tctx);
ctx->current_ret_type_idx = -1;
@@ -67,10 +68,32 @@ void spl_comp_reset(spl_comp_t *ctx) {
}
void spl_comp_error(spl_comp_t *ctx, const char *fmt, ...) {
char body[COMP_ERROR_MAX - 64];
va_list args;
va_start(args, fmt);
vsnprintf(ctx->error_msg, COMP_ERROR_MAX - 1, fmt, args);
vsnprintf(body, sizeof(body), fmt, args);
va_end(args);
if (ctx->error_line > 0)
snprintf(ctx->error_msg, COMP_ERROR_MAX - 1, "line %zu:%zu: %s", ctx->error_line,
ctx->error_col, body);
else
snprintf(ctx->error_msg, COMP_ERROR_MAX - 1, "%s", body);
ctx->has_error = 1;
}
void spl_comp_err_tok(spl_comp_t *ctx, spl_tok_t *tok, const char *fmt, ...) {
char body[COMP_ERROR_MAX - 64];
va_list args;
va_start(args, fmt);
vsnprintf(body, sizeof(body), fmt, args);
va_end(args);
if (tok) {
snprintf(ctx->error_msg, COMP_ERROR_MAX - 1, "line %zu:%zu: %s", tok->line, tok->col, body);
} else {
snprintf(ctx->error_msg, COMP_ERROR_MAX - 1, "%s", body);
}
ctx->has_error = 1;
}

View File

@@ -77,6 +77,8 @@ typedef struct spl_comp {
/* Error state */
char error_msg[COMP_ERROR_MAX];
int has_error;
usize error_line;
usize error_col;
/* Scopes */
spl_scope_vec_t scopes;
@@ -91,6 +93,7 @@ typedef struct spl_comp {
/* Current function context */
int current_func_idx;
int current_ret_type_idx;
char parse_context[COMP_ERROR_MAX]; /* current parsing context for error messages */
/* Loop context for break/continue */
int in_loop;
@@ -124,6 +127,7 @@ void spl_comp_reset(spl_comp_t *ctx);
/* Error reporting */
void spl_comp_error(spl_comp_t *ctx, const char *fmt, ...);
void spl_comp_err_tok(spl_comp_t *ctx, spl_tok_t *tok, const char *fmt, ...);
/* ============================================================
* Parser functions (spl_parser.c)

View File

@@ -93,7 +93,12 @@ static int spl_resolve_type_member(spl_comp_t *ctx, int type_idx, const char *fi
continue;
if (strcmp(it->name, field) == 0) {
emit_push_i32(&ctx->emit, it->enum_field.value);
*result = (spl_expr_result_t){type_idx, 0};
int data_type = it->enum_field.type_idx;
if (data_type >= 0) {
*result = (spl_expr_result_t){data_type, 0};
} else {
*result = (spl_expr_result_t){type_idx, 0};
}
return 1;
}
}
@@ -278,14 +283,17 @@ static spl_expr_result_t parse_array_literal(spl_comp_t *ctx) {
int len_val;
if (!spl_parse_int_literal(ctx, &len_val)) {
spl_comp_error(ctx, "expected array length");
spl_comp_err_tok(ctx, peek(ctx), "expected array length");
spl_expr_result_t r = {-1, 0};
return r;
}
usize len = (usize)len_val;
skip_nl(ctx);
expect(ctx, TOK_R_BRACKET);
if (!expect(ctx, TOK_R_BRACKET)) {
spl_expr_result_t r = {-1, 0};
return r;
}
skip_nl(ctx);
int elem_type_idx = spl_type_parse(&ctx->tctx, ctx);
@@ -295,7 +303,10 @@ static spl_expr_result_t parse_array_literal(spl_comp_t *ctx) {
}
skip_nl(ctx);
expect(ctx, TOK_L_BRACE);
if (!expect(ctx, TOK_L_BRACE)) {
spl_expr_result_t _r = {-1, 0};
return _r;
}
skip_nl(ctx);
for (usize i = 0; i < len; i++) {
@@ -311,7 +322,10 @@ static spl_expr_result_t parse_array_literal(spl_comp_t *ctx) {
advance(ctx);
skip_nl(ctx);
expect(ctx, TOK_R_BRACE);
if (!expect(ctx, TOK_R_BRACE)) {
spl_expr_result_t _r = {-1, 0};
return _r;
}
int arr_type_idx = spl_type_array(&ctx->tctx, elem_type_idx, len);
return (spl_expr_result_t){arr_type_idx, 0};
@@ -320,7 +334,8 @@ static spl_expr_result_t parse_array_literal(spl_comp_t *ctx) {
static void parse_slice_inline(spl_comp_t *ctx) {
advance(ctx);
skip_nl(ctx);
while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
skip_nl(ctx);
while (!ctx->has_error && peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
if (peek(ctx)->type == TOK_COMMA) {
advance(ctx);
skip_nl(ctx);
@@ -347,7 +362,8 @@ static void parse_slice_inline(spl_comp_t *ctx) {
}
skip_nl(ctx);
}
expect(ctx, TOK_R_BRACE);
if (!expect(ctx, TOK_R_BRACE))
return;
emit_drop(&ctx->emit);
}
@@ -363,11 +379,13 @@ static void parse_one_field_init(spl_comp_t *ctx, spl_type_item_vec_t *items, in
advance(ctx);
skip_nl(ctx);
int found = 0;
vec_for(*items, fi) {
spl_type_item_t *it = &vec_at(*items, fi);
if (it->item_kind != ITEM_FIELD)
continue;
if (strcmp(it->name, fname) == 0) {
found = 1;
emit_laddr(&ctx->emit, base_offset);
usize byte_off = extra_offset + it->aggregate_field.offset;
emit_ptr_add(&ctx->emit, byte_off);
@@ -381,16 +399,17 @@ static void parse_one_field_init(spl_comp_t *ctx, spl_type_item_vec_t *items, in
skip_nl(ctx);
int len_val;
if (!spl_parse_int_literal(ctx, &len_val)) {
spl_comp_error(ctx, "expected array length");
spl_comp_err_tok(ctx, peek(ctx), "expected array length");
return;
}
usize arr_len = (usize)len_val;
skip_nl(ctx);
expect(ctx, TOK_R_BRACKET);
if (!expect(ctx, TOK_R_BRACKET))
return;
skip_nl(ctx);
int elem_type_idx = spl_type_parse(&ctx->tctx, ctx);
skip_nl(ctx);
expect(ctx, TOK_L_BRACE);
int elem_type_idx = spl_type_elem_type(&ctx->tctx, ft_idx);
if (!expect(ctx, TOK_L_BRACE))
return;
skip_nl(ctx);
usize stride = spl_type_elem_stride(&ctx->tctx, elem_type_idx);
spl_type_t st = spl_type_emit_type(&ctx->tctx, elem_type_idx);
@@ -417,7 +436,8 @@ static void parse_one_field_init(spl_comp_t *ctx, spl_type_item_vec_t *items, in
if (peek(ctx)->type == TOK_COMMA)
advance(ctx);
skip_nl(ctx);
expect(ctx, TOK_R_BRACE);
if (!expect(ctx, TOK_R_BRACE))
return;
emit_drop(&ctx->emit);
} else if (ft_idx >= 0 &&
(spl_type_kind(&ctx->tctx, ft_idx) == TYPE_STRUCT ||
@@ -444,6 +464,9 @@ static void parse_one_field_init(spl_comp_t *ctx, spl_type_item_vec_t *items, in
break;
}
}
if (!found) {
spl_comp_error(ctx, "unknown field '%s' in struct literal", fname);
}
}
spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, int type_idx) {
@@ -454,7 +477,7 @@ spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, int type_idx) {
skip_nl(ctx);
if (spl_type_kind(&ctx->tctx, type_idx) == TYPE_STRUCT) {
while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
while (!ctx->has_error && peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
if (peek(ctx)->type == TOK_COMMA) {
advance(ctx);
skip_nl(ctx);
@@ -470,14 +493,8 @@ spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, int type_idx) {
skip_nl(ctx);
}
} else if (spl_type_kind(&ctx->tctx, type_idx) == TYPE_SLICE) {
while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
if (peek(ctx)->type == TOK_COMMA) {
advance(ctx);
skip_nl(ctx);
continue;
}
if (peek(ctx)->type == TOK_DOT)
advance(ctx);
while (!ctx->has_error && peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
advance(ctx);
spl_tok_t *ftok = advance(ctx);
char fname[256];
spl_tok_copy_name(ftok, fname, sizeof(fname));
@@ -535,7 +552,8 @@ spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, int type_idx) {
peek(ctx)->type == TOK_L_BRACE) {
advance(ctx);
skip_nl(ctx);
while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
while (!ctx->has_error && peek(ctx)->type != TOK_R_BRACE &&
peek(ctx)->type != TOK_EOF) {
if (peek(ctx)->type == TOK_COMMA) {
advance(ctx);
skip_nl(ctx);
@@ -547,7 +565,10 @@ spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, int type_idx) {
base_offset, DATA_OFFSET);
skip_nl(ctx);
}
expect(ctx, TOK_R_BRACE);
if (!expect(ctx, TOK_R_BRACE)) {
spl_expr_result_t _r = {-1, 0};
return _r;
}
} else if (dt_idx >= 0 &&
(spl_type_kind(&ctx->tctx, dt_idx) == TYPE_STRUCT ||
spl_type_kind(&ctx->tctx, dt_idx) == TYPE_ENUM) &&
@@ -577,11 +598,14 @@ spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, int type_idx) {
}
}
if (!found)
spl_comp_error(ctx, "unknown enum variant '%s'", vname);
spl_comp_err_tok(ctx, vtok, "unknown enum variant '%s'", vname);
}
skip_nl(ctx);
expect(ctx, TOK_R_BRACE);
if (!expect(ctx, TOK_R_BRACE)) {
spl_expr_result_t _r = {-1, 0};
return _r;
}
if (sz <= sizeof(spl_val_t)) {
emit_laddr(&ctx->emit, base_offset);
@@ -596,7 +620,7 @@ spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, int type_idx) {
static int parse_call_args(spl_comp_t *ctx) {
int nargs = 0;
if (peek(ctx)->type != TOK_R_PAREN) {
for (;;) {
while (!ctx->has_error) {
spl_parse_expr(ctx, PREC_MIN);
nargs++;
if (peek(ctx)->type == TOK_COMMA) {
@@ -708,8 +732,8 @@ static void spl_check_arg_type(spl_comp_t *ctx, const char *fname, int arg_type_
type_mismatch:;
const char *as = spl_type_str(&ctx->tctx, arg_type_idx);
const char *ps = spl_type_str(&ctx->tctx, param_type_idx);
spl_comp_error(ctx, "argument %d of '%s' type mismatch: expected '%s', got '%s'", arg_idx + 1,
fname, ps, as);
spl_comp_err_tok(ctx, peek(ctx), "argument %d of '%s' type mismatch: expected '%s', got '%s'",
arg_idx + 1, fname, ps, as);
}
static int parse_call_args_checked(spl_comp_t *ctx, const char *fname, int *param_type_indices,
@@ -717,11 +741,20 @@ static int parse_call_args_checked(spl_comp_t *ctx, const char *fname, int *para
int nargs = 0;
int nlogical = 0;
if (peek(ctx)->type != TOK_R_PAREN) {
for (;;) {
while (!ctx->has_error) {
spl_expr_result_t arg = spl_parse_expr(ctx, PREC_MIN);
if (param_type_indices && nlogical < nparams && param_type_indices[nlogical] >= 0)
if (param_type_indices && nlogical < nparams && param_type_indices[nlogical] >= 0) {
int param_u = spl_type_resolve_underlying(&ctx->tctx, param_type_indices[nlogical]);
int arg_u = spl_type_resolve_underlying(&ctx->tctx, arg.type_idx);
if (arg.type_idx >= 0 && spl_type_kind(&ctx->tctx, param_u) == TYPE_BASIC &&
spl_type_is_integer(spl_type_basic_type(&ctx->tctx, param_u)) &&
spl_type_kind(&ctx->tctx, arg_u) == TYPE_BASIC &&
spl_type_is_integer(spl_type_basic_type(&ctx->tctx, arg_u))) {
arg.type_idx = param_type_indices[nlogical];
}
spl_check_arg_type(ctx, fname, arg.type_idx, param_type_indices[nlogical],
nlogical);
}
int arg_slots = 1;
if (param_type_indices && nlogical < nparams && param_type_indices[nlogical] >= 0 &&
@@ -761,7 +794,7 @@ static spl_expr_result_t parse_ident(spl_comp_t *ctx) {
if (peek(ctx)->type == TOK_L_PAREN) {
int fi = spl_lookup_func(ctx, name);
if (fi < 0) {
spl_comp_error(ctx, "unknown function '%s'", name);
spl_comp_err_tok(ctx, t, "unknown function '%s'", name);
spl_expr_result_t r = {-1, 0};
return r;
}
@@ -769,12 +802,19 @@ static spl_expr_result_t parse_ident(spl_comp_t *ctx) {
advance(ctx);
int nargs = parse_call_args_checked(ctx, f->name, f->param_type_indices, f->nparams);
expect(ctx, TOK_R_PAREN);
if (!expect(ctx, TOK_R_PAREN)) {
spl_expr_result_t _r = {-1, 0};
return _r;
}
if (f->is_extern) {
int nidx = spl_ensure_native(ctx, f->name);
emit_push_i32(&ctx->emit, nidx);
emit_ncall(&ctx->emit, nargs);
if (spl_type_kind(&ctx->tctx, f->ret_type_idx) == TYPE_BASIC &&
spl_type_basic_type(&ctx->tctx, f->ret_type_idx) == SPL_VOID) {
emit_drop(&ctx->emit);
}
} else {
emit_call_with_fixup(&ctx->emit, nargs, f->func_idx);
}
@@ -805,7 +845,12 @@ static spl_expr_result_t parse_ident(spl_comp_t *ctx) {
emit_laddr(&ctx->emit, v->offset);
spl_expr_result_t r;
emit_load_or_addr_type(ctx, &r, vt_idx);
spl_tok_type_t next_type = peek(ctx)->type;
if (next_type == TOK_DOT) {
r = (spl_expr_result_t){vt_idx, 1};
} else {
emit_load_or_addr_type(ctx, &r, vt_idx);
}
return r;
}
@@ -820,7 +865,7 @@ static spl_expr_result_t parse_ident(spl_comp_t *ctx) {
return r;
}
spl_comp_error(ctx, "undefined variable '%s'", name);
spl_comp_err_tok(ctx, t, "undefined variable '%s'", name);
spl_expr_result_t r = {-1, 0};
return r;
}
@@ -828,7 +873,8 @@ static spl_expr_result_t parse_ident(spl_comp_t *ctx) {
static spl_expr_result_t parse_group(spl_comp_t *ctx) {
advance(ctx);
spl_expr_result_t r = spl_parse_expr(ctx, PREC_MIN);
expect(ctx, TOK_R_PAREN);
if (!expect(ctx, TOK_R_PAREN))
return r;
return r;
}
@@ -865,7 +911,9 @@ static spl_expr_result_t parse_prefix_op(spl_comp_t *ctx) {
break;
case TOK_AND:
if (!right.is_lvalue) {
spl_comp_error(ctx, "cannot take address of rvalue");
spl_comp_err_tok(ctx, peek(ctx), "cannot take address of rvalue");
spl_expr_result_t _r = {-1, 0};
return _r;
}
right.type_idx = spl_type_ptr(&ctx->tctx, right.type_idx);
break;
@@ -935,7 +983,7 @@ static spl_expr_result_t parse_primary_expr(spl_comp_t *ctx) {
skip_nl(ctx);
tok = peek(ctx);
if (!tok || tok->type != TOK_IDENT) {
spl_comp_error(ctx, "expected builtin name after '@'");
spl_comp_err_tok(ctx, tok, "expected builtin name after '@'");
spl_expr_result_t r = {-1, 0};
return r;
}
@@ -947,7 +995,10 @@ static spl_expr_result_t parse_primary_expr(spl_comp_t *ctx) {
if (peek(ctx)->type == TOK_L_PAREN) {
advance(ctx);
int nargs = parse_call_args(ctx);
expect(ctx, TOK_R_PAREN);
if (!expect(ctx, TOK_R_PAREN)) {
spl_expr_result_t _r = {-1, 0};
return _r;
}
for (int i = 0; i < nargs; i++) {
emit_dbg_usize(&ctx->emit);
emit_drop(&ctx->emit);
@@ -959,11 +1010,17 @@ static spl_expr_result_t parse_primary_expr(spl_comp_t *ctx) {
}
return (spl_expr_result_t){spl_type_basic(&ctx->tctx, SPL_VOID), 0};
} else if (strcmp(bname, "sizeof") == 0) {
expect(ctx, TOK_L_PAREN);
if (!expect(ctx, TOK_L_PAREN)) {
spl_expr_result_t _r = {-1, 0};
return _r;
}
int ti = spl_type_parse(&ctx->tctx, ctx);
expect(ctx, TOK_R_PAREN);
if (!expect(ctx, TOK_R_PAREN)) {
spl_expr_result_t _r = {-1, 0};
return _r;
}
if (ti < 0) {
spl_comp_error(ctx, "@sizeof: invalid type");
spl_comp_err_tok(ctx, peek(ctx), "@sizeof: invalid type");
spl_expr_result_t r = {-1, 0};
return r;
}
@@ -971,13 +1028,13 @@ static spl_expr_result_t parse_primary_expr(spl_comp_t *ctx) {
emit_push_usize(&ctx->emit, sz);
return (spl_expr_result_t){spl_type_basic(&ctx->tctx, SPL_USIZE), 0};
} else {
spl_comp_error(ctx, "unknown builtin '@%s'", bname);
spl_comp_err_tok(ctx, peek(ctx), "unknown builtin '@%s'", bname);
spl_expr_result_t r = {-1, 0};
return r;
}
}
default:
if (peek(ctx)->type >= KW_AS && peek(ctx)->type <= KW_ANY) {
if (peek(ctx)->type > KW_AS && peek(ctx)->type <= KW_ANY) {
return parse_ident(ctx);
}
spl_expr_result_t r = {-1, 0};
@@ -990,6 +1047,8 @@ static spl_expr_result_t parse_postfix_expr(spl_comp_t *ctx, spl_expr_result_t l
return left;
for (;;) {
if (ctx->has_error)
break;
skip_nl(ctx);
spl_tok_type_t opt = peek(ctx)->type;
@@ -1044,8 +1103,9 @@ static spl_expr_result_t parse_postfix_expr(spl_comp_t *ctx, spl_expr_result_t l
if (is_instance) {
if (!left.is_lvalue && !is_ptr_self) {
spl_comp_error(ctx, "cannot call instance method '%s' on type",
fname);
spl_comp_err_tok(ctx, field,
"cannot call instance method '%s' on type", fname);
return left;
}
emit_drop(&ctx->emit);
nargs = 0;
@@ -1056,7 +1116,10 @@ static spl_expr_result_t parse_postfix_expr(spl_comp_t *ctx, spl_expr_result_t l
int cn = func->nparams;
nargs += parse_call_args_checked(ctx, func->name, cp, cn);
}
expect(ctx, TOK_R_PAREN);
if (!expect(ctx, TOK_R_PAREN)) {
spl_expr_result_t _r = left;
return _r;
}
if (func->func_idx < 0) {
fprintf(stderr, "WARN: method call '%s.' with invalid func_idx=%d\n",
@@ -1083,6 +1146,7 @@ static spl_expr_result_t parse_postfix_expr(spl_comp_t *ctx, spl_expr_result_t l
if (left.type_idx >= 0 && spl_type_kind(&ctx->tctx, left.type_idx) == TYPE_STRUCT) {
spl_type_item_vec_t *f_items = spl_type_items(&ctx->tctx, left.type_idx);
int found = 0;
vec_for(*f_items, fi) {
spl_type_item_t *fit = &vec_at(*f_items, fi);
if (fit->item_kind != ITEM_FIELD)
@@ -1090,10 +1154,12 @@ static spl_expr_result_t parse_postfix_expr(spl_comp_t *ctx, spl_expr_result_t l
if (strcmp(fit->name, fname) == 0) {
emit_ptr_add(&ctx->emit, fit->aggregate_field.offset);
emit_load_or_addr_type(ctx, &left, fit->aggregate_field.type_idx);
found = 1;
break;
}
}
continue;
if (found)
continue;
}
if (left.type_idx >= 0 && spl_type_kind(&ctx->tctx, left.type_idx) == TYPE_PTR) {
@@ -1103,6 +1169,7 @@ static spl_expr_result_t parse_postfix_expr(spl_comp_t *ctx, spl_expr_result_t l
emit_load_ptr(&ctx->emit);
}
spl_type_item_vec_t *st_items = spl_type_items(&ctx->tctx, elem_idx);
int found = 0;
vec_for(*st_items, si) {
spl_type_item_t *sit = &vec_at(*st_items, si);
if (sit->item_kind != ITEM_FIELD)
@@ -1110,10 +1177,12 @@ static spl_expr_result_t parse_postfix_expr(spl_comp_t *ctx, spl_expr_result_t l
if (strcmp(sit->name, fname) == 0) {
emit_ptr_add(&ctx->emit, sit->aggregate_field.offset);
emit_load_or_addr_type(ctx, &left, sit->aggregate_field.type_idx);
found = 1;
break;
}
}
continue;
if (found)
continue;
}
}
@@ -1159,7 +1228,7 @@ static spl_expr_result_t parse_postfix_expr(spl_comp_t *ctx, spl_expr_result_t l
continue;
}
spl_comp_error(ctx, "unknown field '%s'", fname);
spl_comp_err_tok(ctx, field, "unknown field '%s'", fname);
continue;
}
@@ -1186,7 +1255,8 @@ static spl_expr_result_t parse_postfix_expr(spl_comp_t *ctx, spl_expr_result_t l
ctx->addr_of_mode = saved_aom;
}
(void)end_expr;
expect(ctx, TOK_R_BRACKET);
if (!expect(ctx, TOK_R_BRACKET))
return left;
if (!has_explicit_end) {
if (left.type_idx >= 0 &&
@@ -1235,7 +1305,8 @@ static spl_expr_result_t parse_postfix_expr(spl_comp_t *ctx, spl_expr_result_t l
continue;
}
expect(ctx, TOK_R_BRACKET);
if (!expect(ctx, TOK_R_BRACKET))
break;
if (left.type_idx >= 0 && (spl_type_kind(&ctx->tctx, left.type_idx) == TYPE_ARRAY ||
spl_type_kind(&ctx->tctx, left.type_idx) == TYPE_PTR ||
@@ -1304,8 +1375,7 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) {
spl_expr_result_t left = parse_primary_expr(ctx);
if (left.type_idx < 0)
return left;
while (1) {
while (!ctx->has_error) {
left = parse_postfix_expr(ctx, left);
skip_nl(ctx);
@@ -1378,8 +1448,8 @@ static spl_expr_result_t parse_infix(spl_comp_t *ctx, spl_expr_result_t left, sp
spl_parse_expr(ctx, next_prec);
if ((op == TOK_EQ || op == TOK_NEQ) && left.type_idx >= 0 &&
!spl_type_is_scalar(&ctx->tctx, left.type_idx)) {
spl_comp_error(ctx, "type '%s' does not support comparison",
spl_type_str(&ctx->tctx, left.type_idx));
spl_comp_err_tok(ctx, peek(ctx), "type '%s' does not support comparison",
spl_type_str(&ctx->tctx, left.type_idx));
}
spl_type_t bt = spl_type_emit_type(&ctx->tctx, left.type_idx);
int sop = binop_to_sir(op, bt);
@@ -1394,29 +1464,27 @@ static spl_expr_result_t parse_infix(spl_comp_t *ctx, spl_expr_result_t left, sp
int spl_emit_match_enum_cmp(spl_comp_t *ctx, int enum_type_idx, int val_offset, int by_value) {
char vname[256];
spl_tok_t *vtok = NULL;
if (peek(ctx)->type == TOK_DOT) {
advance(ctx);
vtok = advance(ctx);
spl_tok_copy_name(vtok, vname, sizeof(vname));
} else {
spl_tok_t *tok = advance(ctx);
spl_tok_copy_name(tok, vname, sizeof(vname));
vtok = advance(ctx);
spl_tok_copy_name(vtok, vname, sizeof(vname));
while (peek(ctx)->type == TOK_DOT) {
int qt_idx = spl_type_resolve(&ctx->tctx, vname);
(void)qt_idx;
advance(ctx);
tok = advance(ctx);
spl_tok_copy_name(tok, vname, sizeof(vname));
vtok = advance(ctx);
spl_tok_copy_name(vtok, vname, sizeof(vname));
}
goto lookup;
}
{
spl_tok_t *vtok = advance(ctx);
spl_tok_copy_name(vtok, vname, sizeof(vname));
}
lookup: {
spl_type_item_vec_t *e_items = spl_type_items(&ctx->tctx, enum_type_idx);
vec_for(*e_items, vi) {
@@ -1438,7 +1506,7 @@ lookup: {
}
}
}
spl_comp_error(ctx, "unknown variant '%s' in match", vname);
spl_comp_err_tok(ctx, vtok, "unknown variant '%s' in match", vname);
return -1;
}

View File

@@ -14,12 +14,30 @@ spl_tok_t *advance(spl_comp_t *ctx) {
}
int expect(spl_comp_t *ctx, spl_tok_type_t type) {
if (peek(ctx)->type == type) {
spl_tok_t *tok = peek(ctx);
if (tok->type == type) {
advance(ctx);
return 1;
}
spl_comp_error(ctx, "expected '%s', got '%s'", spl_tok_type_name(type),
spl_tok_type_name(peek(ctx)->type));
/* Extract token text for display (up to 40 chars) */
char val_buf[64];
usize display_len = tok->len < 40 ? tok->len : 40;
memcpy(val_buf, tok->lexeme, display_len);
val_buf[display_len] = '\0';
/* Replace newlines with \n for display */
for (usize i = 0; i < display_len; i++) {
if (val_buf[i] == '\n')
val_buf[i] = ' ';
}
if (ctx->parse_context[0]) {
spl_comp_err_tok(ctx, tok, "%s: expected %s, got %s '%s'", ctx->parse_context,
spl_tok_type_name(type), spl_tok_type_name(tok->type), val_buf);
} else {
spl_comp_err_tok(ctx, tok, "expected %s, got %s '%s'", spl_tok_type_name(type),
spl_tok_type_name(tok->type), val_buf);
}
return 0;
}

View File

@@ -14,6 +14,16 @@ static int parse_params_decl(spl_comp_t *ctx, char pnames[][256], int ptypes[])
int nparams = 0;
skip_nl(ctx);
if (peek(ctx)->type != TOK_R_PAREN) {
{
usize saved = ctx->tok_idx;
spl_tok_t *ptok = advance(ctx);
skip_nl(ctx);
if (ptok->type == KW_VOID && peek(ctx)->type == TOK_R_PAREN) {
expect(ctx, TOK_R_PAREN);
return 0;
}
ctx->tok_idx = saved;
}
while (1) {
spl_tok_t *pname = advance(ctx);
spl_tok_copy_name(pname, pnames[nparams], 256);
@@ -45,6 +55,7 @@ static int parse_params_decl(spl_comp_t *ctx, char pnames[][256], int ptypes[])
static int parse_fn_body(spl_comp_t *ctx, const char *fn_name, int ret_type_idx, int nparams,
char pnames[][256], int ptypes[], int is_pub) {
snprintf(ctx->parse_context, sizeof(ctx->parse_context), "function '%s'", fn_name);
int fi = spl_declare_func(ctx, fn_name, ret_type_idx, nparams, 0, is_pub);
{
@@ -75,11 +86,35 @@ static int parse_fn_body(spl_comp_t *ctx, const char *fn_name, int ret_type_idx,
spl_val_t alloc_addr = vec_size(ctx->prog.insns);
emit_alloc(&ctx->emit, 0);
while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
while (!ctx->has_error && peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
spl_parse_stmt(ctx);
skip_nl(ctx);
}
/* Error recovery: skip to matching } if has_error caused early exit */
if (ctx->has_error) {
int depth = 1;
while (depth > 0 && ctx->tok_idx < vec_size(ctx->toks)) {
spl_tok_type_t tt = peek(ctx)->type;
if (tt == TOK_L_BRACE)
depth++;
else if (tt == TOK_R_BRACE) {
depth--;
if (depth == 0)
break;
} else if (tt == TOK_EOF)
break;
advance(ctx);
}
if (peek(ctx)->type == TOK_R_BRACE)
advance(ctx);
else
return 0;
} else {
if (!expect(ctx, TOK_R_BRACE))
return 0;
}
int total_phys_slots = 0;
for (int i = 0; i < nparams; i++) {
usize psz = spl_type_size(&ctx->tctx, ptypes[i]);
@@ -98,7 +133,6 @@ static int parse_fn_body(spl_comp_t *ctx, const char *fn_name, int ret_type_idx,
alloc_slots, ctx->emit.frame.peak_bytes, total_phys_slots);
}
emit_patch(&ctx->emit, alloc_addr, alloc_slots);
expect(ctx, TOK_R_BRACE);
}
spl_emit_defer_epilogue(ctx, ctx->scope_depth);
@@ -122,7 +156,8 @@ static void parse_fn_decl(spl_comp_t *ctx, int is_extern, int is_pub) {
char fn_name[256];
spl_tok_copy_name(fname_tok, fn_name, sizeof(fn_name));
skip_nl(ctx);
expect(ctx, TOK_L_PAREN);
if (!expect(ctx, TOK_L_PAREN))
return;
char pnames[MAX_PARAMS][256];
int ptypes[MAX_PARAMS];
@@ -176,7 +211,8 @@ static void parse_method_decl(spl_comp_t *ctx, int container_type_idx) {
snprintf(qualified, sizeof(qualified), "%s.%s", cname ? cname : "anon", mname);
skip_nl(ctx);
expect(ctx, TOK_L_PAREN);
if (!expect(ctx, TOK_L_PAREN))
return;
char pnames[MAX_PARAMS][256];
int ptypes[MAX_PARAMS];
@@ -200,42 +236,6 @@ static void parse_method_decl(spl_comp_t *ctx, int container_type_idx) {
* Parse type container body (shared for struct, union, enum)
* ============================================================ */
static void skip_type_decl(spl_comp_t *ctx) {
advance(ctx); /* type */
advance(ctx); /* name */
skip_nl(ctx);
expect(ctx, TOK_ASSIGN);
skip_nl(ctx);
spl_tok_type_t tt = peek(ctx)->type;
if (tt == KW_STRUCT || tt == KW_UNION || tt == KW_ENUM) {
advance(ctx);
skip_nl(ctx);
}
if (peek(ctx)->type == TOK_L_BRACE) {
int bd = 1;
advance(ctx);
while (bd > 0 && ctx->tok_idx < vec_size(ctx->toks)) {
spl_tok_type_t tt = peek(ctx)->type;
if (tt == TOK_L_BRACE)
bd++;
else if (tt == TOK_R_BRACE)
bd--;
else if (tt == TOK_EOF)
break;
advance(ctx);
}
} else {
while (peek(ctx)->type != TOK_SEMICOLON && peek(ctx)->type != TOK_EOF)
advance(ctx);
}
skip_nl(ctx);
if (peek(ctx)->type == TOK_SEMICOLON)
advance(ctx);
}
static void parse_type_body(spl_comp_t *ctx, int container_type_idx, int is_enum) {
if (peek(ctx)->type != TOK_L_BRACE)
return;
@@ -248,7 +248,7 @@ static void parse_type_body(spl_comp_t *ctx, int container_type_idx, int is_enum
{
usize saved = ctx->tok_idx;
int depth = 1;
while (depth > 0 && ctx->tok_idx < vec_size(ctx->toks)) {
while (!ctx->has_error && depth > 0 && ctx->tok_idx < vec_size(ctx->toks)) {
spl_tok_type_t tt = peek(ctx)->type;
if (tt == TOK_L_BRACE) {
depth++;
@@ -275,7 +275,7 @@ static void parse_type_body(spl_comp_t *ctx, int container_type_idx, int is_enum
if (cname) {
usize saved = ctx->tok_idx;
int depth = 1;
while (depth > 0 && ctx->tok_idx < vec_size(ctx->toks)) {
while (!ctx->has_error && depth > 0 && ctx->tok_idx < vec_size(ctx->toks)) {
spl_tok_type_t tt = peek(ctx)->type;
if (tt == TOK_L_BRACE) {
depth++;
@@ -331,7 +331,7 @@ static void parse_type_body(spl_comp_t *ctx, int container_type_idx, int is_enum
/* === Pass 2: Parse fields/variants and methods === */
{
int depth = 1;
while (depth > 0 && ctx->tok_idx < vec_size(ctx->toks)) {
while (!ctx->has_error && depth > 0 && ctx->tok_idx < vec_size(ctx->toks)) {
skip_nl(ctx);
spl_tok_type_t tt = peek(ctx)->type;
if (tt == TOK_L_BRACE) {
@@ -345,7 +345,7 @@ static void parse_type_body(spl_comp_t *ctx, int container_type_idx, int is_enum
}
advance(ctx);
} else if (tt == KW_TYPE && depth == 1) {
skip_type_decl(ctx);
parse_type_decl(ctx);
} else if (tt == KW_VAR && depth == 1 && !is_enum) {
advance(ctx); /* var */
skip_nl(ctx);
@@ -358,7 +358,7 @@ static void parse_type_body(spl_comp_t *ctx, int container_type_idx, int is_enum
char fname[256];
spl_tok_copy_name(ftok, fname, sizeof(fname));
if (ftype >= 0)
spl_type_add_field(&ctx->tctx, container_type_idx, fname, ftype);
spl_type_add_var(&ctx->tctx, container_type_idx, fname, ftype);
}
skip_nl(ctx);
if (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_COMMA)
@@ -420,31 +420,48 @@ void parse_type_decl(spl_comp_t *ctx) {
spl_tok_t *name_tok = advance(ctx);
char tname[256];
spl_tok_copy_name(name_tok, tname, sizeof(tname));
snprintf(ctx->parse_context, sizeof(ctx->parse_context), "type '%s'", tname);
skip_nl(ctx);
expect(ctx, TOK_ASSIGN);
if (!expect(ctx, TOK_ASSIGN))
return;
skip_nl(ctx);
int parent_type_idx = ctx->tctx.current_type_idx;
/* Check if already pre-registered in Pass 0 */
int existing = spl_type_resolve(&ctx->tctx, tname);
if (peek(ctx)->type == KW_STRUCT) {
advance(ctx);
int ti = spl_type_struct(&ctx->tctx, tname);
if (parent_type_idx >= 0)
int ti;
if (existing >= 0)
ti = existing;
else
ti = spl_type_struct(&ctx->tctx, tname);
if (parent_type_idx >= 0 && existing < 0)
spl_type_add_nested(&ctx->tctx, parent_type_idx, tname, ti);
skip_nl(ctx);
parse_type_body(ctx, ti, 0);
} else if (peek(ctx)->type == KW_UNION) {
advance(ctx);
int ti = spl_type_union(&ctx->tctx, tname);
if (parent_type_idx >= 0)
int ti;
if (existing >= 0)
ti = existing;
else
ti = spl_type_union(&ctx->tctx, tname);
if (parent_type_idx >= 0 && existing < 0)
spl_type_add_nested(&ctx->tctx, parent_type_idx, tname, ti);
skip_nl(ctx);
parse_type_body(ctx, ti, 1);
} else if (peek(ctx)->type == KW_ENUM) {
advance(ctx);
int ti = spl_type_enum(&ctx->tctx, tname);
if (parent_type_idx >= 0)
int ti;
if (existing >= 0)
ti = existing;
else
ti = spl_type_enum(&ctx->tctx, tname);
if (parent_type_idx >= 0 && existing < 0)
spl_type_add_nested(&ctx->tctx, parent_type_idx, tname, ti);
skip_nl(ctx);
parse_type_body(ctx, ti, 1);
@@ -461,12 +478,213 @@ void parse_type_decl(spl_comp_t *ctx) {
advance(ctx);
}
/* ============================================================
* Pre-registration pass: register all names before filling bodies
* ============================================================ */
/* Skip a { ... } function body */
static void skip_fn_body(spl_comp_t *ctx) {
int depth = 1;
while (depth > 0 && ctx->tok_idx < vec_size(ctx->toks)) {
spl_tok_type_t tt = peek(ctx)->type;
if (tt == TOK_L_BRACE)
depth++;
else if (tt == TOK_R_BRACE) {
depth--;
if (depth == 0)
break;
} else if (tt == TOK_EOF)
break;
advance(ctx);
}
if (peek(ctx)->type == TOK_R_BRACE)
advance(ctx);
}
/* Pre-register a type name (and nested type names), skip the body */
static void pre_register_type_decl(spl_comp_t *ctx) {
advance(ctx); /* type */
spl_tok_t *name_tok = advance(ctx);
char tname[256];
spl_tok_copy_name(name_tok, tname, sizeof(tname));
skip_nl(ctx);
if (!expect(ctx, TOK_ASSIGN))
return;
skip_nl(ctx);
int parent_type_idx = ctx->tctx.current_type_idx;
int ti = -1;
if (peek(ctx)->type == KW_STRUCT) {
advance(ctx);
ti = spl_type_struct(&ctx->tctx, tname);
} else if (peek(ctx)->type == KW_ENUM) {
advance(ctx);
ti = spl_type_enum(&ctx->tctx, tname);
} else if (peek(ctx)->type == KW_UNION) {
advance(ctx);
ti = spl_type_union(&ctx->tctx, tname);
}
if (ti >= 0) {
if (parent_type_idx >= 0)
spl_type_add_nested(&ctx->tctx, parent_type_idx, tname, ti);
/* Register nested type names inside the body */
if (peek(ctx)->type == TOK_L_BRACE) {
int saved_current = ctx->tctx.current_type_idx;
ctx->tctx.current_type_idx = ti;
int depth = 1;
advance(ctx); /* { */
while (depth > 0 && ctx->tok_idx < vec_size(ctx->toks)) {
spl_tok_type_t tt = peek(ctx)->type;
if (tt == TOK_L_BRACE) {
depth++;
advance(ctx);
} else if (tt == TOK_R_BRACE) {
depth--;
if (depth == 0)
break;
advance(ctx);
} else if (tt == KW_TYPE && depth == 1) {
pre_register_type_decl(ctx);
} else if (tt == TOK_EOF) {
break;
} else {
advance(ctx);
}
}
if (peek(ctx)->type == TOK_R_BRACE)
advance(ctx);
ctx->tctx.current_type_idx = saved_current;
}
}
skip_nl(ctx);
if (peek(ctx)->type == TOK_SEMICOLON)
advance(ctx);
}
/* Pre-register a function name and signature, skip the body */
static void pre_register_fn_decl(spl_comp_t *ctx, int is_extern) {
advance(ctx);
skip_nl(ctx);
spl_tok_t *fname_tok = advance(ctx);
char fn_name[256];
spl_tok_copy_name(fname_tok, fn_name, sizeof(fn_name));
skip_nl(ctx);
if (!expect(ctx, TOK_L_PAREN))
return;
char pnames[MAX_PARAMS][256];
int ptypes[MAX_PARAMS];
int nparams = parse_params_decl(ctx, pnames, ptypes);
skip_nl(ctx);
int ret_type_idx = spl_type_basic(&ctx->tctx, SPL_VOID);
if (peek(ctx)->type != TOK_SEMICOLON && peek(ctx)->type != TOK_L_BRACE) {
ret_type_idx = spl_type_parse(&ctx->tctx, ctx);
if (ret_type_idx < 0)
ret_type_idx = spl_type_basic(&ctx->tctx, SPL_VOID);
skip_nl(ctx);
}
if (is_extern) {
spl_declare_func(ctx, fn_name, ret_type_idx, nparams, 1, 0);
spl_ensure_native(ctx, fn_name);
if (peek(ctx)->type == TOK_SEMICOLON)
advance(ctx);
return;
}
if (peek(ctx)->type == TOK_SEMICOLON) {
advance(ctx);
return;
}
skip_nl(ctx);
if (peek(ctx)->type == TOK_L_BRACE) {
skip_fn_body(ctx);
}
skip_nl(ctx);
if (peek(ctx)->type == TOK_SEMICOLON)
advance(ctx);
}
/* ============================================================
* Parse top-level program
* ============================================================ */
void spl_parse_prog(spl_comp_t *ctx) {
while (peek(ctx)->type != TOK_EOF) {
/* Pass 0: Pre-register all type names, function signatures, and variables */
{
usize saved = ctx->tok_idx;
while (!ctx->has_error && peek(ctx)->type != TOK_EOF) {
skip_nl(ctx);
if (peek(ctx)->type == TOK_EOF)
break;
switch (peek(ctx)->type) {
case KW_TYPE:
pre_register_type_decl(ctx);
break;
case KW_FN:
pre_register_fn_decl(ctx, 0);
break;
case KW_PUB: {
advance(ctx);
skip_nl(ctx);
if (peek(ctx)->type == KW_FN)
pre_register_fn_decl(ctx, 0);
else if (peek(ctx)->type == KW_TYPE)
pre_register_type_decl(ctx);
break;
}
case TOK_AT:
case TOK_SHARP: {
int tt = peek(ctx)->type;
advance(ctx);
skip_nl(ctx);
if (tt == TOK_AT) {
if (peek(ctx)->type == KW_EXTERN) {
advance(ctx);
skip_nl(ctx);
if (peek(ctx)->type == TOK_L_PAREN) {
advance(ctx);
skip_nl(ctx);
advance(ctx);
skip_nl(ctx);
if (peek(ctx)->type == TOK_R_PAREN)
advance(ctx);
}
}
} else {
if (peek(ctx)->type == TOK_L_BRACKET) {
advance(ctx);
while (peek(ctx)->type != TOK_R_BRACKET && peek(ctx)->type != TOK_EOF)
advance(ctx);
if (peek(ctx)->type == TOK_R_BRACKET)
advance(ctx);
}
}
skip_nl(ctx);
if (peek(ctx)->type == KW_FN)
pre_register_fn_decl(ctx, 1);
break;
}
default:
/* Skip unrecognized tokens (stmts, etc.) */
advance(ctx);
break;
}
}
ctx->tok_idx = saved;
}
/* Pass 1: Fill all bodies */
while (!ctx->has_error && peek(ctx)->type != TOK_EOF) {
skip_nl(ctx);
if (peek(ctx)->type == TOK_EOF)
break;
@@ -493,7 +711,6 @@ void spl_parse_prog(spl_comp_t *ctx) {
advance(ctx); /* @ or # */
skip_nl(ctx);
if (tt == TOK_AT) {
/* @extern(vm) fn ... */
if (peek(ctx)->type == KW_EXTERN) {
advance(ctx); /* extern */
skip_nl(ctx);
@@ -507,9 +724,8 @@ void spl_parse_prog(spl_comp_t *ctx) {
}
}
} else {
/* #[extern("vm")] fn ... (legacy) */
if (peek(ctx)->type == TOK_L_BRACKET) {
advance(ctx); /* [ */
advance(ctx);
while (peek(ctx)->type != TOK_R_BRACKET && peek(ctx)->type != TOK_EOF)
advance(ctx);
if (peek(ctx)->type == TOK_R_BRACKET)

View File

@@ -9,7 +9,8 @@
* ============================================================ */
static void parse_ret_stmt(spl_comp_t *ctx) {
advance(ctx); /* ret */
snprintf(ctx->parse_context, sizeof(ctx->parse_context), "return statement");
spl_tok_t *ret_tok = advance(ctx); /* ret */
skip_nl(ctx);
/* Before returning, execute all pending defers from innermost scope outward */
@@ -23,7 +24,14 @@ static void parse_ret_stmt(spl_comp_t *ctx) {
emit_return(&ctx->emit, &ctx->tctx, ctx->current_ret_type_idx);
} else {
spl_expr_result_t val = spl_parse_expr(ctx, PREC_MIN);
(void)val;
int void_ty = spl_type_basic(&ctx->tctx, SPL_VOID);
if (ctx->current_ret_type_idx == void_ty) {
spl_comp_err_tok(ctx, ret_tok, "cannot return a value from a void function");
return;
} else if (val.type_idx == void_ty) {
spl_comp_err_tok(ctx, ret_tok, "expected return value");
return;
}
spl_emit_ret(ctx, ctx->current_ret_type_idx);
}
if (peek(ctx)->type == TOK_SEMICOLON)
@@ -41,6 +49,7 @@ static void parse_var_decl(spl_comp_t *ctx, int is_const) {
spl_tok_t *name_tok = advance(ctx);
char vname[256];
spl_tok_copy_name(name_tok, vname, sizeof(vname));
snprintf(ctx->parse_context, sizeof(ctx->parse_context), "variable '%s'", vname);
int var_type_idx = -1;
int has_init = 0;
@@ -123,14 +132,33 @@ void spl_parse_block(spl_comp_t *ctx) {
advance(ctx); /* { */
spl_push_scope(ctx);
while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
while (!ctx->has_error && peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
spl_parse_stmt(ctx);
skip_nl(ctx);
}
/* Error recovery: skip to matching } if has_error caused early exit */
if (ctx->has_error) {
int depth = 1;
while (depth > 0 && ctx->tok_idx < vec_size(ctx->toks)) {
spl_tok_type_t tt = peek(ctx)->type;
if (tt == TOK_L_BRACE)
depth++;
else if (tt == TOK_R_BRACE) {
depth--;
if (depth == 0)
break;
} else if (tt == TOK_EOF)
break;
advance(ctx);
}
if (peek(ctx)->type == TOK_R_BRACE)
advance(ctx);
}
spl_emit_defer_epilogue(ctx, ctx->scope_depth);
spl_pop_scope(ctx);
if (peek(ctx)->type == TOK_R_BRACE)
if (!ctx->has_error && peek(ctx)->type == TOK_R_BRACE)
advance(ctx);
} else {
/* Single statement */
@@ -155,7 +183,7 @@ spl_expr_result_t spl_parse_block_expr(spl_comp_t *ctx) {
spl_expr_result_t result = {0}; /* void by default */
skip_nl(ctx);
while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
while (!ctx->has_error && peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
if (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_ENDLINE) {
advance(ctx);
skip_nl(ctx);
@@ -201,7 +229,8 @@ spl_expr_result_t spl_parse_block_expr(spl_comp_t *ctx) {
spl_emit_defer_epilogue(ctx, ctx->scope_depth);
spl_pop_scope(ctx);
expect(ctx, TOK_R_BRACE);
if (!expect(ctx, TOK_R_BRACE))
return result;
return result;
}
@@ -211,6 +240,7 @@ spl_expr_result_t spl_parse_block_expr(spl_comp_t *ctx) {
static void parse_if_stmt(spl_comp_t *ctx) {
advance(ctx); /* if */
snprintf(ctx->parse_context, sizeof(ctx->parse_context), "if statement");
skip_nl(ctx);
spl_expr_result_t cond = spl_parse_expr(ctx, PREC_MIN);
(void)cond;
@@ -269,6 +299,7 @@ static void loop_exit(spl_comp_t *ctx, spl_loop_save_t *save) {
static void parse_while_stmt(spl_comp_t *ctx) {
advance(ctx); /* while */
snprintf(ctx->parse_context, sizeof(ctx->parse_context), "while statement");
skip_nl(ctx);
spl_loop_save_t save;
@@ -301,6 +332,7 @@ static void parse_loop_stmt(spl_comp_t *ctx) {
static void parse_for_stmt(spl_comp_t *ctx) {
advance(ctx); /* for */
snprintf(ctx->parse_context, sizeof(ctx->parse_context), "for statement");
skip_nl(ctx);
/* Parse the iteration expression(s) */
@@ -357,7 +389,7 @@ static void parse_for_stmt(spl_comp_t *ctx) {
/* Exit: patch bz, drop end */
emit_patch_here(&ctx->emit, bz_addr);
emit_drop(&ctx->emit);
emit_drop(&ctx->emit); /* [] */
spl_emit_defer_epilogue(ctx, ctx->scope_depth);
spl_pop_scope(ctx);
@@ -487,9 +519,10 @@ static void parse_for_stmt(spl_comp_t *ctx) {
* ============================================================ */
static void parse_break_stmt(spl_comp_t *ctx) {
advance(ctx); /* break */
spl_tok_t *tok = advance(ctx); /* break */
if (!ctx->in_loop) {
spl_comp_error(ctx, "break outside loop");
spl_comp_err_tok(ctx, tok, "break outside loop");
return;
}
/* Emit JMP with placeholder, add to patch list */
spl_val_t addr = emit_jmp_here(&ctx->emit);
@@ -504,9 +537,10 @@ static void parse_break_stmt(spl_comp_t *ctx) {
}
static void parse_continue_stmt(spl_comp_t *ctx) {
advance(ctx); /* continue */
spl_tok_t *tok = advance(ctx); /* continue */
if (!ctx->in_loop) {
spl_comp_error(ctx, "continue outside loop");
spl_comp_err_tok(ctx, tok, "continue outside loop");
return;
}
/* Emit JMP with relative offset to continue_target */
spl_val_t here = vec_size(ctx->prog.insns);
@@ -559,17 +593,101 @@ static void parse_match_value_pattern(spl_comp_t *ctx, int val_offset) {
spl_emit_match_value_cmp(ctx, val_offset);
}
/* Parse enum variant data bindings: (name1, name2, ...)
/* Parse enum variant data bindings:
* [var] — global binding: var = entire payload struct
* [.field = var, ...] — field-by-name binding
* (name1, name2, ...) — (legacy) positional field binding
* Declares local variables and loads corresponding field data
* from the matched value's data area (offset 4+).
* Sets *scope_pushed = 1 if bindings declared. */
static void parse_match_enum_bindings(spl_comp_t *ctx, int enum_type_idx, int variant_item_idx,
int val_offset, int *scope_pushed) {
if (peek(ctx)->type == TOK_L_BRACKET) {
/* ---- New syntax: [var] or [.field = var, ...] ---- */
advance(ctx); /* [ */
skip_nl(ctx);
if (peek(ctx)->type == TOK_R_BRACKET) {
if (!expect(ctx, TOK_R_BRACKET))
return;
}
spl_push_scope(ctx);
*scope_pushed = 1;
spl_type_item_t *var_item = spl_type_item_at(&ctx->tctx, enum_type_idx, variant_item_idx);
int data_type_idx = var_item ? var_item->enum_field.type_idx : -1;
if (peek(ctx)->type == TOK_DOT) {
/* [.field = var, ...] — field-by-name binding */
for (;;) {
advance(ctx); /* . */
spl_tok_t *ftok = advance(ctx);
char fname[256];
spl_tok_copy_name(ftok, fname, sizeof(fname));
skip_nl(ctx);
if (peek(ctx)->type == TOK_ASSIGN)
advance(ctx);
skip_nl(ctx);
spl_tok_t *vtok = advance(ctx);
char vname[256];
spl_tok_copy_name(vtok, vname, sizeof(vname));
int field_type = -1;
usize field_off = 0;
if (data_type_idx >= 0 && spl_type_kind(&ctx->tctx, data_type_idx) == TYPE_STRUCT) {
spl_type_item_vec_t *data_items = spl_type_items(&ctx->tctx, data_type_idx);
vec_for(*data_items, di) {
spl_type_item_t *fit = &vec_at(*data_items, di);
if (fit->item_kind == ITEM_FIELD && strcmp(fit->name, fname) == 0) {
field_type = fit->aggregate_field.type_idx;
field_off = fit->aggregate_field.offset;
break;
}
}
}
if (field_type < 0) {
spl_comp_err_tok(ctx, ftok, "unknown field '%s' in enum variant", fname);
break;
}
int boffset = spl_declare_var(ctx, vname, field_type, 0);
emit_load_to_var(&ctx->emit, &ctx->tctx, val_offset, ENUM_TAG_SIZE + field_off,
field_type, boffset);
skip_nl(ctx);
if (peek(ctx)->type == TOK_COMMA) {
advance(ctx);
skip_nl(ctx);
continue;
}
break;
}
} else {
/* [var] — global binding: entire payload as one variable */
spl_tok_t *vtok = advance(ctx);
char vname[256];
spl_tok_copy_name(vtok, vname, sizeof(vname));
if (data_type_idx >= 0) {
int boffset = spl_declare_var(ctx, vname, data_type_idx, 0);
emit_load_to_var(&ctx->emit, &ctx->tctx, val_offset, ENUM_TAG_SIZE, data_type_idx,
boffset);
}
}
if (!expect(ctx, TOK_R_BRACKET))
return;
return;
}
/* ---- Legacy syntax: (name1, name2, ...) ---- */
advance(ctx); /* ( */
skip_nl(ctx);
if (peek(ctx)->type == TOK_R_PAREN) {
expect(ctx, TOK_R_PAREN);
return;
if (!expect(ctx, TOK_R_PAREN))
return;
}
spl_push_scope(ctx);
@@ -626,7 +744,8 @@ static void parse_match_enum_bindings(spl_comp_t *ctx, int enum_type_idx, int va
emit_load_to_var(&ctx->emit, &ctx->tctx, val_offset, ENUM_TAG_SIZE, data_type_idx, boffset);
}
expect(ctx, TOK_R_PAREN);
if (!expect(ctx, TOK_R_PAREN))
return;
}
/* ============================================================
@@ -640,6 +759,7 @@ static void parse_match_enum_bindings(spl_comp_t *ctx, int enum_type_idx, int va
static void parse_match_stmt(spl_comp_t *ctx) {
advance(ctx); /* match */
snprintf(ctx->parse_context, sizeof(ctx->parse_context), "match expression");
skip_nl(ctx);
spl_expr_result_t expr = spl_parse_expr(ctx, PREC_MIN);
@@ -658,7 +778,7 @@ static void parse_match_stmt(spl_comp_t *ctx) {
enum_type_idx = spl_type_elem_type(&ctx->tctx, type_idx);
} else if (!(type_idx >= 0 && spl_type_kind(&ctx->tctx, type_idx) == TYPE_BASIC &&
spl_type_is_integer(spl_type_basic_type(&ctx->tctx, type_idx)))) {
spl_comp_error(ctx, "match expression must be an enum or integer type");
spl_comp_err_tok(ctx, peek(ctx), "match expression must be an enum or integer type");
return;
}
@@ -680,7 +800,7 @@ static void parse_match_stmt(spl_comp_t *ctx) {
int n_jmps = 0;
skip_nl(ctx);
while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
while (!ctx->has_error && peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
skip_nl(ctx);
if (peek(ctx)->type == TOK_COMMA) {
advance(ctx);
@@ -708,7 +828,7 @@ static void parse_match_stmt(spl_comp_t *ctx) {
if (ctx->has_error)
break;
skip_nl(ctx);
if (peek(ctx)->type == TOK_L_PAREN) {
if (peek(ctx)->type == TOK_L_PAREN || peek(ctx)->type == TOK_L_BRACKET) {
has_parens = 1;
break; /* bindings → must be last in fallthrough group */
}
@@ -781,7 +901,8 @@ static void parse_match_stmt(spl_comp_t *ctx) {
skip_nl(ctx);
}
expect(ctx, TOK_R_BRACE);
if (!expect(ctx, TOK_R_BRACE))
return;
/* Patch all JMPs to end */
for (int i = 0; i < n_jmps; i++)
@@ -815,7 +936,8 @@ static void parse_extern_decl(spl_comp_t *ctx) {
}
} else {
/* #[extern("vm")] fn ... (legacy) */
expect(ctx, TOK_L_BRACKET);
if (!expect(ctx, TOK_L_BRACKET))
return;
while (peek(ctx)->type != TOK_R_BRACKET && peek(ctx)->type != TOK_EOF)
advance(ctx);
if (peek(ctx)->type == TOK_R_BRACKET)
@@ -832,7 +954,8 @@ static void parse_extern_decl(spl_comp_t *ctx) {
spl_tok_copy_name(fname_tok, fn_name, sizeof(fn_name));
skip_nl(ctx);
expect(ctx, TOK_L_PAREN);
if (!expect(ctx, TOK_L_PAREN))
return;
/* Count params (we don't store them for extern) */
int nparams = 0;
@@ -860,7 +983,8 @@ static void parse_extern_decl(spl_comp_t *ctx) {
break;
}
}
expect(ctx, TOK_R_PAREN);
if (!expect(ctx, TOK_R_PAREN))
return;
skip_nl(ctx);
/* Return type */
@@ -953,21 +1077,27 @@ void spl_parse_stmt(spl_comp_t *ctx) {
parse_ret_stmt(ctx);
break;
case KW_VAR:
snprintf(ctx->parse_context, sizeof(ctx->parse_context), "variable declaration");
parse_var_decl(ctx, 0);
break;
case KW_CONST:
snprintf(ctx->parse_context, sizeof(ctx->parse_context), "constant declaration");
parse_var_decl(ctx, 1);
break;
case KW_IF:
snprintf(ctx->parse_context, sizeof(ctx->parse_context), "if statement");
parse_if_stmt(ctx);
break;
case KW_WHILE:
snprintf(ctx->parse_context, sizeof(ctx->parse_context), "while statement");
parse_while_stmt(ctx);
break;
case KW_LOOP:
snprintf(ctx->parse_context, sizeof(ctx->parse_context), "loop statement");
parse_loop_stmt(ctx);
break;
case KW_FOR:
snprintf(ctx->parse_context, sizeof(ctx->parse_context), "for statement");
parse_for_stmt(ctx);
break;
case KW_BREAK:
@@ -980,6 +1110,7 @@ void spl_parse_stmt(spl_comp_t *ctx) {
parse_defer_stmt(ctx);
break;
case KW_MATCH:
snprintf(ctx->parse_context, sizeof(ctx->parse_context), "match expression");
parse_match_stmt(ctx);
break;
case KW_TYPE:

View File

@@ -319,6 +319,17 @@ void spl_type_add_field(spl_type_ctx_t *tctx, int type_idx, const char *name, in
vec_push(t->items, item);
}
void spl_type_add_var(spl_type_ctx_t *tctx, int type_idx, const char *name, int var_type_idx) {
spl_type_info_t *t = &vec_at(tctx->types, type_idx);
spl_type_item_t item;
memset(&item, 0, sizeof(item));
item.name = strdup(name);
item.item_kind = ITEM_VAR;
item.aggregate_field.type_idx = var_type_idx;
item.aggregate_field.offset = 0;
vec_push(t->items, item);
}
void spl_type_add_variant(spl_type_ctx_t *tctx, int type_idx, const char *name, int data_type_idx) {
spl_type_info_t *t = &vec_at(tctx->types, type_idx);
spl_type_item_t item;
@@ -379,6 +390,8 @@ void spl_type_compute_layout(spl_type_ctx_t *tctx, int type_idx) {
usize offset = 0;
vec_for(t->items, i) {
spl_type_item_t *it = &vec_at(t->items, i);
if (it->item_kind == ITEM_VAR)
continue;
if (it->item_kind == ITEM_FIELD && it->aggregate_field.type_idx >= 0) {
spl_type_compute_layout(tctx, it->aggregate_field.type_idx);
spl_type_info_t *ft = &vec_at(tctx->types, it->aggregate_field.type_idx);
@@ -398,6 +411,8 @@ void spl_type_compute_layout(spl_type_ctx_t *tctx, int type_idx) {
usize max_sz = 0;
vec_for(t->items, i) {
spl_type_item_t *it = &vec_at(t->items, i);
if (it->item_kind == ITEM_VAR)
continue;
if (it->item_kind == ITEM_FIELD && it->aggregate_field.type_idx >= 0) {
spl_type_compute_layout(tctx, it->aggregate_field.type_idx);
spl_type_info_t *ft = &vec_at(tctx->types, it->aggregate_field.type_idx);
@@ -769,13 +784,13 @@ int spl_type_parse(spl_type_ctx_t *tctx, struct spl_comp *ctx) {
int len_val;
if (!spl_parse_int_literal(ctx, &len_val)) {
spl_comp_error(ctx, "expected array length");
spl_comp_err_tok(ctx, peek(ctx), "expected array length");
return -1;
}
usize len = (usize)len_val;
if (vec_at(ctx->toks, ctx->tok_idx).type != TOK_R_BRACKET) {
spl_comp_error(ctx, "expected ']'");
spl_comp_err_tok(ctx, peek(ctx), "expected ']'");
return -1;
}
ctx->tok_idx++;
@@ -859,6 +874,14 @@ int spl_type_parse(spl_type_ctx_t *tctx, struct spl_comp *ctx) {
id_buf[cplen] = '\0';
int found = spl_type_resolve(tctx, id_buf);
if (found < 0 && tctx->current_type_idx >= 0) {
const char *cur_type_name = spl_type_name(tctx, tctx->current_type_idx);
if (cur_type_name) {
char qualified[512];
snprintf(qualified, sizeof(qualified), "%s.%s", cur_type_name, id_buf);
found = spl_type_resolve(tctx, qualified);
}
}
if (found >= 0) {
ctx->tok_idx++;
@@ -903,10 +926,11 @@ int spl_type_parse(spl_type_ctx_t *tctx, struct spl_comp *ctx) {
return ti;
}
spl_comp_error(ctx, "unknown type '%s'", id_buf);
spl_comp_err_tok(ctx, tok, "unknown type '%s'", id_buf);
ctx->tok_idx++;
return -1;
}
spl_comp_error(ctx, "expected type");
spl_comp_err_tok(ctx, peek(ctx), "expected type");
return -1;
}

View File

@@ -32,6 +32,7 @@ typedef enum {
ITEM_VARIANT, /* enum_field : enum variant */
ITEM_METHOD, /* method : type method */
ITEM_NESTED_TYPE, /* nested_type : child type decl */
ITEM_VAR, /* aggregate_field : type-level var */
} spl_type_item_kind_t;
/* ============================================================
@@ -128,6 +129,7 @@ int spl_type_alias(spl_type_ctx_t *tctx, const char *name, int target_type_idx);
* ============================================================ */
void spl_type_add_field(spl_type_ctx_t *tctx, int type_idx, const char *name, int field_type_idx);
void spl_type_add_var(spl_type_ctx_t *tctx, int type_idx, const char *name, int var_type_idx);
void spl_type_add_variant(spl_type_ctx_t *tctx, int type_idx, const char *name, int data_type_idx);
void spl_type_add_method(spl_type_ctx_t *tctx, int type_idx, const char *name, int func_idx);
void spl_type_add_nested(spl_type_ctx_t *tctx, int parent_idx, const char *name,