1364 lines
49 KiB
C
1364 lines
49 KiB
C
#include "spl_comp.h"
|
|
#include "spl_lex_util.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
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);
|
|
}
|