#include "spl_comp.h" #include "spl_lex_util.h" #include #include #include static int tok_prec(spl_tok_type_t t) { switch (t) { case TOK_OR_OR: return PREC_LOGOR; case TOK_AND_AND: return PREC_LOGAND; case TOK_OR: return PREC_OR; case TOK_XOR: return PREC_XOR; case TOK_AND: return PREC_AND; case TOK_EQ: case TOK_NEQ: return PREC_CMPEQ; case TOK_LT: case TOK_LE: case TOK_GT: case TOK_GE: return PREC_CMP; case TOK_L_SH: case TOK_R_SH: return PREC_SHIFT; case TOK_ADD: case TOK_SUB: return PREC_ADD; case TOK_MUL: case TOK_DIV: case TOK_MOD: return PREC_MUL; case TOK_ASSIGN: case TOK_ASSIGN_ADD: case TOK_ASSIGN_SUB: case TOK_ASSIGN_MUL: case TOK_ASSIGN_DIV: case TOK_ASSIGN_MOD: case TOK_ASSIGN_AND: case TOK_ASSIGN_OR: case TOK_ASSIGN_XOR: case TOK_ASSIGN_L_SH: case TOK_ASSIGN_R_SH: return PREC_ASSIGN; default: return PREC_MIN; } } 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, int type_idx); static spl_expr_result_t parse_postfix_expr(spl_comp_t *ctx, spl_expr_result_t left); static void emit_slice_create(spl_comp_t *ctx, usize stride) { emit_pick(&ctx->emit, 2); emit_pick(&ctx->emit, 2); emit_push_u64(&ctx->emit, stride); emit_mul_u64(&ctx->emit); emit_add_u64(&ctx->emit); emit_pick(&ctx->emit, 1); emit_pick(&ctx->emit, 3); emit_sub_usize(&ctx->emit); emit_rot(&ctx->emit); emit_drop(&ctx->emit); emit_rot(&ctx->emit); emit_drop(&ctx->emit); emit_rot(&ctx->emit); emit_drop(&ctx->emit); } static void emit_slice_index(spl_comp_t *ctx, int elem_type_idx) { usize elem_size = spl_type_elem_stride(&ctx->tctx, elem_type_idx); emit_swap(&ctx->emit); emit_load_ptr(&ctx->emit); emit_swap(&ctx->emit); emit_push_u64(&ctx->emit, elem_size); emit_mul_u64(&ctx->emit); emit_add_u64(&ctx->emit); } static int spl_resolve_type_member(spl_comp_t *ctx, int type_idx, const char *field, spl_expr_result_t *result) { if (spl_type_kind(&ctx->tctx, type_idx) == TYPE_ENUM) { spl_type_item_vec_t *items = spl_type_items(&ctx->tctx, type_idx); vec_for(*items, i) { spl_type_item_t *it = &vec_at(*items, i); if (it->item_kind != ITEM_VARIANT) continue; if (strcmp(it->name, field) == 0) { emit_push_i32(&ctx->emit, it->enum_field.value); *result = (spl_expr_result_t){type_idx, 0}; return 1; } } } const char *type_name = spl_type_name(&ctx->tctx, type_idx); if (type_name) { char qualified[512]; int qlen = snprintf(qualified, sizeof(qualified), "%s.%s", type_name, field); if (qlen > 0 && (usize)qlen < sizeof(qualified)) { int nested = spl_type_resolve(&ctx->tctx, qualified); if (nested >= 0) { *result = (spl_expr_result_t){nested, 1}; return 1; } } } int nested = spl_type_resolve(&ctx->tctx, field); if (nested >= 0) { *result = (spl_expr_result_t){nested, 1}; return 1; } return 0; } 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; } } static int binop_to_sir(spl_tok_type_t t, spl_type_t bt) { int is_signed = (bt == SPL_I32 || bt == SPL_I64 || bt == SPL_I8 || bt == SPL_I16); switch (t) { case TOK_ADD: return SPL_ADD; case TOK_SUB: return SPL_SUB; case TOK_MUL: return SPL_MUL; case TOK_DIV: return is_signed ? SPL_DIV_S : SPL_DIV_U; case TOK_MOD: return is_signed ? SPL_REM_S : SPL_REM_U; case TOK_AND: return SPL_AND; case TOK_OR: return SPL_OR; case TOK_XOR: return SPL_XOR; case TOK_L_SH: return SPL_SHL; case TOK_R_SH: return is_signed ? SPL_SHR_S : SPL_SHR_U; case TOK_EQ: return SPL_EQ; case TOK_NEQ: return SPL_NE; case TOK_LT: return is_signed ? SPL_SLT : SPL_ULT; case TOK_LE: return is_signed ? SPL_SLE : SPL_ULE; case TOK_GT: return is_signed ? SPL_SGT : SPL_UGT; case TOK_GE: return is_signed ? SPL_SGE : SPL_UGE; default: return -1; } } static int64_t parse_int(const char *s, usize len) { char buf[64]; usize clen = len < 63 ? len : 63; memcpy(buf, s, clen); buf[clen] = '\0'; if (clen > 2 && buf[0] == '0') { if (buf[1] == 'x' || buf[1] == 'X') return (int64_t)strtoll(buf, NULL, 16); if (buf[1] == 'b' || buf[1] == 'B') return (int64_t)strtoll(buf + 2, NULL, 2); if (buf[1] == 'o' || buf[1] == 'O') return (int64_t)strtoll(buf + 2, NULL, 8); } return (int64_t)strtoll(buf, NULL, 10); } static spl_expr_result_t parse_int_literal(spl_comp_t *ctx) { spl_tok_t *t = advance(ctx); int64_t val = parse_int(t->lexeme, t->len); emit_push_i32(&ctx->emit, (spl_val_t)val); spl_expr_result_t r = {spl_type_basic(&ctx->tctx, SPL_I32), 0}; return r; } static spl_expr_result_t parse_float_literal(spl_comp_t *ctx) { spl_tok_t *t = advance(ctx); char buf[64]; usize clen = t->len < 63 ? t->len : 63; memcpy(buf, t->lexeme, clen); buf[clen] = '\0'; double val = strtod(buf, NULL); emit_push_f64(&ctx->emit, (spl_val_t)(int64_t)val); (void)val; spl_expr_result_t r = {spl_type_basic(&ctx->tctx, SPL_F64), 0}; return r; } static spl_expr_result_t parse_char_literal(spl_comp_t *ctx) { spl_tok_t *t = advance(ctx); const char *s = t->lexeme; usize l = t->len; int64_t val = 0; if (l >= 3) { if (s[1] == '\\' && l >= 4) { char buf = 0; const char *cp = s + 1; spl_decode_escape(&cp, &buf); val = (unsigned char)buf; } else { val = (unsigned char)s[1]; } } emit_push_i32(&ctx->emit, (spl_val_t)val); spl_expr_result_t r = {spl_type_basic(&ctx->tctx, SPL_I32), 0}; return r; } static spl_expr_result_t parse_string_literal(spl_comp_t *ctx) { spl_tok_t *t = advance(ctx); usize slen = t->len; if (slen >= 2) { slen -= 2; } char *decoded = malloc(slen + 1); usize di = 0; for (usize i = 1; i + 1 < t->len; i++) { if (t->lexeme[i] == '\\' && i + 1 < t->len - 1) { char buf = 0; const char *cp = t->lexeme + i; spl_decode_escape(&cp, &buf); decoded[di++] = buf; i += (usize)(cp - (t->lexeme + i)) - 1; } else { decoded[di++] = t->lexeme[i]; } } decoded[di] = '\0'; int gdi = spl_add_global_data(ctx, decoded, di + 1); free(decoded); emit_gaddr(&ctx->emit, gdi - 1); spl_expr_result_t r = {spl_type_ptr(&ctx->tctx, spl_type_basic(&ctx->tctx, SPL_U8)), 0}; return r; } static spl_expr_result_t parse_array_literal(spl_comp_t *ctx) { advance(ctx); skip_nl(ctx); int len_val; if (!spl_parse_int_literal(ctx, &len_val)) { spl_comp_error(ctx, "expected array length"); spl_expr_result_t r = {-1, 0}; return r; } usize len = (usize)len_val; skip_nl(ctx); expect(ctx, TOK_R_BRACKET); skip_nl(ctx); int elem_type_idx = spl_type_parse(&ctx->tctx, ctx); if (elem_type_idx < 0) { spl_expr_result_t r = {-1, 0}; return r; } skip_nl(ctx); expect(ctx, TOK_L_BRACE); skip_nl(ctx); for (usize i = 0; i < len; i++) { if (i > 0) { if (peek(ctx)->type == TOK_COMMA) advance(ctx); skip_nl(ctx); } spl_parse_expr(ctx, PREC_MIN); skip_nl(ctx); } if (peek(ctx)->type == TOK_COMMA) advance(ctx); skip_nl(ctx); expect(ctx, TOK_R_BRACE); int arr_type_idx = spl_type_array(&ctx->tctx, elem_type_idx, len); return (spl_expr_result_t){arr_type_idx, 0}; } static void parse_slice_inline(spl_comp_t *ctx) { 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 *ftok = advance(ctx); char fname[256]; spl_tok_copy_name(ftok, fname, sizeof(fname)); skip_nl(ctx); if (peek(ctx)->type == TOK_ASSIGN) advance(ctx); skip_nl(ctx); emit_dup(&ctx->emit); if (strcmp(fname, "ptr") == 0) { spl_parse_expr(ctx, PREC_MIN); emit_store_ptr(&ctx->emit); } else if (strcmp(fname, "len") == 0) { emit_ptr_add(&ctx->emit, sizeof(spl_val_t)); spl_parse_expr(ctx, PREC_MIN); emit_store_usize(&ctx->emit); } skip_nl(ctx); } expect(ctx, TOK_R_BRACE); emit_drop(&ctx->emit); } static void parse_one_field_init(spl_comp_t *ctx, spl_type_item_vec_t *items, int base_offset, usize extra_offset) { if (peek(ctx)->type == TOK_DOT) advance(ctx); spl_tok_t *ftok = advance(ctx); char fname[256]; spl_tok_copy_name(ftok, fname, sizeof(fname)); skip_nl(ctx); if (peek(ctx)->type == TOK_ASSIGN) advance(ctx); skip_nl(ctx); vec_for(*items, fi) { spl_type_item_t *it = &vec_at(*items, fi); if (it->item_kind != ITEM_FIELD) continue; if (strcmp(it->name, fname) == 0) { emit_laddr(&ctx->emit, base_offset); usize byte_off = extra_offset + it->aggregate_field.offset; emit_ptr_add(&ctx->emit, byte_off); int ft_idx = it->aggregate_field.type_idx; if (ft_idx >= 0 && spl_type_kind(&ctx->tctx, ft_idx) == TYPE_SLICE && peek(ctx)->type == TOK_L_BRACE) { parse_slice_inline(ctx); } else if (ft_idx >= 0 && spl_type_kind(&ctx->tctx, ft_idx) == TYPE_ARRAY && peek(ctx)->type == TOK_L_BRACKET) { advance(ctx); skip_nl(ctx); int len_val; if (!spl_parse_int_literal(ctx, &len_val)) { spl_comp_error(ctx, "expected array length"); return; } usize arr_len = (usize)len_val; skip_nl(ctx); expect(ctx, TOK_R_BRACKET); skip_nl(ctx); int elem_type_idx = spl_type_parse(&ctx->tctx, ctx); skip_nl(ctx); expect(ctx, TOK_L_BRACE); skip_nl(ctx); usize stride = spl_type_elem_stride(&ctx->tctx, elem_type_idx); spl_type_t st = spl_type_emit_type(&ctx->tctx, elem_type_idx); for (usize i = 0; i < arr_len; i++) { if (i > 0) { if (peek(ctx)->type == TOK_COMMA) advance(ctx); skip_nl(ctx); } emit_dup(&ctx->emit); emit_ptr_add(&ctx->emit, i * stride); int saved_aom = ctx->addr_of_mode; ctx->addr_of_mode = 0; spl_parse_expr(ctx, PREC_MIN); ctx->addr_of_mode = saved_aom; if (elem_type_idx >= 0 && !spl_type_is_scalar(&ctx->tctx, elem_type_idx)) { emit_copy_addr_to_addr(&ctx->emit, spl_type_slot_count(&ctx->tctx, elem_type_idx), 1); } else { emit_store_type(&ctx->emit, st); } skip_nl(ctx); } if (peek(ctx)->type == TOK_COMMA) advance(ctx); skip_nl(ctx); expect(ctx, TOK_R_BRACE); emit_drop(&ctx->emit); } else if (ft_idx >= 0 && (spl_type_kind(&ctx->tctx, ft_idx) == TYPE_STRUCT || spl_type_kind(&ctx->tctx, ft_idx) == TYPE_ENUM) && spl_type_size(&ctx->tctx, ft_idx) > sizeof(spl_val_t)) { spl_expr_result_t fv; if (peek(ctx)->type == TOK_L_BRACE) { fv = spl_parse_struct_literal(ctx, ft_idx); } else { fv = spl_parse_expr(ctx, PREC_MIN); } (void)fv; usize nslots = (spl_type_size(&ctx->tctx, ft_idx) + sizeof(spl_val_t) - 1) / sizeof(spl_val_t); emit_frame_copy(&ctx->emit, base_offset + (int)extra_offset + (int)it->aggregate_field.offset, nslots); emit_drop(&ctx->emit); } else { spl_expr_result_t fv = spl_parse_expr(ctx, PREC_MIN); (void)fv; emit_store_type(&ctx->emit, spl_type_emit_type(&ctx->tctx, ft_idx)); } break; } } } spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, int type_idx) { usize sz = spl_type_size(&ctx->tctx, type_idx); int base_offset = fa_alloc(&ctx->emit.frame, sz); advance(ctx); skip_nl(ctx); if (spl_type_kind(&ctx->tctx, type_idx) == 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) { if (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) advance(ctx); skip_nl(ctx); continue; } parse_one_field_init(ctx, spl_type_items(&ctx->tctx, type_idx), base_offset, 0); skip_nl(ctx); } } else if (spl_type_kind(&ctx->tctx, type_idx) == TYPE_SLICE) { 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]; spl_tok_copy_name(ftok, fname, sizeof(fname)); skip_nl(ctx); if (peek(ctx)->type == TOK_ASSIGN) advance(ctx); skip_nl(ctx); if (strcmp(fname, "ptr") == 0) { emit_laddr(&ctx->emit, base_offset); spl_parse_expr(ctx, PREC_MIN); emit_store_ptr(&ctx->emit); } else if (strcmp(fname, "len") == 0) { emit_laddr(&ctx->emit, base_offset); emit_ptr_add(&ctx->emit, sizeof(spl_val_t)); spl_parse_expr(ctx, PREC_MIN); emit_store_usize(&ctx->emit); } skip_nl(ctx); } } else if (spl_type_kind(&ctx->tctx, type_idx) == TYPE_ENUM) { spl_tok_t *vtok; if (peek(ctx)->type == TOK_DOT) { advance(ctx); vtok = advance(ctx); } else { vtok = advance(ctx); while (peek(ctx)->type == TOK_DOT) { advance(ctx); vtok = advance(ctx); } } char vname[256]; spl_tok_copy_name(vtok, vname, sizeof(vname)); skip_nl(ctx); if (peek(ctx)->type == TOK_ASSIGN) advance(ctx); skip_nl(ctx); int found = 0; spl_type_item_vec_t *items = spl_type_items(&ctx->tctx, type_idx); vec_for(*items, vi) { spl_type_item_t *v = &vec_at(*items, vi); if (v->item_kind != ITEM_VARIANT) continue; if (strcmp(v->name, vname) == 0) { found = 1; emit_laddr(&ctx->emit, base_offset); emit_push_i32(&ctx->emit, v->enum_field.value); emit_store_i32(&ctx->emit); const usize DATA_OFFSET = ENUM_TAG_SIZE; int dt_idx = v->enum_field.type_idx; if (dt_idx >= 0) { if (spl_type_kind(&ctx->tctx, dt_idx) == TYPE_STRUCT && peek(ctx)->type == TOK_L_BRACE) { 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) break; parse_one_field_init(ctx, spl_type_items(&ctx->tctx, dt_idx), base_offset, DATA_OFFSET); skip_nl(ctx); } expect(ctx, TOK_R_BRACE); } else if (dt_idx >= 0 && (spl_type_kind(&ctx->tctx, dt_idx) == TYPE_STRUCT || spl_type_kind(&ctx->tctx, dt_idx) == TYPE_ENUM) && spl_type_size(&ctx->tctx, dt_idx) > sizeof(spl_val_t)) { spl_expr_result_t dv; if (peek(ctx)->type == TOK_L_BRACE) { dv = spl_parse_struct_literal(ctx, dt_idx); } else { dv = spl_parse_expr(ctx, PREC_MIN); } (void)dv; usize nslots = (spl_type_size(&ctx->tctx, dt_idx) + sizeof(spl_val_t) - 1) / sizeof(spl_val_t); emit_frame_copy(&ctx->emit, base_offset + (int)DATA_OFFSET, nslots); emit_drop(&ctx->emit); } else { spl_expr_result_t dv = spl_parse_expr(ctx, PREC_MIN); (void)dv; emit_laddr(&ctx->emit, base_offset); emit_ptr_add(&ctx->emit, DATA_OFFSET); spl_type_t bt = spl_type_emit_type(&ctx->tctx, dt_idx); emit_swap(&ctx->emit); emit_store_type(&ctx->emit, bt); } } break; } } if (!found) spl_comp_error(ctx, "unknown enum variant '%s'", vname); } skip_nl(ctx); expect(ctx, TOK_R_BRACE); if (sz <= sizeof(spl_val_t)) { emit_laddr(&ctx->emit, base_offset); emit_load_type(&ctx->emit, spl_type_emit_type(&ctx->tctx, type_idx)); return (spl_expr_result_t){type_idx, 0}; } else { emit_laddr(&ctx->emit, base_offset); return (spl_expr_result_t){type_idx, 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 void spl_check_arg_type(spl_comp_t *ctx, const char *fname, int arg_type_idx, int param_type_idx, int arg_idx) { if (arg_type_idx < 0 || param_type_idx < 0) return; if (spl_type_kind(&ctx->tctx, param_type_idx) == TYPE_PTR && spl_type_elem_type(&ctx->tctx, param_type_idx) >= 0 && spl_type_kind(&ctx->tctx, arg_type_idx) != TYPE_PTR && spl_type_name(&ctx->tctx, arg_type_idx) && spl_type_name(&ctx->tctx, spl_type_elem_type(&ctx->tctx, param_type_idx)) && strcmp(spl_type_name(&ctx->tctx, arg_type_idx), spl_type_name(&ctx->tctx, spl_type_elem_type(&ctx->tctx, param_type_idx))) == 0) { fprintf(stderr, "%s: warning: argument %d of '%s' expects '%s*', " "got '%s' (missing '&'?)\n", ctx->fname, arg_idx + 1, fname, spl_type_name(&ctx->tctx, spl_type_elem_type(&ctx->tctx, param_type_idx)), spl_type_name(&ctx->tctx, arg_type_idx)); } } static int parse_call_args_checked(spl_comp_t *ctx, const char *fname, int *param_type_indices, int nparams) { int nargs = 0; int nlogical = 0; if (peek(ctx)->type != TOK_R_PAREN) { for (;;) { spl_expr_result_t arg = spl_parse_expr(ctx, PREC_MIN); if (param_type_indices && nlogical < nparams && param_type_indices[nlogical] >= 0) spl_check_arg_type(ctx, fname, arg.type_idx, param_type_indices[nlogical], nlogical); int arg_slots = 1; if (param_type_indices && nlogical < nparams && param_type_indices[nlogical] >= 0 && arg.type_idx >= 0 && spl_type_kind(&ctx->tctx, param_type_indices[nlogical]) != TYPE_PTR && spl_type_needs_multi_slot(&ctx->tctx, param_type_indices[nlogical])) { usize nslots = (spl_type_size(&ctx->tctx, param_type_indices[nlogical]) + sizeof(spl_val_t) - 1) / sizeof(spl_val_t); for (usize i = 0; i < nslots; i++) { emit_dup(&ctx->emit); emit_ptr_add(&ctx->emit, i * sizeof(spl_val_t)); emit_load_ptr(&ctx->emit); emit_swap(&ctx->emit); } emit_drop(&ctx->emit); arg_slots = (int)nslots; } nargs += arg_slots; nlogical++; 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]; spl_tok_copy_name(t, name, sizeof(name)); if (peek(ctx)->type == TOK_L_PAREN) { int fi = spl_lookup_func(ctx, name); if (fi < 0) { spl_comp_error(ctx, "unknown function '%s'", name); spl_expr_result_t r = {-1, 0}; return r; } spl_func_info_t *f = &vec_at(ctx->funcs, fi); advance(ctx); int nargs = parse_call_args_checked(ctx, f->name, f->param_type_indices, f->nparams); expect(ctx, TOK_R_PAREN); if (f->is_extern) { int nidx = spl_ensure_native(ctx, f->name); emit_push_i32(&ctx->emit, nidx); emit_ncall(&ctx->emit, nargs); } else { emit_call_with_fixup(&ctx->emit, nargs, f->func_idx); } spl_expr_result_t r = {f->ret_type_idx, 0}; if (spl_type_needs_multi_slot(&ctx->tctx, f->ret_type_idx)) { usize nslots = spl_type_slot_count(&ctx->tctx, f->ret_type_idx); int temp_offset = fa_alloc_temp(&ctx->emit.frame, nslots); emit_frame_copy(&ctx->emit, temp_offset, nslots); emit_laddr(&ctx->emit, temp_offset); r.is_lvalue = 1; } return r; } spl_var_info_t *v = spl_lookup_var(ctx, name); if (v) { spl_val_t cv = 0; if (map_get(ctx->const_values, name, &cv)) { emit_push_type(&ctx->emit, spl_type_emit_type(&ctx->tctx, v->type_idx), cv); spl_expr_result_t r = {v->type_idx, 0}; return r; } int vt_idx = v->type_idx; emit_laddr(&ctx->emit, v->offset); spl_expr_result_t r; emit_load_or_addr_type(ctx, &r, vt_idx); return r; } int ttype_idx = spl_type_resolve(&ctx->tctx, name); if (ttype_idx >= 0) { if (peek(ctx)->type == TOK_L_BRACE && (spl_type_kind(&ctx->tctx, ttype_idx) == TYPE_STRUCT || spl_type_kind(&ctx->tctx, ttype_idx) == TYPE_ENUM)) { return spl_parse_struct_literal(ctx, ttype_idx); } spl_expr_result_t r = {ttype_idx, 0}; return r; } spl_comp_error(ctx, "undefined variable '%s'", name); spl_expr_result_t r = {-1, 0}; return r; } static spl_expr_result_t parse_group(spl_comp_t *ctx) { advance(ctx); spl_expr_result_t r = spl_parse_expr(ctx, PREC_MIN); expect(ctx, TOK_R_PAREN); return r; } static void emit_load_or_addr_type(spl_comp_t *ctx, spl_expr_result_t *result, int type_idx) { if (spl_type_is_scalar(&ctx->tctx, type_idx)) { if (!ctx->addr_of_mode) { emit_load_type(&ctx->emit, spl_type_emit_type(&ctx->tctx, type_idx)); *result = (spl_expr_result_t){type_idx, 0}; return; } } *result = (spl_expr_result_t){type_idx, 1}; } static spl_expr_result_t parse_prefix_op(spl_comp_t *ctx) { spl_tok_t *op = advance(ctx); if (op->type == TOK_AND) ctx->addr_of_mode = 1; spl_expr_result_t right = spl_parse_expr(ctx, PREC_PREFIX); if (op->type == TOK_AND) ctx->addr_of_mode = 0; switch (op->type) { case TOK_SUB: emit_binop(&ctx->emit, SPL_NEG, spl_type_emit_type(&ctx->tctx, right.type_idx)); break; case TOK_NOT: emit_push_i32(&ctx->emit, 0); emit_binop(&ctx->emit, SPL_EQ, spl_type_emit_type(&ctx->tctx, right.type_idx)); break; case TOK_BIT_NOT: emit_binop(&ctx->emit, SPL_NOT, spl_type_emit_type(&ctx->tctx, right.type_idx)); break; case TOK_AND: if (!right.is_lvalue) { spl_comp_error(ctx, "cannot take address of rvalue"); } right.type_idx = spl_type_ptr(&ctx->tctx, right.type_idx); break; case TOK_MUL: if (right.type_idx >= 0 && spl_type_kind(&ctx->tctx, right.type_idx) == TYPE_PTR && spl_type_elem_type(&ctx->tctx, right.type_idx) >= 0) { if (right.is_lvalue) { emit_load_ptr(&ctx->emit); } emit_load_or_addr_type(ctx, &right, spl_type_elem_type(&ctx->tctx, right.type_idx)); } break; default: break; } return right; } static spl_expr_result_t parse_primary_expr(spl_comp_t *ctx) { spl_tok_t *tok = peek(ctx); if (!tok) { spl_expr_result_t r = {-1, 0}; return r; } switch (tok->type) { case TOK_INT_LITERAL: return parse_int_literal(ctx); case TOK_FLOAT_LITERAL: return parse_float_literal(ctx); case TOK_CHAR_LITERAL: return parse_char_literal(ctx); case TOK_STRING_LITERAL: return parse_string_literal(ctx); case KW_TRUE: advance(ctx); emit_push_i32(&ctx->emit, 1); return (spl_expr_result_t){spl_type_basic(&ctx->tctx, SPL_I32), 0}; case KW_FALSE: advance(ctx); emit_push_i32(&ctx->emit, 0); return (spl_expr_result_t){spl_type_basic(&ctx->tctx, SPL_I32), 0}; case KW_NULL: advance(ctx); emit_push_ptr(&ctx->emit, 0); return (spl_expr_result_t){spl_type_basic(&ctx->tctx, SPL_PTR), 0}; case TOK_IDENT: case KW_BOOL: case KW_VOID: case KW_ANY: return parse_ident(ctx); case TOK_L_PAREN: return parse_group(ctx); case TOK_SUB: case TOK_NOT: case TOK_BIT_NOT: case TOK_AND: case TOK_MUL: return parse_prefix_op(ctx); case TOK_L_BRACKET: return parse_array_literal(ctx); case TOK_L_BRACE: return spl_parse_block_expr(ctx); case TOK_AT: { advance(ctx); skip_nl(ctx); tok = peek(ctx); if (!tok || tok->type != TOK_IDENT) { spl_comp_error(ctx, "expected builtin name after '@'"); spl_expr_result_t r = {-1, 0}; return r; } char bname[256]; spl_tok_copy_name(tok, bname, sizeof bname); advance(ctx); if (strcmp(bname, "dbg") == 0) { if (peek(ctx)->type == TOK_L_PAREN) { advance(ctx); int nargs = parse_call_args(ctx); expect(ctx, TOK_R_PAREN); for (int i = 0; i < nargs; i++) { emit_dbg_usize(&ctx->emit); emit_drop(&ctx->emit); } if (nargs == 0) emit_dbg_void(&ctx->emit); } else { emit_dbg_void(&ctx->emit); } return (spl_expr_result_t){spl_type_basic(&ctx->tctx, SPL_VOID), 0}; } else if (strcmp(bname, "sizeof") == 0) { expect(ctx, TOK_L_PAREN); int ti = spl_type_parse(&ctx->tctx, ctx); expect(ctx, TOK_R_PAREN); if (ti < 0) { spl_comp_error(ctx, "@sizeof: invalid type"); spl_expr_result_t r = {-1, 0}; return r; } usize sz = spl_type_size(&ctx->tctx, ti); emit_push_usize(&ctx->emit, sz); return (spl_expr_result_t){spl_type_basic(&ctx->tctx, SPL_USIZE), 0}; } else { spl_comp_error(ctx, "unknown builtin '@%s'", bname); spl_expr_result_t r = {-1, 0}; return r; } } default: if (peek(ctx)->type >= KW_AS && peek(ctx)->type <= KW_ANY) { return parse_ident(ctx); } spl_expr_result_t r = {-1, 0}; return r; } } static spl_expr_result_t parse_postfix_expr(spl_comp_t *ctx, spl_expr_result_t left) { if (left.type_idx < 0) return left; for (;;) { skip_nl(ctx); spl_tok_type_t opt = peek(ctx)->type; if (opt == TOK_DOT) { advance(ctx); spl_tok_t *field = advance(ctx); char fname[256]; spl_tok_copy_name(field, fname, sizeof(fname)); if (left.type_idx >= 0) { spl_expr_result_t mresult = {-1, 0}; if (spl_resolve_type_member(ctx, left.type_idx, fname, &mresult)) { left = mresult; continue; } } if (left.type_idx >= 0 && peek(ctx)->type == TOK_L_PAREN) { int methods_type_idx = left.type_idx; int is_ptr_self = 0; if (spl_type_kind(&ctx->tctx, methods_type_idx) == TYPE_PTR) { int elem_idx = spl_type_elem_type(&ctx->tctx, methods_type_idx); if (elem_idx >= 0 && (spl_type_kind(&ctx->tctx, elem_idx) == TYPE_STRUCT || spl_type_kind(&ctx->tctx, elem_idx) == TYPE_ENUM)) { is_ptr_self = 1; methods_type_idx = elem_idx; } } int found_method = 0; spl_type_item_vec_t *m_items = spl_type_items(&ctx->tctx, methods_type_idx); vec_for(*m_items, mi) { spl_type_item_t *mit = &vec_at(*m_items, mi); if (mit->item_kind != ITEM_METHOD) continue; if (strcmp(mit->name, fname) == 0) { spl_func_info_t *func = &vec_at(ctx->funcs, mit->method.func_idx); advance(ctx); found_method = 1; int nargs = 0; int is_instance = 0; if (func->nparams > 0 && func->param_type_indices && func->param_type_indices[0] >= 0 && spl_type_kind(&ctx->tctx, func->param_type_indices[0]) == TYPE_PTR && spl_type_elem_type(&ctx->tctx, func->param_type_indices[0]) == methods_type_idx) { 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); } emit_drop(&ctx->emit); nargs = 0; } { int *cp = func->param_type_indices; int cn = func->nparams; nargs += parse_call_args_checked(ctx, func->name, cp, cn); } expect(ctx, TOK_R_PAREN); emit_call_with_fixup(&ctx->emit, nargs, func->func_idx); left = (spl_expr_result_t){func->ret_type_idx, 0}; if (spl_type_needs_multi_slot(&ctx->tctx, func->ret_type_idx)) { usize nslots = spl_type_slot_count(&ctx->tctx, func->ret_type_idx); int temp_offset = fa_alloc_temp(&ctx->emit.frame, nslots); emit_frame_copy(&ctx->emit, temp_offset, nslots); emit_laddr(&ctx->emit, temp_offset); left.is_lvalue = 1; } break; } } if (found_method) continue; } if (left.type_idx >= 0 && spl_type_kind(&ctx->tctx, left.type_idx) == TYPE_STRUCT) { spl_type_item_vec_t *f_items = spl_type_items(&ctx->tctx, left.type_idx); vec_for(*f_items, fi) { spl_type_item_t *fit = &vec_at(*f_items, fi); if (fit->item_kind != ITEM_FIELD) continue; if (strcmp(fit->name, fname) == 0) { emit_ptr_add(&ctx->emit, fit->aggregate_field.offset); emit_load_or_addr_type(ctx, &left, fit->aggregate_field.type_idx); break; } } continue; } if (left.type_idx >= 0 && spl_type_kind(&ctx->tctx, left.type_idx) == TYPE_PTR) { int elem_idx = spl_type_elem_type(&ctx->tctx, left.type_idx); if (elem_idx >= 0 && spl_type_kind(&ctx->tctx, elem_idx) == TYPE_STRUCT) { if (left.is_lvalue) { emit_load_ptr(&ctx->emit); } spl_type_item_vec_t *st_items = spl_type_items(&ctx->tctx, elem_idx); vec_for(*st_items, si) { spl_type_item_t *sit = &vec_at(*st_items, si); if (sit->item_kind != ITEM_FIELD) continue; if (strcmp(sit->name, fname) == 0) { emit_ptr_add(&ctx->emit, sit->aggregate_field.offset); emit_load_or_addr_type(ctx, &left, sit->aggregate_field.type_idx); break; } } continue; } } if (left.type_idx >= 0 && spl_type_kind(&ctx->tctx, left.type_idx) == TYPE_SLICE) { if (strcmp(fname, "len") == 0) { if (ctx->addr_of_mode) { emit_push_u64(&ctx->emit, (spl_val_t)sizeof(spl_val_t)); emit_add_u64(&ctx->emit); left = (spl_expr_result_t){spl_type_basic(&ctx->tctx, SPL_USIZE), 1}; } else { emit_push_u64(&ctx->emit, (spl_val_t)sizeof(spl_val_t)); emit_add_u64(&ctx->emit); emit_load_usize(&ctx->emit); left = (spl_expr_result_t){spl_type_basic(&ctx->tctx, SPL_USIZE), 0}; } } else if (strcmp(fname, "ptr") == 0) { int sl_elem_idx = spl_type_elem_type(&ctx->tctx, left.type_idx); if (ctx->addr_of_mode) { left = (spl_expr_result_t){sl_elem_idx >= 0 ? spl_type_ptr(&ctx->tctx, sl_elem_idx) : spl_type_basic(&ctx->tctx, SPL_PTR), 1}; } else { emit_load_ptr(&ctx->emit); left = (spl_expr_result_t){sl_elem_idx >= 0 ? spl_type_ptr(&ctx->tctx, sl_elem_idx) : spl_type_basic(&ctx->tctx, SPL_PTR), 0}; } } continue; } if (strcmp(fname, "*") == 0 && left.type_idx >= 0 && spl_type_kind(&ctx->tctx, left.type_idx) == TYPE_PTR) { int elem_idx = spl_type_elem_type(&ctx->tctx, left.type_idx); if (elem_idx >= 0) { if (left.is_lvalue) { emit_load_ptr(&ctx->emit); } emit_load_or_addr_type(ctx, &left, elem_idx); } continue; } spl_comp_error(ctx, "unknown field '%s'", fname); continue; } if (opt == TOK_L_BRACKET) { advance(ctx); if (peek(ctx)->type == TOK_R_BRACKET) { advance(ctx); continue; } int saved_aom = ctx->addr_of_mode; ctx->addr_of_mode = 0; spl_parse_expr(ctx, PREC_MIN); ctx->addr_of_mode = saved_aom; if (peek(ctx)->type == TOK_RANGE) { advance(ctx); spl_expr_result_t end_expr = {-1, 0}; int has_explicit_end = (peek(ctx)->type != TOK_R_BRACKET); if (has_explicit_end) { int saved_aom = ctx->addr_of_mode; ctx->addr_of_mode = 0; end_expr = spl_parse_expr(ctx, PREC_MIN); ctx->addr_of_mode = saved_aom; } (void)end_expr; expect(ctx, TOK_R_BRACKET); if (!has_explicit_end) { if (left.type_idx >= 0 && spl_type_kind(&ctx->tctx, left.type_idx) == TYPE_ARRAY) { emit_push_u64(&ctx->emit, spl_type_array_len(&ctx->tctx, left.type_idx)); } else if (left.type_idx >= 0 && spl_type_kind(&ctx->tctx, left.type_idx) == TYPE_SLICE) { emit_pick(&ctx->emit, 1); emit_push_u64(&ctx->emit, sizeof(spl_val_t)); emit_add_u64(&ctx->emit); emit_load_usize(&ctx->emit); } } if (left.type_idx >= 0 && (spl_type_kind(&ctx->tctx, left.type_idx) == TYPE_ARRAY || spl_type_kind(&ctx->tctx, left.type_idx) == TYPE_SLICE)) { int rng_elem_idx = spl_type_elem_type(&ctx->tctx, left.type_idx); usize stride = spl_type_elem_stride(&ctx->tctx, rng_elem_idx); if (spl_type_kind(&ctx->tctx, left.type_idx) == TYPE_SLICE) { emit_pick(&ctx->emit, 2); emit_load_ptr(&ctx->emit); emit_pick(&ctx->emit, 2); emit_push_u64(&ctx->emit, stride); emit_mul_u64(&ctx->emit); emit_add_u64(&ctx->emit); emit_pick(&ctx->emit, 1); emit_pick(&ctx->emit, 3); emit_sub_usize(&ctx->emit); emit_rot(&ctx->emit); emit_drop(&ctx->emit); emit_rot(&ctx->emit); emit_drop(&ctx->emit); emit_rot(&ctx->emit); emit_drop(&ctx->emit); } else { emit_slice_create(ctx, stride); } } left = (spl_expr_result_t){ left.type_idx >= 0 ? spl_type_slice(&ctx->tctx, spl_type_elem_type(&ctx->tctx, left.type_idx)) : -1, 0}; continue; } expect(ctx, TOK_R_BRACKET); if (left.type_idx >= 0 && (spl_type_kind(&ctx->tctx, left.type_idx) == TYPE_ARRAY || spl_type_kind(&ctx->tctx, left.type_idx) == TYPE_PTR || spl_type_kind(&ctx->tctx, left.type_idx) == TYPE_SLICE)) { int idx_elem_idx = spl_type_elem_type(&ctx->tctx, left.type_idx); /* void* -> treat as u8* */ if (idx_elem_idx >= 0 && spl_type_kind(&ctx->tctx, idx_elem_idx) == TYPE_BASIC && spl_type_basic_type(&ctx->tctx, idx_elem_idx) == SPL_VOID) { idx_elem_idx = spl_type_basic(&ctx->tctx, SPL_U8); } if (spl_type_kind(&ctx->tctx, left.type_idx) == TYPE_SLICE) { emit_slice_index(ctx, idx_elem_idx); } else { if (spl_type_kind(&ctx->tctx, left.type_idx) == TYPE_PTR && left.is_lvalue) { emit_swap(&ctx->emit); emit_load_ptr(&ctx->emit); emit_swap(&ctx->emit); } usize stride = spl_type_elem_stride(&ctx->tctx, idx_elem_idx); emit_push_u64(&ctx->emit, stride); emit_mul_u64(&ctx->emit); emit_add_u64(&ctx->emit); } if (idx_elem_idx >= 0) emit_load_or_addr_type(ctx, &left, idx_elem_idx); } continue; } if (opt == KW_AS) { usize saved = ctx->tok_idx; int saved_err = ctx->has_error; char saved_msg[COMP_ERROR_MAX]; memcpy(saved_msg, ctx->error_msg, COMP_ERROR_MAX); advance(ctx); /* as */ skip_nl(ctx); int target_type_idx = spl_type_parse(&ctx->tctx, ctx); if (target_type_idx < 0) { ctx->tok_idx = saved; ctx->has_error = saved_err; memcpy(ctx->error_msg, saved_msg, COMP_ERROR_MAX); break; } usize src_sz = spl_type_size(&ctx->tctx, left.type_idx); usize dst_sz = spl_type_size(&ctx->tctx, target_type_idx); if (dst_sz > src_sz) { int src_signed = spl_type_is_integer(spl_type_basic_type(&ctx->tctx, left.type_idx)); uint16_t opc = src_signed ? SPL_SEXT : SPL_ZEXT; emit_raw(&ctx->emit, opc, 0, dst_sz * 8); } else if (dst_sz < src_sz) { emit_raw(&ctx->emit, SPL_TRUNC, 0, dst_sz * 8); } left = (spl_expr_result_t){target_type_idx, 0}; continue; } break; } return left; } 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_idx < 0) return left; while (1) { left = parse_postfix_expr(ctx, left); skip_nl(ctx); spl_tok_type_t opt = peek(ctx)->type; int prec = tok_prec(opt); if (prec == 0 || prec < min_prec) break; advance(ctx); left = parse_infix(ctx, left, opt); } return left; } static spl_expr_result_t parse_infix(spl_comp_t *ctx, spl_expr_result_t left, spl_tok_type_t op) { int prec = tok_prec(op); int next_prec = prec + 1; if (op == TOK_AND_AND) { spl_val_t bz_addr = emit_bz_here(&ctx->emit); spl_parse_expr(ctx, next_prec); spl_val_t jmp_addr = emit_jmp_here(&ctx->emit); emit_patch_here(&ctx->emit, bz_addr); emit_push_i32(&ctx->emit, 0); emit_patch_here(&ctx->emit, jmp_addr); return (spl_expr_result_t){spl_type_basic(&ctx->tctx, SPL_I32), 0}; } if (op == TOK_OR_OR) { spl_val_t bnz_addr = emit_bnz_here(&ctx->emit); spl_parse_expr(ctx, next_prec); spl_val_t jmp_addr = emit_jmp_here(&ctx->emit); emit_patch_here(&ctx->emit, bnz_addr); emit_push_i32(&ctx->emit, 1); emit_patch_here(&ctx->emit, jmp_addr); return (spl_expr_result_t){spl_type_basic(&ctx->tctx, SPL_I32), 0}; } if (op == TOK_ASSIGN || op == TOK_ASSIGN_ADD || op == TOK_ASSIGN_SUB || op == TOK_ASSIGN_MUL || op == TOK_ASSIGN_DIV || op == TOK_ASSIGN_MOD || op == TOK_ASSIGN_AND || op == TOK_ASSIGN_OR || op == TOK_ASSIGN_XOR || op == TOK_ASSIGN_L_SH || op == TOK_ASSIGN_R_SH) { 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); ctx->addr_of_mode = saved_addr_of_mode; if (left.is_lvalue) { spl_type_t bt = spl_type_emit_type(&ctx->tctx, left.type_idx); if (op == TOK_ASSIGN && left.type_idx >= 0 && !spl_type_is_scalar(&ctx->tctx, left.type_idx) && right.is_lvalue) { emit_copy_addr_to_addr(&ctx->emit, spl_type_slot_count(&ctx->tctx, left.type_idx), 0); } else if (op == TOK_ASSIGN) { emit_store_type(&ctx->emit, bt); } else { emit_pick(&ctx->emit, 1); emit_load_type(&ctx->emit, bt); emit_swap(&ctx->emit); int sop = binop_to_sir(assign_to_binop(op), bt); if (sop >= 0) emit_binop(&ctx->emit, sop, bt); emit_store_type(&ctx->emit, bt); } } return right; } spl_parse_expr(ctx, next_prec); if ((op == TOK_EQ || op == TOK_NEQ) && left.type_idx >= 0 && !spl_type_is_scalar(&ctx->tctx, left.type_idx)) { spl_comp_error(ctx, "type '%s' does not support comparison", spl_type_str(&ctx->tctx, left.type_idx)); } spl_type_t bt = spl_type_emit_type(&ctx->tctx, left.type_idx); int sop = binop_to_sir(op, bt); if (sop >= 0) { emit_binop(&ctx->emit, sop, bt); } int is_compare = (op == TOK_EQ || op == TOK_NEQ || op == TOK_LT || op == TOK_GT || op == TOK_LE || op == TOK_GE); int result_type_idx = is_compare ? spl_type_basic(&ctx->tctx, SPL_I32) : left.type_idx; return (spl_expr_result_t){result_type_idx, 0}; } int spl_emit_match_enum_cmp(spl_comp_t *ctx, int enum_type_idx, int val_offset, int by_value) { char vname[256]; if (peek(ctx)->type == TOK_DOT) { advance(ctx); } else { spl_tok_t *tok = advance(ctx); spl_tok_copy_name(tok, vname, sizeof(vname)); while (peek(ctx)->type == TOK_DOT) { int qt_idx = spl_type_resolve(&ctx->tctx, vname); (void)qt_idx; advance(ctx); tok = advance(ctx); spl_tok_copy_name(tok, vname, sizeof(vname)); } goto lookup; } { spl_tok_t *vtok = advance(ctx); spl_tok_copy_name(vtok, vname, sizeof(vname)); } lookup: { spl_type_item_vec_t *e_items = spl_type_items(&ctx->tctx, enum_type_idx); vec_for(*e_items, vi) { spl_type_item_t *vit = &vec_at(*e_items, vi); if (vit->item_kind != ITEM_VARIANT) continue; if (strcmp(vit->name, vname) == 0) { emit_laddr(&ctx->emit, val_offset); if (by_value) { emit_load_type(&ctx->emit, SPL_I32); } else { emit_load_ptr(&ctx->emit); emit_ptr_add(&ctx->emit, 0); emit_load_type(&ctx->emit, SPL_I32); } emit_push_i32(&ctx->emit, vit->enum_field.value); emit_binop(&ctx->emit, SPL_EQ, SPL_I32); return (int)vi; } } } spl_comp_error(ctx, "unknown variant '%s' in match", vname); return -1; } void spl_emit_match_value_cmp(spl_comp_t *ctx, int val_offset) { emit_laddr(&ctx->emit, val_offset); emit_load_ptr(&ctx->emit); spl_parse_expr(ctx, PREC_LOGOR); emit_binop(&ctx->emit, SPL_EQ, SPL_I32); }