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

@@ -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: