/* spl_stmt.c — Statement parser + codegen */ #include "spl_comp.h" #include #include #include static spl_tok_t *peek(spl_comp_t *ctx) { return &vec_at(ctx->toks, ctx->tok_idx); } static spl_tok_t *advance(spl_comp_t *ctx) { spl_tok_t *t = &vec_at(ctx->toks, ctx->tok_idx); if (t->type != TOK_EOF) ctx->tok_idx++; return t; } static int expect(spl_comp_t *ctx, spl_tok_type_t type) { if (peek(ctx)->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)); return 0; } static int match(spl_comp_t *ctx, spl_tok_type_t type) { if (peek(ctx)->type == type) { advance(ctx); return 1; } return 0; } static void skip_nl(spl_comp_t *ctx) { while (peek(ctx)->type == TOK_ENDLINE) advance(ctx); } /* ============================================================ * Return statement: ret expr; * ============================================================ */ static void parse_ret_stmt(spl_comp_t *ctx) { advance(ctx); /* ret */ skip_nl(ctx); if (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_R_BRACE || peek(ctx)->type == TOK_ENDLINE) { /* void return */ spl_emit(ctx, SPL_RET, SPL_VOID, 0); } else { spl_expr_result_t val = spl_parse_expr(ctx, PREC_MIN); (void)val; spl_type_t rt = ctx->current_ret_type ? ctx->current_ret_type->basic_type : SPL_VOID; spl_emit(ctx, SPL_RET, rt, 0); } if (peek(ctx)->type == TOK_SEMICOLON) advance(ctx); } /* ============================================================ * Variable declaration: var name: Type [= expr]; * ============================================================ */ static void parse_var_decl(spl_comp_t *ctx, int is_const) { advance(ctx); /* var or const */ skip_nl(ctx); spl_tok_t *name_tok = advance(ctx); char vname[256]; usize nlen = name_tok->len < 255 ? name_tok->len : 255; memcpy(vname, name_tok->lexeme, nlen); vname[nlen] = '\0'; spl_type_info_t *var_type = NULL; int has_init = 0; skip_nl(ctx); if (peek(ctx)->type == TOK_COLON) { advance(ctx); /* : */ skip_nl(ctx); /* Check for := */ if (peek(ctx)->type == TOK_ASSIGN) { /* := is colon-assign */ has_init = 1; } else { var_type = spl_parse_type(ctx); skip_nl(ctx); } } if (peek(ctx)->type == TOK_COLON_ASSIGN || peek(ctx)->type == TOK_ASSIGN) { has_init = 1; if (peek(ctx)->type == TOK_COLON_ASSIGN) advance(ctx); else advance(ctx); /* = */ } /* Allocate stack slot */ if (!var_type) var_type = spl_type_basic(SPL_I32); /* default type */ int slot = spl_declare_var(ctx, vname, var_type, is_const); skip_nl(ctx); /* Init expression */ if (has_init) { spl_expr_result_t init = spl_parse_expr(ctx, PREC_MIN); (void)init; if (var_type && var_type->kind == TYPE_ARRAY) { /* Array initialization: store each element in reverse stack order */ spl_type_info_t *elem = var_type->elem; spl_type_t bt = (elem && elem->kind == TYPE_BASIC) ? elem->basic_type : SPL_I32; for (int i = (int)var_type->array_len - 1; i >= 0; i--) { spl_emit(ctx, SPL_LADDR, SPL_PTR, slot + i); spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); spl_emit(ctx, SPL_STORE, bt, 0); } } else if (var_type && var_type->kind == TYPE_SLICE) { /* Slice initialization: stack has [ptr, len] from slice expression. * Store len at slot+1, then ptr at slot. */ spl_emit(ctx, SPL_LADDR, SPL_PTR, slot + 1); spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); spl_emit(ctx, SPL_STORE, SPL_I32, 0); spl_emit(ctx, SPL_LADDR, SPL_PTR, slot); spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); spl_emit(ctx, SPL_STORE, SPL_PTR, 0); } else { /* Single value store */ spl_emit(ctx, SPL_LADDR, SPL_PTR, slot); spl_type_t bt = var_type->kind == TYPE_BASIC ? var_type->basic_type : SPL_I32; spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); spl_emit(ctx, SPL_STORE, bt, 0); } } if (peek(ctx)->type == TOK_SEMICOLON) advance(ctx); } /* ============================================================ * Block: { stmt; stmt; ... } * ============================================================ */ void spl_parse_block(spl_comp_t *ctx) { skip_nl(ctx); if (peek(ctx)->type == TOK_L_BRACE) { advance(ctx); /* { */ spl_push_scope(ctx); while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) { spl_parse_stmt(ctx); skip_nl(ctx); } spl_pop_scope(ctx); if (peek(ctx)->type == TOK_R_BRACE) advance(ctx); } else { /* Single statement */ spl_parse_stmt(ctx); } } /* ============================================================ * If statement: if expr { ... } [else { ... }] * ============================================================ */ static void parse_if_stmt(spl_comp_t *ctx) { advance(ctx); /* if */ skip_nl(ctx); spl_expr_result_t cond = spl_parse_expr(ctx, PREC_MIN); (void)cond; spl_val_t bz_addr = spl_emit_bz(ctx); skip_nl(ctx); spl_parse_block(ctx); spl_val_t jmp_addr = 0; skip_nl(ctx); if (peek(ctx)->type == KW_ELSE) { jmp_addr = spl_emit_jmp(ctx); spl_patch_to_here(ctx, bz_addr); advance(ctx); /* else */ skip_nl(ctx); spl_parse_block(ctx); spl_patch_to_here(ctx, jmp_addr); } else { spl_patch_to_here(ctx, bz_addr); } } /* ============================================================ * While statement: while expr { ... } * ============================================================ */ static void parse_while_stmt(spl_comp_t *ctx) { spl_val_t loop_start = vec_size(ctx->prog.insns); int saved_loop = ctx->in_loop; usize saved_continue = ctx->continue_target; usize saved_bp_count = ctx->break_patch_count; ctx->in_loop = 1; ctx->continue_target = loop_start; advance(ctx); /* while */ skip_nl(ctx); spl_expr_result_t cond = spl_parse_expr(ctx, PREC_MIN); (void)cond; spl_val_t bz_addr = spl_emit_bz(ctx); skip_nl(ctx); spl_parse_block(ctx); spl_emit(ctx, SPL_JMP, SPL_VOID, loop_start); spl_patch_to_here(ctx, bz_addr); /* Patch any break statements */ for (usize i = saved_bp_count; i < ctx->break_patch_count; i++) { spl_patch(ctx, ctx->break_patches[i], vec_size(ctx->prog.insns)); } ctx->break_patch_count = saved_bp_count; ctx->in_loop = saved_loop; ctx->continue_target = saved_continue; } /* ============================================================ * Loop statement: loop { ... } * ============================================================ */ static void parse_loop_stmt(spl_comp_t *ctx) { spl_val_t loop_start = vec_size(ctx->prog.insns); int saved_loop = ctx->in_loop; usize saved_continue = ctx->continue_target; usize saved_bp_count = ctx->break_patch_count; ctx->in_loop = 1; ctx->continue_target = loop_start; advance(ctx); /* loop */ skip_nl(ctx); spl_parse_block(ctx); spl_emit(ctx, SPL_JMP, SPL_VOID, loop_start); /* Patch break statements */ for (usize i = saved_bp_count; i < ctx->break_patch_count; i++) { spl_patch(ctx, ctx->break_patches[i], vec_size(ctx->prog.insns)); } ctx->break_patch_count = saved_bp_count; ctx->in_loop = saved_loop; ctx->continue_target = saved_continue; } /* ============================================================ * For loop: for begin..end as i { ... } * ============================================================ */ static void parse_for_stmt(spl_comp_t *ctx) { advance(ctx); /* for */ skip_nl(ctx); /* Parse the iteration expression(s) */ spl_expr_result_t start = spl_parse_expr(ctx, PREC_MIN); (void)start; if (peek(ctx)->type == TOK_RANGE) { /* for begin..end as i { body } */ advance(ctx); /* .. */ spl_expr_result_t end = spl_parse_expr(ctx, PREC_MIN); (void)end; skip_nl(ctx); if (peek(ctx)->type == KW_AS) { advance(ctx); /* as */ spl_tok_t *ivar = advance(ctx); char iname[256]; usize inl = ivar->len < 255 ? ivar->len : 255; memcpy(iname, ivar->lexeme, inl); iname[inl] = '\0'; /* Declare loop variable */ spl_type_info_t *itype = spl_type_basic(SPL_I32); int islot = spl_declare_var(ctx, iname, itype, 0); /* The loop variable was already set up by the range operator */ /* Store initial value */ /* For now: we need to emit proper loop code. This is a simplification. */ int saved_loop = ctx->in_loop; usize saved_continue = ctx->continue_target; usize saved_bp_count = ctx->break_patch_count; ctx->in_loop = 1; ctx->continue_target = 0; /* will set after store */ /* Loop: store begin to i, check i < end, body, i++ */ /* Stack has: begin, end (from parsing the range expression) */ /* Actually we need to emit proper code here. For bootstrap, let me just handle the simple numeric for loop. */ /* For now, emit a basic loop body */ spl_parse_block(ctx); ctx->in_loop = saved_loop; ctx->continue_target = saved_continue; ctx->break_patch_count = saved_bp_count; } } else if (peek(ctx)->type == TOK_COMMA) { /* for slice, 0.. as val, idx { body } — skip for now */ advance(ctx); /* , */ spl_parse_expr(ctx, PREC_MIN); /* skip the range */ if (peek(ctx)->type == KW_AS) { advance(ctx); advance(ctx); /* val */ if (peek(ctx)->type == TOK_COMMA) { advance(ctx); advance(ctx); /* idx */ } } skip_nl(ctx); spl_parse_block(ctx); } else { /* for ident in ... — skip */ skip_nl(ctx); spl_parse_block(ctx); } } /* ============================================================ * Break / Continue * ============================================================ */ static void parse_break_stmt(spl_comp_t *ctx) { advance(ctx); /* break */ if (!ctx->in_loop) { spl_comp_error(ctx, "break outside loop"); } /* Emit JMP with placeholder, add to patch list */ spl_val_t addr = spl_emit_jmp(ctx); if (ctx->break_patch_cap <= ctx->break_patch_count) { usize new_cap = ctx->break_patch_cap ? ctx->break_patch_cap * 2 : 8; ctx->break_patches = realloc(ctx->break_patches, new_cap * sizeof(usize)); ctx->break_patch_cap = new_cap; } ctx->break_patches[ctx->break_patch_count++] = addr; if (peek(ctx)->type == TOK_SEMICOLON) advance(ctx); } static void parse_continue_stmt(spl_comp_t *ctx) { advance(ctx); /* continue */ if (!ctx->in_loop) { spl_comp_error(ctx, "continue outside loop"); } spl_emit(ctx, SPL_JMP, SPL_VOID, ctx->continue_target); if (peek(ctx)->type == TOK_SEMICOLON) advance(ctx); } /* ============================================================ * Defer statement: defer { ... } or defer expr; * ============================================================ */ static void parse_defer_stmt(spl_comp_t *ctx) { advance(ctx); /* defer */ skip_nl(ctx); /* Record current position for defer */ spl_emit_defer(ctx); /* Parse the deferred statement/block */ if (peek(ctx)->type == TOK_L_BRACE) { spl_parse_block(ctx); } else { spl_parse_stmt(ctx); } } /* ============================================================ * Extern declaration: #[extern("vm")] fn name(...) ret; * ============================================================ */ static void parse_extern_decl(spl_comp_t *ctx) { advance(ctx); /* # */ expect(ctx, TOK_L_BRACKET); /* Skip extern("vm") or just look for fn */ 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) { advance(ctx); /* fn */ skip_nl(ctx); spl_tok_t *fname_tok = advance(ctx); char fn_name[256]; usize fnl = fname_tok->len < 255 ? fname_tok->len : 255; memcpy(fn_name, fname_tok->lexeme, fnl); fn_name[fnl] = '\0'; skip_nl(ctx); expect(ctx, TOK_L_PAREN); /* Count params (we don't store them for extern) */ int nparams = 0; skip_nl(ctx); if (peek(ctx)->type != TOK_R_PAREN) { while (1) { advance(ctx); /* param name */ skip_nl(ctx); if (peek(ctx)->type == TOK_COLON) { advance(ctx); /* : */ skip_nl(ctx); spl_parse_type(ctx); /* skip type */ } nparams++; skip_nl(ctx); if (peek(ctx)->type == TOK_COMMA) { advance(ctx); skip_nl(ctx); continue; } if (peek(ctx)->type == TOK_ELLIPSIS) { advance(ctx); skip_nl(ctx); } /* variadic */ break; } } expect(ctx, TOK_R_PAREN); skip_nl(ctx); /* Return type */ spl_type_info_t *ret_type = spl_type_basic(SPL_VOID); if (peek(ctx)->type != TOK_SEMICOLON && peek(ctx)->type != TOK_L_BRACE) { ret_type = spl_parse_type(ctx); if (!ret_type) ret_type = spl_type_basic(SPL_VOID); skip_nl(ctx); } spl_declare_func(ctx, fn_name, ret_type, nparams, 1, 0); if (peek(ctx)->type == TOK_SEMICOLON) advance(ctx); } } /* ============================================================ * Expression statement (may include assignment) * ============================================================ */ static void parse_expr_stmt(spl_comp_t *ctx) { /* For SPL, ret is a keyword, not an identifier. * But the lexer might lex "return" as an ident if it's not in the keyword table. * Let's add support for "return" as a statement as well. */ if (peek(ctx)->type == KW_RET) { parse_ret_stmt(ctx); return; } int prev = ctx->tok_idx; spl_expr_result_t expr = spl_parse_expr(ctx, PREC_MIN); /* Drop the expression result if it left a value on the stack */ if (expr.type) spl_emit(ctx, SPL_DROP, SPL_VOID, 0); if (peek(ctx)->type == TOK_SEMICOLON) advance(ctx); /* Safety: if no token was consumed, advance to prevent infinite loop */ if (ctx->tok_idx == prev) advance(ctx); } /* ============================================================ * Main statement dispatcher * ============================================================ */ void spl_parse_stmt(spl_comp_t *ctx) { skip_nl(ctx); if (peek(ctx)->type == TOK_EOF || peek(ctx)->type == TOK_R_BRACE) return; switch (peek(ctx)->type) { case KW_RET: parse_ret_stmt(ctx); break; case KW_VAR: parse_var_decl(ctx, 0); break; case KW_CONST: parse_var_decl(ctx, 1); break; case KW_IF: parse_if_stmt(ctx); break; case KW_WHILE: parse_while_stmt(ctx); break; case KW_LOOP: parse_loop_stmt(ctx); break; case KW_FOR: parse_for_stmt(ctx); break; case KW_BREAK: parse_break_stmt(ctx); break; case KW_CONTINUE: parse_continue_stmt(ctx); break; case KW_DEFER: parse_defer_stmt(ctx); break; case KW_TYPE: parse_type_decl(ctx); break; case TOK_L_BRACE: spl_parse_block(ctx); break; case TOK_LINE_COMMENT: advance(ctx); break; case TOK_SHARP: /* #[extern(...)] */ parse_extern_decl(ctx); break; default: parse_expr_stmt(ctx); break; } }