stage1 修复错误 提供类型检查
This commit is contained in:
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user