From 118c153280b9da26d9355442375cf097e0f8235c Mon Sep 17 00:00:00 2001 From: zzy <2450266535@qq.com> Date: Sun, 12 Jul 2026 10:01:57 +0800 Subject: [PATCH] =?UTF-8?q?stage1=20=E4=BC=98=E5=8C=96=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- stage1/spl_comp.c | 15 ++ stage1/spl_comp.h | 7 +- stage1/spl_expr.c | 374 +++++++++++------------------------ stage1/spl_parser.c | 471 ++++++++++++++------------------------------ stage1/spl_stmt.c | 92 +++------ stage1/spl_type.c | 12 +- 6 files changed, 316 insertions(+), 655 deletions(-) diff --git a/stage1/spl_comp.c b/stage1/spl_comp.c index b14e039..37af7d5 100644 --- a/stage1/spl_comp.c +++ b/stage1/spl_comp.c @@ -240,6 +240,21 @@ int spl_lookup_func(spl_comp_t *ctx, const char *name) { return -1; } +/* Ensure a native function is registered for NCALL dispatch. + * Returns the native index. */ +int spl_ensure_native(spl_comp_t *ctx, const char *name) { + vec_for(ctx->prog.natives, ni) { + if (strcmp(vec_at(ctx->prog.natives, ni).name, name) == 0) + return (int)ni; + } + spl_native_t nat; + nat.name = strdup(name); + nat.idx_of_strtab = 0; + nat.impl_fn = NULL; + vec_push(ctx->prog.natives, nat); + return (int)vec_size(ctx->prog.natives) - 1; +} + /* ---- String/data management ---- */ int spl_add_string(spl_comp_t *ctx, const char *str) { diff --git a/stage1/spl_comp.h b/stage1/spl_comp.h index 646b22c..7d43f6f 100644 --- a/stage1/spl_comp.h +++ b/stage1/spl_comp.h @@ -252,8 +252,8 @@ spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, spl_type_info_t *typ * so that match arms behave as "enhanced if-conditions". * For enum: parses .VariantName, emits tag comparison, returns variant. * For value: parses expression, emits equality comparison. */ -spl_enum_variant_t *spl_emit_match_enum_cmp(spl_comp_t *ctx, - spl_type_info_t *enum_type, int val_offset); +spl_enum_variant_t *spl_emit_match_enum_cmp(spl_comp_t *ctx, spl_type_info_t *enum_type, + int val_offset); void spl_emit_match_value_cmp(spl_comp_t *ctx, int val_offset); /* ============================================================ @@ -292,6 +292,9 @@ int spl_declare_func(spl_comp_t *ctx, const char *name, spl_type_info_t *ret_typ int is_extern, int is_pub); int spl_lookup_func(spl_comp_t *ctx, const char *name); +/* Ensure a native function is registered for NCALL dispatch, returns index */ +int spl_ensure_native(spl_comp_t *ctx, const char *name); + /* String/data management */ int spl_add_string(spl_comp_t *ctx, const char *str); int spl_add_global_data(spl_comp_t *ctx, void *data, usize size); diff --git a/stage1/spl_expr.c b/stage1/spl_expr.c index 216adaa..e4c9a0d 100644 --- a/stage1/spl_expr.c +++ b/stage1/spl_expr.c @@ -62,6 +62,9 @@ static int tok_prec(spl_tok_type_t t) { * ============================================================ */ static spl_expr_result_t parse_infix(spl_comp_t *ctx, spl_expr_result_t left, spl_tok_type_t op); +static void emit_load_or_addr_type(spl_comp_t *ctx, spl_expr_result_t *result, + spl_type_info_t *type); +static spl_expr_result_t parse_postfix_expr(spl_comp_t *ctx, spl_expr_result_t left); /* Slice creation from array/slice range expression. * Stack in: [base_addr, begin, end] @@ -273,13 +276,11 @@ static spl_expr_result_t parse_float_literal(spl_comp_t *ctx) { static spl_expr_result_t parse_char_literal(spl_comp_t *ctx) { spl_tok_t *t = advance(ctx); - /* Lexeme: 'x' or '\n' etc, extract the character value */ const char *s = t->lexeme; usize l = t->len; int64_t val = 0; if (l >= 3) { if (s[1] == '\\' && l >= 4) { - /* Escape sequence */ char buf = 0; const char *cp = s + 1; spl_decode_escape(&cp, &buf); @@ -295,12 +296,10 @@ static spl_expr_result_t parse_char_literal(spl_comp_t *ctx) { static spl_expr_result_t parse_string_literal(spl_comp_t *ctx) { spl_tok_t *t = advance(ctx); - /* Decode the string content (strip quotes, process escapes) */ usize slen = t->len; if (slen >= 2) { - slen -= 2; /* remove outer quotes */ + slen -= 2; } - /* Build decoded string */ char *decoded = malloc(slen + 1); usize di = 0; for (usize i = 1; i + 1 < t->len; i++) { @@ -315,10 +314,9 @@ static spl_expr_result_t parse_string_literal(spl_comp_t *ctx) { } } decoded[di] = '\0'; - /* Add to global data */ int gdi = spl_add_global_data(ctx, decoded, di + 1); free(decoded); - spl_emit(ctx, SPL_GADDR, SPL_PTR, gdi - 1); /* gdi is 1-based from spl_prog_add_data */ + spl_emit(ctx, SPL_GADDR, SPL_PTR, gdi - 1); spl_expr_result_t r = {spl_type_ptr(spl_type_basic(SPL_U8)), 0}; return r; } @@ -328,7 +326,6 @@ static spl_expr_result_t parse_array_literal(spl_comp_t *ctx) { advance(ctx); /* skip [ */ skip_nl(ctx); - /* Parse array length */ int len_val; if (!spl_parse_int_literal(ctx, &len_val)) { spl_comp_error(ctx, "expected array length"); @@ -341,7 +338,6 @@ static spl_expr_result_t parse_array_literal(spl_comp_t *ctx) { expect(ctx, TOK_R_BRACKET); skip_nl(ctx); - /* Parse element type */ spl_type_info_t *elem_type = spl_parse_type(ctx); if (!elem_type) { spl_expr_result_t r = {0}; @@ -352,7 +348,6 @@ static spl_expr_result_t parse_array_literal(spl_comp_t *ctx) { expect(ctx, TOK_L_BRACE); skip_nl(ctx); - /* Parse each element value */ for (usize i = 0; i < len; i++) { if (i > 0) { if (peek(ctx)->type == TOK_COMMA) @@ -363,7 +358,7 @@ static spl_expr_result_t parse_array_literal(spl_comp_t *ctx) { skip_nl(ctx); } if (peek(ctx)->type == TOK_COMMA) - advance(ctx); /* trailing comma */ + advance(ctx); skip_nl(ctx); expect(ctx, TOK_R_BRACE); @@ -373,7 +368,7 @@ static spl_expr_result_t parse_array_literal(spl_comp_t *ctx) { } /* Parse slice inline literal: { .ptr = expr, .len = expr } - * Expects base address on TOS, consumes it. Stores ptr to base+0, len to base+sizeof(spl_val_t). */ + * Expects base address on TOS, consumes it. */ static void parse_slice_inline(spl_comp_t *ctx) { advance(ctx); /* { */ skip_nl(ctx); @@ -387,36 +382,30 @@ static void parse_slice_inline(spl_comp_t *ctx) { 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'; + spl_tok_copy_name(ftok, fname, sizeof(fname)); skip_nl(ctx); if (peek(ctx)->type == TOK_ASSIGN) advance(ctx); skip_nl(ctx); - spl_emit(ctx, SPL_DUP, SPL_VOID, 0); /* [addr, addr] */ + spl_emit(ctx, SPL_DUP, SPL_VOID, 0); if (strcmp(fname, "ptr") == 0) { spl_parse_expr(ctx, PREC_MIN); - spl_emit(ctx, SPL_STORE, SPL_PTR, 0); /* [addr] */ + spl_emit(ctx, SPL_STORE, SPL_PTR, 0); } else if (strcmp(fname, "len") == 0) { spl_emit(ctx, SPL_PUSH, SPL_USIZE, sizeof(spl_val_t)); - spl_emit(ctx, SPL_ADD, SPL_USIZE, 0); /* [addr, addr+8] */ + spl_emit(ctx, SPL_ADD, SPL_USIZE, 0); spl_parse_expr(ctx, PREC_MIN); - spl_emit(ctx, SPL_STORE, SPL_USIZE, 0); /* [addr] */ + spl_emit(ctx, SPL_STORE, SPL_USIZE, 0); } skip_nl(ctx); } expect(ctx, TOK_R_BRACE); - spl_emit(ctx, SPL_DROP, SPL_VOID, 0); /* drop addr */ + spl_emit(ctx, SPL_DROP, SPL_VOID, 0); } -/* Parse a single .field = value in a struct literal. - * Iterates fields to find the match, emits LADDR + offset, and handles - * value parsing (slice inline, array inline, multi-slot struct, basic store). - * Reused by both TYPE_STRUCT and TYPE_ENUM variant struct data. */ -static void parse_one_field_init(spl_comp_t *ctx, spl_field_vec_t *fields, - int base_offset, usize extra_offset) { +static void parse_one_field_init(spl_comp_t *ctx, spl_field_vec_t *fields, int base_offset, + usize extra_offset) { if (peek(ctx)->type == TOK_DOT) advance(ctx); spl_tok_t *ftok = advance(ctx); @@ -436,13 +425,10 @@ static void parse_one_field_init(spl_comp_t *ctx, spl_field_vec_t *fields, spl_emit(ctx, SPL_PUSH, SPL_USIZE, byte_off); spl_emit(ctx, SPL_ADD, SPL_USIZE, 0); } - if (f->type && f->type->kind == TYPE_SLICE && - peek(ctx)->type == TOK_L_BRACE) { + if (f->type && f->type->kind == TYPE_SLICE && peek(ctx)->type == TOK_L_BRACE) { parse_slice_inline(ctx); - } else if (f->type && f->type->kind == TYPE_ARRAY && - peek(ctx)->type == TOK_L_BRACKET) { - /* Inline array initializer: [N]Type{val1, val2, ...} */ - advance(ctx); /* [ */ + } else if (f->type && f->type->kind == TYPE_ARRAY && peek(ctx)->type == TOK_L_BRACKET) { + advance(ctx); skip_nl(ctx); int len_val; if (!spl_parse_int_literal(ctx, &len_val)) { @@ -461,7 +447,8 @@ static void parse_one_field_init(spl_comp_t *ctx, spl_field_vec_t *fields, 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) advance(ctx); + if (peek(ctx)->type == TOK_COMMA) + advance(ctx); skip_nl(ctx); } spl_emit(ctx, SPL_DUP, SPL_VOID, 0); @@ -473,12 +460,12 @@ static void parse_one_field_init(spl_comp_t *ctx, spl_field_vec_t *fields, spl_emit(ctx, SPL_STORE, st, 0); skip_nl(ctx); } - if (peek(ctx)->type == TOK_COMMA) advance(ctx); + if (peek(ctx)->type == TOK_COMMA) + advance(ctx); skip_nl(ctx); expect(ctx, TOK_R_BRACE); spl_emit(ctx, SPL_DROP, SPL_VOID, 0); - } else if (f->type && - (f->type->kind == TYPE_STRUCT || f->type->kind == TYPE_ENUM) && + } else if (f->type && (f->type->kind == TYPE_STRUCT || f->type->kind == TYPE_ENUM) && spl_type_size(f->type) > sizeof(spl_val_t)) { spl_expr_result_t fv = spl_parse_expr(ctx, PREC_MIN); (void)fv; @@ -495,9 +482,6 @@ static void parse_one_field_init(spl_comp_t *ctx, spl_field_vec_t *fields, } } -/* 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. */ spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, spl_type_info_t *type) { usize sz = spl_type_size(type); int base_offset = ctx->current_local_bytes; @@ -558,14 +542,11 @@ spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, spl_type_info_t *typ 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'; + spl_tok_copy_name(vtok, vname, sizeof(vname)); skip_nl(ctx); if (peek(ctx)->type == TOK_ASSIGN) advance(ctx); @@ -576,16 +557,13 @@ spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, spl_type_info_t *typ 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_offset); 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) { @@ -596,7 +574,8 @@ spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, spl_type_info_t *typ } if (peek(ctx)->type != TOK_DOT) break; - parse_one_field_init(ctx, &v->data_type->fields, base_offset, DATA_OFFSET); + parse_one_field_init(ctx, &v->data_type->fields, base_offset, + DATA_OFFSET); skip_nl(ctx); } expect(ctx, TOK_R_BRACE); @@ -604,7 +583,6 @@ spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, spl_type_info_t *typ (v->data_type->kind == TYPE_STRUCT || v->data_type->kind == TYPE_ENUM) && spl_type_size(v->data_type) > sizeof(spl_val_t)) { - /* Multi-slot struct/enum data: copy from temp to enum data area */ spl_expr_result_t dv = spl_parse_expr(ctx, PREC_MIN); (void)dv; usize nslots = (spl_type_size(v->data_type) + sizeof(spl_val_t) - 1) / @@ -612,7 +590,6 @@ spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, spl_type_info_t *typ spl_emit_copy_slots(ctx, base_offset + (int)DATA_OFFSET, nslots); spl_emit(ctx, SPL_DROP, SPL_VOID, 0); } 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_offset); @@ -633,28 +610,40 @@ spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, spl_type_info_t *typ skip_nl(ctx); expect(ctx, TOK_R_BRACE); - /* Return: if size fits in one slot, push packed value. Otherwise push address. */ if (sz <= sizeof(spl_val_t)) { spl_emit(ctx, SPL_LADDR, SPL_PTR, base_offset); spl_emit(ctx, SPL_LOAD, SPL_PTR, 0); - return (spl_expr_result_t){type, 0}; /* value on stack */ + return (spl_expr_result_t){type, 0}; } else { spl_emit(ctx, SPL_LADDR, SPL_PTR, base_offset); - return (spl_expr_result_t){type, 1}; /* address on stack */ + return (spl_expr_result_t){type, 1}; } } +static int parse_call_args(spl_comp_t *ctx) { + int nargs = 0; + if (peek(ctx)->type != TOK_R_PAREN) { + for (;;) { + spl_parse_expr(ctx, PREC_MIN); + nargs++; + if (peek(ctx)->type == TOK_COMMA) { + advance(ctx); + continue; + } + break; + } + } + return nargs; +} + static spl_expr_result_t parse_ident(spl_comp_t *ctx) { spl_tok_t *t = advance(ctx); char name[256]; - usize nlen = t->len < 255 ? t->len : 255; - memcpy(name, t->lexeme, nlen); - name[nlen] = '\0'; + spl_tok_copy_name(t, name, sizeof(name)); /* 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); @@ -668,44 +657,14 @@ static spl_expr_result_t parse_ident(spl_comp_t *ctx) { spl_func_info_t *f = &vec_at(ctx->funcs, fi); advance(ctx); /* skip ( */ - int nargs = 0; - 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; - } - } + int nargs = parse_call_args(ctx); expect(ctx, TOK_R_PAREN); if (f->is_extern) { - /* Find the native function index in prog */ - int nidx = -1; - vec_for(ctx->prog.natives, ni) { - if (strcmp(vec_at(ctx->prog.natives, ni).name, f->name) == 0) { - nidx = (int)ni; - break; - } - } - if (nidx < 0) { - /* Register it */ - spl_native_t nat; - nat.name = strdup(f->name); - nat.idx_of_strtab = 0; - nat.impl_fn = NULL; - vec_push(ctx->prog.natives, nat); - nidx = (int)vec_size(ctx->prog.natives) - 1; - } - /* Push native index, then NCALL with imm = nargs */ + int nidx = spl_ensure_native(ctx, f->name); spl_emit(ctx, SPL_PUSH, SPL_I32, nidx); spl_emit(ctx, SPL_NCALL, SPL_VOID, nargs); } else { - /* Regular function call: push func addr, then CALL */ spl_val_t addr = vec_at(ctx->prog.funcs, f->func_idx).address; spl_emit(ctx, SPL_PUSH, SPL_PTR, addr); spl_emit(ctx, SPL_CALL, SPL_VOID, nargs); @@ -718,7 +677,6 @@ static spl_expr_result_t parse_ident(spl_comp_t *ctx) { /* Variable reference */ spl_var_info_t *v = spl_lookup_var(ctx, name); if (v) { - /* Check const values first */ spl_val_t cv = 0; if (map_get(ctx->const_values, name, &cv)) { spl_emit(ctx, SPL_PUSH, SPL_I32, cv); @@ -729,27 +687,14 @@ static spl_expr_result_t parse_ident(spl_comp_t *ctx) { spl_type_info_t *vt = v->type; spl_emit(ctx, SPL_LADDR, SPL_PTR, v->offset); - if (vt->kind == TYPE_BASIC || vt->kind == TYPE_PTR) { - if (!ctx->addr_of_mode) { - /* Load value for basic types and pointers */ - spl_type_t bt = (vt->kind == TYPE_BASIC) ? vt->basic_type : SPL_PTR; - spl_emit(ctx, SPL_LOAD, bt, 0); - spl_expr_result_t r = {vt, 0}; - return r; - } - /* In addr_of_mode, keep address on stack */ - spl_expr_result_t r = {vt, 1}; - return r; - } - /* Array/struct/slice: address stays on stack */ - spl_expr_result_t r = {vt, 1}; + spl_expr_result_t r; + emit_load_or_addr_type(ctx, &r, vt); return r; } /* 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) { - /* Struct/enum literal: Type { .field = val, ... } */ if (peek(ctx)->type == TOK_L_BRACE && (ttype->kind == TYPE_STRUCT || ttype->kind == TYPE_ENUM)) { return spl_parse_struct_literal(ctx, ttype); @@ -770,6 +715,19 @@ static spl_expr_result_t parse_group(spl_comp_t *ctx) { return r; } +static void emit_load_or_addr_type(spl_comp_t *ctx, spl_expr_result_t *result, + spl_type_info_t *type) { + if (type->kind == TYPE_BASIC || type->kind == TYPE_PTR) { + if (!ctx->addr_of_mode) { + spl_type_t bt = (type->kind == TYPE_BASIC) ? type->basic_type : SPL_PTR; + spl_emit(ctx, SPL_LOAD, bt, 0); + *result = (spl_expr_result_t){type, 0}; + return; + } + } + *result = (spl_expr_result_t){type, 1}; +} + static spl_expr_result_t parse_prefix_op(spl_comp_t *ctx) { spl_tok_t *op = advance(ctx); @@ -784,7 +742,6 @@ static spl_expr_result_t parse_prefix_op(spl_comp_t *ctx) { spl_emit(ctx, SPL_NEG, SPL_I32, 0); break; case TOK_NOT: - /* !expr → EQ 0 */ spl_emit(ctx, SPL_PUSH, SPL_I32, 0); spl_emit(ctx, SPL_EQ, SPL_I32, 0); break; @@ -792,31 +749,16 @@ static spl_expr_result_t parse_prefix_op(spl_comp_t *ctx) { spl_emit(ctx, SPL_NOT, SPL_I32, 0); break; case TOK_AND: - /* &expr — address-of, already an lvalue */ if (!right.is_lvalue) { spl_comp_error(ctx, "cannot take address of rvalue"); } break; case TOK_MUL: - /* *expr — dereference */ if (right.type && right.type->kind == TYPE_PTR && right.type->elem) { - /* If right is still an lvalue, load the pointer value for the target */ if (right.is_lvalue) { spl_emit(ctx, SPL_LOAD, SPL_PTR, 0); } - spl_type_info_t *elem = right.type->elem; - if (elem->kind == TYPE_BASIC || elem->kind == TYPE_PTR) { - if (!ctx->addr_of_mode) { - spl_type_t bt = (elem->kind == TYPE_BASIC) ? elem->basic_type : SPL_PTR; - spl_emit(ctx, SPL_LOAD, bt, 0); - right = (spl_expr_result_t){elem, 0}; - } else { - right = (spl_expr_result_t){elem, 1}; - } - } else { - /* Struct/array/slice: keep address on stack */ - right = (spl_expr_result_t){elem, 1}; - } + emit_load_or_addr_type(ctx, &right, right.type->elem); } break; default: @@ -876,7 +818,6 @@ static spl_expr_result_t parse_primary_expr(spl_comp_t *ctx) { case TOK_L_BRACE: return spl_parse_block_expr(ctx); case TOK_AT: { - /* @builtin(...) — compiler intrinsic */ advance(ctx); skip_nl(ctx); tok = peek(ctx); @@ -892,18 +833,7 @@ static spl_expr_result_t parse_primary_expr(spl_comp_t *ctx) { if (strcmp(bname, "dbg") == 0) { if (peek(ctx)->type == TOK_L_PAREN) { advance(ctx); - int nargs = 0; - if (peek(ctx)->type != TOK_R_PAREN) { - for (;;) { - spl_parse_expr(ctx, PREC_MIN); - nargs++; - if (peek(ctx)->type == TOK_COMMA) { - advance(ctx); - continue; - } - break; - } - } + int nargs = parse_call_args(ctx); expect(ctx, TOK_R_PAREN); for (int i = 0; i < nargs; i++) { spl_emit(ctx, SPL_DBG, SPL_USIZE, 0); @@ -931,28 +861,23 @@ static spl_expr_result_t parse_primary_expr(spl_comp_t *ctx) { } /* ============================================================ - * Main expression parser (top-level) + * Postfix expression parser (.field, [index], [begin..end]) * ============================================================ */ -spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) { - skip_nl(ctx); - spl_expr_result_t left = parse_primary_expr(ctx); +static spl_expr_result_t parse_postfix_expr(spl_comp_t *ctx, spl_expr_result_t left) { if (!left.type) return left; - /* Infix parsing (precedence climbing) */ - while (1) { + for (;;) { skip_nl(ctx); spl_tok_type_t opt = peek(ctx)->type; - /* Postfix operators */ + /* Postfix . operator */ if (opt == TOK_DOT) { advance(ctx); spl_tok_t *field = advance(ctx); char fname[256]; - usize fnl = field->len < 255 ? field->len : 255; - memcpy(fname, field->lexeme, fnl); - fname[fnl] = '\0'; + spl_tok_copy_name(field, fname, sizeof(fname)); /* Compile-time type member resolution (enum variants, nested types) */ if (left.type) { @@ -965,7 +890,6 @@ 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 && @@ -986,7 +910,6 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) { 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 && @@ -999,22 +922,10 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) { spl_comp_error(ctx, "cannot call instance method '%s' on type", fname); } - nargs = 1; /* self already on stack */ + nargs = 1; } - /* 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; - } - } + nargs += parse_call_args(ctx); expect(ctx, TOK_R_PAREN); spl_val_t addr = vec_at(ctx->prog.funcs, func->func_idx).address; @@ -1027,12 +938,10 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) { } 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) */ vec_for(left.type->fields, fi) { if (strcmp(vec_at(left.type->fields, fi).name, fname) == 0) { spl_field_t *f = &vec_at(left.type->fields, fi); @@ -1040,19 +949,7 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) { spl_emit(ctx, SPL_PUSH, SPL_USIZE, f->offset); spl_emit(ctx, SPL_ADD, SPL_USIZE, 0); } - /* Load value if basic type */ - spl_type_info_t *ft = f->type; - if (ft->kind == TYPE_BASIC || ft->kind == TYPE_PTR) { - if (!ctx->addr_of_mode) { - spl_type_t bt = (ft->kind == TYPE_BASIC) ? ft->basic_type : SPL_PTR; - spl_emit(ctx, SPL_LOAD, bt, 0); - left = (spl_expr_result_t){ft, 0}; - } else { - left = (spl_expr_result_t){ft, 1}; - } - } else { - left = (spl_expr_result_t){ft, 1}; - } + emit_load_or_addr_type(ctx, &left, f->type); break; } } @@ -1063,11 +960,9 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) { if (left.type && left.type->kind == TYPE_PTR && left.type->elem && left.type->elem->kind == TYPE_STRUCT) { spl_type_info_t *st = left.type->elem; - /* Load pointer value to get struct address if left is still lvalue */ if (left.is_lvalue) { spl_emit(ctx, SPL_LOAD, SPL_PTR, 0); } - /* Now search field */ vec_for(st->fields, fi) { if (strcmp(vec_at(st->fields, fi).name, fname) == 0) { spl_field_t *f = &vec_at(st->fields, fi); @@ -1075,18 +970,7 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) { spl_emit(ctx, SPL_PUSH, SPL_USIZE, f->offset); spl_emit(ctx, SPL_ADD, SPL_USIZE, 0); } - spl_type_info_t *ft = f->type; - if (ft->kind == TYPE_BASIC || ft->kind == TYPE_PTR) { - if (!ctx->addr_of_mode) { - spl_type_t bt = (ft->kind == TYPE_BASIC) ? ft->basic_type : SPL_PTR; - spl_emit(ctx, SPL_LOAD, bt, 0); - left = (spl_expr_result_t){ft, 0}; - } else { - left = (spl_expr_result_t){ft, 1}; - } - } else { - left = (spl_expr_result_t){ft, 1}; - } + emit_load_or_addr_type(ctx, &left, f->type); break; } } @@ -1097,12 +981,10 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) { if (left.type && left.type->kind == TYPE_SLICE) { if (strcmp(fname, "len") == 0) { if (ctx->addr_of_mode) { - /* lvalue: push address of len field at offset sizeof(spl_val_t) */ spl_emit(ctx, SPL_PUSH, SPL_U64, (spl_val_t)sizeof(spl_val_t)); spl_emit(ctx, SPL_ADD, SPL_U64, 0); left = (spl_expr_result_t){spl_type_basic(SPL_USIZE), 1}; } else { - /* rvalue: load len value */ spl_emit(ctx, SPL_PUSH, SPL_U64, (spl_val_t)sizeof(spl_val_t)); spl_emit(ctx, SPL_ADD, SPL_U64, 0); spl_emit(ctx, SPL_LOAD, SPL_USIZE, 0); @@ -1110,7 +992,6 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) { } } else if (strcmp(fname, "ptr") == 0) { if (ctx->addr_of_mode) { - /* lvalue: ptr is at offset 0, address already on stack */ left = (spl_expr_result_t){left.type->elem ? spl_type_ptr(left.type->elem) : spl_type_basic(SPL_PTR), 1}; @@ -1130,18 +1011,7 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) { if (left.is_lvalue) { spl_emit(ctx, SPL_LOAD, SPL_PTR, 0); } - spl_type_info_t *elem = left.type->elem; - if (elem->kind == TYPE_BASIC || elem->kind == TYPE_PTR) { - if (!ctx->addr_of_mode) { - spl_type_t bt = (elem->kind == TYPE_BASIC) ? elem->basic_type : SPL_PTR; - spl_emit(ctx, SPL_LOAD, bt, 0); - left = (spl_expr_result_t){elem, 0}; - } else { - left = (spl_expr_result_t){elem, 1}; - } - } else { - left = (spl_expr_result_t){elem, 1}; - } + emit_load_or_addr_type(ctx, &left, left.type->elem); continue; } @@ -1153,16 +1023,14 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) { if (opt == TOK_L_BRACKET) { advance(ctx); /* skip [ */ if (peek(ctx)->type == TOK_R_BRACKET) { - advance(ctx); /* empty brackets */ + advance(ctx); continue; } - /* Parse index/begin expression before checking for range */ spl_parse_expr(ctx, PREC_MIN); - /* Check for slice: expr[begin..end] or expr[begin..] */ if (peek(ctx)->type == TOK_RANGE) { - advance(ctx); /* skip .. */ + advance(ctx); spl_expr_result_t end_expr = {0}; int has_explicit_end = (peek(ctx)->type != TOK_R_BRACKET); if (has_explicit_end) { @@ -1170,13 +1038,10 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) { } expect(ctx, TOK_R_BRACKET); - /* Push implicit end value (array length/slice len) before the outer - * type-check so it always runs regardless of left.type validity. */ if (!has_explicit_end) { if (left.type && left.type->kind == TYPE_ARRAY) { spl_emit(ctx, SPL_PUSH, SPL_U64, left.type->array_len); } else if (left.type && left.type->kind == TYPE_SLICE) { - /* TYPE_SLICE: load len from struct at offset sizeof(spl_val_t) */ spl_emit(ctx, SPL_PICK, SPL_VOID, 1); spl_emit(ctx, SPL_PUSH, SPL_U64, sizeof(spl_val_t)); spl_emit(ctx, SPL_ADD, SPL_U64, 0); @@ -1184,29 +1049,25 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) { } } - /* Generate slice: compute ptr = base + begin * stride, len = end - begin */ if (left.type && (left.type->kind == TYPE_ARRAY || left.type->kind == TYPE_SLICE)) { usize stride = spl_type_elem_stride(left.type->elem); if (left.type->kind == TYPE_SLICE) { - /* Stack: [struct_addr, begin, end]. - * Inline ptr/len: ptr = data_ptr + begin*stride, len = end - begin. */ spl_emit(ctx, SPL_PICK, SPL_VOID, 2); - spl_emit(ctx, SPL_LOAD, SPL_PTR, 0); /* [s,b,e,data_ptr] */ - spl_emit(ctx, SPL_PICK, SPL_VOID, 2); /* [s,b,e,d,begin] */ + spl_emit(ctx, SPL_LOAD, SPL_PTR, 0); + spl_emit(ctx, SPL_PICK, SPL_VOID, 2); spl_emit(ctx, SPL_PUSH, SPL_U64, stride); spl_emit(ctx, SPL_MUL, SPL_U64, 0); - spl_emit(ctx, SPL_ADD, SPL_U64, 0); /* [s,b,e,ptr] */ - spl_emit(ctx, SPL_PICK, SPL_VOID, 1); /* [s,b,e,ptr,end] */ - spl_emit(ctx, SPL_PICK, SPL_VOID, 3); /* [s,b,e,ptr,end,begin] */ - spl_emit(ctx, SPL_SUB, SPL_USIZE, 0); /* [s,b,e,ptr,len] */ - /* cleanup: drop [s,b,e] */ - spl_emit(ctx, SPL_ROT, SPL_VOID, 0); /* [s,b,ptr,len,e] */ - spl_emit(ctx, SPL_DROP, SPL_VOID, 0); /* [s,b,ptr,len] */ - spl_emit(ctx, SPL_ROT, SPL_VOID, 0); /* [s,ptr,len,b] */ - spl_emit(ctx, SPL_DROP, SPL_VOID, 0); /* [s,ptr,len] */ - spl_emit(ctx, SPL_ROT, SPL_VOID, 0); /* [ptr,len,s] */ - spl_emit(ctx, SPL_DROP, SPL_VOID, 0); /* [ptr,len] */ + spl_emit(ctx, SPL_ADD, SPL_U64, 0); + spl_emit(ctx, SPL_PICK, SPL_VOID, 1); + spl_emit(ctx, SPL_PICK, SPL_VOID, 3); + spl_emit(ctx, SPL_SUB, SPL_USIZE, 0); + spl_emit(ctx, SPL_ROT, SPL_VOID, 0); + spl_emit(ctx, SPL_DROP, SPL_VOID, 0); + spl_emit(ctx, SPL_ROT, SPL_VOID, 0); + spl_emit(ctx, SPL_DROP, SPL_VOID, 0); + spl_emit(ctx, SPL_ROT, SPL_VOID, 0); + spl_emit(ctx, SPL_DROP, SPL_VOID, 0); } else { emit_slice_create(ctx, stride); } @@ -1217,20 +1078,14 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) { expect(ctx, TOK_R_BRACKET); - /* Array/slice/pointer indexing */ if (left.type && (left.type->kind == TYPE_ARRAY || left.type->kind == TYPE_PTR || left.type->kind == TYPE_SLICE)) { spl_type_info_t *elem = left.type->elem; if (left.type->kind == TYPE_SLICE) { - /* Slice: stack has [slice_struct_addr, index]. - * Load data ptr first, then compute element address. */ emit_slice_index(ctx, elem); } else { - /* Stack arrays and pointer indexing */ if (left.type->kind == TYPE_PTR && left.is_lvalue) { - /* Stack: [addr_of_ptr, index]. Swap to get addr on top, load ptr value, - * swap back */ spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); spl_emit(ctx, SPL_LOAD, SPL_PTR, 0); spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); @@ -1240,21 +1095,34 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) { spl_emit(ctx, SPL_MUL, SPL_U64, 0); spl_emit(ctx, SPL_ADD, SPL_U64, 0); } - if (elem && (elem->kind == TYPE_BASIC || elem->kind == TYPE_PTR)) { - if (!ctx->addr_of_mode) { - spl_type_t bt = (elem->kind == TYPE_BASIC) ? elem->basic_type : SPL_PTR; - spl_emit(ctx, SPL_LOAD, bt, 0); - left = (spl_expr_result_t){elem, 0}; - } else { - left = (spl_expr_result_t){elem, 1}; - } - } else { - left = (spl_expr_result_t){elem, 1}; - } + if (elem) + emit_load_or_addr_type(ctx, &left, elem); } continue; } + break; + } + return left; +} + +/* ============================================================ + * Main expression parser (top-level) + * ============================================================ */ + +spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) { + skip_nl(ctx); + spl_expr_result_t left = parse_primary_expr(ctx); + if (!left.type) + return left; + + /* Infix parsing (precedence climbing) */ + while (1) { + left = parse_postfix_expr(ctx, left); + + skip_nl(ctx); + spl_tok_type_t opt = peek(ctx)->type; + /* Binary operators */ int prec = tok_prec(opt); if (prec == 0 || prec < min_prec) @@ -1276,14 +1144,12 @@ static spl_expr_result_t parse_infix(spl_comp_t *ctx, spl_expr_result_t left, sp /* Short-circuit logical operators */ if (op == TOK_AND_AND) { - /* left is already evaluated and on stack. If it's false (0), skip right. */ spl_val_t bz_addr = spl_emit_bz(ctx); spl_parse_expr(ctx, next_prec); spl_patch_to_here(ctx, bz_addr); return (spl_expr_result_t){spl_type_basic(SPL_I32), 0}; } if (op == TOK_OR_OR) { - /* If left is true (non-zero), skip right. */ spl_val_t bnz_addr = spl_emit_bnz(ctx); spl_parse_expr(ctx, next_prec); spl_patch_to_here(ctx, bnz_addr); @@ -1296,7 +1162,6 @@ static spl_expr_result_t parse_infix(spl_comp_t *ctx, spl_expr_result_t left, sp op == TOK_ASSIGN_OR || op == TOK_ASSIGN_XOR || op == TOK_ASSIGN_L_SH || op == TOK_ASSIGN_R_SH) { - /* RHS must not inherit addr_of_mode from LHS */ int saved_addr_of_mode = ctx->addr_of_mode; ctx->addr_of_mode = 0; spl_expr_result_t right = spl_parse_expr(ctx, PREC_MIN); @@ -1307,17 +1172,14 @@ static spl_expr_result_t parse_infix(spl_comp_t *ctx, spl_expr_result_t left, sp ? (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 */ 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); + spl_emit(ctx, SPL_LOAD, bt, 0); + spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); 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); spl_emit(ctx, SPL_STORE, bt, 0); } } @@ -1349,9 +1211,7 @@ spl_enum_variant_t *spl_emit_match_enum_cmp(spl_comp_t *ctx, spl_type_info_t *en 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_tok_copy_name(vtok, vname, sizeof(vname)); vec_for(enum_type->variants, vi) { spl_enum_variant_t *v = &vec_at(enum_type->variants, vi); diff --git a/stage1/spl_parser.c b/stage1/spl_parser.c index e94b1a7..269d425 100644 --- a/stage1/spl_parser.c +++ b/stage1/spl_parser.c @@ -21,6 +21,44 @@ spl_tok_t *advance(spl_comp_t *ctx) { /* Shared helper: register a function, declare params, parse body, end function. * Used by both top-level fn decl and methods inside type bodies. */ +/* Shared helper: parse function/method parameter list: (name: type, name: type, ...) + * Returns number of params parsed. Stores names in pnames and types in ptypes + * (both must be MAX_PARAMS-sized arrays). */ +enum { MAX_PARAMS = 64 }; + +static int parse_params_decl(spl_comp_t *ctx, char pnames[][256], spl_type_info_t *ptypes[]) { + int nparams = 0; + skip_nl(ctx); + if (peek(ctx)->type != TOK_R_PAREN) { + while (1) { + spl_tok_t *pname = advance(ctx); + spl_tok_copy_name(pname, pnames[nparams], 256); + 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; + } + if (peek(ctx)->type == TOK_ELLIPSIS) { + advance(ctx); + skip_nl(ctx); + } + break; + } + } + expect(ctx, TOK_R_PAREN); + return nparams; +} + 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); @@ -68,48 +106,15 @@ static void parse_fn_decl(spl_comp_t *ctx, int is_extern, int is_pub) { 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'; + spl_tok_copy_name(fname_tok, fn_name, sizeof(fn_name)); skip_nl(ctx); expect(ctx, TOK_L_PAREN); /* Parse parameters — collect names and types */ - 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; - } - if (peek(ctx)->type == TOK_ELLIPSIS) { - advance(ctx); - skip_nl(ctx); - } - break; - } - } - expect(ctx, TOK_R_PAREN); + int nparams = parse_params_decl(ctx, pnames, ptypes); skip_nl(ctx); /* Return type (default: void) */ @@ -124,22 +129,7 @@ static void parse_fn_decl(spl_comp_t *ctx, int is_extern, int is_pub) { /* Extern function: register as native, no body */ if (is_extern) { spl_declare_func(ctx, fn_name, ret_type, nparams, 1, is_pub); - /* Add to prog->natives for NCALL dispatch */ - int found = -1; - vec_for(ctx->prog.natives, ni) { - if (strcmp(vec_at(ctx->prog.natives, ni).name, fn_name) == 0) { - found = (int)ni; - break; - } - } - if (found < 0) { - spl_native_t nat; - memset(&nat, 0, sizeof(nat)); - nat.name = strdup(fn_name); - nat.idx_of_strtab = 0; - nat.impl_fn = NULL; /* resolved by VM at runtime */ - vec_push(ctx->prog.natives, nat); - } + spl_ensure_native(ctx, fn_name); if (peek(ctx)->type == TOK_SEMICOLON) advance(ctx); return; @@ -158,22 +148,84 @@ static void parse_fn_decl(spl_comp_t *ctx, int is_extern, int is_pub) { } /* ============================================================ - * Parse struct/union body (shared for struct and union containers) + * Shared helper: parse a method declaration inside a type body. + * Used by both struct_body and enum_body parsers. + * fn name(params) ret-type { body } + * ============================================================ */ + +static void parse_method_decl(spl_comp_t *ctx, spl_type_info_t *container) { + advance(ctx); /* fn */ + skip_nl(ctx); + spl_tok_t *mname_tok = advance(ctx); + char mname[256]; + spl_tok_copy_name(mname_tok, mname, sizeof(mname)); + + /* Build qualified name: TypeName.method_name */ + char qualified[512]; + snprintf(qualified, sizeof(qualified), "%s.%s", container->name ? container->name : "anon", + mname); + + skip_nl(ctx); + expect(ctx, TOK_L_PAREN); + + /* Parse parameters using shared helper */ + char pnames[MAX_PARAMS][256]; + spl_type_info_t *ptypes[MAX_PARAMS]; + int nparams = parse_params_decl(ctx, pnames, ptypes); + 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 = container->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(container, mname, fi); +} + +/* ============================================================ + * Parse type container body (shared for struct, union, enum) + * + * For struct/union: fields are added for identifiers + * For enum: variants are added for identifiers * * Body supports: - * var name: type; — field declarations - * name: type, — field declarations (old-style) + * var name: type; — field declarations (struct/union only) + * name: type, — field/variant declarations + * name: Type, — enum variant with data + * name, — simple enum variant * type Name = ...; — nested type declarations * fn name(...) type { } — methods * ============================================================ */ -static void parse_struct_body(spl_comp_t *ctx, spl_type_info_t *st) { +static void parse_type_body(spl_comp_t *ctx, spl_type_info_t *container, int is_enum) { if (peek(ctx)->type != TOK_L_BRACE) return; advance(ctx); /* { */ /* === Pass 1: Parse all nested type declarations first === - * This allows field declarations to reference types defined later in the body. */ + * This allows fields/variants to reference types defined later. */ { usize saved = ctx->tok_idx; int depth = 1; @@ -193,11 +245,10 @@ static void parse_struct_body(spl_comp_t *ctx, spl_type_info_t *st) { advance(ctx); } } - /* Reset to start of body for second pass */ ctx->tok_idx = saved; } - /* === Pass 2: Parse fields and methods === */ + /* === Pass 2: Parse fields/variants and methods === */ { int depth = 1; while (depth > 0 && ctx->tok_idx < vec_size(ctx->toks)) { @@ -214,10 +265,10 @@ static void parse_struct_body(spl_comp_t *ctx, spl_type_info_t *st) { } advance(ctx); } else if (tt == KW_TYPE && depth == 1) { - /* Nested type — already parsed in pass 1, skip by re-parsing */ + /* Nested type — already parsed in pass 1, skip */ parse_type_decl(ctx); - } else if (tt == KW_VAR && depth == 1) { - /* var name: type; */ + } else if (tt == KW_VAR && depth == 1 && !is_enum) { + /* var name: type; (struct/union only) */ advance(ctx); /* var */ skip_nl(ctx); spl_tok_t *ftok = advance(ctx); @@ -227,286 +278,54 @@ static void parse_struct_body(spl_comp_t *ctx, spl_type_info_t *st) { skip_nl(ctx); spl_type_info_t *ftype = spl_parse_type(ctx); char fname[256]; - usize fnl = ftok->len < 255 ? ftok->len : 255; - memcpy(fname, ftok->lexeme, fnl); - fname[fnl] = '\0'; - spl_type_add_field(st, fname, ftype); + spl_tok_copy_name(ftok, fname, sizeof(fname)); + spl_type_add_field(container, fname, ftype); } skip_nl(ctx); if (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_COMMA) advance(ctx); } else if (tt == TOK_IDENT && depth == 1) { - /* Old-style field: name: type, */ - spl_tok_t *ftok = advance(ctx); - skip_nl(ctx); - if (peek(ctx)->type == TOK_COLON) { - advance(ctx); /* : */ + if (is_enum) { + /* Variant: Name or Name: Type */ + spl_tok_t *vtok = advance(ctx); skip_nl(ctx); - spl_type_info_t *ftype = spl_parse_type(ctx); - char fname[256]; - usize fnl = ftok->len < 255 ? ftok->len : 255; - memcpy(fname, ftok->lexeme, fnl); - fname[fnl] = '\0'; - spl_type_add_field(st, fname, ftype); - } - skip_nl(ctx); - if (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_COMMA) - advance(ctx); - } else if (tt == KW_FN && depth == 1) { - /* Method — parse properly using parse_fn_body */ - advance(ctx); /* fn */ - 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'; + if (peek(ctx)->type == TOK_COLON) { + advance(ctx); /* : */ 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; + spl_type_info_t *dtype = spl_parse_type(ctx); + char vname[256]; + spl_tok_copy_name(vtok, vname, sizeof(vname)); + spl_type_add_variant(container, vname, dtype); + } else { + char vname[256]; + spl_tok_copy_name(vtok, vname, sizeof(vname)); + spl_type_add_variant(container, vname, NULL); } - } - 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); - } - } - } - - spl_type_compute_layout(st); -} - -/* ============================================================ - * Parse enum body - * - * Body supports: - * Name — simple variant - * Name: Type — variant with data - * var name: type; — field-style variant - * type Name = ...; — nested type declarations - * fn name(...) type { } — methods (skipped) - * ============================================================ */ - -static void parse_enum_body(spl_comp_t *ctx, spl_type_info_t *et) { - if (peek(ctx)->type != TOK_L_BRACE) - return; - advance(ctx); /* { */ - - /* === Pass 1: Parse all nested type declarations first === */ - { - usize saved = ctx->tok_idx; - 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) { - parse_type_decl(ctx); - } else { - advance(ctx); - } - } - ctx->tok_idx = saved; - } - - /* === Pass 2: Parse variants and methods === */ - { - int depth = 1; - 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) { - /* Already parsed in pass 1, re-parse to skip */ - parse_type_decl(ctx); - } else if (tt == TOK_IDENT && depth == 1) { - /* Variant: Name or Name: Type */ - spl_tok_t *vtok = advance(ctx); - skip_nl(ctx); - if (peek(ctx)->type == TOK_COLON) { - advance(ctx); /* : */ - skip_nl(ctx); - spl_type_info_t *dtype = spl_parse_type(ctx); - char vname[256]; - usize vnl = vtok->len < 255 ? vtok->len : 255; - memcpy(vname, vtok->lexeme, vnl); - vname[vnl] = '\0'; - spl_type_add_variant(et, vname, dtype); } else { - char vname[256]; - usize vnl = vtok->len < 255 ? vtok->len : 255; - memcpy(vname, vtok->lexeme, vnl); - vname[vnl] = '\0'; - spl_type_add_variant(et, vname, NULL); + /* Old-style field: name: type, */ + spl_tok_t *ftok = advance(ctx); + skip_nl(ctx); + if (peek(ctx)->type == TOK_COLON) { + advance(ctx); /* : */ + skip_nl(ctx); + spl_type_info_t *ftype = spl_parse_type(ctx); + char fname[256]; + spl_tok_copy_name(ftok, fname, sizeof(fname)); + spl_type_add_field(container, fname, ftype); + } } skip_nl(ctx); if (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_COMMA) advance(ctx); } else if (tt == KW_FN && depth == 1) { - /* Method — parse properly using parse_fn_body */ - advance(ctx); /* fn */ - 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; - } - } - 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); + parse_method_decl(ctx, container); } else { advance(ctx); } } } - spl_type_compute_layout(et); + spl_type_compute_layout(container); } /* ============================================================ @@ -521,9 +340,7 @@ void parse_type_decl(spl_comp_t *ctx) { advance(ctx); /* type */ spl_tok_t *name_tok = advance(ctx); char tname[256]; - usize tnl = name_tok->len < 255 ? name_tok->len : 255; - memcpy(tname, name_tok->lexeme, tnl); - tname[tnl] = '\0'; + spl_tok_copy_name(name_tok, tname, sizeof(tname)); skip_nl(ctx); expect(ctx, TOK_ASSIGN); @@ -535,20 +352,20 @@ void parse_type_decl(spl_comp_t *ctx) { /* Register type early to allow self-referential fields */ map_put(ctx->type_defs, strdup(tname), st); skip_nl(ctx); - parse_struct_body(ctx, st); + parse_type_body(ctx, st, 0); } else if (peek(ctx)->type == KW_UNION) { advance(ctx); spl_type_info_t *ut = spl_type_union(tname); map_put(ctx->type_defs, strdup(tname), ut); skip_nl(ctx); - parse_struct_body(ctx, ut); + parse_type_body(ctx, ut, 0); } else if (peek(ctx)->type == KW_ENUM) { advance(ctx); spl_type_info_t *et = spl_type_enum(tname); /* Register type early to allow self-referential variants */ map_put(ctx->type_defs, strdup(tname), et); skip_nl(ctx); - parse_enum_body(ctx, et); + parse_type_body(ctx, et, 1); } else if (peek(ctx)->type == TOK_IDENT || (peek(ctx)->type >= KW_AS && peek(ctx)->type <= KW_ANY)) { spl_type_info_t *base = spl_parse_type(ctx); diff --git a/stage1/spl_stmt.c b/stage1/spl_stmt.c index 022baa2..0608a19 100644 --- a/stage1/spl_stmt.c +++ b/stage1/spl_stmt.c @@ -3,7 +3,6 @@ #include "spl_comp.h" #include "spl_lex_util.h" #include -#include /* ============================================================ * Return statement: ret expr; @@ -41,9 +40,7 @@ static void parse_var_decl(spl_comp_t *ctx, int is_const) { 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_tok_copy_name(name_tok, vname, sizeof(vname)); spl_type_info_t *var_type = NULL; int has_init = 0; @@ -141,6 +138,7 @@ void spl_parse_block(spl_comp_t *ctx) { /* Forward declaration for block expr */ static int is_assign_op(spl_tok_type_t t); +static int lookahead_is_assign(spl_comp_t *ctx); /* ============================================================ * Block expression: { stmts; [trailing_expr] } @@ -174,24 +172,7 @@ spl_expr_result_t spl_parse_block_expr(spl_comp_t *ctx) { result = (spl_expr_result_t){0}; } else { /* Expression — look ahead for assignment */ - int is_assign = 0; - { - usize look = ctx->tok_idx; - while (look < vec_size(ctx->toks)) { - spl_tok_type_t lt = vec_at(ctx->toks, look).type; - if (lt == TOK_SEMICOLON || lt == TOK_ENDLINE || lt == TOK_R_BRACE || - lt == TOK_EOF) - break; - if (lt == TOK_L_PAREN) - break; - if (is_assign_op(lt)) { - is_assign = 1; - break; - } - look++; - } - } - + int is_assign = lookahead_is_assign(ctx); int saved_addr = ctx->addr_of_mode; if (is_assign) ctx->addr_of_mode = 1; @@ -286,11 +267,11 @@ 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 */ skip_nl(ctx); - spl_expr_result_t cond = spl_parse_expr(ctx, PREC_MIN); - (void)cond; spl_loop_save_t save; loop_enter(ctx, &save); + 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); @@ -338,9 +319,7 @@ static void parse_for_stmt(spl_comp_t *ctx) { 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'; + spl_tok_copy_name(ivar, iname, sizeof(iname)); spl_push_scope(ctx); int ioffset = spl_declare_var(ctx, iname, spl_type_basic(SPL_USIZE), 0); @@ -410,18 +389,14 @@ static void parse_for_stmt(spl_comp_t *ctx) { /* Parse val variable name */ 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_tok_copy_name(vtok, vname, sizeof(vname)); /* Parse optional idx variable name */ char iname[256] = {0}; if (peek(ctx)->type == TOK_COMMA) { advance(ctx); spl_tok_t *itok = advance(ctx); - usize inl = itok->len < 255 ? itok->len : 255; - memcpy(iname, itok->lexeme, inl); - iname[inl] = '\0'; + spl_tok_copy_name(itok, iname, sizeof(iname)); } /* Stack: [slice_addr] or whatever the slice expression left */ @@ -602,9 +577,7 @@ static void parse_match_enum_bindings(spl_comp_t *ctx, spl_enum_variant_t *varia 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_tok_copy_name(btok, bname, sizeof(bname)); spl_type_info_t *btype = spl_type_basic(SPL_I32); usize field_byte_off = 4; /* skip tag */ @@ -628,9 +601,7 @@ static void parse_match_enum_bindings(spl_comp_t *ctx, spl_enum_variant_t *varia /* Single-value 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_tok_copy_name(btok, bname, sizeof(bname)); spl_type_info_t *btype = variant->data_type; int boffset = spl_declare_var(ctx, bname, btype, 0); @@ -816,9 +787,7 @@ static void parse_extern_decl(spl_comp_t *ctx) { 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'; + spl_tok_copy_name(fname_tok, fn_name, sizeof(fn_name)); skip_nl(ctx); expect(ctx, TOK_L_PAREN); @@ -880,6 +849,25 @@ static int is_assign_op(spl_tok_type_t t) { t == TOK_ASSIGN_R_SH; } +/* Scan ahead to check if the expression at current position is an assignment. + * Returns 1 if an assignment operator is found before a statement terminator + * or function-call paren. */ +static int lookahead_is_assign(spl_comp_t *ctx) { + usize look = ctx->tok_idx; + while (look < vec_size(ctx->toks)) { + spl_tok_type_t t = vec_at(ctx->toks, look).type; + if (t == TOK_SEMICOLON || t == TOK_ENDLINE || t == TOK_L_BRACE || t == TOK_R_BRACE || + t == TOK_EOF) + break; + if (t == TOK_L_PAREN) + break; + if (is_assign_op(t)) + return 1; + look++; + } + return 0; +} + static void parse_expr_stmt(spl_comp_t *ctx) { skip_nl(ctx); if (peek(ctx)->type == KW_RET) { @@ -889,24 +877,8 @@ static void parse_expr_stmt(spl_comp_t *ctx) { usize prev = ctx->tok_idx; - /* Look ahead for assignment operator (scan past expression tokens) */ - int is_assign = 0; - { - usize look = ctx->tok_idx; - while (look < vec_size(ctx->toks)) { - spl_tok_type_t t = vec_at(ctx->toks, look).type; - if (t == TOK_SEMICOLON || t == TOK_ENDLINE || t == TOK_L_BRACE || t == TOK_R_BRACE || - t == TOK_EOF) - break; - if (t == TOK_L_PAREN) - break; /* function call, not assignment */ - if (is_assign_op(t)) { - is_assign = 1; - break; - } - look++; - } - } + /* Look ahead for assignment operator */ + int is_assign = lookahead_is_assign(ctx); if (is_assign) ctx->addr_of_mode = 1; diff --git a/stage1/spl_type.c b/stage1/spl_type.c index 9cf4e9a..a4097fe 100644 --- a/stage1/spl_type.c +++ b/stage1/spl_type.c @@ -403,9 +403,7 @@ spl_type_info_t *spl_parse_type(spl_comp_t *ctx) { advance(ctx); /* : */ spl_type_info_t *ftype = spl_parse_type(ctx); char fname[256]; - usize fnl = ftok->len < 255 ? ftok->len : 255; - memcpy(fname, ftok->lexeme, fnl); - fname[fnl] = '\0'; + spl_tok_copy_name(ftok, fname, sizeof(fname)); spl_type_add_field(t, fname, ftype); } if (peek(ctx)->type == TOK_COMMA) @@ -430,15 +428,11 @@ spl_type_info_t *spl_parse_type(spl_comp_t *ctx) { advance(ctx); /* : */ spl_type_info_t *dtype = spl_parse_type(ctx); char vname[256]; - usize vnl = vtok->len < 255 ? vtok->len : 255; - memcpy(vname, vtok->lexeme, vnl); - vname[vnl] = '\0'; + spl_tok_copy_name(vtok, vname, sizeof(vname)); spl_type_add_variant(t, vname, dtype); } else { char vname[256]; - usize vnl = vtok->len < 255 ? vtok->len : 255; - memcpy(vname, vtok->lexeme, vnl); - vname[vnl] = '\0'; + spl_tok_copy_name(vtok, vname, sizeof(vname)); spl_type_add_variant(t, vname, NULL); } if (peek(ctx)->type == TOK_COMMA)