diff --git a/stage1/spl_comp.c b/stage1/spl_comp.c index 5f5dcff..b14e039 100644 --- a/stage1/spl_comp.c +++ b/stage1/spl_comp.c @@ -48,6 +48,7 @@ void spl_comp_reset(spl_comp_t *ctx) { ctx->current_func_idx = -1; ctx->current_ret_type = NULL; ctx->current_local_bytes = 0; + ctx->peak_local_bytes = 0; ctx->in_loop = 0; ctx->break_patch_count = 0; ctx->break_patch_cap = 0; @@ -113,6 +114,35 @@ void spl_emit_copy_slots(spl_comp_t *ctx, int dest_offset, usize nslots) { } } +/* ---- Uniform LOAD/STORE type helper ---- */ + +spl_type_t spl_type_emit_type(spl_type_info_t *type) { + if (!type) + return SPL_I32; + if (type->kind == TYPE_BASIC) + return type->basic_type; + if (type->kind == TYPE_PTR) + return SPL_PTR; + return SPL_PTR; +} + +/* ---- Load from [saved_ptr+offset], store to local var ---- */ + +void spl_emit_load_to_var(spl_comp_t *ctx, int ptr_slot_offset, usize byte_offset, + spl_type_info_t *data_type, int var_offset) { + spl_type_t bt = spl_type_emit_type(data_type); + spl_emit(ctx, SPL_LADDR, SPL_PTR, ptr_slot_offset); + spl_emit(ctx, SPL_LOAD, SPL_PTR, 0); + if (byte_offset > 0) { + spl_emit(ctx, SPL_PUSH, SPL_USIZE, byte_offset); + spl_emit(ctx, SPL_ADD, SPL_USIZE, 0); + } + spl_emit(ctx, SPL_LOAD, bt, 0); + spl_emit(ctx, SPL_LADDR, SPL_PTR, var_offset); + spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); + spl_emit(ctx, SPL_STORE, bt, 0); +} + /* ---- Scope management ---- */ void spl_push_scope(spl_comp_t *ctx) { @@ -151,6 +181,8 @@ int spl_declare_var(spl_comp_t *ctx, const char *name, spl_type_info_t *type, in if (aligned < sizeof(spl_val_t)) aligned = sizeof(spl_val_t); ctx->current_local_bytes += (int)aligned; + if (ctx->current_local_bytes > ctx->peak_local_bytes) + ctx->peak_local_bytes = ctx->current_local_bytes; /* Add to current scope */ if (vec_size(ctx->scopes) > 0) { diff --git a/stage1/spl_comp.h b/stage1/spl_comp.h index d605d40..3702900 100644 --- a/stage1/spl_comp.h +++ b/stage1/spl_comp.h @@ -28,8 +28,9 @@ typedef enum { TYPE_INFER, /* _ (to be inferred) */ } spl_type_kind_t; -/* Forward declaration */ +/* Forward declarations */ typedef struct spl_type_info spl_type_info_t; +typedef struct spl_comp spl_comp_t; /* Struct field */ typedef struct { @@ -89,6 +90,15 @@ const char *spl_type_str(spl_type_info_t *t); int spl_type_is_integer(spl_type_t bt); spl_type_info_t *spl_type_clone(spl_type_info_t *t); +/* Determine the uniform type for LOAD/STORE codegen (i32→SPL_I32, ptr→SPL_PTR, else→SPL_PTR) */ +spl_type_t spl_type_emit_type(spl_type_info_t *type); + +/* Emit LOAD from [saved_ptr + byte_offset] and STORE to local var at var_offset. + * Stack: [] → [] + * The saved_ptr is loaded from the 1-slot temp at ptr_slot_offset. */ +void spl_emit_load_to_var(spl_comp_t *ctx, int ptr_slot_offset, usize byte_offset, + spl_type_info_t *data_type, int var_offset); + /* ============================================================ * Scope / symbol table * ============================================================ */ @@ -134,7 +144,7 @@ typedef struct { int count_at_decl; /* defer_count when declared */ } spl_defer_entry_t; -typedef struct { +typedef struct spl_comp { /* Lexer output */ spl_tok_vec_t toks; usize tok_idx; @@ -162,6 +172,7 @@ typedef struct { int current_func_idx; spl_type_info_t *current_ret_type; int current_local_bytes; /* next free local byte offset */ + int peak_local_bytes; /* peak current_local_bytes for ALLOC sizing */ const char *current_type_name; /* name of type whose body we're parsing (for method short-name lookup) */ diff --git a/stage1/spl_expr.c b/stage1/spl_expr.c index aa44e3b..81f217e 100644 --- a/stage1/spl_expr.c +++ b/stage1/spl_expr.c @@ -329,13 +329,13 @@ static spl_expr_result_t parse_array_literal(spl_comp_t *ctx) { skip_nl(ctx); /* Parse array length */ - spl_tok_t *len_tok = advance(ctx); - if (len_tok->type != TOK_INT_LITERAL) { + int len_val; + if (!spl_parse_int_literal(ctx, &len_val)) { spl_comp_error(ctx, "expected array length"); spl_expr_result_t r = {0}; return r; } - usize len = (usize)strtoull(len_tok->lexeme, NULL, 0); + usize len = (usize)len_val; skip_nl(ctx); expect(ctx, TOK_R_BRACKET); @@ -372,19 +372,6 @@ static spl_expr_result_t parse_array_literal(spl_comp_t *ctx) { return (spl_expr_result_t){arr_type, 0}; } -/* Determine correct STORE type for a type (handles ptr = 8 bytes). */ -static spl_type_t spl_store_type(spl_type_info_t *type) { - if (!type) - return SPL_I32; - if (type->kind == TYPE_BASIC) - return type->basic_type; - if (type->kind == TYPE_PTR) - return SPL_PTR; - if (spl_type_size(type) <= sizeof(spl_val_t)) - return SPL_PTR; - return SPL_I32; -} - /* Parse struct/enum literal: Type { .field = val, ... } * Allocates temp slots for the value, returns lvalue (addr on stack). * For types fitting in one slot, pushes the packed value directly. */ @@ -392,8 +379,12 @@ static spl_expr_result_t parse_struct_literal(spl_comp_t *ctx, spl_type_info_t * usize sz = spl_type_size(type); int base_offset = ctx->current_local_bytes; ctx->current_local_bytes += (int)((sz + sizeof(spl_val_t) - 1) & ~(sizeof(spl_val_t) - 1)); + if (ctx->current_local_bytes > ctx->peak_local_bytes) + ctx->peak_local_bytes = ctx->current_local_bytes; if (ctx->current_local_bytes - base_offset < (int)sizeof(spl_val_t)) ctx->current_local_bytes = base_offset + (int)sizeof(spl_val_t); + if (ctx->current_local_bytes > ctx->peak_local_bytes) + ctx->peak_local_bytes = ctx->current_local_bytes; advance(ctx); /* { */ skip_nl(ctx); @@ -473,8 +464,13 @@ static spl_expr_result_t parse_struct_literal(spl_comp_t *ctx, spl_type_info_t * * Stack: [field_addr] — store each element at computed offsets */ advance(ctx); /* [ */ skip_nl(ctx); - spl_tok_t *len_tok = advance(ctx); - usize arr_len = (usize)strtoull(len_tok->lexeme, NULL, 0); + int len_val; + if (!spl_parse_int_literal(ctx, &len_val)) { + spl_comp_error(ctx, "expected array length"); + spl_expr_result_t r = {0}; + return r; + } + usize arr_len = (usize)len_val; skip_nl(ctx); expect(ctx, TOK_R_BRACKET); skip_nl(ctx); @@ -483,7 +479,7 @@ static spl_expr_result_t parse_struct_literal(spl_comp_t *ctx, spl_type_info_t * expect(ctx, TOK_L_BRACE); skip_nl(ctx); usize stride = spl_type_elem_stride(elem_type); - spl_type_t st = spl_store_type(elem_type); + spl_type_t st = spl_type_emit_type(elem_type); for (usize i = 0; i < arr_len; i++) { if (i > 0) { if (peek(ctx)->type == TOK_COMMA) @@ -519,7 +515,7 @@ static spl_expr_result_t parse_struct_literal(spl_comp_t *ctx, spl_type_info_t * } else { spl_expr_result_t fv = spl_parse_expr(ctx, PREC_MIN); (void)fv; - spl_emit(ctx, SPL_STORE, spl_store_type(f->type), 0); + spl_emit(ctx, SPL_STORE, spl_type_emit_type(f->type), 0); } break; } @@ -649,7 +645,7 @@ static spl_expr_result_t parse_struct_literal(spl_comp_t *ctx, spl_type_info_t * } else { spl_expr_result_t sfv = spl_parse_expr(ctx, PREC_MIN); (void)sfv; - spl_emit(ctx, SPL_STORE, spl_store_type(sf->type), 0); + spl_emit(ctx, SPL_STORE, spl_type_emit_type(sf->type), 0); } break; } @@ -675,7 +671,7 @@ static spl_expr_result_t parse_struct_literal(spl_comp_t *ctx, spl_type_info_t * spl_emit(ctx, SPL_LADDR, SPL_PTR, base_offset); spl_emit(ctx, SPL_PUSH, SPL_USIZE, DATA_OFFSET); spl_emit(ctx, SPL_ADD, SPL_USIZE, 0); - spl_type_t bt = spl_store_type(v->data_type); + spl_type_t bt = spl_type_emit_type(v->data_type); spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); spl_emit(ctx, SPL_STORE, bt, 0); } @@ -1368,10 +1364,9 @@ static spl_expr_result_t parse_infix(spl_comp_t *ctx, spl_expr_result_t left, sp ctx->addr_of_mode = saved_addr_of_mode; if (left.is_lvalue) { - spl_type_t bt = left.type ? (left.type->kind == TYPE_BASIC ? left.type->basic_type - : left.type->kind == TYPE_PTR ? SPL_PTR - : SPL_I32) - : SPL_I32; + spl_type_t bt = left.type + ? (left.type->kind == TYPE_BASIC ? left.type->basic_type : SPL_PTR) + : SPL_I32; if (op == TOK_ASSIGN) { /* Simple assignment: stack is [addr, rhs] */ /* STORE pops TOS=value, TOS-1=address — already correct order */ diff --git a/stage1/spl_lex_util.c b/stage1/spl_lex_util.c index 9094b8f..b6d639a 100644 --- a/stage1/spl_lex_util.c +++ b/stage1/spl_lex_util.c @@ -27,6 +27,23 @@ void skip_nl(spl_comp_t *ctx) { advance(ctx); } +int spl_parse_int_literal(spl_comp_t *ctx, int *val) { + int negate = 0; + if (peek(ctx)->type == TOK_SUB) { + negate = 1; + advance(ctx); + } + if (peek(ctx)->type != TOK_INT_LITERAL) { + if (negate) + ctx->tok_idx--; /* un-consume the SUB token */ + return 0; + } + spl_tok_t *tok = advance(ctx); + long long v = strtoll(tok->lexeme, NULL, 0); + *val = negate ? -(int)v : (int)v; + return 1; +} + const char *spl_tok_type_name(spl_tok_type_t type) { switch (type) { #define X(name, enum_name, dummy) \ diff --git a/stage1/spl_lex_util.h b/stage1/spl_lex_util.h index c4c3918..8ba44a3 100644 --- a/stage1/spl_lex_util.h +++ b/stage1/spl_lex_util.h @@ -9,4 +9,9 @@ int expect(spl_comp_t *ctx, spl_tok_type_t type); int match(spl_comp_t *ctx, spl_tok_type_t type); void skip_nl(spl_comp_t *ctx); +/* Parse an integer literal value, handling optional '-' prefix for negatives. + * Returns 1 on success (value in *val), 0 if current token isn't an integer. + * On success, advances past all consumed tokens. On failure, does not advance. */ +int spl_parse_int_literal(spl_comp_t *ctx, int *val); + #endif /* __SPL_LEX_UTIL_H__ */ diff --git a/stage1/spl_parser.c b/stage1/spl_parser.c index 5a2489d..e94b1a7 100644 --- a/stage1/spl_parser.c +++ b/stage1/spl_parser.c @@ -27,6 +27,7 @@ static int parse_fn_body(spl_comp_t *ctx, const char *fn_name, spl_type_info_t * ctx->current_func_idx = fi; ctx->current_ret_type = ret_type; ctx->current_local_bytes = 0; + ctx->peak_local_bytes = 0; spl_push_scope(ctx); @@ -47,7 +48,7 @@ static int parse_fn_body(spl_comp_t *ctx, const char *fn_name, spl_type_info_t * skip_nl(ctx); } - spl_patch(ctx, alloc_addr, ctx->current_local_bytes / (int)sizeof(spl_val_t) - nparams); + spl_patch(ctx, alloc_addr, ctx->peak_local_bytes / (int)sizeof(spl_val_t) - nparams); expect(ctx, TOK_R_BRACE); } diff --git a/stage1/spl_stmt.c b/stage1/spl_stmt.c index 3230503..e9142d1 100644 --- a/stage1/spl_stmt.c +++ b/stage1/spl_stmt.c @@ -583,7 +583,9 @@ static void parse_defer_stmt(spl_comp_t *ctx) { } /* ============================================================ - * Match statement: match expr { .Variant(bindings) => stmt, ... } + * Match statement: + * Enum: match expr { .Variant(bindings) => stmt, ... } + * Int: match expr { literal => stmt, ..., _ => stmt } * ============================================================ */ static void parse_match_stmt(spl_comp_t *ctx) { @@ -592,21 +594,33 @@ static void parse_match_stmt(spl_comp_t *ctx) { spl_expr_result_t expr = spl_parse_expr(ctx, PREC_MIN); - /* Determine the enum type (deref pointer if needed) */ - spl_type_info_t *enum_type = expr.type; - if (enum_type && enum_type->kind == TYPE_PTR && enum_type->elem && - enum_type->elem->kind == TYPE_ENUM) { - enum_type = enum_type->elem; + /* Determine match type (enum or integer) */ + int is_enum_match = 0; + int is_int_match = 0; + spl_type_info_t *enum_type = NULL; + + spl_type_info_t *t = expr.type; + if (t && t->kind == TYPE_ENUM) { + is_enum_match = 1; + enum_type = t; + } else if (t && t->kind == TYPE_PTR && t->elem && t->elem->kind == TYPE_ENUM) { + is_enum_match = 1; + enum_type = t->elem; + } else if (t && t->kind == TYPE_BASIC && spl_type_is_integer(t->basic_type)) { + is_int_match = 1; } - if (!enum_type || enum_type->kind != TYPE_ENUM) { - spl_comp_error(ctx, "match expression must be an enum"); + + if (!is_enum_match && !is_int_match) { + spl_comp_error(ctx, "match expression must be an enum or integer type"); return; } - /* Save the enum address (pointer value) to a temp slot */ - int addr_offset = ctx->current_local_bytes; + /* Save the value/address to a temp slot */ + int val_offset = ctx->current_local_bytes; ctx->current_local_bytes += (int)sizeof(spl_val_t); - spl_emit(ctx, SPL_LADDR, SPL_PTR, addr_offset); + if (ctx->current_local_bytes > ctx->peak_local_bytes) + ctx->peak_local_bytes = ctx->current_local_bytes; + spl_emit(ctx, SPL_LADDR, SPL_PTR, val_offset); spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); spl_emit(ctx, SPL_STORE, SPL_PTR, 0); @@ -628,125 +642,140 @@ static void parse_match_stmt(spl_comp_t *ctx) { continue; } - /* Parse .VariantName */ - if (peek(ctx)->type == TOK_DOT) - advance(ctx); - spl_tok_t *vtok = advance(ctx); - char vname[256]; - usize vnl = vtok->len < 255 ? vtok->len : 255; - memcpy(vname, vtok->lexeme, vnl); - vname[vnl] = '\0'; + spl_val_t bz_addr = 0; + int scope_pushed = 0; + int has_parens = 0; + spl_val_t body_start = 0; + spl_val_t bnz_addrs[16]; + int n_bnz = 0; + spl_enum_variant_t *arm_variant = NULL; - /* Find the variant in the enum type */ - spl_enum_variant_t *variant = NULL; - vec_for(enum_type->variants, vi) { - if (strcmp(vec_at(enum_type->variants, vi).name, vname) == 0) { - variant = &vec_at(enum_type->variants, vi); + /* --- Parse arm pattern(s): comma-separated values with fallthrough --- */ + if (peek(ctx)->type == KW_ANY) { + /* _ default arm: always matches, no comparison */ + advance(ctx); + } else { + for (;;) { + if (is_enum_match) { + /* .VariantName */ + if (peek(ctx)->type == TOK_DOT) + advance(ctx); + spl_tok_t *vtok = advance(ctx); + char vname[256]; + usize vnl = vtok->len < 255 ? vtok->len : 255; + memcpy(vname, vtok->lexeme, vnl); + vname[vnl] = '\0'; + + spl_enum_variant_t *variant = NULL; + vec_for(enum_type->variants, vi) { + if (strcmp(vec_at(enum_type->variants, vi).name, vname) == 0) { + variant = &vec_at(enum_type->variants, vi); + break; + } + } + if (!variant) { + spl_comp_error(ctx, "unknown variant '%s' in match", vname); + break; + } + arm_variant = variant; + + /* Compare tag: load [val_offset] → addr → +0 → tag */ + spl_emit(ctx, SPL_LADDR, SPL_PTR, val_offset); + spl_emit(ctx, SPL_LOAD, SPL_PTR, 0); + spl_emit(ctx, SPL_PUSH, SPL_USIZE, 0); + spl_emit(ctx, SPL_ADD, SPL_USIZE, 0); + spl_emit(ctx, SPL_LOAD, SPL_I32, 0); + spl_emit(ctx, SPL_PUSH, SPL_I32, variant->value); + spl_emit(ctx, SPL_EQ, SPL_I32, 0); + + /* If bindings follow, this must be the last pattern */ + skip_nl(ctx); + if (peek(ctx)->type == TOK_L_PAREN) { + has_parens = 1; + break; + } + } else if (is_int_match) { + /* Parse expression as arm value */ + spl_parse_expr(ctx, PREC_MIN); + /* Compare: load saved match value, then EQ */ + spl_emit(ctx, SPL_LADDR, SPL_PTR, val_offset); + spl_emit(ctx, SPL_LOAD, SPL_PTR, 0); + spl_emit(ctx, SPL_EQ, SPL_I32, 0); + } + + /* Check for more comma-separated patterns */ + skip_nl(ctx); + if (peek(ctx)->type == TOK_COMMA) { + advance(ctx); + skip_nl(ctx); + /* If another pattern follows (not ⇒), emit BNZ for fallthrough */ + if (peek(ctx)->type != TOK_ASSIGN) { + bnz_addrs[n_bnz++] = spl_emit_bnz(ctx); + continue; + } + break; + } break; } - } - if (!variant) { - spl_comp_error(ctx, "unknown variant '%s' in match", vname); - break; + + /* Last pattern: BZ to skip arm if no value matched */ + if (!ctx->has_error) { + bz_addr = spl_emit_bz(ctx); + body_start = vec_size(ctx->prog.insns); + } } - /* Compare tag: load [addr_offset+0] == variant->value */ - /* Load tag and compare */ - spl_emit(ctx, SPL_LADDR, SPL_PTR, addr_offset); - spl_emit(ctx, SPL_LOAD, SPL_PTR, 0); - spl_emit(ctx, SPL_PUSH, SPL_USIZE, 0); - spl_emit(ctx, SPL_ADD, SPL_USIZE, 0); - spl_emit(ctx, SPL_LOAD, SPL_I32, 0); - spl_emit(ctx, SPL_PUSH, SPL_I32, variant->value); - spl_emit(ctx, SPL_EQ, SPL_I32, 0); - spl_val_t bz_addr = spl_emit_bz(ctx); - - /* This arm matches — parse bindings and body */ - skip_nl(ctx); - - /* Parse binding list: (name, name, ...) */ - int has_parens = 0; - if (peek(ctx)->type == TOK_L_PAREN) { - has_parens = 1; + /* --- Parse enum bindings (only for last variant) --- */ + if (is_enum_match && has_parens && arm_variant) { advance(ctx); /* ( */ skip_nl(ctx); - } + if (peek(ctx)->type != TOK_R_PAREN) { + spl_push_scope(ctx); + scope_pushed = 1; - /* Push scope for bindings if there are any */ - int scope_pushed = 0; - if (has_parens && peek(ctx)->type != TOK_R_PAREN) { - spl_push_scope(ctx); - scope_pushed = 1; + if (arm_variant->data_type && arm_variant->data_type->kind == TYPE_STRUCT) { + int bi = 0; + for (;;) { + spl_tok_t *btok = advance(ctx); + char bname[256]; + usize bnl = btok->len < 255 ? btok->len : 255; + memcpy(bname, btok->lexeme, bnl); + bname[bnl] = '\0'; - if (variant->data_type && variant->data_type->kind == TYPE_STRUCT) { - /* Struct data: each struct field is a binding */ - int bi = 0; - for (;;) { + spl_type_info_t *btype = spl_type_basic(SPL_I32); + usize field_byte_off = 4; + if ((usize)bi < vec_size(arm_variant->data_type->fields)) { + btype = vec_at(arm_variant->data_type->fields, bi).type; + field_byte_off = 4 + vec_at(arm_variant->data_type->fields, bi).offset; + } + + int boffset = spl_declare_var(ctx, bname, btype, 0); + spl_emit_load_to_var(ctx, val_offset, field_byte_off, btype, boffset); + + bi++; + skip_nl(ctx); + if (peek(ctx)->type == TOK_COMMA) { + advance(ctx); + skip_nl(ctx); + continue; + } + break; + } + } else if (arm_variant->data_type) { spl_tok_t *btok = advance(ctx); char bname[256]; usize bnl = btok->len < 255 ? btok->len : 255; memcpy(bname, btok->lexeme, bnl); bname[bnl] = '\0'; - spl_type_info_t *btype = spl_type_basic(SPL_I32); - usize field_byte_off = 4; - if ((usize)bi < vec_size(variant->data_type->fields)) { - btype = vec_at(variant->data_type->fields, bi).type; - field_byte_off = 4 + vec_at(variant->data_type->fields, bi).offset; - } - + spl_type_info_t *btype = arm_variant->data_type; int boffset = spl_declare_var(ctx, bname, btype, 0); - - /* Load from enum data and store to variable */ - spl_emit(ctx, SPL_LADDR, SPL_PTR, addr_offset); - spl_emit(ctx, SPL_LOAD, SPL_PTR, 0); - spl_emit(ctx, SPL_PUSH, SPL_USIZE, field_byte_off); - spl_emit(ctx, SPL_ADD, SPL_USIZE, 0); - spl_type_t bt = (btype->kind == TYPE_BASIC) ? btype->basic_type - : (btype->kind == TYPE_PTR) ? SPL_PTR - : SPL_I32; - spl_emit(ctx, SPL_LOAD, bt, 0); - spl_emit(ctx, SPL_LADDR, SPL_PTR, boffset); - spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); - spl_emit(ctx, SPL_STORE, bt, 0); - - bi++; - skip_nl(ctx); - if (peek(ctx)->type == TOK_COMMA) { - advance(ctx); - skip_nl(ctx); - continue; - } - break; + spl_emit_load_to_var(ctx, val_offset, 4, btype, boffset); } - } else if (variant->data_type) { - /* Simple data type: one binding */ - spl_tok_t *btok = advance(ctx); - char bname[256]; - usize bnl = btok->len < 255 ? btok->len : 255; - memcpy(bname, btok->lexeme, bnl); - bname[bnl] = '\0'; - - spl_type_info_t *btype = variant->data_type; - int boffset = spl_declare_var(ctx, bname, btype, 0); - - /* Load from enum data at offset 4 */ - spl_emit(ctx, SPL_LADDR, SPL_PTR, addr_offset); - spl_emit(ctx, SPL_LOAD, SPL_PTR, 0); - spl_emit(ctx, SPL_PUSH, SPL_USIZE, 4); - spl_emit(ctx, SPL_ADD, SPL_USIZE, 0); - spl_type_t bt = (btype->kind == TYPE_BASIC) ? btype->basic_type - : (btype->kind == TYPE_PTR) ? SPL_PTR - : SPL_I32; - spl_emit(ctx, SPL_LOAD, bt, 0); - spl_emit(ctx, SPL_LADDR, SPL_PTR, boffset); - spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); - spl_emit(ctx, SPL_STORE, bt, 0); } + expect(ctx, TOK_R_PAREN); } - if (has_parens) - expect(ctx, TOK_R_PAREN); skip_nl(ctx); /* => (two tokens: = >) */ @@ -771,7 +800,14 @@ static void parse_match_stmt(spl_comp_t *ctx) { jmp_to_end[n_jmps++] = spl_emit_jmp(ctx); /* Patch BZ to here (next arm or end) */ - spl_patch_to_here(ctx, bz_addr); + if (bz_addr) + spl_patch_to_here(ctx, bz_addr); + + /* Patch BNZs to body start (fallthrough values matched) */ + for (int i = 0; i < n_bnz; i++) { + spl_val_t offset = body_start - bnz_addrs[i] - 1; + spl_patch(ctx, bnz_addrs[i], offset); + } skip_nl(ctx); } diff --git a/stage1/spl_type.c b/stage1/spl_type.c index 3f739aa..9cf4e9a 100644 --- a/stage1/spl_type.c +++ b/stage1/spl_type.c @@ -1,6 +1,7 @@ /* spl_type.c — Type system implementation */ #include "spl_comp.h" +#include "spl_lex_util.h" #include #include #include @@ -369,12 +370,12 @@ spl_type_info_t *spl_parse_type(spl_comp_t *ctx) { } /* Parse array length as integer literal */ - if (tok->type != TOK_INT_LITERAL) { + int len_val; + if (!spl_parse_int_literal(ctx, &len_val)) { spl_comp_error(ctx, "expected array length"); return NULL; } - usize len = (usize)strtoull(tok->lexeme, NULL, 0); - ctx->tok_idx++; + usize len = (usize)len_val; if (vec_at(ctx->toks, ctx->tok_idx).type != TOK_R_BRACKET) { spl_comp_error(ctx, "expected ']'"); diff --git a/stage1/test18_match.spl b/stage1/test18_match.spl new file mode 100644 index 0000000..5f89aee --- /dev/null +++ b/stage1/test18_match.spl @@ -0,0 +1,488 @@ +/* ===== 模块4:match 语句 ===== + * test18_match — match 枚举匹配 + 整数匹配(类似 switch) + * 难度:3/5 + * 验证点:枚举匹配、带数据绑定、结构体字段绑定、枚举指针、 + * 整数自变量匹配、默认分支 _ + */ + +/* ---- 简单枚举(无数据) ---- */ +type Color = enum { + Red; + Green; + Blue; +} + +/* ---- 带 i32 数据的枚举 ---- */ +type Optional = enum { + Some: i32; + None; +} + +/* ---- 带结构体数据的枚举 ---- */ +type Point = struct { + var x: i32; + var y: i32; +} + +type Shape = enum { + Circle: i32; + Rect: Point; +} + +/* ---- 多变体枚举 ---- */ +type ActionResult = enum { + Success: i32; + NotFound; + Timeout: i32; + Error: *u8; +} + +/* ============================================================ + * 测试 1: 无数据枚举匹配 + * ============================================================ */ + +fn test_color_match() i32 { + var c: Color = Color { .Red }; + var val: i32 = 0; + + match c { + .Red => { val = 1; }, + .Green => { val = 2; }, + .Blue => { val = 3; } + } + if val != 1 { ret 1; } + + c = Color { .Green }; + match c { + .Red => { val = 0; }, + .Green => { val = 2; }, + .Blue => { val = 0; } + } + if val != 2 { ret 2; } + + c = Color { .Blue }; + match c { + .Red => { val = 0; }, + .Green => { val = 0; }, + .Blue => { val = 3; } + } + if val != 3 { ret 3; } + + ret 0; +} + +/* ============================================================ + * 测试 2: 带 i32 数据枚举匹配 + * ============================================================ */ + +fn test_optional_match() i32 { + var o: Optional = Optional { .Some = 42 }; + + match o { + .Some(val) => { + if val != 42 { ret 1; } + }, + .None => { + ret 2; + } + } + + o = Optional { .None }; + var is_none: i32 = 0; + match o { + .Some(val) => {}, + .None => { is_none = 1; } + } + if is_none != 1 { ret 3; } + + /* 多次提取不同值 */ + o = Optional { .Some = 99 }; + match o { + .Some(val) => { + if val != 99 { ret 4; } + }, + .None => { ret 5; } + } + + ret 0; +} + +/* ============================================================ + * 测试 3: 带结构体数据枚举匹配(多字段绑定) + * ============================================================ */ + +fn test_shape_match() i32 { + /* Circle: 单数据 */ + var s: Shape = Shape { .Circle = 10 }; + match s { + .Circle(r) => { + if r != 10 { ret 1; } + }, + .Rect(w, h) => { + ret 2; + } + } + + /* Rect: 结构体数据,绑定为 (x, y) 对应 Point 的字段 */ + s = Shape { .Rect = Point { .x = 3, .y = 4 } }; + match s { + .Circle(r) => { ret 3; }, + .Rect(w, h) => { + if w != 3 { ret 4; } + if h != 4 { ret 5; } + } + } + + ret 0; +} + +/* ============================================================ + * 测试 4: 多数据变体枚举匹配 + * ============================================================ */ + +fn test_action_result_match() i32 { + var r: ActionResult = ActionResult { .Success = 200 }; + match r { + .Success(code) => { + if code != 200 { ret 1; } + }, + .NotFound => { + ret 2; + }, + .Timeout(ms) => { + ret 3; + }, + .Error(msg) => { + ret 4; + } + } + + r = ActionResult { .NotFound }; + var found: i32 = 1; + match r { + .Success(code) => { found = 0; }, + .NotFound => { }, + .Timeout(ms) => { found = 0; }, + .Error(msg) => { found = 0; } + } + if found != 1 { ret 5; } + + r = ActionResult { .Timeout = 5000 }; + match r { + .Success(code) => { ret 6; }, + .NotFound => { ret 7; }, + .Timeout(ms) => { + if ms != 5000 { ret 8; } + }, + .Error(msg) => { ret 9; } + } + + ret 0; +} + +/* ============================================================ + * 测试 5: 枚举指针匹配 + * ============================================================ */ + +fn test_ptr_match() i32 { + var c: Color = Color { .Green }; + var p: *Color = &c; + + match p { + .Red => { ret 1; }, + .Green => { }, + .Blue => { ret 2; } + } + + /* 修改后通过指针匹配 */ + c = Color { .Blue }; + match p { + .Red => { ret 3; }, + .Green => { ret 4; }, + .Blue => { } + } + + ret 0; +} + +/* ============================================================ + * 测试 6: match 作为函数返回值 + * ============================================================ */ + +fn classify_color(c: Color) i32 { + match c { + .Red => { ret 1; }, + .Green => { ret 2; }, + .Blue => { ret 3; } + } + ret 0; +} + +fn test_match_in_func() i32 { + var r: i32; + + r = classify_color(Color { .Red }); + if r != 1 { ret 1; } + + r = classify_color(Color { .Green }); + if r != 2 { ret 2; } + + r = classify_color(Color { .Blue }); + if r != 3 { ret 3; } + + ret 0; +} + +/* ============================================================ + * 测试 7: match 嵌套在循环中 + * ============================================================ */ + +fn test_match_in_loop() i32 { + var i: i32 = 0; + var sum: i32 = 0; + + while i < 3 { + var o: Optional; + if i == 0 { + o = Optional { .Some = 10 }; + } else if i == 1 { + o = Optional { .Some = 20 }; + } else { + o = Optional { .Some = 30 }; + } + + match o { + .Some(val) => { + sum = sum + val; + }, + .None => { } + } + i = i + 1; + } + if sum != 60 { ret 1; } + + ret 0; +} + +/* ============================================================ + * 测试 8: 整数 match(类似 switch) + * ============================================================ */ + +fn test_int_match() i32 { + var x: i32 = 2; + var result: i32 = 0; + + match x { + 1 => { result = 10; }, + 2 => { result = 20; }, + 3 => { result = 30; } + } + if result != 20 { ret 1; } + + /* 匹配第一个值 */ + x = 1; + match x { + 1 => { result = 100; }, + 2 => { result = 200; }, + 3 => { result = 300; } + } + if result != 100 { ret 2; } + + /* 匹配最后一个值 */ + x = 3; + match x { + 1 => { result = 1000; }, + 2 => { result = 2000; }, + 3 => { result = 3000; } + } + if result != 3000 { ret 3; } + + ret 0; +} + +/* ============================================================ + * 测试 9: 整数 match 带默认分支 _ + * ============================================================ */ + +fn test_int_match_default() i32 { + var x: i32 = 99; + var result: i32 = 0; + + match x { + 1 => { result = 1; }, + 2 => { result = 2; }, + _ => { result = 99; } + } + if result != 99 { ret 1; } + + /* 默认分支未触发 */ + x = 1; + match x { + 1 => { result = 1; }, + 2 => { result = 2; }, + _ => { result = 99; } + } + if result != 1 { ret 2; } + + ret 0; +} + +/* ============================================================ + * 测试 10: 整数 match 多个值跳转到相同逻辑 + * ============================================================ */ + +fn test_int_match_multi() i32 { + var x: i32 = 2; + var result: i32 = 0; + + /* 每个分支独立 */ + match x { + 0 => { result = 0; }, + 1 => { result = 1; }, + 2 => { result = 2; }, + 3 => { result = 3; } + } + if result != 2 { ret 1; } + + /* 负数和零 */ + x = -1; + match x { + -1 => { result = -1; }, + 0 => { result = 0; }, + 1 => { result = 1; } + } + if result != -1 { ret 2; } + + x = 0; + match x { + -1 => { result = -1; }, + 0 => { result = 0; }, + 1 => { result = 1; } + } + if result != 0 { ret 3; } + + ret 0; +} + +/* ============================================================ + * 测试 11a: match 多值 fallthrough: 1, 2, 3 => body + * ============================================================ */ + +fn test_int_match_fallthrough() i32 { + var x: i32; + var r: i32; + + /* 三个值映射到同一个 body */ + x = 1; r = 0; + match x { + 1, 2, 3 => { r = 10; }, + 4, 5 => { r = 20; } + } + if r != 10 { ret 1; } + + x = 3; + match x { + 1, 2, 3 => { r = 10; }, + 4, 5 => { r = 20; } + } + if r != 10 { ret 2; } + + x = 5; + match x { + 1, 2, 3 => { r = 10; }, + 4, 5 => { r = 20; } + } + if r != 20 { ret 3; } + + /* 单一值(非fallthrough)仍然正常 */ + x = 7; r = 0; + match x { + 1 => { r = 1; }, + 7 => { r = 7; } + } + if r != 7 { ret 4; } + + /* 多个负数值 */ + x = -2; r = 0; + match x { + -3, -2, -1 => { r = 100; }, + 0, 1 => { r = 200; } + } + if r != 100 { ret 5; } + + ret 0; +} + +/* ============================================================ + * 测试 11b: match with body 中修改变量 + * ============================================================ */ + +fn test_match_with_side_effects() i32 { + var x: i32 = 3; + var acc: i32 = 0; + + match x { + 1 => { acc = acc + 1; }, + 2 => { acc = acc + 2; }, + 3 => { acc = acc + 3; }, + 4 => { acc = acc + 4; } + } + if acc != 3 { ret 1; } + + /* 再次 match 同一个变量 */ + match x { + 1 => { acc = acc + 1; }, + 2 => { acc = acc + 2; }, + 3 => { acc = acc + 3; }, + 4 => { acc = acc + 4; } + } + if acc != 6 { ret 2; } + + ret 0; +} + +/* ============================================================ + * 主函数 + * ============================================================ */ + +fn main() i32 { + var r: i32; + + r = test_color_match(); + if r != 0 { ret r; } + + r = test_optional_match(); + if r != 0 { ret r + 10; } + + r = test_shape_match(); + if r != 0 { ret r + 20; } + + r = test_action_result_match(); + if r != 0 { ret r + 30; } + + r = test_ptr_match(); + if r != 0 { ret r + 40; } + + r = test_match_in_func(); + if r != 0 { ret r + 50; } + + r = test_match_in_loop(); + if r != 0 { ret r + 60; } + + r = test_int_match(); + if r != 0 { ret r + 70; } + + r = test_int_match_default(); + if r != 0 { ret r + 80; } + + r = test_int_match_multi(); + if r != 0 { ret r + 90; } + + r = test_match_with_side_effects(); + if r != 0 { ret r + 100; } + + r = test_int_match_fallthrough(); + if r != 0 { ret r + 110; } + + ret 0; +}