From 3cf11f922e9944bf24399cc1cfc5d297e1f5c59a Mon Sep 17 00:00:00 2001 From: zzy <2450266535@qq.com> Date: Tue, 7 Jul 2026 10:40:30 +0800 Subject: [PATCH] =?UTF-8?q?stage1=20=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=20=E5=AE=8C=E6=88=9016=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- stage0/spl_vm.c | 2 + stage1/spl_comp.c | 36 ++-- stage1/spl_comp.h | 2 + stage1/spl_expr.c | 320 +++++++++++++++++++++++++++------ stage1/spl_lex_util.c | 34 +++- stage1/spl_lex_util.h | 12 ++ stage1/spl_lexer.h | 1 + stage1/spl_parser.c | 348 ++++++++++++++++++++++++------------ stage1/spl_stmt.c | 294 ++++++++++++++++++++++++++---- stage1/spl_type.c | 13 +- stage1/test08_arrays.spl | 3 - stage1/test12_functions.spl | 9 + stage1/test16_methods.spl | 2 +- stage1/test19_mutiarray.spl | 1 - 14 files changed, 849 insertions(+), 228 deletions(-) create mode 100644 stage1/spl_lex_util.h delete mode 100644 stage1/test19_mutiarray.spl diff --git a/stage0/spl_vm.c b/stage0/spl_vm.c index fcf31e7..ac757c6 100644 --- a/stage0/spl_vm.c +++ b/stage0/spl_vm.c @@ -512,6 +512,8 @@ LONG WINAPI UnhandledExceptionFilterImpl(EXCEPTION_POINTERS *pExceptionInfo) { void spl_vm_init_ex(spl_vm_t *vm, int stack_size, int call_depth) { #ifdef _WIN32 SetUnhandledExceptionFilter(UnhandledExceptionFilterImpl); + SetConsoleOutputCP(CP_UTF8); + SetConsoleCP(CP_UTF8); #endif if (!vm) return; diff --git a/stage1/spl_comp.c b/stage1/spl_comp.c index b88e71d..d5790c4 100644 --- a/stage1/spl_comp.c +++ b/stage1/spl_comp.c @@ -1,10 +1,10 @@ /* spl_comp.c — SPL compiler main logic and codegen helpers */ #include "spl_comp.h" +#include #include #include #include -#include /* ---- Compiler context init/drop ---- */ @@ -17,14 +17,14 @@ 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->current_type_name = NULL; } void spl_comp_drop(spl_comp_t *ctx) { - if (!ctx) return; + if (!ctx) + return; spl_tok_vec_drop(&ctx->toks); - vec_for(ctx->scopes, i) { - vec_free(vec_at(ctx->scopes, i).vars); - } + vec_for(ctx->scopes, i) { vec_free(vec_at(ctx->scopes, i).vars); } vec_free(ctx->scopes); vec_free(ctx->funcs); /* Free type defs — complex, leak for now in bootstrap */ @@ -38,9 +38,7 @@ void spl_comp_reset(spl_comp_t *ctx) { /* Keep prog, reset everything else */ spl_tok_vec_drop(&ctx->toks); vec_init(ctx->toks); - vec_for(ctx->scopes, i) { - vec_free(vec_at(ctx->scopes, i).vars); - } + vec_for(ctx->scopes, i) { vec_free(vec_at(ctx->scopes, i).vars); } vec_free(ctx->scopes); vec_init(ctx->scopes); ctx->scope_depth = 0; @@ -57,6 +55,7 @@ void spl_comp_reset(spl_comp_t *ctx) { ctx->defer_count = 0; ctx->next_gdata_idx = 0; ctx->addr_of_mode = 0; + ctx->current_type_name = NULL; free(ctx->break_patches); ctx->break_patches = NULL; } @@ -86,13 +85,9 @@ spl_val_t spl_emit_jmp(spl_comp_t *ctx) { return spl_emit(ctx, SPL_JMP, SPL_VOID, 0); } -spl_val_t spl_emit_bz(spl_comp_t *ctx) { - return spl_emit(ctx, SPL_BZ, SPL_VOID, 0); -} +spl_val_t spl_emit_bz(spl_comp_t *ctx) { return spl_emit(ctx, SPL_BZ, SPL_VOID, 0); } -spl_val_t spl_emit_bnz(spl_comp_t *ctx) { - return spl_emit(ctx, SPL_BNZ, SPL_VOID, 0); -} +spl_val_t spl_emit_bnz(spl_comp_t *ctx) { return spl_emit(ctx, SPL_BNZ, SPL_VOID, 0); } void spl_patch_to_here(spl_comp_t *ctx, spl_val_t addr) { /* Compute relative offset: target - (source + 1) */ @@ -113,9 +108,7 @@ void spl_push_scope(spl_comp_t *ctx) { void spl_pop_scope(spl_comp_t *ctx) { if (vec_size(ctx->scopes) > 0) { spl_scope_t *scope = &vec_at(ctx->scopes, vec_size(ctx->scopes) - 1); - vec_for(scope->vars, i) { - free(vec_at(scope->vars, i).name); - } + vec_for(scope->vars, i) { free(vec_at(scope->vars, i).name); } vec_free(scope->vars); ctx->scope_depth--; ctx->scopes.size--; @@ -166,8 +159,8 @@ int spl_get_var_slot(spl_comp_t *ctx, const char *name) { /* ---- Function management ---- */ -int spl_declare_func(spl_comp_t *ctx, const char *name, spl_type_info_t *ret_type, - int nparams, int is_extern, int is_pub) { +int spl_declare_func(spl_comp_t *ctx, const char *name, spl_type_info_t *ret_type, int nparams, + int is_extern, int is_pub) { spl_func_info_t fi; memset(&fi, 0, sizeof(fi)); fi.name = strdup(name); @@ -197,7 +190,7 @@ int spl_lookup_func(spl_comp_t *ctx, const char *name) { /* ---- String/data management ---- */ int spl_add_string(spl_comp_t *ctx, const char *str) { - return spl_add_global_data(ctx, (void*)str, strlen(str) + 1); + return spl_add_global_data(ctx, (void *)str, strlen(str) + 1); } int spl_add_global_data(spl_comp_t *ctx, void *data, usize size) { @@ -295,6 +288,7 @@ int spl_compile(spl_comp_t *ctx, const char *source, const char *fname) { /* Phase 2-4: Parse and codegen */ spl_parse_prog(ctx); - if (ctx->has_error) return -1; + if (ctx->has_error) + return -1; return 0; } diff --git a/stage1/spl_comp.h b/stage1/spl_comp.h index 46980a4..11ec5d6 100644 --- a/stage1/spl_comp.h +++ b/stage1/spl_comp.h @@ -79,6 +79,7 @@ spl_type_info_t *spl_type_union(const char *name); spl_type_info_t *spl_type_enum(const char *name); void spl_type_add_field(spl_type_info_t *st, const char *name, spl_type_info_t *ftype); void spl_type_add_variant(spl_type_info_t *et, const char *name, spl_type_info_t *dtype); +void spl_type_add_method(spl_type_info_t *t, const char *name, int func_idx); void spl_type_compute_layout(spl_type_info_t *t); usize spl_type_size(spl_type_info_t *t); usize spl_type_slot_count(spl_type_info_t *t); @@ -161,6 +162,7 @@ typedef struct { int current_func_idx; spl_type_info_t *current_ret_type; int current_local_slot; /* next free local slot */ + const char *current_type_name; /* name of type whose body we're parsing (for method short-name lookup) */ /* Loop context for break/continue */ int in_loop; diff --git a/stage1/spl_expr.c b/stage1/spl_expr.c index b09dc61..59b93dc 100644 --- a/stage1/spl_expr.c +++ b/stage1/spl_expr.c @@ -1,7 +1,7 @@ /* spl_expr.c — Expression parser + codegen (Pratt parser / precedence climbing) */ #include "spl_comp.h" -#include +#include "spl_lex_util.h" #include #include #include @@ -10,32 +10,6 @@ * Token helpers * ============================================================ */ -/* Token helpers — using shared peek/advance from spl_parser.c */ - -static int match(spl_comp_t *ctx, spl_tok_type_t type) { - if (peek(ctx)->type == type) { - advance(ctx); - return 1; - } - return 0; -} - -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; -} - -/* Skip newline tokens */ -static void skip_nl(spl_comp_t *ctx) { - while (peek(ctx)->type == TOK_ENDLINE) - advance(ctx); -} - /* Check if current token starts a statement */ static int is_stmt_start(spl_comp_t *ctx) { spl_tok_type_t t = peek(ctx)->type; @@ -155,8 +129,8 @@ static void emit_slice_index(spl_comp_t *ctx, spl_type_info_t *elem) { * Uses qualified name (TypeName.Member) first for namespace isolation, * then falls back to simple name lookup. * Returns 1 if resolved. */ -static int spl_resolve_type_member(spl_comp_t *ctx, spl_type_info_t *type, - const char *field, spl_expr_result_t *result) { +static int spl_resolve_type_member(spl_comp_t *ctx, spl_type_info_t *type, const char *field, + spl_expr_result_t *result) { /* For enum: check variants first */ if (type->kind == TYPE_ENUM) { vec_for(type->variants, i) { @@ -198,17 +172,28 @@ static int spl_resolve_type_member(spl_comp_t *ctx, spl_type_info_t *type, /* Map assignment operator token (e.g. +=) to corresponding binary operator (e.g. +) */ static spl_tok_type_t assign_to_binop(spl_tok_type_t t) { switch (t) { - case TOK_ASSIGN_ADD: return TOK_ADD; - case TOK_ASSIGN_SUB: return TOK_SUB; - case TOK_ASSIGN_MUL: return TOK_MUL; - case TOK_ASSIGN_DIV: return TOK_DIV; - case TOK_ASSIGN_MOD: return TOK_MOD; - case TOK_ASSIGN_AND: return TOK_AND; - case TOK_ASSIGN_OR: return TOK_OR; - case TOK_ASSIGN_XOR: return TOK_XOR; - case TOK_ASSIGN_L_SH: return TOK_L_SH; - case TOK_ASSIGN_R_SH: return TOK_R_SH; - default: return (spl_tok_type_t)-1; + case TOK_ASSIGN_ADD: + return TOK_ADD; + case TOK_ASSIGN_SUB: + return TOK_SUB; + case TOK_ASSIGN_MUL: + return TOK_MUL; + case TOK_ASSIGN_DIV: + return TOK_DIV; + case TOK_ASSIGN_MOD: + return TOK_MOD; + case TOK_ASSIGN_AND: + return TOK_AND; + case TOK_ASSIGN_OR: + return TOK_OR; + case TOK_ASSIGN_XOR: + return TOK_XOR; + case TOK_ASSIGN_L_SH: + return TOK_L_SH; + case TOK_ASSIGN_R_SH: + return TOK_R_SH; + default: + return (spl_tok_type_t)-1; } } @@ -398,10 +383,165 @@ static spl_expr_result_t parse_array_literal(spl_comp_t *ctx) { return (spl_expr_result_t){arr_type, 0}; } -/* Parse a type name with optional .field suffix for enum variants: - * TypeName - * TypeName.field - */ +/* 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_slot_count(type) <= 1) + 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 single-slot types, pushes the packed value directly. */ +static spl_expr_result_t parse_struct_literal(spl_comp_t *ctx, spl_type_info_t *type) { + usize sc = spl_type_slot_count(type); + int base_slot = ctx->current_local_slot; + ctx->current_local_slot += (int)sc; + + advance(ctx); /* { */ + skip_nl(ctx); + + if (type->kind == TYPE_STRUCT) { + 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); + spl_tok_t *ftok = advance(ctx); + char fname[256]; + usize fnl = ftok->len < 255 ? ftok->len : 255; + memcpy(fname, ftok->lexeme, fnl); + fname[fnl] = '\0'; + skip_nl(ctx); + if (peek(ctx)->type == TOK_ASSIGN) + advance(ctx); + skip_nl(ctx); + + vec_for(type->fields, fi) { + spl_field_t *f = &vec_at(type->fields, fi); + if (strcmp(f->name, fname) == 0) { + spl_emit(ctx, SPL_LADDR, SPL_PTR, base_slot); + if (f->offset > 0) { + spl_emit(ctx, SPL_PUSH, SPL_USIZE, f->offset); + spl_emit(ctx, SPL_ADD, SPL_USIZE, 0); + } + spl_expr_result_t fv = spl_parse_expr(ctx, PREC_MIN); + (void)fv; + spl_emit(ctx, SPL_STORE, spl_store_type(f->type), 0); + break; + } + } + } + skip_nl(ctx); + } + } else if (type->kind == TYPE_ENUM) { + /* Parse .Variant [= value] */ + 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'; + skip_nl(ctx); + if (peek(ctx)->type == TOK_ASSIGN) + advance(ctx); + skip_nl(ctx); + + int found = 0; + vec_for(type->variants, vi) { + spl_enum_variant_t *v = &vec_at(type->variants, vi); + if (strcmp(v->name, vname) == 0) { + found = 1; + /* Store tag at offset 0 */ + spl_emit(ctx, SPL_LADDR, SPL_PTR, base_slot); + spl_emit(ctx, SPL_PUSH, SPL_I32, v->value); + spl_emit(ctx, SPL_STORE, SPL_I32, 0); + + /* Store variant data at offset 4 */ + const usize DATA_OFFSET = 4; + if (v->data_type) { + if (v->data_type->kind == TYPE_STRUCT && peek(ctx)->type == TOK_L_BRACE) { + /* Struct data: { .field = val, ... } */ + advance(ctx); /* { */ + skip_nl(ctx); + 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); + spl_tok_t *sftok = advance(ctx); + char sfname[256]; + usize sfnl = sftok->len < 255 ? sftok->len : 255; + memcpy(sfname, sftok->lexeme, sfnl); + sfname[sfnl] = '\0'; + skip_nl(ctx); + if (peek(ctx)->type == TOK_ASSIGN) + advance(ctx); + skip_nl(ctx); + + vec_for(v->data_type->fields, sfi) { + spl_field_t *sf = &vec_at(v->data_type->fields, sfi); + if (strcmp(sf->name, sfname) == 0) { + spl_emit(ctx, SPL_LADDR, SPL_PTR, base_slot); + usize byte_off = DATA_OFFSET + sf->offset; + if (byte_off > 0) { + spl_emit(ctx, SPL_PUSH, SPL_USIZE, byte_off); + spl_emit(ctx, SPL_ADD, SPL_USIZE, 0); + } + spl_expr_result_t sfv = spl_parse_expr(ctx, PREC_MIN); + (void)sfv; + spl_emit(ctx, SPL_STORE, spl_store_type(sf->type), 0); + break; + } + } + skip_nl(ctx); + } + expect(ctx, TOK_R_BRACE); + } else { + /* Simple data: parse expression */ + spl_expr_result_t dv = spl_parse_expr(ctx, PREC_MIN); + (void)dv; + spl_emit(ctx, SPL_LADDR, SPL_PTR, base_slot); + 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_emit(ctx, SPL_SWAP, SPL_VOID, 0); + spl_emit(ctx, SPL_STORE, bt, 0); + } + } + break; + } + } + if (!found) + spl_comp_error(ctx, "unknown enum variant '%s'", vname); + } + + skip_nl(ctx); + expect(ctx, TOK_R_BRACE); + + /* Return: for single-slot, push packed value. For multi-slot, push address. */ + if (sc == 1) { + spl_emit(ctx, SPL_LADDR, SPL_PTR, base_slot); + spl_emit(ctx, SPL_LOAD, SPL_PTR, 0); + return (spl_expr_result_t){type, 0}; /* value on stack */ + } else { + spl_emit(ctx, SPL_LADDR, SPL_PTR, base_slot); + return (spl_expr_result_t){type, 1}; /* address on stack */ + } +} static spl_type_info_t *parse_qualified_type(spl_comp_t *ctx, const char *name) { /* Check if it's a known type */ spl_type_info_t *t = spl_resolve_type(ctx, name); @@ -436,6 +576,12 @@ static spl_expr_result_t parse_ident(spl_comp_t *ctx) { /* Check if it's a function call: ident(...) */ if (peek(ctx)->type == TOK_L_PAREN) { int fi = spl_lookup_func(ctx, name); + /* Fallback: try qualified name for short-name resolution inside methods */ + if (fi < 0 && ctx->current_type_name) { + char qualified[512]; + snprintf(qualified, sizeof(qualified), "%s.%s", ctx->current_type_name, name); + fi = spl_lookup_func(ctx, qualified); + } if (fi < 0) { spl_comp_error(ctx, "unknown function '%s'", name); spl_expr_result_t r = {0}; @@ -525,7 +671,12 @@ static spl_expr_result_t parse_ident(spl_comp_t *ctx) { /* Check if it's a type name (for enum variant access like Color.Red) */ spl_type_info_t *ttype = spl_resolve_type(ctx, name); if (ttype) { - spl_expr_result_t r = {ttype, 1}; + /* Struct/enum literal: Type { .field = val, ... } */ + if (peek(ctx)->type == TOK_L_BRACE && + (ttype->kind == TYPE_STRUCT || ttype->kind == TYPE_ENUM)) { + return parse_struct_literal(ctx, ttype); + } + spl_expr_result_t r = {ttype, 0}; return r; } @@ -669,9 +820,7 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) { break; } char bname[256]; - usize bnlen = tok->len < 255 ? tok->len : 255; - memcpy(bname, tok->lexeme, bnlen); - bname[bnlen] = '\0'; + spl_tok_copy_name(tok, bname, sizeof bname); advance(ctx); /* consume builtin name */ if (strcmp(bname, "dbg") == 0) { @@ -746,6 +895,73 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) { } } + /* Method call on type/instance: left.field(args) */ + if (left.type && peek(ctx)->type == TOK_L_PAREN) { + /* Determine the type that owns methods (deref pointer if needed) */ + spl_type_info_t *methods_type = left.type; + int is_ptr_self = 0; + if (methods_type->kind == TYPE_PTR && methods_type->elem && + (methods_type->elem->kind == TYPE_STRUCT || + methods_type->elem->kind == TYPE_ENUM)) { + is_ptr_self = 1; + methods_type = methods_type->elem; + } + + int found_method = 0; + vec_for(methods_type->methods, mi) { + if (strcmp(vec_at(methods_type->methods, mi).name, fname) == 0) { + spl_method_info_t *method = &vec_at(methods_type->methods, mi); + spl_func_info_t *func = &vec_at(ctx->funcs, method->func_idx); + + advance(ctx); /* ( */ + found_method = 1; + + int nargs = 0; + + /* Check if instance method: first param is self: *Type */ + int is_instance = 0; + if (func->nparams > 0 && func->param_types[0] && + func->param_types[0]->kind == TYPE_PTR && + func->param_types[0]->elem == methods_type) { + is_instance = 1; + } + + if (is_instance) { + if (!left.is_lvalue && !is_ptr_self) { + spl_comp_error(ctx, "cannot call instance method '%s' on type", + fname); + } + nargs = 1; /* self already on stack */ + } + + /* Parse remaining arguments */ + if (peek(ctx)->type != TOK_R_PAREN) { + for (;;) { + spl_expr_result_t arg = spl_parse_expr(ctx, PREC_MIN); + (void)arg; + nargs++; + if (peek(ctx)->type == TOK_COMMA) { + advance(ctx); + continue; + } + break; + } + } + expect(ctx, TOK_R_PAREN); + + spl_val_t addr = vec_at(ctx->prog.funcs, func->func_idx).address; + spl_emit(ctx, SPL_PUSH, SPL_PTR, addr); + spl_emit(ctx, SPL_CALL, SPL_VOID, nargs); + + left = (spl_expr_result_t){func->ret_type, 0}; + break; + } + } + if (found_method) + continue; + /* If method not found, fall through to field access below */ + } + /* Struct field access */ if (left.type && left.type->kind == TYPE_STRUCT) { /* The struct address is on stack (as lvalue or from previous computation) */ @@ -998,12 +1214,12 @@ static spl_expr_result_t parse_infix(spl_comp_t *ctx, spl_expr_result_t left, sp spl_emit(ctx, SPL_STORE, bt, 0); } else { /* Compound: left = left op right — stack: [addr, rhs] */ - spl_emit(ctx, SPL_PICK, SPL_VOID, 1); /* [addr, rhs, addr] */ - spl_emit(ctx, SPL_LOAD, bt, 0); /* [addr, rhs, old_val] */ - spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); /* [addr, old_val, rhs] */ + spl_emit(ctx, SPL_PICK, SPL_VOID, 1); /* [addr, rhs, addr] */ + spl_emit(ctx, SPL_LOAD, bt, 0); /* [addr, rhs, old_val] */ + spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); /* [addr, old_val, rhs] */ int sop = binop_to_sir(assign_to_binop(op), bt); if (sop >= 0) - spl_emit(ctx, sop, bt, 0); /* [addr, result] */ + spl_emit(ctx, sop, bt, 0); /* [addr, result] */ spl_emit(ctx, SPL_STORE, bt, 0); } } diff --git a/stage1/spl_lex_util.c b/stage1/spl_lex_util.c index 962077a..9094b8f 100644 --- a/stage1/spl_lex_util.c +++ b/stage1/spl_lex_util.c @@ -1,9 +1,32 @@ /* spl_lex_util.c — Lexer utility functions */ -#include "spl_lexer.h" +#include "spl_lex_util.h" #include #include +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; +} + +int match(spl_comp_t *ctx, spl_tok_type_t type) { + if (peek(ctx)->type == type) { + advance(ctx); + return 1; + } + return 0; +} + +void skip_nl(spl_comp_t *ctx) { + while (peek(ctx)->type == TOK_ENDLINE) + advance(ctx); +} + const char *spl_tok_type_name(spl_tok_type_t type) { switch (type) { #define X(name, enum_name, dummy) \ @@ -64,3 +87,12 @@ void spl_tok_vec_drop(spl_tok_vec_t *toks) { vec_free(*toks); } } + +usize spl_tok_copy_name(spl_tok_t *tok, char *buf, usize buf_size) { + if (!tok || !buf || buf_size == 0) + return 0; + usize nlen = tok->len < buf_size - 1 ? tok->len : buf_size - 1; + memcpy(buf, tok->lexeme, nlen); + buf[nlen] = '\0'; + return nlen; +} diff --git a/stage1/spl_lex_util.h b/stage1/spl_lex_util.h new file mode 100644 index 0000000..c4c3918 --- /dev/null +++ b/stage1/spl_lex_util.h @@ -0,0 +1,12 @@ +/* spl_lex_util.h — Shared token helper utilities */ +#ifndef __SPL_LEX_UTIL_H__ +#define __SPL_LEX_UTIL_H__ + +#include "spl_comp.h" + +/* Token matching helpers */ +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); + +#endif /* __SPL_LEX_UTIL_H__ */ diff --git a/stage1/spl_lexer.h b/stage1/spl_lexer.h index fbdf644..b51b35e 100644 --- a/stage1/spl_lexer.h +++ b/stage1/spl_lexer.h @@ -135,6 +135,7 @@ const char *spl_tok_type_name(spl_tok_type_t type); void spl_tok_dump(spl_tok_t *tok); void spl_tok_vec_dump(spl_tok_vec_t *toks); void spl_tok_vec_drop(spl_tok_vec_t *toks); +usize spl_tok_copy_name(spl_tok_t *tok, char *buf, usize buf_size); /* Decode escape sequence, advance *s past it. Returns 0 on success. */ int spl_decode_escape(const char **s, char *out); diff --git a/stage1/spl_parser.c b/stage1/spl_parser.c index 320cc7d..31cf72d 100644 --- a/stage1/spl_parser.c +++ b/stage1/spl_parser.c @@ -1,6 +1,7 @@ /* spl_parser.c — Top-level parser: function declarations, type declarations, etc. */ #include "spl_comp.h" +#include "spl_lex_util.h" #include spl_tok_t *peek(spl_comp_t *ctx) { return &vec_at(ctx->toks, ctx->tok_idx); } @@ -12,20 +13,6 @@ spl_tok_t *advance(spl_comp_t *ctx) { 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 void skip_nl(spl_comp_t *ctx) { - while (peek(ctx)->type == TOK_ENDLINE) - advance(ctx); -} /* ============================================================ * Parse function definition @@ -33,6 +20,48 @@ static void skip_nl(spl_comp_t *ctx) { * or fn name(params) ret-type; (forward decl, not used for stage1) * ============================================================ */ +/* Shared helper: register a function, declare params, parse body, end function. + * Used by both top-level fn decl and methods inside type bodies. */ +static int parse_fn_body(spl_comp_t *ctx, const char *fn_name, spl_type_info_t *ret_type, + int nparams, char pnames[][256], spl_type_info_t *ptypes[], int is_pub) { + int fi = spl_declare_func(ctx, fn_name, ret_type, nparams, 0, is_pub); + ctx->current_func_idx = fi; + ctx->current_ret_type = ret_type; + ctx->current_local_slot = 0; + + spl_push_scope(ctx); + + for (int i = 0; i < nparams; i++) { + spl_declare_var(ctx, pnames[i], ptypes[i], 0); + } + + skip_nl(ctx); + if (peek(ctx)->type == TOK_L_BRACE) { + advance(ctx); /* { */ + skip_nl(ctx); + + spl_val_t alloc_addr = vec_size(ctx->prog.insns); + spl_emit(ctx, SPL_ALLOC, SPL_VOID, 0); + + while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) { + spl_parse_stmt(ctx); + skip_nl(ctx); + } + + spl_patch(ctx, alloc_addr, ctx->current_local_slot - nparams); + expect(ctx, TOK_R_BRACE); + } + + spl_emit_defer_epilogue(ctx, ctx->scope_depth); + spl_pop_scope(ctx); + + spl_emit(ctx, SPL_RET, SPL_VOID, 0); + spl_prog_end_func(&ctx->prog, fi); + ctx->current_func_idx = -1; + ctx->current_ret_type = NULL; + return fi; +} + static void parse_fn_decl(spl_comp_t *ctx, int is_extern, int is_pub) { advance(ctx); /* fn */ skip_nl(ctx); @@ -124,53 +153,8 @@ static void parse_fn_decl(spl_comp_t *ctx, int is_extern, int is_pub) { skip_nl(ctx); - /* Declare function in prog */ - int fi = spl_declare_func(ctx, fn_name, ret_type, nparams, 0, is_pub); - ctx->current_func_idx = fi; - ctx->current_ret_type = ret_type; - ctx->current_local_slot = 0; - - /* Push function scope for params + locals */ - spl_push_scope(ctx); - - /* Declare parameters as variables in the function scope */ - for (int i = 0; i < nparams; i++) { - spl_declare_var(ctx, pnames[i], ptypes[i], 0); - } - - /* Parse body */ - skip_nl(ctx); - if (peek(ctx)->type == TOK_L_BRACE) { - advance(ctx); /* { */ - skip_nl(ctx); - - /* Emit ALLOC placeholder to reserve stack space for locals. - * Without ALLOC, PUSH in expressions overwrites variable slots - * because the stack and locals share the same memory. */ - spl_val_t alloc_addr = vec_size(ctx->prog.insns); - spl_emit(ctx, SPL_ALLOC, SPL_VOID, 0); - - while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) { - spl_parse_stmt(ctx); - skip_nl(ctx); - } - - /* Patch ALLOC to local slot count only (excludes param slots) */ - spl_patch(ctx, alloc_addr, ctx->current_local_slot - nparams); - - expect(ctx, TOK_R_BRACE); - } - - spl_emit_defer_epilogue(ctx, ctx->scope_depth); - spl_pop_scope(ctx); - - /* Emit implicit RET for void functions without explicit return */ - spl_emit(ctx, SPL_RET, SPL_VOID, 0); - - /* End function */ - spl_prog_end_func(&ctx->prog, fi); - ctx->current_func_idx = -1; - ctx->current_ret_type = NULL; + /* Use shared helper for function body parsing */ + parse_fn_body(ctx, fn_name, ret_type, nparams, pnames, ptypes, is_pub); } /* ============================================================ @@ -180,7 +164,7 @@ static void parse_fn_decl(spl_comp_t *ctx, int is_extern, int is_pub) { * var name: type; — field declarations * name: type, — field declarations (old-style) * type Name = ...; — nested type declarations - * fn name(...) type { } — methods (skipped) + * fn name(...) type { } — methods * ============================================================ */ static void parse_struct_body(spl_comp_t *ctx, spl_type_info_t *st) { @@ -195,9 +179,15 @@ static void parse_struct_body(spl_comp_t *ctx, spl_type_info_t *st) { 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++; advance(ctx); } - else if (tt == TOK_R_BRACE) { depth--; if (depth == 0) break; advance(ctx); } - else if (tt == KW_TYPE && depth == 1) { + 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) { parse_type_decl(ctx); } else { advance(ctx); @@ -213,13 +203,20 @@ static void parse_struct_body(spl_comp_t *ctx, spl_type_info_t *st) { while (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) { depth++; advance(ctx); } - else if (tt == TOK_R_BRACE) { depth--; if (depth == 0) { advance(ctx); break; } advance(ctx); } - else if (tt == KW_TYPE && depth == 1) { + if (tt == TOK_L_BRACE) { + depth++; + advance(ctx); + } else if (tt == TOK_R_BRACE) { + depth--; + if (depth == 0) { + advance(ctx); + break; + } + advance(ctx); + } else if (tt == KW_TYPE && depth == 1) { /* Nested type — already parsed in pass 1, skip by re-parsing */ parse_type_decl(ctx); - } - else if (tt == KW_VAR && depth == 1) { + } else if (tt == KW_VAR && depth == 1) { /* var name: type; */ advance(ctx); /* var */ skip_nl(ctx); @@ -238,8 +235,7 @@ static void parse_struct_body(spl_comp_t *ctx, spl_type_info_t *st) { skip_nl(ctx); if (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_COMMA) advance(ctx); - } - else if (tt == TOK_IDENT && depth == 1) { + } else if (tt == TOK_IDENT && depth == 1) { /* Old-style field: name: type, */ spl_tok_t *ftok = advance(ctx); skip_nl(ctx); @@ -256,28 +252,86 @@ static void parse_struct_body(spl_comp_t *ctx, spl_type_info_t *st) { skip_nl(ctx); if (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_COMMA) advance(ctx); - } - else if (tt == KW_FN && depth == 1) { - /* Method — skip over fn name(params) rettype { body } */ + } else if (tt == KW_FN && depth == 1) { + /* Method — parse properly using parse_fn_body */ advance(ctx); /* fn */ - while (ctx->tok_idx < vec_size(ctx->toks)) { - spl_tok_type_t mt = peek(ctx)->type; - if (mt == TOK_SEMICOLON) { advance(ctx); break; } - if (mt == TOK_L_BRACE) { - /* Skip function body counting braces */ - advance(ctx); - int bd = 1; - while (bd > 0 && ctx->tok_idx < vec_size(ctx->toks)) { - if (peek(ctx)->type == TOK_L_BRACE) bd++; - else if (peek(ctx)->type == TOK_R_BRACE) bd--; + skip_nl(ctx); + spl_tok_t *mname_tok = advance(ctx); + char mname[256]; + usize mnl = mname_tok->len < 255 ? mname_tok->len : 255; + memcpy(mname, mname_tok->lexeme, mnl); + mname[mnl] = '\0'; + + /* Build qualified name: TypeName.method_name */ + char qualified[512]; + snprintf(qualified, sizeof(qualified), "%s.%s", st->name ? st->name : "anon", + mname); + + skip_nl(ctx); + expect(ctx, TOK_L_PAREN); + + /* Parse parameters */ + int nparams = 0; + enum { MAX_PARAMS = 64 }; + char pnames[MAX_PARAMS][256]; + spl_type_info_t *ptypes[MAX_PARAMS]; + skip_nl(ctx); + if (peek(ctx)->type != TOK_R_PAREN) { + while (1) { + spl_tok_t *pname = advance(ctx); + usize pnl = pname->len < 255 ? pname->len : 255; + memcpy(pnames[nparams], pname->lexeme, pnl); + pnames[nparams][pnl] = '\0'; + skip_nl(ctx); + if (peek(ctx)->type == TOK_COLON) { + advance(ctx); /* : */ + skip_nl(ctx); + ptypes[nparams] = spl_parse_type(ctx); + } else { + ptypes[nparams] = spl_type_basic(SPL_I32); + } + nparams++; + skip_nl(ctx); + if (peek(ctx)->type == TOK_COMMA) { advance(ctx); + skip_nl(ctx); + continue; } break; } - advance(ctx); } - } - else { + expect(ctx, TOK_R_PAREN); + skip_nl(ctx); + + /* Return type (default: void) */ + 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); + } + + /* Set current_type_name for short-name resolution inside method body */ + const char *saved_type_name = ctx->current_type_name; + ctx->current_type_name = st->name; + + int fi = parse_fn_body(ctx, qualified, ret_type, nparams, pnames, ptypes, 0); + + { + spl_func_info_t *f = &vec_at(ctx->funcs, fi); + f->param_types = calloc(nparams, sizeof(spl_type_info_t *)); + f->param_names = calloc(nparams, sizeof(char *)); + for (int i = 0; i < nparams; i++) { + f->param_types[i] = ptypes[i]; + f->param_names[i] = strdup(pnames[i]); + } + } + + ctx->current_type_name = saved_type_name; + + spl_type_add_method(st, mname, fi); + } else { advance(ctx); } } @@ -308,9 +362,15 @@ static void parse_enum_body(spl_comp_t *ctx, spl_type_info_t *et) { 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++; advance(ctx); } - else if (tt == TOK_R_BRACE) { depth--; if (depth == 0) break; advance(ctx); } - else if (tt == KW_TYPE && depth == 1) { + 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) { parse_type_decl(ctx); } else { advance(ctx); @@ -325,13 +385,20 @@ static void parse_enum_body(spl_comp_t *ctx, spl_type_info_t *et) { while (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) { depth++; advance(ctx); } - else if (tt == TOK_R_BRACE) { depth--; if (depth == 0) { advance(ctx); break; } advance(ctx); } - else if (tt == KW_TYPE && depth == 1) { + if (tt == TOK_L_BRACE) { + depth++; + advance(ctx); + } else if (tt == TOK_R_BRACE) { + depth--; + if (depth == 0) { + advance(ctx); + break; + } + advance(ctx); + } else if (tt == KW_TYPE && depth == 1) { /* Already parsed in pass 1, re-parse to skip */ parse_type_decl(ctx); - } - else if (tt == TOK_IDENT && depth == 1) { + } else if (tt == TOK_IDENT && depth == 1) { /* Variant: Name or Name: Type */ spl_tok_t *vtok = advance(ctx); skip_nl(ctx); @@ -354,27 +421,86 @@ static void parse_enum_body(spl_comp_t *ctx, spl_type_info_t *et) { skip_nl(ctx); if (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_COMMA) advance(ctx); - } - else if (tt == KW_FN && depth == 1) { - /* Method — skip */ + } else if (tt == KW_FN && depth == 1) { + /* Method — parse properly using parse_fn_body */ advance(ctx); /* fn */ - while (ctx->tok_idx < vec_size(ctx->toks)) { - spl_tok_type_t mt = peek(ctx)->type; - if (mt == TOK_SEMICOLON) { advance(ctx); break; } - if (mt == TOK_L_BRACE) { - advance(ctx); - int bd = 1; - while (bd > 0 && ctx->tok_idx < vec_size(ctx->toks)) { - if (peek(ctx)->type == TOK_L_BRACE) bd++; - else if (peek(ctx)->type == TOK_R_BRACE) bd--; + skip_nl(ctx); + spl_tok_t *mname_tok = advance(ctx); + char mname[256]; + usize mnl = mname_tok->len < 255 ? mname_tok->len : 255; + memcpy(mname, mname_tok->lexeme, mnl); + mname[mnl] = '\0'; + + /* Build qualified name: TypeName.method_name */ + char qualified[512]; + snprintf(qualified, sizeof(qualified), "%s.%s", et->name ? et->name : "anon", + mname); + + skip_nl(ctx); + expect(ctx, TOK_L_PAREN); + + /* Parse parameters */ + int nparams = 0; + enum { MAX_PARAMS = 64 }; + char pnames[MAX_PARAMS][256]; + spl_type_info_t *ptypes[MAX_PARAMS]; + skip_nl(ctx); + if (peek(ctx)->type != TOK_R_PAREN) { + while (1) { + spl_tok_t *pname = advance(ctx); + usize pnl = pname->len < 255 ? pname->len : 255; + memcpy(pnames[nparams], pname->lexeme, pnl); + pnames[nparams][pnl] = '\0'; + skip_nl(ctx); + if (peek(ctx)->type == TOK_COLON) { + advance(ctx); /* : */ + skip_nl(ctx); + ptypes[nparams] = spl_parse_type(ctx); + } else { + ptypes[nparams] = spl_type_basic(SPL_I32); + } + nparams++; + skip_nl(ctx); + if (peek(ctx)->type == TOK_COMMA) { advance(ctx); + skip_nl(ctx); + continue; } break; } - advance(ctx); } - } - else { + expect(ctx, TOK_R_PAREN); + skip_nl(ctx); + + /* Return type (default: void) */ + 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); + } + + /* Set current_type_name for short-name resolution inside method body */ + const char *saved_type_name = ctx->current_type_name; + ctx->current_type_name = et->name; + + int fi = parse_fn_body(ctx, qualified, ret_type, nparams, pnames, ptypes, 0); + + ctx->current_type_name = saved_type_name; + + { + spl_func_info_t *f = &vec_at(ctx->funcs, fi); + f->param_types = calloc(nparams, sizeof(spl_type_info_t *)); + f->param_names = calloc(nparams, sizeof(char *)); + for (int i = 0; i < nparams; i++) { + f->param_types[i] = ptypes[i]; + f->param_names[i] = strdup(pnames[i]); + } + } + + spl_type_add_method(et, mname, fi); + } else { advance(ctx); } } diff --git a/stage1/spl_stmt.c b/stage1/spl_stmt.c index f26bce5..641889c 100644 --- a/stage1/spl_stmt.c +++ b/stage1/spl_stmt.c @@ -1,34 +1,10 @@ /* spl_stmt.c — Statement parser + codegen */ #include "spl_comp.h" +#include "spl_lex_util.h" #include #include -/* Token helpers — using shared peek/advance from spl_parser.c */ - -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; * ============================================================ */ @@ -49,7 +25,17 @@ static void parse_ret_stmt(spl_comp_t *ctx) { } 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_type_t rt = SPL_VOID; + if (ctx->current_ret_type) { + if (ctx->current_ret_type->kind == TYPE_BASIC) { + rt = ctx->current_ret_type->basic_type; + } else if (ctx->current_ret_type->kind == TYPE_PTR) { + rt = SPL_PTR; + } else if (spl_type_slot_count(ctx->current_ret_type) == 1) { + /* 1-slot struct/enum/etc: use PTR type */ + rt = SPL_PTR; + } + } spl_emit(ctx, SPL_RET, rt, 0); } if (peek(ctx)->type == TOK_SEMICOLON) @@ -96,19 +82,44 @@ static void parse_var_decl(spl_comp_t *ctx, int is_const) { advance(ctx); /* = */ } - /* Allocate stack slot */ - if (!var_type) - var_type = spl_type_basic(SPL_I32); /* default type */ + /* Init expression: parse before declare to enable type inference */ + spl_expr_result_t init = {0}; + if (has_init) { + skip_nl(ctx); + init = spl_parse_expr(ctx, PREC_MIN); + } + + if (!var_type) { + var_type = init.type ? init.type : spl_type_basic(SPL_I32); + } int slot = spl_declare_var(ctx, vname, var_type, is_const); - skip_nl(ctx); - - /* Init expression */ + /* Store init value */ if (has_init) { - spl_expr_result_t init = spl_parse_expr(ctx, PREC_MIN); - (void)init; - - if (var_type && var_type->kind == TYPE_ARRAY) { + if (var_type && (var_type->kind == TYPE_STRUCT || var_type->kind == TYPE_ENUM)) { + /* Struct/enum initialization */ + usize sc = spl_type_slot_count(var_type); + if (sc == 1) { + /* Single-slot: value on stack, store directly */ + 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 { + /* Multi-slot: copy from temp addr to var slots */ + for (usize i = 0; i < sc; i++) { + if (i < sc - 1) + spl_emit(ctx, SPL_DUP, SPL_VOID, 0); + if (i > 0) { + spl_emit(ctx, SPL_PUSH, SPL_USIZE, i * sizeof(spl_val_t)); + spl_emit(ctx, SPL_ADD, SPL_USIZE, 0); + } + spl_emit(ctx, SPL_LOAD, SPL_PTR, 0); + spl_emit(ctx, SPL_LADDR, SPL_PTR, slot + (int)i); + spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); + spl_emit(ctx, SPL_STORE, SPL_PTR, 0); + } + } + } else 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; @@ -544,6 +555,211 @@ static void parse_defer_stmt(spl_comp_t *ctx) { } } +/* ============================================================ + * Match statement: match expr { .Variant(bindings) => stmt, ... } + * ============================================================ */ + +static void parse_match_stmt(spl_comp_t *ctx) { + advance(ctx); /* match */ + skip_nl(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; + } + if (!enum_type || enum_type->kind != TYPE_ENUM) { + spl_comp_error(ctx, "match expression must be an enum"); + return; + } + + /* Save the enum address (pointer value) to a temp slot */ + int addr_slot = ctx->current_local_slot; + ctx->current_local_slot++; + spl_emit(ctx, SPL_LADDR, SPL_PTR, addr_slot); + spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); + spl_emit(ctx, SPL_STORE, SPL_PTR, 0); + + skip_nl(ctx); + if (peek(ctx)->type == TOK_L_BRACE) + advance(ctx); /* { */ + + /* Collect JMP-to-end addresses for patching */ + enum { MAX_MATCH_ARMS = 32 }; + spl_val_t jmp_to_end[MAX_MATCH_ARMS]; + int n_jmps = 0; + + skip_nl(ctx); + while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) { + skip_nl(ctx); + if (peek(ctx)->type == TOK_COMMA) { + advance(ctx); + skip_nl(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'; + + /* Find the variant in the enum type */ + spl_enum_variant_t *variant = NULL; + int vi; + 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; + } + + /* Compare tag: load [addr_slot+0] == variant->value */ + /* Load tag and compare */ + spl_emit(ctx, SPL_LADDR, SPL_PTR, addr_slot); + 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; + advance(ctx); /* ( */ + skip_nl(ctx); + } + + /* 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 (variant->data_type && variant->data_type->kind == TYPE_STRUCT) { + /* Struct data: each struct field is a binding */ + 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'; + + 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; + } + + int bslot = spl_declare_var(ctx, bname, btype, 0); + + /* Load from enum data and store to variable */ + spl_emit(ctx, SPL_LADDR, SPL_PTR, addr_slot); + 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, bslot); + 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; + } + } 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 bslot = spl_declare_var(ctx, bname, btype, 0); + + /* Load from enum data at offset 4 */ + spl_emit(ctx, SPL_LADDR, SPL_PTR, addr_slot); + 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, bslot); + spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); + spl_emit(ctx, SPL_STORE, bt, 0); + } + } + + if (has_parens) + expect(ctx, TOK_R_PAREN); + skip_nl(ctx); + + /* => (two tokens: = >) */ + if (peek(ctx)->type == TOK_ASSIGN) { + advance(ctx); + if (peek(ctx)->type == TOK_GT) + advance(ctx); + } + skip_nl(ctx); + + /* Parse arm body statement */ + spl_parse_stmt(ctx); + + /* Pop scope if we pushed one */ + if (scope_pushed) { + spl_emit_defer_epilogue(ctx, ctx->scope_depth); + spl_pop_scope(ctx); + } + + /* JMP to end (skip remaining arms) */ + if (n_jmps < MAX_MATCH_ARMS) + jmp_to_end[n_jmps++] = spl_emit_jmp(ctx); + + /* Patch BZ to here (next arm or end) */ + spl_patch_to_here(ctx, bz_addr); + + skip_nl(ctx); + } + + expect(ctx, TOK_R_BRACE); + + /* Patch all JMPs to end */ + for (int i = 0; i < n_jmps; i++) + spl_patch_to_here(ctx, jmp_to_end[i]); + + /* Release temp slot */ + ctx->current_local_slot--; +} + /* ============================================================ * Extern declaration: #[extern("vm")] fn name(...) ret; * ============================================================ */ @@ -662,7 +878,8 @@ static void parse_expr_stmt(spl_comp_t *ctx) { if (is_assign) ctx->addr_of_mode = 0; - if (!is_assign && expr.type && expr.type->kind == TYPE_BASIC && expr.type->basic_type != SPL_VOID) + if (!is_assign && expr.type && expr.type->kind == TYPE_BASIC && + expr.type->basic_type != SPL_VOID) spl_emit(ctx, SPL_DROP, SPL_VOID, 0); if (peek(ctx)->type == TOK_SEMICOLON) advance(ctx); @@ -712,6 +929,9 @@ void spl_parse_stmt(spl_comp_t *ctx) { case KW_DEFER: parse_defer_stmt(ctx); break; + case KW_MATCH: + parse_match_stmt(ctx); + break; case KW_TYPE: parse_type_decl(ctx); break; diff --git a/stage1/spl_type.c b/stage1/spl_type.c index 5ed0b9d..dec9c90 100644 --- a/stage1/spl_type.c +++ b/stage1/spl_type.c @@ -132,6 +132,7 @@ spl_type_info_t *spl_type_struct(const char *name) { if (name) t->name = strdup(name); vec_init(t->fields); + vec_init(t->methods); t->resolved = 0; return t; } @@ -142,6 +143,7 @@ spl_type_info_t *spl_type_union(const char *name) { if (name) t->name = strdup(name); vec_init(t->fields); + vec_init(t->methods); t->resolved = 0; return t; } @@ -152,6 +154,7 @@ spl_type_info_t *spl_type_enum(const char *name) { if (name) t->name = strdup(name); vec_init(t->variants); + vec_init(t->methods); t->byte_size = 4; t->slot_count = 1; t->resolved = 0; @@ -174,6 +177,13 @@ void spl_type_add_variant(spl_type_info_t *et, const char *name, spl_type_info_t vec_push(et->variants, v); } +void spl_type_add_method(spl_type_info_t *t, const char *name, int func_idx) { + spl_method_info_t m; + m.name = strdup(name); + m.func_idx = func_idx; + vec_push(t->methods, m); +} + void spl_type_compute_layout(spl_type_info_t *t) { if (!t || t->resolved) return; @@ -205,7 +215,8 @@ void spl_type_compute_layout(spl_type_info_t *t) { /* Round up to slot alignment */ usize slot_sz = sizeof(spl_val_t); t->slot_count = (t->byte_size + slot_sz - 1) / slot_sz; - if (t->slot_count < 1) t->slot_count = 1; + if (t->slot_count < 1) + t->slot_count = 1; t->resolved = 1; } else if (t->kind == TYPE_ENUM) { /* Enums with data need special handling */ diff --git a/stage1/test08_arrays.spl b/stage1/test08_arrays.spl index d0a4b24..0141da6 100644 --- a/stage1/test08_arrays.spl +++ b/stage1/test08_arrays.spl @@ -34,8 +34,5 @@ fn main() i32 { } if s != 20 { ret 6; } - /* 多维效果:数组元素为数组 */ - /* 目前只测试一维 */ - ret 0; } diff --git a/stage1/test12_functions.spl b/stage1/test12_functions.spl index a9d29c2..83a7a61 100644 --- a/stage1/test12_functions.spl +++ b/stage1/test12_functions.spl @@ -19,6 +19,10 @@ fn identity(x: i32) i32 { ret x; } +fn addr(x: *i32) *i32 { + ret x; +} + fn main() i32 { /* 函数调用 */ var r1 := add(3, 4); @@ -42,5 +46,10 @@ fn main() i32 { var r6 := add(add(1, 2), add(3, 4)); if r6 != 10 { ret 6; } + /* 返回地址 */ + var r7 := addr(&r6); + if r7 != &r6 { ret 7; } + if r7.* != 10 { ret 8; } + ret 0; } diff --git a/stage1/test16_methods.spl b/stage1/test16_methods.spl index cccbe6b..165dd0c 100644 --- a/stage1/test16_methods.spl +++ b/stage1/test16_methods.spl @@ -40,7 +40,7 @@ fn main() i32 { var expr_l := Expr { .Int = 3 }; var expr_r := Expr { .Int = 4 }; var expr := Expr { .Add = { .left = expr_l, .right = expr_r } }; - var result := expr.eval(); + var result := expr.eval(&expr); vm_printf("eval result: %d\n", result); if result != 7 { ret 1; } diff --git a/stage1/test19_mutiarray.spl b/stage1/test19_mutiarray.spl deleted file mode 100644 index 70b786d..0000000 --- a/stage1/test19_mutiarray.spl +++ /dev/null @@ -1 +0,0 @@ -// TODO