diff --git a/stage1/spl_comp.c b/stage1/spl_comp.c index 7e0fefc..57d69b5 100644 --- a/stage1/spl_comp.c +++ b/stage1/spl_comp.c @@ -68,6 +68,9 @@ void spl_comp_reset(spl_comp_t *ctx) { } void spl_comp_error(spl_comp_t *ctx, const char *fmt, ...) { + if (ctx->has_error) + return; + char body[COMP_ERROR_MAX - 64]; va_list args; va_start(args, fmt); @@ -83,6 +86,9 @@ void spl_comp_error(spl_comp_t *ctx, const char *fmt, ...) { } void spl_comp_err_tok(spl_comp_t *ctx, spl_tok_t *tok, const char *fmt, ...) { + if (ctx->has_error) + return; + char body[COMP_ERROR_MAX - 64]; va_list args; va_start(args, fmt); diff --git a/stage1/spl_expr.c b/stage1/spl_expr.c index 11d0982..be65dec 100644 --- a/stage1/spl_expr.c +++ b/stage1/spl_expr.c @@ -465,7 +465,7 @@ static void parse_one_field_init(spl_comp_t *ctx, spl_type_item_vec_t *items, in } } if (!found) { - spl_comp_error(ctx, "unknown field '%s' in struct literal", fname); + spl_comp_err_tok(ctx, ftok, "unknown field '%s' in struct literal", fname); } } @@ -794,6 +794,16 @@ static spl_expr_result_t parse_ident(spl_comp_t *ctx) { if (peek(ctx)->type == TOK_L_PAREN) { int fi = spl_lookup_func(ctx, name); if (fi < 0) { + /* Not a known function — check if it's a function pointer variable */ + spl_var_info_t *v = spl_lookup_var(ctx, name); + if (v && v->type_idx >= 0 && spl_type_kind(&ctx->tctx, v->type_idx) == TYPE_FN) { + /* Load function pointer value, let postfix handle the call */ + int vt_idx = v->type_idx; + emit_laddr(&ctx->emit, v->offset); + emit_load_ptr(&ctx->emit); + spl_expr_result_t r = {vt_idx, 0}; + return r; + } spl_comp_err_tok(ctx, t, "unknown function '%s'", name); spl_expr_result_t r = {-1, 0}; return r; @@ -802,7 +812,16 @@ static spl_expr_result_t parse_ident(spl_comp_t *ctx) { advance(ctx); int nargs = parse_call_args_checked(ctx, f->name, f->param_type_indices, f->nparams); - if (!expect(ctx, TOK_R_PAREN)) { + if (!ctx->has_error && nargs < f->nparams) { + spl_comp_err_tok(ctx, peek(ctx), "too few arguments to '%s': expected %d, got %d", + f->name, f->nparams, nargs); + } + if (!ctx->has_error) { + if (!expect(ctx, TOK_R_PAREN)) { + spl_expr_result_t _r = {-1, 0}; + return _r; + } + } else { spl_expr_result_t _r = {-1, 0}; return _r; } @@ -854,6 +873,21 @@ static spl_expr_result_t parse_ident(spl_comp_t *ctx) { return r; } + /* Function reference (not a call): get function address */ + { + int fi = spl_lookup_func(ctx, name); + if (fi >= 0) { + spl_func_info_t *f = &vec_at(ctx->funcs, fi); + if (!f->is_extern) { + emit_push_u64(&ctx->emit, f->func_idx); + int fn_ptr_type = + spl_type_fn(&ctx->tctx, spl_type_basic(&ctx->tctx, SPL_USIZE), f->ret_type_idx); + spl_expr_result_t r = {fn_ptr_type, 1}; + return r; + } + } + } + int ttype_idx = spl_type_resolve(&ctx->tctx, name); if (ttype_idx >= 0) { if (peek(ctx)->type == TOK_L_BRACE && @@ -873,8 +907,10 @@ static spl_expr_result_t parse_ident(spl_comp_t *ctx) { static spl_expr_result_t parse_group(spl_comp_t *ctx) { advance(ctx); spl_expr_result_t r = spl_parse_expr(ctx, PREC_MIN); - if (!expect(ctx, TOK_R_PAREN)) - return r; + if (!ctx->has_error) { + if (!expect(ctx, TOK_R_PAREN)) + return r; + } return r; } @@ -910,6 +946,9 @@ static spl_expr_result_t parse_prefix_op(spl_comp_t *ctx) { emit_binop(&ctx->emit, SPL_NOT, spl_type_emit_type(&ctx->tctx, right.type_idx)); break; case TOK_AND: + if (right.type_idx >= 0 && spl_type_kind(&ctx->tctx, right.type_idx) == TYPE_FN) { + return right; + } if (!right.is_lvalue) { spl_comp_err_tok(ctx, peek(ctx), "cannot take address of rvalue"); spl_expr_result_t _r = {-1, 0}; @@ -995,7 +1034,12 @@ static spl_expr_result_t parse_primary_expr(spl_comp_t *ctx) { if (peek(ctx)->type == TOK_L_PAREN) { advance(ctx); int nargs = parse_call_args(ctx); - if (!expect(ctx, TOK_R_PAREN)) { + if (!ctx->has_error) { + if (!expect(ctx, TOK_R_PAREN)) { + spl_expr_result_t _r = {-1, 0}; + return _r; + } + } else { spl_expr_result_t _r = {-1, 0}; return _r; } @@ -1015,7 +1059,12 @@ static spl_expr_result_t parse_primary_expr(spl_comp_t *ctx) { return _r; } int ti = spl_type_parse(&ctx->tctx, ctx); - if (!expect(ctx, TOK_R_PAREN)) { + if (!ctx->has_error) { + if (!expect(ctx, TOK_R_PAREN)) { + spl_expr_result_t _r = {-1, 0}; + return _r; + } + } else { spl_expr_result_t _r = {-1, 0}; return _r; } @@ -1112,11 +1161,26 @@ static spl_expr_result_t parse_postfix_expr(spl_comp_t *ctx, spl_expr_result_t l } { - int *cp = func->param_type_indices; - int cn = func->nparams; - nargs += parse_call_args_checked(ctx, func->name, cp, cn); + int full_np = func->nparams; + nargs += parse_call_args_checked(ctx, func->name, + func->param_type_indices, full_np); + if (is_instance && !ctx->has_error) { + int ok = + (nargs >= full_np) || (nargs == full_np - 1 && full_np == 1); + if (!ok) { + spl_comp_err_tok( + ctx, peek(ctx), + "too few arguments to '%s': expected %d, got %d", + func->name, full_np, nargs); + } + } } - if (!expect(ctx, TOK_R_PAREN)) { + if (!ctx->has_error) { + if (!expect(ctx, TOK_R_PAREN)) { + spl_expr_result_t _r = left; + return _r; + } + } else { spl_expr_result_t _r = left; return _r; } @@ -1142,6 +1206,48 @@ static spl_expr_result_t parse_postfix_expr(spl_comp_t *ctx, spl_expr_result_t l } if (found_method) continue; + + /* Indirect call through function pointer: fn_ptr(args) */ + if (left.type_idx >= 0 && peek(ctx)->type == TOK_L_PAREN && + spl_type_kind(&ctx->tctx, left.type_idx) == TYPE_FN) { + advance(ctx); /* ( */ + + int nargs = parse_call_args(ctx); + if (!ctx->has_error) { + if (!expect(ctx, TOK_R_PAREN)) { + spl_expr_result_t _r = left; + return _r; + } + } else { + spl_expr_result_t _r = left; + return _r; + } + + /* If left is lvalue, load the function address value first */ + if (left.is_lvalue) { + emit_load_ptr(&ctx->emit); + } + + /* Stack currently: [args..., func_addr]. + * CALLI expects: POP addr, POP nargs, call(addr, nargs). + * After all args are on stack: push nargs, push func_addr. + * But func_addr is already below args? No — we need: + * Stack: [..., func_addr, arg0, ..., argN-1] + * CALLI: POP addr, POP nargs, call(addr, nargs) + * Actually CALLI pops: TOS=addr, TOS-1=nargs. + * So we need: [args..., nargs, addr] + * But currently: [args..., addr] (addr is on TOS, loaded above) + * Push nargs ABOVE addr, then CALLI pops addr, then nargs: + * Stack: [args..., nargs, addr] + * TOS=addr → POP returns addr, then POP returns nargs. ✓ */ + + emit_push_u64(&ctx->emit, nargs); + emit_raw(&ctx->emit, SPL_CALLI, SPL_VOID, 0); + + /* For now, result type is void — we don't know the return type from fn ptr */ + left = (spl_expr_result_t){spl_type_basic(&ctx->tctx, SPL_VOID), 0}; + continue; + } } if (left.type_idx >= 0 && spl_type_kind(&ctx->tctx, left.type_idx) == TYPE_STRUCT) { diff --git a/stage1/spl_parser.c b/stage1/spl_parser.c index 6805118..4673132 100644 --- a/stage1/spl_parser.c +++ b/stage1/spl_parser.c @@ -25,6 +25,10 @@ static int parse_params_decl(spl_comp_t *ctx, char pnames[][256], int ptypes[]) ctx->tok_idx = saved; } while (1) { + if (peek(ctx)->type == TOK_ELLIPSIS) { + advance(ctx); + break; + } spl_tok_t *pname = advance(ctx); spl_tok_copy_name(pname, pnames[nparams], 256); skip_nl(ctx); @@ -42,10 +46,6 @@ static int parse_params_decl(spl_comp_t *ctx, char pnames[][256], int ptypes[]) skip_nl(ctx); continue; } - if (peek(ctx)->type == TOK_ELLIPSIS) { - advance(ctx); - skip_nl(ctx); - } break; } } @@ -408,7 +408,6 @@ static void parse_type_body(spl_comp_t *ctx, int container_type_idx, int is_enum } ctx->tctx.current_type_idx = saved_current; - spl_type_compute_layout(&ctx->tctx, container_type_idx); } /* ============================================================ @@ -746,4 +745,9 @@ void spl_parse_prog(spl_comp_t *ctx) { } } } + + /* Recompute all type layouts now that forward references are resolved */ + for (int i = 0; i < (int)vec_size(ctx->tctx.types); i++) { + spl_type_compute_layout(&ctx->tctx, i); + } } diff --git a/stage1/spl_stmt.c b/stage1/spl_stmt.c index ff72a54..d78a5b7 100644 --- a/stage1/spl_stmt.c +++ b/stage1/spl_stmt.c @@ -203,6 +203,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 */ + usize before = ctx->tok_idx; int is_assign = lookahead_is_assign(ctx); int saved_addr = ctx->addr_of_mode; if (is_assign) @@ -210,6 +211,14 @@ spl_expr_result_t spl_parse_block_expr(spl_comp_t *ctx) { spl_expr_result_t expr = spl_parse_expr(ctx, PREC_MIN); ctx->addr_of_mode = saved_addr; + /* Safety: if no token was consumed, advance to prevent infinite loop */ + if (ctx->tok_idx == before) { + advance(ctx); + result = (spl_expr_result_t){0}; + skip_nl(ctx); + continue; + } + skip_nl(ctx); if (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_ENDLINE) { /* Expression statement: drop value, consume ; */ @@ -1140,6 +1149,7 @@ void spl_parse_stmt(spl_comp_t *ctx) { parse_extern_decl(ctx); break; default: + snprintf(ctx->parse_context, sizeof(ctx->parse_context), "expression statement"); parse_expr_stmt(ctx); break; } diff --git a/stage1/spl_type.c b/stage1/spl_type.c index 6712772..a68b568 100644 --- a/stage1/spl_type.c +++ b/stage1/spl_type.c @@ -304,6 +304,32 @@ int spl_type_alias(spl_type_ctx_t *tctx, const char *name, int target_type_idx) return idx; } +int spl_type_fn(spl_type_ctx_t *tctx, int params_type_idx, int ret_type_idx) { + spl_type_info_t t; + memset(&t, 0, sizeof(t)); + t.kind = TYPE_FN; + t.byte_size = sizeof(spl_val_t); + t.slot_count = 1; + t.resolved = 1; + vec_init(t.items); + + spl_type_item_t pi; + memset(&pi, 0, sizeof(pi)); + pi.item_kind = ITEM_FIELD; + pi.aggregate_field.type_idx = params_type_idx; + pi.aggregate_field.offset = 0; + vec_push(t.items, pi); + + spl_type_item_t ri; + memset(&ri, 0, sizeof(ri)); + ri.item_kind = ITEM_FIELD; + ri.aggregate_field.type_idx = ret_type_idx; + ri.aggregate_field.offset = sizeof(spl_val_t); + vec_push(t.items, ri); + + return spl_type_add(tctx, t); +} + /* ============================================================ * Item management * ============================================================ */ @@ -317,6 +343,9 @@ void spl_type_add_field(spl_type_ctx_t *tctx, int type_idx, const char *name, in item.aggregate_field.type_idx = field_type_idx; item.aggregate_field.offset = 0; vec_push(t->items, item); + /* Adding field after layout was computed → needs recompute */ + if (t->resolved == 1) + t->resolved = 0; } void spl_type_add_var(spl_type_ctx_t *tctx, int type_idx, const char *name, int var_type_idx) { @@ -339,6 +368,8 @@ void spl_type_add_variant(spl_type_ctx_t *tctx, int type_idx, const char *name, item.enum_field.type_idx = data_type_idx; item.enum_field.value = vec_size(t->items); vec_push(t->items, item); + if (t->resolved == 1) + t->resolved = 0; } void spl_type_add_method(spl_type_ctx_t *tctx, int type_idx, const char *name, int func_idx) { @@ -382,8 +413,14 @@ void spl_type_compute_layout(spl_type_ctx_t *tctx, int type_idx) { if (type_idx < 0) return; spl_type_info_t *t = &vec_at(tctx->types, type_idx); - if (t->resolved) + if (t->resolved == 1) return; + if (t->resolved == 2) { + fprintf(stderr, "error: circular type dependency in '%s'\n", t->name ? t->name : "?"); + return; + } + + t->resolved = 2; /* mark as in-progress */ switch (t->kind) { case TYPE_STRUCT: { @@ -534,8 +571,9 @@ usize spl_type_array_len(spl_type_ctx_t *tctx, int type_idx) { int spl_type_is_scalar(spl_type_ctx_t *tctx, int type_idx) { if (type_idx < 0) return 1; + type_idx = spl_type_resolve_underlying(tctx, type_idx); spl_type_info_t *t = &vec_at(tctx->types, type_idx); - if (t->kind == TYPE_BASIC || t->kind == TYPE_PTR) + if (t->kind == TYPE_BASIC || t->kind == TYPE_PTR || t->kind == TYPE_FN) return 1; if (t->kind == TYPE_ENUM) { vec_for(t->items, i) { @@ -575,6 +613,7 @@ int spl_type_resolve_underlying(spl_type_ctx_t *tctx, int type_idx) { spl_type_t spl_type_emit_type(spl_type_ctx_t *tctx, int type_idx) { if (type_idx < 0) return SPL_I32; + type_idx = spl_type_resolve_underlying(tctx, type_idx); spl_type_info_t *t = &vec_at(tctx->types, type_idx); if (t->kind == TYPE_BASIC) return t->basic_type; @@ -656,6 +695,8 @@ const char *spl_type_str(spl_type_ctx_t *tctx, int type_idx) { return t->name ? t->name : ""; case TYPE_NAME: return t->name ? t->name : ""; + case TYPE_FN: + return "fn"; default: return ""; } @@ -857,6 +898,39 @@ int spl_type_parse(spl_type_ctx_t *tctx, struct spl_comp *ctx) { return ti; } + /* Inline function type: fn(params...) rettype */ + if (tok->type == KW_FN) { + ctx->tok_idx++; + if (!expect(ctx, TOK_L_PAREN)) + return -1; + + /* Parse comma-separated parameter types */ + int param_types[64]; /* max 64 params */ + int nparams = 0; + if (peek(ctx)->type != TOK_R_PAREN) { + for (;;) { + int pt = spl_type_parse(tctx, ctx); + if (pt >= 0 && nparams < 64) + param_types[nparams++] = pt; + if (peek(ctx)->type == TOK_COMMA) { + advance(ctx); + continue; + } + break; + } + } + if (!expect(ctx, TOK_R_PAREN)) + return -1; + + int ret_type = spl_type_parse(tctx, ctx); + if (ret_type < 0) + ret_type = spl_type_basic(tctx, SPL_VOID); + + /* Store param types in a synthetic array type (slot 0 = count, slot 1..N = params) */ + int params_arr = spl_type_array(tctx, spl_type_basic(tctx, SPL_USIZE), nparams); + return spl_type_fn(tctx, params_arr, ret_type); + } + /* Identifier: basic type or named type */ if (tok->type == TOK_IDENT || ((int)tok->type >= (int)KW_AS && (int)tok->type <= (int)KW_ANY)) { const char *name = tok->lexeme; diff --git a/stage1/spl_type.h b/stage1/spl_type.h index b420e61..264490f 100644 --- a/stage1/spl_type.h +++ b/stage1/spl_type.h @@ -18,6 +18,7 @@ typedef enum { TYPE_UNION, TYPE_ENUM, TYPE_NAME, + TYPE_FN, TYPE_COUNT, } spl_type_kind_t; @@ -123,6 +124,7 @@ int spl_type_struct(spl_type_ctx_t *tctx, const char *name); int spl_type_union(spl_type_ctx_t *tctx, const char *name); int spl_type_enum(spl_type_ctx_t *tctx, const char *name); int spl_type_alias(spl_type_ctx_t *tctx, const char *name, int target_type_idx); +int spl_type_fn(spl_type_ctx_t *tctx, int params_type_idx, int ret_type_idx); /* ============================================================ * Item management — add members to aggregate types