From 02095524b1b2e58acd26ae96266bcd731a3528b1 Mon Sep 17 00:00:00 2001 From: zzy <2450266535@qq.com> Date: Sat, 25 Jul 2026 10:28:24 +0800 Subject: [PATCH] =?UTF-8?q?stage1=20=E4=BF=AE=E5=A4=8D=E9=94=99=E8=AF=AF?= =?UTF-8?q?=20=E6=8F=90=E4=BE=9B=E7=B1=BB=E5=9E=8B=E6=A3=80=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- stage0/spl_ir.h | 2 +- stage0/spl_syscall.c | 1 + stage1/spl_comp.c | 25 +++- stage1/spl_comp.h | 4 + stage1/spl_expr.c | 200 +++++++++++++++++--------- stage1/spl_lex_util.c | 24 +++- stage1/spl_parser.c | 328 ++++++++++++++++++++++++++++++++++-------- stage1/spl_stmt.c | 175 +++++++++++++++++++--- stage1/spl_type.c | 32 ++++- stage1/spl_type.h | 2 + 10 files changed, 640 insertions(+), 153 deletions(-) diff --git a/stage0/spl_ir.h b/stage0/spl_ir.h index 0974cab..844e4f7 100644 --- a/stage0/spl_ir.h +++ b/stage0/spl_ir.h @@ -13,7 +13,7 @@ typedef uintptr_t usize; typedef intptr_t isize; typedef enum { - SPL_VOID = 0, + SPL_VOID, SPL_I8, SPL_U8, SPL_I16, diff --git a/stage0/spl_syscall.c b/stage0/spl_syscall.c index 4bf8e9b..8cf88d7 100644 --- a/stage0/spl_syscall.c +++ b/stage0/spl_syscall.c @@ -200,6 +200,7 @@ static spl_val_t vm_printf(int nargs, spl_val_t *args) { for (usize j = 0; j < args[arg_idx]; ++j) { vec_push(buffer, ((const char *)args[arg_idx + 1])[j]); } + i += 2; } break; case 'd': snprintf(tmp_buf, sizeof(tmp_buf), "%zd", args[arg_idx]); diff --git a/stage1/spl_comp.c b/stage1/spl_comp.c index b614830..7e0fefc 100644 --- a/stage1/spl_comp.c +++ b/stage1/spl_comp.c @@ -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; } diff --git a/stage1/spl_comp.h b/stage1/spl_comp.h index 2e098b9..9f8d028 100644 --- a/stage1/spl_comp.h +++ b/stage1/spl_comp.h @@ -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) diff --git a/stage1/spl_expr.c b/stage1/spl_expr.c index e25b021..11d0982 100644 --- a/stage1/spl_expr.c +++ b/stage1/spl_expr.c @@ -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; } diff --git a/stage1/spl_lex_util.c b/stage1/spl_lex_util.c index 6138174..9a3661d 100644 --- a/stage1/spl_lex_util.c +++ b/stage1/spl_lex_util.c @@ -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; } diff --git a/stage1/spl_parser.c b/stage1/spl_parser.c index 12332a3..6805118 100644 --- a/stage1/spl_parser.c +++ b/stage1/spl_parser.c @@ -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) diff --git a/stage1/spl_stmt.c b/stage1/spl_stmt.c index 9e15295..ff72a54 100644 --- a/stage1/spl_stmt.c +++ b/stage1/spl_stmt.c @@ -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: diff --git a/stage1/spl_type.c b/stage1/spl_type.c index 0bddece..6712772 100644 --- a/stage1/spl_type.c +++ b/stage1/spl_type.c @@ -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; } diff --git a/stage1/spl_type.h b/stage1/spl_type.h index 6716e4a..b420e61 100644 --- a/stage1/spl_type.h +++ b/stage1/spl_type.h @@ -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,