Compare commits
3 Commits
a2d5dd9ce8
...
89d4bae8db
| Author | SHA1 | Date | |
|---|---|---|---|
| 89d4bae8db | |||
| 02095524b1 | |||
| da9dae734c |
@@ -13,7 +13,7 @@ typedef uintptr_t usize;
|
|||||||
typedef intptr_t isize;
|
typedef intptr_t isize;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
SPL_VOID = 0,
|
SPL_VOID,
|
||||||
SPL_I8,
|
SPL_I8,
|
||||||
SPL_U8,
|
SPL_U8,
|
||||||
SPL_I16,
|
SPL_I16,
|
||||||
|
|||||||
@@ -194,6 +194,14 @@ static spl_val_t vm_printf(int nargs, spl_val_t *args) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
switch (fmt[i]) {
|
switch (fmt[i]) {
|
||||||
|
case '.': {
|
||||||
|
Assert(i + 2 < fmt_len && arg_idx + 1 <= nargs && fmt[i + 1] == '*' &&
|
||||||
|
fmt[i + 2] == 's');
|
||||||
|
for (usize j = 0; j < args[arg_idx]; ++j) {
|
||||||
|
vec_push(buffer, ((const char *)args[arg_idx + 1])[j]);
|
||||||
|
}
|
||||||
|
i += 2;
|
||||||
|
} break;
|
||||||
case 'd':
|
case 'd':
|
||||||
snprintf(tmp_buf, sizeof(tmp_buf), "%zd", args[arg_idx]);
|
snprintf(tmp_buf, sizeof(tmp_buf), "%zd", args[arg_idx]);
|
||||||
for (usize j = 0; j < strlen(tmp_buf); ++j) {
|
for (usize j = 0; j < strlen(tmp_buf); ++j) {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ void spl_comp_init(spl_comp_t *ctx) {
|
|||||||
map_init(ctx->const_values, MAP_HASH_STR, MAP_CMP_STR);
|
map_init(ctx->const_values, MAP_HASH_STR, MAP_CMP_STR);
|
||||||
spl_prog_init(&ctx->prog);
|
spl_prog_init(&ctx->prog);
|
||||||
ctx->error_msg[0] = '\0';
|
ctx->error_msg[0] = '\0';
|
||||||
|
ctx->parse_context[0] = '\0';
|
||||||
spl_emit_init(&ctx->emit, &ctx->prog);
|
spl_emit_init(&ctx->emit, &ctx->prog);
|
||||||
spl_type_ctx_init(&ctx->tctx);
|
spl_type_ctx_init(&ctx->tctx);
|
||||||
ctx->current_ret_type_idx = -1;
|
ctx->current_ret_type_idx = -1;
|
||||||
@@ -67,10 +68,38 @@ void spl_comp_reset(spl_comp_t *ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void spl_comp_error(spl_comp_t *ctx, const char *fmt, ...) {
|
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_list args;
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
vsnprintf(ctx->error_msg, COMP_ERROR_MAX - 1, fmt, args);
|
vsnprintf(body, sizeof(body), fmt, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
||||||
|
if (ctx->error_line > 0)
|
||||||
|
snprintf(ctx->error_msg, COMP_ERROR_MAX - 1, "line %zu:%zu: %s", ctx->error_line,
|
||||||
|
ctx->error_col, body);
|
||||||
|
else
|
||||||
|
snprintf(ctx->error_msg, COMP_ERROR_MAX - 1, "%s", body);
|
||||||
|
ctx->has_error = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
vsnprintf(body, sizeof(body), fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
if (tok) {
|
||||||
|
snprintf(ctx->error_msg, COMP_ERROR_MAX - 1, "line %zu:%zu: %s", tok->line, tok->col, body);
|
||||||
|
} else {
|
||||||
|
snprintf(ctx->error_msg, COMP_ERROR_MAX - 1, "%s", body);
|
||||||
|
}
|
||||||
ctx->has_error = 1;
|
ctx->has_error = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -77,6 +77,8 @@ typedef struct spl_comp {
|
|||||||
/* Error state */
|
/* Error state */
|
||||||
char error_msg[COMP_ERROR_MAX];
|
char error_msg[COMP_ERROR_MAX];
|
||||||
int has_error;
|
int has_error;
|
||||||
|
usize error_line;
|
||||||
|
usize error_col;
|
||||||
|
|
||||||
/* Scopes */
|
/* Scopes */
|
||||||
spl_scope_vec_t scopes;
|
spl_scope_vec_t scopes;
|
||||||
@@ -91,6 +93,7 @@ typedef struct spl_comp {
|
|||||||
/* Current function context */
|
/* Current function context */
|
||||||
int current_func_idx;
|
int current_func_idx;
|
||||||
int current_ret_type_idx;
|
int current_ret_type_idx;
|
||||||
|
char parse_context[COMP_ERROR_MAX]; /* current parsing context for error messages */
|
||||||
|
|
||||||
/* Loop context for break/continue */
|
/* Loop context for break/continue */
|
||||||
int in_loop;
|
int in_loop;
|
||||||
@@ -124,6 +127,7 @@ void spl_comp_reset(spl_comp_t *ctx);
|
|||||||
|
|
||||||
/* Error reporting */
|
/* Error reporting */
|
||||||
void spl_comp_error(spl_comp_t *ctx, const char *fmt, ...);
|
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, ...);
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
* Parser functions (spl_parser.c)
|
* Parser functions (spl_parser.c)
|
||||||
|
|||||||
@@ -185,6 +185,9 @@ void emit_patch_call_fixups(spl_emit_t *e, spl_prog_t *prog) {
|
|||||||
if (fe->func_idx >= 0 && fe->func_idx < (int)vec_size(prog->funcs)) {
|
if (fe->func_idx >= 0 && fe->func_idx < (int)vec_size(prog->funcs)) {
|
||||||
spl_val_t addr = vec_at(prog->funcs, fe->func_idx).address;
|
spl_val_t addr = vec_at(prog->funcs, fe->func_idx).address;
|
||||||
vec_at(prog->insns, fe->insn_idx).imm = addr;
|
vec_at(prog->insns, fe->insn_idx).imm = addr;
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "WARN: fixup func_idx=%d out of bounds [0,%zu), insn_idx=%zu\n",
|
||||||
|
fe->func_idx, vec_size(prog->funcs), fe->insn_idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,7 +93,12 @@ static int spl_resolve_type_member(spl_comp_t *ctx, int type_idx, const char *fi
|
|||||||
continue;
|
continue;
|
||||||
if (strcmp(it->name, field) == 0) {
|
if (strcmp(it->name, field) == 0) {
|
||||||
emit_push_i32(&ctx->emit, it->enum_field.value);
|
emit_push_i32(&ctx->emit, it->enum_field.value);
|
||||||
|
int data_type = it->enum_field.type_idx;
|
||||||
|
if (data_type >= 0) {
|
||||||
|
*result = (spl_expr_result_t){data_type, 0};
|
||||||
|
} else {
|
||||||
*result = (spl_expr_result_t){type_idx, 0};
|
*result = (spl_expr_result_t){type_idx, 0};
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -278,14 +283,17 @@ static spl_expr_result_t parse_array_literal(spl_comp_t *ctx) {
|
|||||||
|
|
||||||
int len_val;
|
int len_val;
|
||||||
if (!spl_parse_int_literal(ctx, &len_val)) {
|
if (!spl_parse_int_literal(ctx, &len_val)) {
|
||||||
spl_comp_error(ctx, "expected array length");
|
spl_comp_err_tok(ctx, peek(ctx), "expected array length");
|
||||||
spl_expr_result_t r = {-1, 0};
|
spl_expr_result_t r = {-1, 0};
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
usize len = (usize)len_val;
|
usize len = (usize)len_val;
|
||||||
|
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
expect(ctx, TOK_R_BRACKET);
|
if (!expect(ctx, TOK_R_BRACKET)) {
|
||||||
|
spl_expr_result_t r = {-1, 0};
|
||||||
|
return r;
|
||||||
|
}
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
|
|
||||||
int elem_type_idx = spl_type_parse(&ctx->tctx, ctx);
|
int elem_type_idx = spl_type_parse(&ctx->tctx, ctx);
|
||||||
@@ -295,7 +303,10 @@ static spl_expr_result_t parse_array_literal(spl_comp_t *ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
expect(ctx, TOK_L_BRACE);
|
if (!expect(ctx, TOK_L_BRACE)) {
|
||||||
|
spl_expr_result_t _r = {-1, 0};
|
||||||
|
return _r;
|
||||||
|
}
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
|
|
||||||
for (usize i = 0; i < len; i++) {
|
for (usize i = 0; i < len; i++) {
|
||||||
@@ -311,7 +322,10 @@ static spl_expr_result_t parse_array_literal(spl_comp_t *ctx) {
|
|||||||
advance(ctx);
|
advance(ctx);
|
||||||
|
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
expect(ctx, TOK_R_BRACE);
|
if (!expect(ctx, TOK_R_BRACE)) {
|
||||||
|
spl_expr_result_t _r = {-1, 0};
|
||||||
|
return _r;
|
||||||
|
}
|
||||||
|
|
||||||
int arr_type_idx = spl_type_array(&ctx->tctx, elem_type_idx, len);
|
int arr_type_idx = spl_type_array(&ctx->tctx, elem_type_idx, len);
|
||||||
return (spl_expr_result_t){arr_type_idx, 0};
|
return (spl_expr_result_t){arr_type_idx, 0};
|
||||||
@@ -320,7 +334,8 @@ static spl_expr_result_t parse_array_literal(spl_comp_t *ctx) {
|
|||||||
static void parse_slice_inline(spl_comp_t *ctx) {
|
static void parse_slice_inline(spl_comp_t *ctx) {
|
||||||
advance(ctx);
|
advance(ctx);
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
|
skip_nl(ctx);
|
||||||
|
while (!ctx->has_error && peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
|
||||||
if (peek(ctx)->type == TOK_COMMA) {
|
if (peek(ctx)->type == TOK_COMMA) {
|
||||||
advance(ctx);
|
advance(ctx);
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
@@ -347,7 +362,8 @@ static void parse_slice_inline(spl_comp_t *ctx) {
|
|||||||
}
|
}
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
}
|
}
|
||||||
expect(ctx, TOK_R_BRACE);
|
if (!expect(ctx, TOK_R_BRACE))
|
||||||
|
return;
|
||||||
emit_drop(&ctx->emit);
|
emit_drop(&ctx->emit);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,11 +379,13 @@ static void parse_one_field_init(spl_comp_t *ctx, spl_type_item_vec_t *items, in
|
|||||||
advance(ctx);
|
advance(ctx);
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
|
|
||||||
|
int found = 0;
|
||||||
vec_for(*items, fi) {
|
vec_for(*items, fi) {
|
||||||
spl_type_item_t *it = &vec_at(*items, fi);
|
spl_type_item_t *it = &vec_at(*items, fi);
|
||||||
if (it->item_kind != ITEM_FIELD)
|
if (it->item_kind != ITEM_FIELD)
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(it->name, fname) == 0) {
|
if (strcmp(it->name, fname) == 0) {
|
||||||
|
found = 1;
|
||||||
emit_laddr(&ctx->emit, base_offset);
|
emit_laddr(&ctx->emit, base_offset);
|
||||||
usize byte_off = extra_offset + it->aggregate_field.offset;
|
usize byte_off = extra_offset + it->aggregate_field.offset;
|
||||||
emit_ptr_add(&ctx->emit, byte_off);
|
emit_ptr_add(&ctx->emit, byte_off);
|
||||||
@@ -381,16 +399,17 @@ static void parse_one_field_init(spl_comp_t *ctx, spl_type_item_vec_t *items, in
|
|||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
int len_val;
|
int len_val;
|
||||||
if (!spl_parse_int_literal(ctx, &len_val)) {
|
if (!spl_parse_int_literal(ctx, &len_val)) {
|
||||||
spl_comp_error(ctx, "expected array length");
|
spl_comp_err_tok(ctx, peek(ctx), "expected array length");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
usize arr_len = (usize)len_val;
|
usize arr_len = (usize)len_val;
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
expect(ctx, TOK_R_BRACKET);
|
if (!expect(ctx, TOK_R_BRACKET))
|
||||||
|
return;
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
int elem_type_idx = spl_type_parse(&ctx->tctx, ctx);
|
int elem_type_idx = spl_type_elem_type(&ctx->tctx, ft_idx);
|
||||||
skip_nl(ctx);
|
if (!expect(ctx, TOK_L_BRACE))
|
||||||
expect(ctx, TOK_L_BRACE);
|
return;
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
usize stride = spl_type_elem_stride(&ctx->tctx, elem_type_idx);
|
usize stride = spl_type_elem_stride(&ctx->tctx, elem_type_idx);
|
||||||
spl_type_t st = spl_type_emit_type(&ctx->tctx, elem_type_idx);
|
spl_type_t st = spl_type_emit_type(&ctx->tctx, elem_type_idx);
|
||||||
@@ -417,7 +436,8 @@ static void parse_one_field_init(spl_comp_t *ctx, spl_type_item_vec_t *items, in
|
|||||||
if (peek(ctx)->type == TOK_COMMA)
|
if (peek(ctx)->type == TOK_COMMA)
|
||||||
advance(ctx);
|
advance(ctx);
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
expect(ctx, TOK_R_BRACE);
|
if (!expect(ctx, TOK_R_BRACE))
|
||||||
|
return;
|
||||||
emit_drop(&ctx->emit);
|
emit_drop(&ctx->emit);
|
||||||
} else if (ft_idx >= 0 &&
|
} else if (ft_idx >= 0 &&
|
||||||
(spl_type_kind(&ctx->tctx, ft_idx) == TYPE_STRUCT ||
|
(spl_type_kind(&ctx->tctx, ft_idx) == TYPE_STRUCT ||
|
||||||
@@ -444,6 +464,9 @@ static void parse_one_field_init(spl_comp_t *ctx, spl_type_item_vec_t *items, in
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!found) {
|
||||||
|
spl_comp_err_tok(ctx, ftok, "unknown field '%s' in struct literal", fname);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, int type_idx) {
|
spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, int type_idx) {
|
||||||
@@ -454,7 +477,7 @@ spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, int type_idx) {
|
|||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
|
|
||||||
if (spl_type_kind(&ctx->tctx, type_idx) == TYPE_STRUCT) {
|
if (spl_type_kind(&ctx->tctx, type_idx) == TYPE_STRUCT) {
|
||||||
while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
|
while (!ctx->has_error && peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
|
||||||
if (peek(ctx)->type == TOK_COMMA) {
|
if (peek(ctx)->type == TOK_COMMA) {
|
||||||
advance(ctx);
|
advance(ctx);
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
@@ -470,13 +493,7 @@ spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, int type_idx) {
|
|||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
}
|
}
|
||||||
} else if (spl_type_kind(&ctx->tctx, type_idx) == TYPE_SLICE) {
|
} else if (spl_type_kind(&ctx->tctx, type_idx) == TYPE_SLICE) {
|
||||||
while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
|
while (!ctx->has_error && 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);
|
advance(ctx);
|
||||||
spl_tok_t *ftok = advance(ctx);
|
spl_tok_t *ftok = advance(ctx);
|
||||||
char fname[256];
|
char fname[256];
|
||||||
@@ -535,7 +552,8 @@ spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, int type_idx) {
|
|||||||
peek(ctx)->type == TOK_L_BRACE) {
|
peek(ctx)->type == TOK_L_BRACE) {
|
||||||
advance(ctx);
|
advance(ctx);
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
|
while (!ctx->has_error && peek(ctx)->type != TOK_R_BRACE &&
|
||||||
|
peek(ctx)->type != TOK_EOF) {
|
||||||
if (peek(ctx)->type == TOK_COMMA) {
|
if (peek(ctx)->type == TOK_COMMA) {
|
||||||
advance(ctx);
|
advance(ctx);
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
@@ -547,7 +565,10 @@ spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, int type_idx) {
|
|||||||
base_offset, DATA_OFFSET);
|
base_offset, DATA_OFFSET);
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
}
|
}
|
||||||
expect(ctx, TOK_R_BRACE);
|
if (!expect(ctx, TOK_R_BRACE)) {
|
||||||
|
spl_expr_result_t _r = {-1, 0};
|
||||||
|
return _r;
|
||||||
|
}
|
||||||
} else if (dt_idx >= 0 &&
|
} else if (dt_idx >= 0 &&
|
||||||
(spl_type_kind(&ctx->tctx, dt_idx) == TYPE_STRUCT ||
|
(spl_type_kind(&ctx->tctx, dt_idx) == TYPE_STRUCT ||
|
||||||
spl_type_kind(&ctx->tctx, dt_idx) == TYPE_ENUM) &&
|
spl_type_kind(&ctx->tctx, dt_idx) == TYPE_ENUM) &&
|
||||||
@@ -577,11 +598,14 @@ spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, int type_idx) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!found)
|
if (!found)
|
||||||
spl_comp_error(ctx, "unknown enum variant '%s'", vname);
|
spl_comp_err_tok(ctx, vtok, "unknown enum variant '%s'", vname);
|
||||||
}
|
}
|
||||||
|
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
expect(ctx, TOK_R_BRACE);
|
if (!expect(ctx, TOK_R_BRACE)) {
|
||||||
|
spl_expr_result_t _r = {-1, 0};
|
||||||
|
return _r;
|
||||||
|
}
|
||||||
|
|
||||||
if (sz <= sizeof(spl_val_t)) {
|
if (sz <= sizeof(spl_val_t)) {
|
||||||
emit_laddr(&ctx->emit, base_offset);
|
emit_laddr(&ctx->emit, base_offset);
|
||||||
@@ -596,7 +620,7 @@ spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, int type_idx) {
|
|||||||
static int parse_call_args(spl_comp_t *ctx) {
|
static int parse_call_args(spl_comp_t *ctx) {
|
||||||
int nargs = 0;
|
int nargs = 0;
|
||||||
if (peek(ctx)->type != TOK_R_PAREN) {
|
if (peek(ctx)->type != TOK_R_PAREN) {
|
||||||
for (;;) {
|
while (!ctx->has_error) {
|
||||||
spl_parse_expr(ctx, PREC_MIN);
|
spl_parse_expr(ctx, PREC_MIN);
|
||||||
nargs++;
|
nargs++;
|
||||||
if (peek(ctx)->type == TOK_COMMA) {
|
if (peek(ctx)->type == TOK_COMMA) {
|
||||||
@@ -609,24 +633,107 @@ static int parse_call_args(spl_comp_t *ctx) {
|
|||||||
return nargs;
|
return nargs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int types_equal(spl_type_ctx_t *tctx, int a, int b) {
|
||||||
|
if (a < 0 || b < 0)
|
||||||
|
return 0;
|
||||||
|
a = spl_type_resolve_underlying(tctx, a);
|
||||||
|
b = spl_type_resolve_underlying(tctx, b);
|
||||||
|
if (a == b)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
spl_type_kind_t ak = spl_type_kind(tctx, a);
|
||||||
|
spl_type_kind_t bk = spl_type_kind(tctx, b);
|
||||||
|
if (ak != bk)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
switch (ak) {
|
||||||
|
case TYPE_VOID:
|
||||||
|
return 1;
|
||||||
|
case TYPE_BASIC:
|
||||||
|
return spl_type_basic_type(tctx, a) == spl_type_basic_type(tctx, b);
|
||||||
|
case TYPE_PTR:
|
||||||
|
return types_equal(tctx, spl_type_elem_type(tctx, a), spl_type_elem_type(tctx, b));
|
||||||
|
case TYPE_ARRAY:
|
||||||
|
return spl_type_array_len(tctx, a) == spl_type_array_len(tctx, b) &&
|
||||||
|
types_equal(tctx, spl_type_elem_type(tctx, a), spl_type_elem_type(tctx, b));
|
||||||
|
case TYPE_SLICE:
|
||||||
|
return types_equal(tctx, spl_type_elem_type(tctx, a), spl_type_elem_type(tctx, b));
|
||||||
|
case TYPE_STRUCT:
|
||||||
|
case TYPE_UNION:
|
||||||
|
case TYPE_ENUM: {
|
||||||
|
const char *an = spl_type_name(tctx, a);
|
||||||
|
const char *bn = spl_type_name(tctx, b);
|
||||||
|
return an && bn && strcmp(an, bn) == 0;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void spl_check_arg_type(spl_comp_t *ctx, const char *fname, int arg_type_idx,
|
static void spl_check_arg_type(spl_comp_t *ctx, const char *fname, int arg_type_idx,
|
||||||
int param_type_idx, int arg_idx) {
|
int param_type_idx, int arg_idx) {
|
||||||
if (arg_type_idx < 0 || param_type_idx < 0)
|
if (arg_type_idx < 0 || param_type_idx < 0)
|
||||||
return;
|
return;
|
||||||
if (spl_type_kind(&ctx->tctx, param_type_idx) == TYPE_PTR &&
|
|
||||||
spl_type_elem_type(&ctx->tctx, param_type_idx) >= 0 &&
|
/* Level 1: structurally equal types */
|
||||||
spl_type_kind(&ctx->tctx, arg_type_idx) != TYPE_PTR &&
|
if (types_equal(&ctx->tctx, arg_type_idx, param_type_idx))
|
||||||
spl_type_name(&ctx->tctx, arg_type_idx) &&
|
return;
|
||||||
spl_type_name(&ctx->tctx, spl_type_elem_type(&ctx->tctx, param_type_idx)) &&
|
|
||||||
strcmp(spl_type_name(&ctx->tctx, arg_type_idx),
|
int arg_u = spl_type_resolve_underlying(&ctx->tctx, arg_type_idx);
|
||||||
spl_type_name(&ctx->tctx, spl_type_elem_type(&ctx->tctx, param_type_idx))) == 0) {
|
int param_u = spl_type_resolve_underlying(&ctx->tctx, param_type_idx);
|
||||||
|
|
||||||
|
/* Level 1b: void pointer (*_) accepts any pointer */
|
||||||
|
if (spl_type_kind(&ctx->tctx, param_u) == TYPE_PTR) {
|
||||||
|
int param_elem = spl_type_elem_type(&ctx->tctx, param_u);
|
||||||
|
int param_elem_u = spl_type_resolve_underlying(&ctx->tctx, param_elem);
|
||||||
|
if (param_elem_u >= 0 && spl_type_kind(&ctx->tctx, param_elem_u) == TYPE_BASIC &&
|
||||||
|
spl_type_basic_type(&ctx->tctx, param_elem_u) == SPL_VOID &&
|
||||||
|
spl_type_kind(&ctx->tctx, arg_u) == TYPE_PTR) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Level 2: pointer indirection mismatch with same inner type */
|
||||||
|
if (spl_type_kind(&ctx->tctx, param_u) == TYPE_PTR) {
|
||||||
|
int param_elem = spl_type_elem_type(&ctx->tctx, param_u);
|
||||||
|
if (param_elem < 0)
|
||||||
|
goto type_mismatch;
|
||||||
|
|
||||||
|
/* 2a: param = *T, arg = T (missing '&') */
|
||||||
|
if (spl_type_kind(&ctx->tctx, arg_u) != TYPE_PTR) {
|
||||||
|
if (types_equal(&ctx->tctx, arg_type_idx, param_elem)) {
|
||||||
|
const char *is = spl_type_str(&ctx->tctx, param_elem);
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"%s: warning: argument %d of '%s' expects '%s*', "
|
"%s: warning: argument %d of '%s' expects '%s*', "
|
||||||
"got '%s' (missing '&'?)\n",
|
"got '%s' (missing '&'?)\n",
|
||||||
ctx->fname, arg_idx + 1, fname,
|
ctx->fname, arg_idx + 1, fname, is, is);
|
||||||
spl_type_name(&ctx->tctx, spl_type_elem_type(&ctx->tctx, param_type_idx)),
|
return;
|
||||||
spl_type_name(&ctx->tctx, arg_type_idx));
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 2b: param = *T, arg = **T (extra '&') */
|
||||||
|
if (spl_type_kind(&ctx->tctx, arg_u) == TYPE_PTR) {
|
||||||
|
int arg_elem = spl_type_elem_type(&ctx->tctx, arg_u);
|
||||||
|
int arg_elem_u = spl_type_resolve_underlying(&ctx->tctx, arg_elem);
|
||||||
|
if (spl_type_kind(&ctx->tctx, arg_elem_u) == TYPE_PTR) {
|
||||||
|
if (types_equal(&ctx->tctx, spl_type_elem_type(&ctx->tctx, arg_elem), param_elem)) {
|
||||||
|
const char *is = spl_type_str(&ctx->tctx, param_elem);
|
||||||
|
fprintf(stderr,
|
||||||
|
"%s: warning: argument %d of '%s' expects '%s*', "
|
||||||
|
"got '%s**' (extra '&'?)\n",
|
||||||
|
ctx->fname, arg_idx + 1, fname, is, is);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Level 3: general type mismatch → error */
|
||||||
|
type_mismatch:;
|
||||||
|
const char *as = spl_type_str(&ctx->tctx, arg_type_idx);
|
||||||
|
const char *ps = spl_type_str(&ctx->tctx, param_type_idx);
|
||||||
|
spl_comp_err_tok(ctx, peek(ctx), "argument %d of '%s' type mismatch: expected '%s', got '%s'",
|
||||||
|
arg_idx + 1, fname, ps, as);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_call_args_checked(spl_comp_t *ctx, const char *fname, int *param_type_indices,
|
static int parse_call_args_checked(spl_comp_t *ctx, const char *fname, int *param_type_indices,
|
||||||
@@ -634,11 +741,20 @@ static int parse_call_args_checked(spl_comp_t *ctx, const char *fname, int *para
|
|||||||
int nargs = 0;
|
int nargs = 0;
|
||||||
int nlogical = 0;
|
int nlogical = 0;
|
||||||
if (peek(ctx)->type != TOK_R_PAREN) {
|
if (peek(ctx)->type != TOK_R_PAREN) {
|
||||||
for (;;) {
|
while (!ctx->has_error) {
|
||||||
spl_expr_result_t arg = spl_parse_expr(ctx, PREC_MIN);
|
spl_expr_result_t arg = spl_parse_expr(ctx, PREC_MIN);
|
||||||
if (param_type_indices && nlogical < nparams && param_type_indices[nlogical] >= 0)
|
if (param_type_indices && nlogical < nparams && param_type_indices[nlogical] >= 0) {
|
||||||
|
int param_u = spl_type_resolve_underlying(&ctx->tctx, param_type_indices[nlogical]);
|
||||||
|
int arg_u = spl_type_resolve_underlying(&ctx->tctx, arg.type_idx);
|
||||||
|
if (arg.type_idx >= 0 && spl_type_kind(&ctx->tctx, param_u) == TYPE_BASIC &&
|
||||||
|
spl_type_is_integer(spl_type_basic_type(&ctx->tctx, param_u)) &&
|
||||||
|
spl_type_kind(&ctx->tctx, arg_u) == TYPE_BASIC &&
|
||||||
|
spl_type_is_integer(spl_type_basic_type(&ctx->tctx, arg_u))) {
|
||||||
|
arg.type_idx = param_type_indices[nlogical];
|
||||||
|
}
|
||||||
spl_check_arg_type(ctx, fname, arg.type_idx, param_type_indices[nlogical],
|
spl_check_arg_type(ctx, fname, arg.type_idx, param_type_indices[nlogical],
|
||||||
nlogical);
|
nlogical);
|
||||||
|
}
|
||||||
|
|
||||||
int arg_slots = 1;
|
int arg_slots = 1;
|
||||||
if (param_type_indices && nlogical < nparams && param_type_indices[nlogical] >= 0 &&
|
if (param_type_indices && nlogical < nparams && param_type_indices[nlogical] >= 0 &&
|
||||||
@@ -678,7 +794,17 @@ static spl_expr_result_t parse_ident(spl_comp_t *ctx) {
|
|||||||
if (peek(ctx)->type == TOK_L_PAREN) {
|
if (peek(ctx)->type == TOK_L_PAREN) {
|
||||||
int fi = spl_lookup_func(ctx, name);
|
int fi = spl_lookup_func(ctx, name);
|
||||||
if (fi < 0) {
|
if (fi < 0) {
|
||||||
spl_comp_error(ctx, "unknown function '%s'", name);
|
/* 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};
|
spl_expr_result_t r = {-1, 0};
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
@@ -686,12 +812,28 @@ static spl_expr_result_t parse_ident(spl_comp_t *ctx) {
|
|||||||
advance(ctx);
|
advance(ctx);
|
||||||
|
|
||||||
int nargs = parse_call_args_checked(ctx, f->name, f->param_type_indices, f->nparams);
|
int nargs = parse_call_args_checked(ctx, f->name, f->param_type_indices, f->nparams);
|
||||||
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;
|
||||||
|
}
|
||||||
|
|
||||||
if (f->is_extern) {
|
if (f->is_extern) {
|
||||||
int nidx = spl_ensure_native(ctx, f->name);
|
int nidx = spl_ensure_native(ctx, f->name);
|
||||||
emit_push_i32(&ctx->emit, nidx);
|
emit_push_i32(&ctx->emit, nidx);
|
||||||
emit_ncall(&ctx->emit, nargs);
|
emit_ncall(&ctx->emit, nargs);
|
||||||
|
if (spl_type_kind(&ctx->tctx, f->ret_type_idx) == TYPE_BASIC &&
|
||||||
|
spl_type_basic_type(&ctx->tctx, f->ret_type_idx) == SPL_VOID) {
|
||||||
|
emit_drop(&ctx->emit);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
emit_call_with_fixup(&ctx->emit, nargs, f->func_idx);
|
emit_call_with_fixup(&ctx->emit, nargs, f->func_idx);
|
||||||
}
|
}
|
||||||
@@ -722,10 +864,30 @@ static spl_expr_result_t parse_ident(spl_comp_t *ctx) {
|
|||||||
emit_laddr(&ctx->emit, v->offset);
|
emit_laddr(&ctx->emit, v->offset);
|
||||||
|
|
||||||
spl_expr_result_t r;
|
spl_expr_result_t r;
|
||||||
|
spl_tok_type_t next_type = peek(ctx)->type;
|
||||||
|
if (next_type == TOK_DOT) {
|
||||||
|
r = (spl_expr_result_t){vt_idx, 1};
|
||||||
|
} else {
|
||||||
emit_load_or_addr_type(ctx, &r, vt_idx);
|
emit_load_or_addr_type(ctx, &r, vt_idx);
|
||||||
|
}
|
||||||
return r;
|
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);
|
int ttype_idx = spl_type_resolve(&ctx->tctx, name);
|
||||||
if (ttype_idx >= 0) {
|
if (ttype_idx >= 0) {
|
||||||
if (peek(ctx)->type == TOK_L_BRACE &&
|
if (peek(ctx)->type == TOK_L_BRACE &&
|
||||||
@@ -737,7 +899,7 @@ static spl_expr_result_t parse_ident(spl_comp_t *ctx) {
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
spl_comp_error(ctx, "undefined variable '%s'", name);
|
spl_comp_err_tok(ctx, t, "undefined variable '%s'", name);
|
||||||
spl_expr_result_t r = {-1, 0};
|
spl_expr_result_t r = {-1, 0};
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
@@ -745,7 +907,10 @@ static spl_expr_result_t parse_ident(spl_comp_t *ctx) {
|
|||||||
static spl_expr_result_t parse_group(spl_comp_t *ctx) {
|
static spl_expr_result_t parse_group(spl_comp_t *ctx) {
|
||||||
advance(ctx);
|
advance(ctx);
|
||||||
spl_expr_result_t r = spl_parse_expr(ctx, PREC_MIN);
|
spl_expr_result_t r = spl_parse_expr(ctx, PREC_MIN);
|
||||||
expect(ctx, TOK_R_PAREN);
|
if (!ctx->has_error) {
|
||||||
|
if (!expect(ctx, TOK_R_PAREN))
|
||||||
|
return r;
|
||||||
|
}
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -781,8 +946,13 @@ 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));
|
emit_binop(&ctx->emit, SPL_NOT, spl_type_emit_type(&ctx->tctx, right.type_idx));
|
||||||
break;
|
break;
|
||||||
case TOK_AND:
|
case TOK_AND:
|
||||||
|
if (right.type_idx >= 0 && spl_type_kind(&ctx->tctx, right.type_idx) == TYPE_FN) {
|
||||||
|
return right;
|
||||||
|
}
|
||||||
if (!right.is_lvalue) {
|
if (!right.is_lvalue) {
|
||||||
spl_comp_error(ctx, "cannot take address of rvalue");
|
spl_comp_err_tok(ctx, peek(ctx), "cannot take address of rvalue");
|
||||||
|
spl_expr_result_t _r = {-1, 0};
|
||||||
|
return _r;
|
||||||
}
|
}
|
||||||
right.type_idx = spl_type_ptr(&ctx->tctx, right.type_idx);
|
right.type_idx = spl_type_ptr(&ctx->tctx, right.type_idx);
|
||||||
break;
|
break;
|
||||||
@@ -852,7 +1022,7 @@ static spl_expr_result_t parse_primary_expr(spl_comp_t *ctx) {
|
|||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
tok = peek(ctx);
|
tok = peek(ctx);
|
||||||
if (!tok || tok->type != TOK_IDENT) {
|
if (!tok || tok->type != TOK_IDENT) {
|
||||||
spl_comp_error(ctx, "expected builtin name after '@'");
|
spl_comp_err_tok(ctx, tok, "expected builtin name after '@'");
|
||||||
spl_expr_result_t r = {-1, 0};
|
spl_expr_result_t r = {-1, 0};
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
@@ -864,7 +1034,15 @@ static spl_expr_result_t parse_primary_expr(spl_comp_t *ctx) {
|
|||||||
if (peek(ctx)->type == TOK_L_PAREN) {
|
if (peek(ctx)->type == TOK_L_PAREN) {
|
||||||
advance(ctx);
|
advance(ctx);
|
||||||
int nargs = parse_call_args(ctx);
|
int nargs = parse_call_args(ctx);
|
||||||
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;
|
||||||
|
}
|
||||||
for (int i = 0; i < nargs; i++) {
|
for (int i = 0; i < nargs; i++) {
|
||||||
emit_dbg_usize(&ctx->emit);
|
emit_dbg_usize(&ctx->emit);
|
||||||
emit_drop(&ctx->emit);
|
emit_drop(&ctx->emit);
|
||||||
@@ -876,11 +1054,22 @@ static spl_expr_result_t parse_primary_expr(spl_comp_t *ctx) {
|
|||||||
}
|
}
|
||||||
return (spl_expr_result_t){spl_type_basic(&ctx->tctx, SPL_VOID), 0};
|
return (spl_expr_result_t){spl_type_basic(&ctx->tctx, SPL_VOID), 0};
|
||||||
} else if (strcmp(bname, "sizeof") == 0) {
|
} else if (strcmp(bname, "sizeof") == 0) {
|
||||||
expect(ctx, TOK_L_PAREN);
|
if (!expect(ctx, TOK_L_PAREN)) {
|
||||||
|
spl_expr_result_t _r = {-1, 0};
|
||||||
|
return _r;
|
||||||
|
}
|
||||||
int ti = spl_type_parse(&ctx->tctx, ctx);
|
int ti = spl_type_parse(&ctx->tctx, ctx);
|
||||||
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;
|
||||||
|
}
|
||||||
if (ti < 0) {
|
if (ti < 0) {
|
||||||
spl_comp_error(ctx, "@sizeof: invalid type");
|
spl_comp_err_tok(ctx, peek(ctx), "@sizeof: invalid type");
|
||||||
spl_expr_result_t r = {-1, 0};
|
spl_expr_result_t r = {-1, 0};
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
@@ -888,13 +1077,13 @@ static spl_expr_result_t parse_primary_expr(spl_comp_t *ctx) {
|
|||||||
emit_push_usize(&ctx->emit, sz);
|
emit_push_usize(&ctx->emit, sz);
|
||||||
return (spl_expr_result_t){spl_type_basic(&ctx->tctx, SPL_USIZE), 0};
|
return (spl_expr_result_t){spl_type_basic(&ctx->tctx, SPL_USIZE), 0};
|
||||||
} else {
|
} else {
|
||||||
spl_comp_error(ctx, "unknown builtin '@%s'", bname);
|
spl_comp_err_tok(ctx, peek(ctx), "unknown builtin '@%s'", bname);
|
||||||
spl_expr_result_t r = {-1, 0};
|
spl_expr_result_t r = {-1, 0};
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
if (peek(ctx)->type >= KW_AS && peek(ctx)->type <= KW_ANY) {
|
if (peek(ctx)->type > KW_AS && peek(ctx)->type <= KW_ANY) {
|
||||||
return parse_ident(ctx);
|
return parse_ident(ctx);
|
||||||
}
|
}
|
||||||
spl_expr_result_t r = {-1, 0};
|
spl_expr_result_t r = {-1, 0};
|
||||||
@@ -907,6 +1096,8 @@ static spl_expr_result_t parse_postfix_expr(spl_comp_t *ctx, spl_expr_result_t l
|
|||||||
return left;
|
return left;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
if (ctx->has_error)
|
||||||
|
break;
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
spl_tok_type_t opt = peek(ctx)->type;
|
spl_tok_type_t opt = peek(ctx)->type;
|
||||||
|
|
||||||
@@ -961,20 +1152,43 @@ static spl_expr_result_t parse_postfix_expr(spl_comp_t *ctx, spl_expr_result_t l
|
|||||||
|
|
||||||
if (is_instance) {
|
if (is_instance) {
|
||||||
if (!left.is_lvalue && !is_ptr_self) {
|
if (!left.is_lvalue && !is_ptr_self) {
|
||||||
spl_comp_error(ctx, "cannot call instance method '%s' on type",
|
spl_comp_err_tok(ctx, field,
|
||||||
fname);
|
"cannot call instance method '%s' on type", fname);
|
||||||
|
return left;
|
||||||
}
|
}
|
||||||
emit_drop(&ctx->emit);
|
emit_drop(&ctx->emit);
|
||||||
nargs = 0;
|
nargs = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
int *cp = func->param_type_indices;
|
int full_np = func->nparams;
|
||||||
int cn = func->nparams;
|
nargs += parse_call_args_checked(ctx, func->name,
|
||||||
nargs += parse_call_args_checked(ctx, func->name, cp, cn);
|
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 (!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;
|
||||||
}
|
}
|
||||||
expect(ctx, TOK_R_PAREN);
|
|
||||||
|
|
||||||
|
if (func->func_idx < 0) {
|
||||||
|
fprintf(stderr, "WARN: method call '%s.' with invalid func_idx=%d\n",
|
||||||
|
func->name, func->func_idx);
|
||||||
|
}
|
||||||
emit_call_with_fixup(&ctx->emit, nargs, func->func_idx);
|
emit_call_with_fixup(&ctx->emit, nargs, func->func_idx);
|
||||||
|
|
||||||
left = (spl_expr_result_t){func->ret_type_idx, 0};
|
left = (spl_expr_result_t){func->ret_type_idx, 0};
|
||||||
@@ -992,10 +1206,53 @@ static spl_expr_result_t parse_postfix_expr(spl_comp_t *ctx, spl_expr_result_t l
|
|||||||
}
|
}
|
||||||
if (found_method)
|
if (found_method)
|
||||||
continue;
|
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) {
|
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);
|
spl_type_item_vec_t *f_items = spl_type_items(&ctx->tctx, left.type_idx);
|
||||||
|
int found = 0;
|
||||||
vec_for(*f_items, fi) {
|
vec_for(*f_items, fi) {
|
||||||
spl_type_item_t *fit = &vec_at(*f_items, fi);
|
spl_type_item_t *fit = &vec_at(*f_items, fi);
|
||||||
if (fit->item_kind != ITEM_FIELD)
|
if (fit->item_kind != ITEM_FIELD)
|
||||||
@@ -1003,9 +1260,11 @@ static spl_expr_result_t parse_postfix_expr(spl_comp_t *ctx, spl_expr_result_t l
|
|||||||
if (strcmp(fit->name, fname) == 0) {
|
if (strcmp(fit->name, fname) == 0) {
|
||||||
emit_ptr_add(&ctx->emit, fit->aggregate_field.offset);
|
emit_ptr_add(&ctx->emit, fit->aggregate_field.offset);
|
||||||
emit_load_or_addr_type(ctx, &left, fit->aggregate_field.type_idx);
|
emit_load_or_addr_type(ctx, &left, fit->aggregate_field.type_idx);
|
||||||
|
found = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (found)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1016,6 +1275,7 @@ static spl_expr_result_t parse_postfix_expr(spl_comp_t *ctx, spl_expr_result_t l
|
|||||||
emit_load_ptr(&ctx->emit);
|
emit_load_ptr(&ctx->emit);
|
||||||
}
|
}
|
||||||
spl_type_item_vec_t *st_items = spl_type_items(&ctx->tctx, elem_idx);
|
spl_type_item_vec_t *st_items = spl_type_items(&ctx->tctx, elem_idx);
|
||||||
|
int found = 0;
|
||||||
vec_for(*st_items, si) {
|
vec_for(*st_items, si) {
|
||||||
spl_type_item_t *sit = &vec_at(*st_items, si);
|
spl_type_item_t *sit = &vec_at(*st_items, si);
|
||||||
if (sit->item_kind != ITEM_FIELD)
|
if (sit->item_kind != ITEM_FIELD)
|
||||||
@@ -1023,9 +1283,11 @@ static spl_expr_result_t parse_postfix_expr(spl_comp_t *ctx, spl_expr_result_t l
|
|||||||
if (strcmp(sit->name, fname) == 0) {
|
if (strcmp(sit->name, fname) == 0) {
|
||||||
emit_ptr_add(&ctx->emit, sit->aggregate_field.offset);
|
emit_ptr_add(&ctx->emit, sit->aggregate_field.offset);
|
||||||
emit_load_or_addr_type(ctx, &left, sit->aggregate_field.type_idx);
|
emit_load_or_addr_type(ctx, &left, sit->aggregate_field.type_idx);
|
||||||
|
found = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (found)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1072,7 +1334,7 @@ static spl_expr_result_t parse_postfix_expr(spl_comp_t *ctx, spl_expr_result_t l
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
spl_comp_error(ctx, "unknown field '%s'", fname);
|
spl_comp_err_tok(ctx, field, "unknown field '%s'", fname);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1099,7 +1361,8 @@ static spl_expr_result_t parse_postfix_expr(spl_comp_t *ctx, spl_expr_result_t l
|
|||||||
ctx->addr_of_mode = saved_aom;
|
ctx->addr_of_mode = saved_aom;
|
||||||
}
|
}
|
||||||
(void)end_expr;
|
(void)end_expr;
|
||||||
expect(ctx, TOK_R_BRACKET);
|
if (!expect(ctx, TOK_R_BRACKET))
|
||||||
|
return left;
|
||||||
|
|
||||||
if (!has_explicit_end) {
|
if (!has_explicit_end) {
|
||||||
if (left.type_idx >= 0 &&
|
if (left.type_idx >= 0 &&
|
||||||
@@ -1148,7 +1411,8 @@ static spl_expr_result_t parse_postfix_expr(spl_comp_t *ctx, spl_expr_result_t l
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(ctx, TOK_R_BRACKET);
|
if (!expect(ctx, TOK_R_BRACKET))
|
||||||
|
break;
|
||||||
|
|
||||||
if (left.type_idx >= 0 && (spl_type_kind(&ctx->tctx, left.type_idx) == TYPE_ARRAY ||
|
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_PTR ||
|
||||||
@@ -1217,8 +1481,7 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) {
|
|||||||
spl_expr_result_t left = parse_primary_expr(ctx);
|
spl_expr_result_t left = parse_primary_expr(ctx);
|
||||||
if (left.type_idx < 0)
|
if (left.type_idx < 0)
|
||||||
return left;
|
return left;
|
||||||
|
while (!ctx->has_error) {
|
||||||
while (1) {
|
|
||||||
left = parse_postfix_expr(ctx, left);
|
left = parse_postfix_expr(ctx, left);
|
||||||
|
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
@@ -1291,7 +1554,7 @@ static spl_expr_result_t parse_infix(spl_comp_t *ctx, spl_expr_result_t left, sp
|
|||||||
spl_parse_expr(ctx, next_prec);
|
spl_parse_expr(ctx, next_prec);
|
||||||
if ((op == TOK_EQ || op == TOK_NEQ) && left.type_idx >= 0 &&
|
if ((op == TOK_EQ || op == TOK_NEQ) && left.type_idx >= 0 &&
|
||||||
!spl_type_is_scalar(&ctx->tctx, left.type_idx)) {
|
!spl_type_is_scalar(&ctx->tctx, left.type_idx)) {
|
||||||
spl_comp_error(ctx, "type '%s' does not support comparison",
|
spl_comp_err_tok(ctx, peek(ctx), "type '%s' does not support comparison",
|
||||||
spl_type_str(&ctx->tctx, left.type_idx));
|
spl_type_str(&ctx->tctx, left.type_idx));
|
||||||
}
|
}
|
||||||
spl_type_t bt = spl_type_emit_type(&ctx->tctx, left.type_idx);
|
spl_type_t bt = spl_type_emit_type(&ctx->tctx, left.type_idx);
|
||||||
@@ -1307,29 +1570,27 @@ static spl_expr_result_t parse_infix(spl_comp_t *ctx, spl_expr_result_t left, sp
|
|||||||
|
|
||||||
int spl_emit_match_enum_cmp(spl_comp_t *ctx, int enum_type_idx, int val_offset, int by_value) {
|
int spl_emit_match_enum_cmp(spl_comp_t *ctx, int enum_type_idx, int val_offset, int by_value) {
|
||||||
char vname[256];
|
char vname[256];
|
||||||
|
spl_tok_t *vtok = NULL;
|
||||||
|
|
||||||
if (peek(ctx)->type == TOK_DOT) {
|
if (peek(ctx)->type == TOK_DOT) {
|
||||||
advance(ctx);
|
advance(ctx);
|
||||||
|
vtok = advance(ctx);
|
||||||
|
spl_tok_copy_name(vtok, vname, sizeof(vname));
|
||||||
} else {
|
} else {
|
||||||
spl_tok_t *tok = advance(ctx);
|
vtok = advance(ctx);
|
||||||
spl_tok_copy_name(tok, vname, sizeof(vname));
|
spl_tok_copy_name(vtok, vname, sizeof(vname));
|
||||||
|
|
||||||
while (peek(ctx)->type == TOK_DOT) {
|
while (peek(ctx)->type == TOK_DOT) {
|
||||||
int qt_idx = spl_type_resolve(&ctx->tctx, vname);
|
int qt_idx = spl_type_resolve(&ctx->tctx, vname);
|
||||||
(void)qt_idx;
|
(void)qt_idx;
|
||||||
advance(ctx);
|
advance(ctx);
|
||||||
tok = advance(ctx);
|
vtok = advance(ctx);
|
||||||
spl_tok_copy_name(tok, vname, sizeof(vname));
|
spl_tok_copy_name(vtok, vname, sizeof(vname));
|
||||||
}
|
}
|
||||||
|
|
||||||
goto lookup;
|
goto lookup;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
|
||||||
spl_tok_t *vtok = advance(ctx);
|
|
||||||
spl_tok_copy_name(vtok, vname, sizeof(vname));
|
|
||||||
}
|
|
||||||
|
|
||||||
lookup: {
|
lookup: {
|
||||||
spl_type_item_vec_t *e_items = spl_type_items(&ctx->tctx, enum_type_idx);
|
spl_type_item_vec_t *e_items = spl_type_items(&ctx->tctx, enum_type_idx);
|
||||||
vec_for(*e_items, vi) {
|
vec_for(*e_items, vi) {
|
||||||
@@ -1351,7 +1612,7 @@ lookup: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
spl_comp_error(ctx, "unknown variant '%s' in match", vname);
|
spl_comp_err_tok(ctx, vtok, "unknown variant '%s' in match", vname);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,12 +14,30 @@ spl_tok_t *advance(spl_comp_t *ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int expect(spl_comp_t *ctx, spl_tok_type_t type) {
|
int expect(spl_comp_t *ctx, spl_tok_type_t type) {
|
||||||
if (peek(ctx)->type == type) {
|
spl_tok_t *tok = peek(ctx);
|
||||||
|
if (tok->type == type) {
|
||||||
advance(ctx);
|
advance(ctx);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
spl_comp_error(ctx, "expected '%s', got '%s'", spl_tok_type_name(type),
|
|
||||||
spl_tok_type_name(peek(ctx)->type));
|
/* Extract token text for display (up to 40 chars) */
|
||||||
|
char val_buf[64];
|
||||||
|
usize display_len = tok->len < 40 ? tok->len : 40;
|
||||||
|
memcpy(val_buf, tok->lexeme, display_len);
|
||||||
|
val_buf[display_len] = '\0';
|
||||||
|
/* Replace newlines with \n for display */
|
||||||
|
for (usize i = 0; i < display_len; i++) {
|
||||||
|
if (val_buf[i] == '\n')
|
||||||
|
val_buf[i] = ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->parse_context[0]) {
|
||||||
|
spl_comp_err_tok(ctx, tok, "%s: expected %s, got %s '%s'", ctx->parse_context,
|
||||||
|
spl_tok_type_name(type), spl_tok_type_name(tok->type), val_buf);
|
||||||
|
} else {
|
||||||
|
spl_comp_err_tok(ctx, tok, "expected %s, got %s '%s'", spl_tok_type_name(type),
|
||||||
|
spl_tok_type_name(tok->type), val_buf);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,21 @@ static int parse_params_decl(spl_comp_t *ctx, char pnames[][256], int ptypes[])
|
|||||||
int nparams = 0;
|
int nparams = 0;
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
if (peek(ctx)->type != TOK_R_PAREN) {
|
if (peek(ctx)->type != TOK_R_PAREN) {
|
||||||
|
{
|
||||||
|
usize saved = ctx->tok_idx;
|
||||||
|
spl_tok_t *ptok = advance(ctx);
|
||||||
|
skip_nl(ctx);
|
||||||
|
if (ptok->type == KW_VOID && peek(ctx)->type == TOK_R_PAREN) {
|
||||||
|
expect(ctx, TOK_R_PAREN);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
ctx->tok_idx = saved;
|
||||||
|
}
|
||||||
while (1) {
|
while (1) {
|
||||||
|
if (peek(ctx)->type == TOK_ELLIPSIS) {
|
||||||
|
advance(ctx);
|
||||||
|
break;
|
||||||
|
}
|
||||||
spl_tok_t *pname = advance(ctx);
|
spl_tok_t *pname = advance(ctx);
|
||||||
spl_tok_copy_name(pname, pnames[nparams], 256);
|
spl_tok_copy_name(pname, pnames[nparams], 256);
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
@@ -32,10 +46,6 @@ static int parse_params_decl(spl_comp_t *ctx, char pnames[][256], int ptypes[])
|
|||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (peek(ctx)->type == TOK_ELLIPSIS) {
|
|
||||||
advance(ctx);
|
|
||||||
skip_nl(ctx);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -45,6 +55,7 @@ static int parse_params_decl(spl_comp_t *ctx, char pnames[][256], int ptypes[])
|
|||||||
|
|
||||||
static int parse_fn_body(spl_comp_t *ctx, const char *fn_name, int ret_type_idx, int nparams,
|
static int parse_fn_body(spl_comp_t *ctx, const char *fn_name, int ret_type_idx, int nparams,
|
||||||
char pnames[][256], int ptypes[], int is_pub) {
|
char pnames[][256], int ptypes[], int is_pub) {
|
||||||
|
snprintf(ctx->parse_context, sizeof(ctx->parse_context), "function '%s'", fn_name);
|
||||||
int fi = spl_declare_func(ctx, fn_name, ret_type_idx, nparams, 0, is_pub);
|
int fi = spl_declare_func(ctx, fn_name, ret_type_idx, nparams, 0, is_pub);
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -75,11 +86,35 @@ static int parse_fn_body(spl_comp_t *ctx, const char *fn_name, int ret_type_idx,
|
|||||||
spl_val_t alloc_addr = vec_size(ctx->prog.insns);
|
spl_val_t alloc_addr = vec_size(ctx->prog.insns);
|
||||||
emit_alloc(&ctx->emit, 0);
|
emit_alloc(&ctx->emit, 0);
|
||||||
|
|
||||||
while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
|
while (!ctx->has_error && peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
|
||||||
spl_parse_stmt(ctx);
|
spl_parse_stmt(ctx);
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Error recovery: skip to matching } if has_error caused early exit */
|
||||||
|
if (ctx->has_error) {
|
||||||
|
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++;
|
||||||
|
else if (tt == TOK_R_BRACE) {
|
||||||
|
depth--;
|
||||||
|
if (depth == 0)
|
||||||
|
break;
|
||||||
|
} else if (tt == TOK_EOF)
|
||||||
|
break;
|
||||||
|
advance(ctx);
|
||||||
|
}
|
||||||
|
if (peek(ctx)->type == TOK_R_BRACE)
|
||||||
|
advance(ctx);
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
if (!expect(ctx, TOK_R_BRACE))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int total_phys_slots = 0;
|
int total_phys_slots = 0;
|
||||||
for (int i = 0; i < nparams; i++) {
|
for (int i = 0; i < nparams; i++) {
|
||||||
usize psz = spl_type_size(&ctx->tctx, ptypes[i]);
|
usize psz = spl_type_size(&ctx->tctx, ptypes[i]);
|
||||||
@@ -90,9 +125,14 @@ static int parse_fn_body(spl_comp_t *ctx, const char *fn_name, int ret_type_idx,
|
|||||||
if ((usize)ctx->emit.frame.peak_bytes < min_bytes)
|
if ((usize)ctx->emit.frame.peak_bytes < min_bytes)
|
||||||
ctx->emit.frame.peak_bytes = (int)min_bytes;
|
ctx->emit.frame.peak_bytes = (int)min_bytes;
|
||||||
}
|
}
|
||||||
emit_patch(&ctx->emit, alloc_addr,
|
int alloc_slots = ctx->emit.frame.peak_bytes / (int)sizeof(spl_val_t) - total_phys_slots;
|
||||||
ctx->emit.frame.peak_bytes / (int)sizeof(spl_val_t) - total_phys_slots);
|
if (alloc_slots < 0 || alloc_slots > 65536) {
|
||||||
expect(ctx, TOK_R_BRACE);
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"WARN: parse_fn_body: suspicious alloc_slots=%d (peak_bytes=%d, phys_slots=%d)\n",
|
||||||
|
alloc_slots, ctx->emit.frame.peak_bytes, total_phys_slots);
|
||||||
|
}
|
||||||
|
emit_patch(&ctx->emit, alloc_addr, alloc_slots);
|
||||||
}
|
}
|
||||||
|
|
||||||
spl_emit_defer_epilogue(ctx, ctx->scope_depth);
|
spl_emit_defer_epilogue(ctx, ctx->scope_depth);
|
||||||
@@ -116,7 +156,8 @@ static void parse_fn_decl(spl_comp_t *ctx, int is_extern, int is_pub) {
|
|||||||
char fn_name[256];
|
char fn_name[256];
|
||||||
spl_tok_copy_name(fname_tok, fn_name, sizeof(fn_name));
|
spl_tok_copy_name(fname_tok, fn_name, sizeof(fn_name));
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
expect(ctx, TOK_L_PAREN);
|
if (!expect(ctx, TOK_L_PAREN))
|
||||||
|
return;
|
||||||
|
|
||||||
char pnames[MAX_PARAMS][256];
|
char pnames[MAX_PARAMS][256];
|
||||||
int ptypes[MAX_PARAMS];
|
int ptypes[MAX_PARAMS];
|
||||||
@@ -170,7 +211,8 @@ static void parse_method_decl(spl_comp_t *ctx, int container_type_idx) {
|
|||||||
snprintf(qualified, sizeof(qualified), "%s.%s", cname ? cname : "anon", mname);
|
snprintf(qualified, sizeof(qualified), "%s.%s", cname ? cname : "anon", mname);
|
||||||
|
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
expect(ctx, TOK_L_PAREN);
|
if (!expect(ctx, TOK_L_PAREN))
|
||||||
|
return;
|
||||||
|
|
||||||
char pnames[MAX_PARAMS][256];
|
char pnames[MAX_PARAMS][256];
|
||||||
int ptypes[MAX_PARAMS];
|
int ptypes[MAX_PARAMS];
|
||||||
@@ -194,42 +236,6 @@ static void parse_method_decl(spl_comp_t *ctx, int container_type_idx) {
|
|||||||
* Parse type container body (shared for struct, union, enum)
|
* Parse type container body (shared for struct, union, enum)
|
||||||
* ============================================================ */
|
* ============================================================ */
|
||||||
|
|
||||||
static void skip_type_decl(spl_comp_t *ctx) {
|
|
||||||
advance(ctx); /* type */
|
|
||||||
advance(ctx); /* name */
|
|
||||||
skip_nl(ctx);
|
|
||||||
expect(ctx, TOK_ASSIGN);
|
|
||||||
skip_nl(ctx);
|
|
||||||
|
|
||||||
spl_tok_type_t tt = peek(ctx)->type;
|
|
||||||
if (tt == KW_STRUCT || tt == KW_UNION || tt == KW_ENUM) {
|
|
||||||
advance(ctx);
|
|
||||||
skip_nl(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (peek(ctx)->type == TOK_L_BRACE) {
|
|
||||||
int bd = 1;
|
|
||||||
advance(ctx);
|
|
||||||
while (bd > 0 && ctx->tok_idx < vec_size(ctx->toks)) {
|
|
||||||
spl_tok_type_t tt = peek(ctx)->type;
|
|
||||||
if (tt == TOK_L_BRACE)
|
|
||||||
bd++;
|
|
||||||
else if (tt == TOK_R_BRACE)
|
|
||||||
bd--;
|
|
||||||
else if (tt == TOK_EOF)
|
|
||||||
break;
|
|
||||||
advance(ctx);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
while (peek(ctx)->type != TOK_SEMICOLON && peek(ctx)->type != TOK_EOF)
|
|
||||||
advance(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
skip_nl(ctx);
|
|
||||||
if (peek(ctx)->type == TOK_SEMICOLON)
|
|
||||||
advance(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void parse_type_body(spl_comp_t *ctx, int container_type_idx, int is_enum) {
|
static void parse_type_body(spl_comp_t *ctx, int container_type_idx, int is_enum) {
|
||||||
if (peek(ctx)->type != TOK_L_BRACE)
|
if (peek(ctx)->type != TOK_L_BRACE)
|
||||||
return;
|
return;
|
||||||
@@ -242,7 +248,7 @@ static void parse_type_body(spl_comp_t *ctx, int container_type_idx, int is_enum
|
|||||||
{
|
{
|
||||||
usize saved = ctx->tok_idx;
|
usize saved = ctx->tok_idx;
|
||||||
int depth = 1;
|
int depth = 1;
|
||||||
while (depth > 0 && ctx->tok_idx < vec_size(ctx->toks)) {
|
while (!ctx->has_error && depth > 0 && ctx->tok_idx < vec_size(ctx->toks)) {
|
||||||
spl_tok_type_t tt = peek(ctx)->type;
|
spl_tok_type_t tt = peek(ctx)->type;
|
||||||
if (tt == TOK_L_BRACE) {
|
if (tt == TOK_L_BRACE) {
|
||||||
depth++;
|
depth++;
|
||||||
@@ -269,7 +275,7 @@ static void parse_type_body(spl_comp_t *ctx, int container_type_idx, int is_enum
|
|||||||
if (cname) {
|
if (cname) {
|
||||||
usize saved = ctx->tok_idx;
|
usize saved = ctx->tok_idx;
|
||||||
int depth = 1;
|
int depth = 1;
|
||||||
while (depth > 0 && ctx->tok_idx < vec_size(ctx->toks)) {
|
while (!ctx->has_error && depth > 0 && ctx->tok_idx < vec_size(ctx->toks)) {
|
||||||
spl_tok_type_t tt = peek(ctx)->type;
|
spl_tok_type_t tt = peek(ctx)->type;
|
||||||
if (tt == TOK_L_BRACE) {
|
if (tt == TOK_L_BRACE) {
|
||||||
depth++;
|
depth++;
|
||||||
@@ -325,7 +331,7 @@ static void parse_type_body(spl_comp_t *ctx, int container_type_idx, int is_enum
|
|||||||
/* === Pass 2: Parse fields/variants and methods === */
|
/* === Pass 2: Parse fields/variants and methods === */
|
||||||
{
|
{
|
||||||
int depth = 1;
|
int depth = 1;
|
||||||
while (depth > 0 && ctx->tok_idx < vec_size(ctx->toks)) {
|
while (!ctx->has_error && depth > 0 && ctx->tok_idx < vec_size(ctx->toks)) {
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
spl_tok_type_t tt = peek(ctx)->type;
|
spl_tok_type_t tt = peek(ctx)->type;
|
||||||
if (tt == TOK_L_BRACE) {
|
if (tt == TOK_L_BRACE) {
|
||||||
@@ -339,7 +345,7 @@ static void parse_type_body(spl_comp_t *ctx, int container_type_idx, int is_enum
|
|||||||
}
|
}
|
||||||
advance(ctx);
|
advance(ctx);
|
||||||
} else if (tt == KW_TYPE && depth == 1) {
|
} else if (tt == KW_TYPE && depth == 1) {
|
||||||
skip_type_decl(ctx);
|
parse_type_decl(ctx);
|
||||||
} else if (tt == KW_VAR && depth == 1 && !is_enum) {
|
} else if (tt == KW_VAR && depth == 1 && !is_enum) {
|
||||||
advance(ctx); /* var */
|
advance(ctx); /* var */
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
@@ -352,7 +358,7 @@ static void parse_type_body(spl_comp_t *ctx, int container_type_idx, int is_enum
|
|||||||
char fname[256];
|
char fname[256];
|
||||||
spl_tok_copy_name(ftok, fname, sizeof(fname));
|
spl_tok_copy_name(ftok, fname, sizeof(fname));
|
||||||
if (ftype >= 0)
|
if (ftype >= 0)
|
||||||
spl_type_add_field(&ctx->tctx, container_type_idx, fname, ftype);
|
spl_type_add_var(&ctx->tctx, container_type_idx, fname, ftype);
|
||||||
}
|
}
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
if (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_COMMA)
|
if (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_COMMA)
|
||||||
@@ -402,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;
|
ctx->tctx.current_type_idx = saved_current;
|
||||||
spl_type_compute_layout(&ctx->tctx, container_type_idx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
@@ -414,31 +419,48 @@ void parse_type_decl(spl_comp_t *ctx) {
|
|||||||
spl_tok_t *name_tok = advance(ctx);
|
spl_tok_t *name_tok = advance(ctx);
|
||||||
char tname[256];
|
char tname[256];
|
||||||
spl_tok_copy_name(name_tok, tname, sizeof(tname));
|
spl_tok_copy_name(name_tok, tname, sizeof(tname));
|
||||||
|
snprintf(ctx->parse_context, sizeof(ctx->parse_context), "type '%s'", tname);
|
||||||
|
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
expect(ctx, TOK_ASSIGN);
|
if (!expect(ctx, TOK_ASSIGN))
|
||||||
|
return;
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
|
|
||||||
int parent_type_idx = ctx->tctx.current_type_idx;
|
int parent_type_idx = ctx->tctx.current_type_idx;
|
||||||
|
|
||||||
|
/* Check if already pre-registered in Pass 0 */
|
||||||
|
int existing = spl_type_resolve(&ctx->tctx, tname);
|
||||||
|
|
||||||
if (peek(ctx)->type == KW_STRUCT) {
|
if (peek(ctx)->type == KW_STRUCT) {
|
||||||
advance(ctx);
|
advance(ctx);
|
||||||
int ti = spl_type_struct(&ctx->tctx, tname);
|
int ti;
|
||||||
if (parent_type_idx >= 0)
|
if (existing >= 0)
|
||||||
|
ti = existing;
|
||||||
|
else
|
||||||
|
ti = spl_type_struct(&ctx->tctx, tname);
|
||||||
|
if (parent_type_idx >= 0 && existing < 0)
|
||||||
spl_type_add_nested(&ctx->tctx, parent_type_idx, tname, ti);
|
spl_type_add_nested(&ctx->tctx, parent_type_idx, tname, ti);
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
parse_type_body(ctx, ti, 0);
|
parse_type_body(ctx, ti, 0);
|
||||||
} else if (peek(ctx)->type == KW_UNION) {
|
} else if (peek(ctx)->type == KW_UNION) {
|
||||||
advance(ctx);
|
advance(ctx);
|
||||||
int ti = spl_type_union(&ctx->tctx, tname);
|
int ti;
|
||||||
if (parent_type_idx >= 0)
|
if (existing >= 0)
|
||||||
|
ti = existing;
|
||||||
|
else
|
||||||
|
ti = spl_type_union(&ctx->tctx, tname);
|
||||||
|
if (parent_type_idx >= 0 && existing < 0)
|
||||||
spl_type_add_nested(&ctx->tctx, parent_type_idx, tname, ti);
|
spl_type_add_nested(&ctx->tctx, parent_type_idx, tname, ti);
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
parse_type_body(ctx, ti, 1);
|
parse_type_body(ctx, ti, 1);
|
||||||
} else if (peek(ctx)->type == KW_ENUM) {
|
} else if (peek(ctx)->type == KW_ENUM) {
|
||||||
advance(ctx);
|
advance(ctx);
|
||||||
int ti = spl_type_enum(&ctx->tctx, tname);
|
int ti;
|
||||||
if (parent_type_idx >= 0)
|
if (existing >= 0)
|
||||||
|
ti = existing;
|
||||||
|
else
|
||||||
|
ti = spl_type_enum(&ctx->tctx, tname);
|
||||||
|
if (parent_type_idx >= 0 && existing < 0)
|
||||||
spl_type_add_nested(&ctx->tctx, parent_type_idx, tname, ti);
|
spl_type_add_nested(&ctx->tctx, parent_type_idx, tname, ti);
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
parse_type_body(ctx, ti, 1);
|
parse_type_body(ctx, ti, 1);
|
||||||
@@ -455,12 +477,213 @@ void parse_type_decl(spl_comp_t *ctx) {
|
|||||||
advance(ctx);
|
advance(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
* Pre-registration pass: register all names before filling bodies
|
||||||
|
* ============================================================ */
|
||||||
|
|
||||||
|
/* Skip a { ... } function body */
|
||||||
|
static void skip_fn_body(spl_comp_t *ctx) {
|
||||||
|
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++;
|
||||||
|
else if (tt == TOK_R_BRACE) {
|
||||||
|
depth--;
|
||||||
|
if (depth == 0)
|
||||||
|
break;
|
||||||
|
} else if (tt == TOK_EOF)
|
||||||
|
break;
|
||||||
|
advance(ctx);
|
||||||
|
}
|
||||||
|
if (peek(ctx)->type == TOK_R_BRACE)
|
||||||
|
advance(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pre-register a type name (and nested type names), skip the body */
|
||||||
|
static void pre_register_type_decl(spl_comp_t *ctx) {
|
||||||
|
advance(ctx); /* type */
|
||||||
|
spl_tok_t *name_tok = advance(ctx);
|
||||||
|
char tname[256];
|
||||||
|
spl_tok_copy_name(name_tok, tname, sizeof(tname));
|
||||||
|
skip_nl(ctx);
|
||||||
|
if (!expect(ctx, TOK_ASSIGN))
|
||||||
|
return;
|
||||||
|
skip_nl(ctx);
|
||||||
|
|
||||||
|
int parent_type_idx = ctx->tctx.current_type_idx;
|
||||||
|
|
||||||
|
int ti = -1;
|
||||||
|
if (peek(ctx)->type == KW_STRUCT) {
|
||||||
|
advance(ctx);
|
||||||
|
ti = spl_type_struct(&ctx->tctx, tname);
|
||||||
|
} else if (peek(ctx)->type == KW_ENUM) {
|
||||||
|
advance(ctx);
|
||||||
|
ti = spl_type_enum(&ctx->tctx, tname);
|
||||||
|
} else if (peek(ctx)->type == KW_UNION) {
|
||||||
|
advance(ctx);
|
||||||
|
ti = spl_type_union(&ctx->tctx, tname);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ti >= 0) {
|
||||||
|
if (parent_type_idx >= 0)
|
||||||
|
spl_type_add_nested(&ctx->tctx, parent_type_idx, tname, ti);
|
||||||
|
|
||||||
|
/* Register nested type names inside the body */
|
||||||
|
if (peek(ctx)->type == TOK_L_BRACE) {
|
||||||
|
int saved_current = ctx->tctx.current_type_idx;
|
||||||
|
ctx->tctx.current_type_idx = ti;
|
||||||
|
int depth = 1;
|
||||||
|
advance(ctx); /* { */
|
||||||
|
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) {
|
||||||
|
pre_register_type_decl(ctx);
|
||||||
|
} else if (tt == TOK_EOF) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
advance(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (peek(ctx)->type == TOK_R_BRACE)
|
||||||
|
advance(ctx);
|
||||||
|
ctx->tctx.current_type_idx = saved_current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
skip_nl(ctx);
|
||||||
|
if (peek(ctx)->type == TOK_SEMICOLON)
|
||||||
|
advance(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pre-register a function name and signature, skip the body */
|
||||||
|
static void pre_register_fn_decl(spl_comp_t *ctx, int is_extern) {
|
||||||
|
advance(ctx);
|
||||||
|
skip_nl(ctx);
|
||||||
|
spl_tok_t *fname_tok = advance(ctx);
|
||||||
|
char fn_name[256];
|
||||||
|
spl_tok_copy_name(fname_tok, fn_name, sizeof(fn_name));
|
||||||
|
skip_nl(ctx);
|
||||||
|
if (!expect(ctx, TOK_L_PAREN))
|
||||||
|
return;
|
||||||
|
|
||||||
|
char pnames[MAX_PARAMS][256];
|
||||||
|
int ptypes[MAX_PARAMS];
|
||||||
|
int nparams = parse_params_decl(ctx, pnames, ptypes);
|
||||||
|
skip_nl(ctx);
|
||||||
|
|
||||||
|
int ret_type_idx = spl_type_basic(&ctx->tctx, SPL_VOID);
|
||||||
|
if (peek(ctx)->type != TOK_SEMICOLON && peek(ctx)->type != TOK_L_BRACE) {
|
||||||
|
ret_type_idx = spl_type_parse(&ctx->tctx, ctx);
|
||||||
|
if (ret_type_idx < 0)
|
||||||
|
ret_type_idx = spl_type_basic(&ctx->tctx, SPL_VOID);
|
||||||
|
skip_nl(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_extern) {
|
||||||
|
spl_declare_func(ctx, fn_name, ret_type_idx, nparams, 1, 0);
|
||||||
|
spl_ensure_native(ctx, fn_name);
|
||||||
|
if (peek(ctx)->type == TOK_SEMICOLON)
|
||||||
|
advance(ctx);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (peek(ctx)->type == TOK_SEMICOLON) {
|
||||||
|
advance(ctx);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
skip_nl(ctx);
|
||||||
|
|
||||||
|
if (peek(ctx)->type == TOK_L_BRACE) {
|
||||||
|
skip_fn_body(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
skip_nl(ctx);
|
||||||
|
if (peek(ctx)->type == TOK_SEMICOLON)
|
||||||
|
advance(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
* Parse top-level program
|
* Parse top-level program
|
||||||
* ============================================================ */
|
* ============================================================ */
|
||||||
|
|
||||||
void spl_parse_prog(spl_comp_t *ctx) {
|
void spl_parse_prog(spl_comp_t *ctx) {
|
||||||
while (peek(ctx)->type != TOK_EOF) {
|
/* Pass 0: Pre-register all type names, function signatures, and variables */
|
||||||
|
{
|
||||||
|
usize saved = ctx->tok_idx;
|
||||||
|
while (!ctx->has_error && peek(ctx)->type != TOK_EOF) {
|
||||||
|
skip_nl(ctx);
|
||||||
|
if (peek(ctx)->type == TOK_EOF)
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (peek(ctx)->type) {
|
||||||
|
case KW_TYPE:
|
||||||
|
pre_register_type_decl(ctx);
|
||||||
|
break;
|
||||||
|
case KW_FN:
|
||||||
|
pre_register_fn_decl(ctx, 0);
|
||||||
|
break;
|
||||||
|
case KW_PUB: {
|
||||||
|
advance(ctx);
|
||||||
|
skip_nl(ctx);
|
||||||
|
if (peek(ctx)->type == KW_FN)
|
||||||
|
pre_register_fn_decl(ctx, 0);
|
||||||
|
else if (peek(ctx)->type == KW_TYPE)
|
||||||
|
pre_register_type_decl(ctx);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TOK_AT:
|
||||||
|
case TOK_SHARP: {
|
||||||
|
int tt = peek(ctx)->type;
|
||||||
|
advance(ctx);
|
||||||
|
skip_nl(ctx);
|
||||||
|
if (tt == TOK_AT) {
|
||||||
|
if (peek(ctx)->type == KW_EXTERN) {
|
||||||
|
advance(ctx);
|
||||||
|
skip_nl(ctx);
|
||||||
|
if (peek(ctx)->type == TOK_L_PAREN) {
|
||||||
|
advance(ctx);
|
||||||
|
skip_nl(ctx);
|
||||||
|
advance(ctx);
|
||||||
|
skip_nl(ctx);
|
||||||
|
if (peek(ctx)->type == TOK_R_PAREN)
|
||||||
|
advance(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (peek(ctx)->type == TOK_L_BRACKET) {
|
||||||
|
advance(ctx);
|
||||||
|
while (peek(ctx)->type != TOK_R_BRACKET && peek(ctx)->type != TOK_EOF)
|
||||||
|
advance(ctx);
|
||||||
|
if (peek(ctx)->type == TOK_R_BRACKET)
|
||||||
|
advance(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
skip_nl(ctx);
|
||||||
|
if (peek(ctx)->type == KW_FN)
|
||||||
|
pre_register_fn_decl(ctx, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
/* Skip unrecognized tokens (stmts, etc.) */
|
||||||
|
advance(ctx);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx->tok_idx = saved;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pass 1: Fill all bodies */
|
||||||
|
while (!ctx->has_error && peek(ctx)->type != TOK_EOF) {
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
if (peek(ctx)->type == TOK_EOF)
|
if (peek(ctx)->type == TOK_EOF)
|
||||||
break;
|
break;
|
||||||
@@ -487,7 +710,6 @@ void spl_parse_prog(spl_comp_t *ctx) {
|
|||||||
advance(ctx); /* @ or # */
|
advance(ctx); /* @ or # */
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
if (tt == TOK_AT) {
|
if (tt == TOK_AT) {
|
||||||
/* @extern(vm) fn ... */
|
|
||||||
if (peek(ctx)->type == KW_EXTERN) {
|
if (peek(ctx)->type == KW_EXTERN) {
|
||||||
advance(ctx); /* extern */
|
advance(ctx); /* extern */
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
@@ -501,9 +723,8 @@ void spl_parse_prog(spl_comp_t *ctx) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* #[extern("vm")] fn ... (legacy) */
|
|
||||||
if (peek(ctx)->type == TOK_L_BRACKET) {
|
if (peek(ctx)->type == TOK_L_BRACKET) {
|
||||||
advance(ctx); /* [ */
|
advance(ctx);
|
||||||
while (peek(ctx)->type != TOK_R_BRACKET && peek(ctx)->type != TOK_EOF)
|
while (peek(ctx)->type != TOK_R_BRACKET && peek(ctx)->type != TOK_EOF)
|
||||||
advance(ctx);
|
advance(ctx);
|
||||||
if (peek(ctx)->type == TOK_R_BRACKET)
|
if (peek(ctx)->type == TOK_R_BRACKET)
|
||||||
@@ -524,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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,8 @@
|
|||||||
* ============================================================ */
|
* ============================================================ */
|
||||||
|
|
||||||
static void parse_ret_stmt(spl_comp_t *ctx) {
|
static void parse_ret_stmt(spl_comp_t *ctx) {
|
||||||
advance(ctx); /* ret */
|
snprintf(ctx->parse_context, sizeof(ctx->parse_context), "return statement");
|
||||||
|
spl_tok_t *ret_tok = advance(ctx); /* ret */
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
|
|
||||||
/* Before returning, execute all pending defers from innermost scope outward */
|
/* Before returning, execute all pending defers from innermost scope outward */
|
||||||
@@ -23,7 +24,14 @@ static void parse_ret_stmt(spl_comp_t *ctx) {
|
|||||||
emit_return(&ctx->emit, &ctx->tctx, ctx->current_ret_type_idx);
|
emit_return(&ctx->emit, &ctx->tctx, ctx->current_ret_type_idx);
|
||||||
} else {
|
} else {
|
||||||
spl_expr_result_t val = spl_parse_expr(ctx, PREC_MIN);
|
spl_expr_result_t val = spl_parse_expr(ctx, PREC_MIN);
|
||||||
(void)val;
|
int void_ty = spl_type_basic(&ctx->tctx, SPL_VOID);
|
||||||
|
if (ctx->current_ret_type_idx == void_ty) {
|
||||||
|
spl_comp_err_tok(ctx, ret_tok, "cannot return a value from a void function");
|
||||||
|
return;
|
||||||
|
} else if (val.type_idx == void_ty) {
|
||||||
|
spl_comp_err_tok(ctx, ret_tok, "expected return value");
|
||||||
|
return;
|
||||||
|
}
|
||||||
spl_emit_ret(ctx, ctx->current_ret_type_idx);
|
spl_emit_ret(ctx, ctx->current_ret_type_idx);
|
||||||
}
|
}
|
||||||
if (peek(ctx)->type == TOK_SEMICOLON)
|
if (peek(ctx)->type == TOK_SEMICOLON)
|
||||||
@@ -41,6 +49,7 @@ static void parse_var_decl(spl_comp_t *ctx, int is_const) {
|
|||||||
spl_tok_t *name_tok = advance(ctx);
|
spl_tok_t *name_tok = advance(ctx);
|
||||||
char vname[256];
|
char vname[256];
|
||||||
spl_tok_copy_name(name_tok, vname, sizeof(vname));
|
spl_tok_copy_name(name_tok, vname, sizeof(vname));
|
||||||
|
snprintf(ctx->parse_context, sizeof(ctx->parse_context), "variable '%s'", vname);
|
||||||
|
|
||||||
int var_type_idx = -1;
|
int var_type_idx = -1;
|
||||||
int has_init = 0;
|
int has_init = 0;
|
||||||
@@ -123,14 +132,33 @@ void spl_parse_block(spl_comp_t *ctx) {
|
|||||||
advance(ctx); /* { */
|
advance(ctx); /* { */
|
||||||
spl_push_scope(ctx);
|
spl_push_scope(ctx);
|
||||||
|
|
||||||
while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
|
while (!ctx->has_error && peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
|
||||||
spl_parse_stmt(ctx);
|
spl_parse_stmt(ctx);
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Error recovery: skip to matching } if has_error caused early exit */
|
||||||
|
if (ctx->has_error) {
|
||||||
|
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++;
|
||||||
|
else if (tt == TOK_R_BRACE) {
|
||||||
|
depth--;
|
||||||
|
if (depth == 0)
|
||||||
|
break;
|
||||||
|
} else if (tt == TOK_EOF)
|
||||||
|
break;
|
||||||
|
advance(ctx);
|
||||||
|
}
|
||||||
|
if (peek(ctx)->type == TOK_R_BRACE)
|
||||||
|
advance(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
spl_emit_defer_epilogue(ctx, ctx->scope_depth);
|
spl_emit_defer_epilogue(ctx, ctx->scope_depth);
|
||||||
spl_pop_scope(ctx);
|
spl_pop_scope(ctx);
|
||||||
if (peek(ctx)->type == TOK_R_BRACE)
|
if (!ctx->has_error && peek(ctx)->type == TOK_R_BRACE)
|
||||||
advance(ctx);
|
advance(ctx);
|
||||||
} else {
|
} else {
|
||||||
/* Single statement */
|
/* Single statement */
|
||||||
@@ -155,7 +183,7 @@ spl_expr_result_t spl_parse_block_expr(spl_comp_t *ctx) {
|
|||||||
spl_expr_result_t result = {0}; /* void by default */
|
spl_expr_result_t result = {0}; /* void by default */
|
||||||
|
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
|
while (!ctx->has_error && peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
|
||||||
if (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_ENDLINE) {
|
if (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_ENDLINE) {
|
||||||
advance(ctx);
|
advance(ctx);
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
@@ -175,6 +203,7 @@ spl_expr_result_t spl_parse_block_expr(spl_comp_t *ctx) {
|
|||||||
result = (spl_expr_result_t){0};
|
result = (spl_expr_result_t){0};
|
||||||
} else {
|
} else {
|
||||||
/* Expression — look ahead for assignment */
|
/* Expression — look ahead for assignment */
|
||||||
|
usize before = ctx->tok_idx;
|
||||||
int is_assign = lookahead_is_assign(ctx);
|
int is_assign = lookahead_is_assign(ctx);
|
||||||
int saved_addr = ctx->addr_of_mode;
|
int saved_addr = ctx->addr_of_mode;
|
||||||
if (is_assign)
|
if (is_assign)
|
||||||
@@ -182,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);
|
spl_expr_result_t expr = spl_parse_expr(ctx, PREC_MIN);
|
||||||
ctx->addr_of_mode = saved_addr;
|
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);
|
skip_nl(ctx);
|
||||||
if (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_ENDLINE) {
|
if (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_ENDLINE) {
|
||||||
/* Expression statement: drop value, consume ; */
|
/* Expression statement: drop value, consume ; */
|
||||||
@@ -201,7 +238,8 @@ spl_expr_result_t spl_parse_block_expr(spl_comp_t *ctx) {
|
|||||||
|
|
||||||
spl_emit_defer_epilogue(ctx, ctx->scope_depth);
|
spl_emit_defer_epilogue(ctx, ctx->scope_depth);
|
||||||
spl_pop_scope(ctx);
|
spl_pop_scope(ctx);
|
||||||
expect(ctx, TOK_R_BRACE);
|
if (!expect(ctx, TOK_R_BRACE))
|
||||||
|
return result;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,6 +249,7 @@ spl_expr_result_t spl_parse_block_expr(spl_comp_t *ctx) {
|
|||||||
|
|
||||||
static void parse_if_stmt(spl_comp_t *ctx) {
|
static void parse_if_stmt(spl_comp_t *ctx) {
|
||||||
advance(ctx); /* if */
|
advance(ctx); /* if */
|
||||||
|
snprintf(ctx->parse_context, sizeof(ctx->parse_context), "if statement");
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
spl_expr_result_t cond = spl_parse_expr(ctx, PREC_MIN);
|
spl_expr_result_t cond = spl_parse_expr(ctx, PREC_MIN);
|
||||||
(void)cond;
|
(void)cond;
|
||||||
@@ -269,6 +308,7 @@ static void loop_exit(spl_comp_t *ctx, spl_loop_save_t *save) {
|
|||||||
|
|
||||||
static void parse_while_stmt(spl_comp_t *ctx) {
|
static void parse_while_stmt(spl_comp_t *ctx) {
|
||||||
advance(ctx); /* while */
|
advance(ctx); /* while */
|
||||||
|
snprintf(ctx->parse_context, sizeof(ctx->parse_context), "while statement");
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
|
|
||||||
spl_loop_save_t save;
|
spl_loop_save_t save;
|
||||||
@@ -301,6 +341,7 @@ static void parse_loop_stmt(spl_comp_t *ctx) {
|
|||||||
|
|
||||||
static void parse_for_stmt(spl_comp_t *ctx) {
|
static void parse_for_stmt(spl_comp_t *ctx) {
|
||||||
advance(ctx); /* for */
|
advance(ctx); /* for */
|
||||||
|
snprintf(ctx->parse_context, sizeof(ctx->parse_context), "for statement");
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
|
|
||||||
/* Parse the iteration expression(s) */
|
/* Parse the iteration expression(s) */
|
||||||
@@ -357,7 +398,7 @@ static void parse_for_stmt(spl_comp_t *ctx) {
|
|||||||
|
|
||||||
/* Exit: patch bz, drop end */
|
/* Exit: patch bz, drop end */
|
||||||
emit_patch_here(&ctx->emit, bz_addr);
|
emit_patch_here(&ctx->emit, bz_addr);
|
||||||
emit_drop(&ctx->emit);
|
emit_drop(&ctx->emit); /* [] */
|
||||||
|
|
||||||
spl_emit_defer_epilogue(ctx, ctx->scope_depth);
|
spl_emit_defer_epilogue(ctx, ctx->scope_depth);
|
||||||
spl_pop_scope(ctx);
|
spl_pop_scope(ctx);
|
||||||
@@ -487,9 +528,10 @@ static void parse_for_stmt(spl_comp_t *ctx) {
|
|||||||
* ============================================================ */
|
* ============================================================ */
|
||||||
|
|
||||||
static void parse_break_stmt(spl_comp_t *ctx) {
|
static void parse_break_stmt(spl_comp_t *ctx) {
|
||||||
advance(ctx); /* break */
|
spl_tok_t *tok = advance(ctx); /* break */
|
||||||
if (!ctx->in_loop) {
|
if (!ctx->in_loop) {
|
||||||
spl_comp_error(ctx, "break outside loop");
|
spl_comp_err_tok(ctx, tok, "break outside loop");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
/* Emit JMP with placeholder, add to patch list */
|
/* Emit JMP with placeholder, add to patch list */
|
||||||
spl_val_t addr = emit_jmp_here(&ctx->emit);
|
spl_val_t addr = emit_jmp_here(&ctx->emit);
|
||||||
@@ -504,9 +546,10 @@ static void parse_break_stmt(spl_comp_t *ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void parse_continue_stmt(spl_comp_t *ctx) {
|
static void parse_continue_stmt(spl_comp_t *ctx) {
|
||||||
advance(ctx); /* continue */
|
spl_tok_t *tok = advance(ctx); /* continue */
|
||||||
if (!ctx->in_loop) {
|
if (!ctx->in_loop) {
|
||||||
spl_comp_error(ctx, "continue outside loop");
|
spl_comp_err_tok(ctx, tok, "continue outside loop");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
/* Emit JMP with relative offset to continue_target */
|
/* Emit JMP with relative offset to continue_target */
|
||||||
spl_val_t here = vec_size(ctx->prog.insns);
|
spl_val_t here = vec_size(ctx->prog.insns);
|
||||||
@@ -559,16 +602,100 @@ static void parse_match_value_pattern(spl_comp_t *ctx, int val_offset) {
|
|||||||
spl_emit_match_value_cmp(ctx, val_offset);
|
spl_emit_match_value_cmp(ctx, val_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Parse enum variant data bindings: (name1, name2, ...)
|
/* Parse enum variant data bindings:
|
||||||
|
* [var] — global binding: var = entire payload struct
|
||||||
|
* [.field = var, ...] — field-by-name binding
|
||||||
|
* (name1, name2, ...) — (legacy) positional field binding
|
||||||
* Declares local variables and loads corresponding field data
|
* Declares local variables and loads corresponding field data
|
||||||
* from the matched value's data area (offset 4+).
|
* from the matched value's data area (offset 4+).
|
||||||
* Sets *scope_pushed = 1 if bindings declared. */
|
* Sets *scope_pushed = 1 if bindings declared. */
|
||||||
static void parse_match_enum_bindings(spl_comp_t *ctx, int enum_type_idx, int variant_item_idx,
|
static void parse_match_enum_bindings(spl_comp_t *ctx, int enum_type_idx, int variant_item_idx,
|
||||||
int val_offset, int *scope_pushed) {
|
int val_offset, int *scope_pushed) {
|
||||||
|
if (peek(ctx)->type == TOK_L_BRACKET) {
|
||||||
|
/* ---- New syntax: [var] or [.field = var, ...] ---- */
|
||||||
|
advance(ctx); /* [ */
|
||||||
|
skip_nl(ctx);
|
||||||
|
if (peek(ctx)->type == TOK_R_BRACKET) {
|
||||||
|
if (!expect(ctx, TOK_R_BRACKET))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_push_scope(ctx);
|
||||||
|
*scope_pushed = 1;
|
||||||
|
|
||||||
|
spl_type_item_t *var_item = spl_type_item_at(&ctx->tctx, enum_type_idx, variant_item_idx);
|
||||||
|
int data_type_idx = var_item ? var_item->enum_field.type_idx : -1;
|
||||||
|
|
||||||
|
if (peek(ctx)->type == TOK_DOT) {
|
||||||
|
/* [.field = var, ...] — field-by-name binding */
|
||||||
|
for (;;) {
|
||||||
|
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);
|
||||||
|
|
||||||
|
spl_tok_t *vtok = advance(ctx);
|
||||||
|
char vname[256];
|
||||||
|
spl_tok_copy_name(vtok, vname, sizeof(vname));
|
||||||
|
|
||||||
|
int field_type = -1;
|
||||||
|
usize field_off = 0;
|
||||||
|
if (data_type_idx >= 0 && spl_type_kind(&ctx->tctx, data_type_idx) == TYPE_STRUCT) {
|
||||||
|
spl_type_item_vec_t *data_items = spl_type_items(&ctx->tctx, data_type_idx);
|
||||||
|
vec_for(*data_items, di) {
|
||||||
|
spl_type_item_t *fit = &vec_at(*data_items, di);
|
||||||
|
if (fit->item_kind == ITEM_FIELD && strcmp(fit->name, fname) == 0) {
|
||||||
|
field_type = fit->aggregate_field.type_idx;
|
||||||
|
field_off = fit->aggregate_field.offset;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (field_type < 0) {
|
||||||
|
spl_comp_err_tok(ctx, ftok, "unknown field '%s' in enum variant", fname);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int boffset = spl_declare_var(ctx, vname, field_type, 0);
|
||||||
|
emit_load_to_var(&ctx->emit, &ctx->tctx, val_offset, ENUM_TAG_SIZE + field_off,
|
||||||
|
field_type, boffset);
|
||||||
|
|
||||||
|
skip_nl(ctx);
|
||||||
|
if (peek(ctx)->type == TOK_COMMA) {
|
||||||
|
advance(ctx);
|
||||||
|
skip_nl(ctx);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* [var] — global binding: entire payload as one variable */
|
||||||
|
spl_tok_t *vtok = advance(ctx);
|
||||||
|
char vname[256];
|
||||||
|
spl_tok_copy_name(vtok, vname, sizeof(vname));
|
||||||
|
|
||||||
|
if (data_type_idx >= 0) {
|
||||||
|
int boffset = spl_declare_var(ctx, vname, data_type_idx, 0);
|
||||||
|
emit_load_to_var(&ctx->emit, &ctx->tctx, val_offset, ENUM_TAG_SIZE, data_type_idx,
|
||||||
|
boffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!expect(ctx, TOK_R_BRACKET))
|
||||||
|
return;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Legacy syntax: (name1, name2, ...) ---- */
|
||||||
advance(ctx); /* ( */
|
advance(ctx); /* ( */
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
if (peek(ctx)->type == TOK_R_PAREN) {
|
if (peek(ctx)->type == TOK_R_PAREN) {
|
||||||
expect(ctx, TOK_R_PAREN);
|
if (!expect(ctx, TOK_R_PAREN))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -626,7 +753,8 @@ static void parse_match_enum_bindings(spl_comp_t *ctx, int enum_type_idx, int va
|
|||||||
emit_load_to_var(&ctx->emit, &ctx->tctx, val_offset, ENUM_TAG_SIZE, data_type_idx, boffset);
|
emit_load_to_var(&ctx->emit, &ctx->tctx, val_offset, ENUM_TAG_SIZE, data_type_idx, boffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(ctx, TOK_R_PAREN);
|
if (!expect(ctx, TOK_R_PAREN))
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
@@ -640,6 +768,7 @@ static void parse_match_enum_bindings(spl_comp_t *ctx, int enum_type_idx, int va
|
|||||||
|
|
||||||
static void parse_match_stmt(spl_comp_t *ctx) {
|
static void parse_match_stmt(spl_comp_t *ctx) {
|
||||||
advance(ctx); /* match */
|
advance(ctx); /* match */
|
||||||
|
snprintf(ctx->parse_context, sizeof(ctx->parse_context), "match expression");
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
|
|
||||||
spl_expr_result_t expr = spl_parse_expr(ctx, PREC_MIN);
|
spl_expr_result_t expr = spl_parse_expr(ctx, PREC_MIN);
|
||||||
@@ -658,7 +787,7 @@ static void parse_match_stmt(spl_comp_t *ctx) {
|
|||||||
enum_type_idx = spl_type_elem_type(&ctx->tctx, type_idx);
|
enum_type_idx = spl_type_elem_type(&ctx->tctx, type_idx);
|
||||||
} else if (!(type_idx >= 0 && spl_type_kind(&ctx->tctx, type_idx) == TYPE_BASIC &&
|
} else if (!(type_idx >= 0 && spl_type_kind(&ctx->tctx, type_idx) == TYPE_BASIC &&
|
||||||
spl_type_is_integer(spl_type_basic_type(&ctx->tctx, type_idx)))) {
|
spl_type_is_integer(spl_type_basic_type(&ctx->tctx, type_idx)))) {
|
||||||
spl_comp_error(ctx, "match expression must be an enum or integer type");
|
spl_comp_err_tok(ctx, peek(ctx), "match expression must be an enum or integer type");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -680,7 +809,7 @@ static void parse_match_stmt(spl_comp_t *ctx) {
|
|||||||
int n_jmps = 0;
|
int n_jmps = 0;
|
||||||
|
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
|
while (!ctx->has_error && peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
if (peek(ctx)->type == TOK_COMMA) {
|
if (peek(ctx)->type == TOK_COMMA) {
|
||||||
advance(ctx);
|
advance(ctx);
|
||||||
@@ -708,7 +837,7 @@ static void parse_match_stmt(spl_comp_t *ctx) {
|
|||||||
if (ctx->has_error)
|
if (ctx->has_error)
|
||||||
break;
|
break;
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
if (peek(ctx)->type == TOK_L_PAREN) {
|
if (peek(ctx)->type == TOK_L_PAREN || peek(ctx)->type == TOK_L_BRACKET) {
|
||||||
has_parens = 1;
|
has_parens = 1;
|
||||||
break; /* bindings → must be last in fallthrough group */
|
break; /* bindings → must be last in fallthrough group */
|
||||||
}
|
}
|
||||||
@@ -781,7 +910,8 @@ static void parse_match_stmt(spl_comp_t *ctx) {
|
|||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(ctx, TOK_R_BRACE);
|
if (!expect(ctx, TOK_R_BRACE))
|
||||||
|
return;
|
||||||
|
|
||||||
/* Patch all JMPs to end */
|
/* Patch all JMPs to end */
|
||||||
for (int i = 0; i < n_jmps; i++)
|
for (int i = 0; i < n_jmps; i++)
|
||||||
@@ -815,7 +945,8 @@ static void parse_extern_decl(spl_comp_t *ctx) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* #[extern("vm")] fn ... (legacy) */
|
/* #[extern("vm")] fn ... (legacy) */
|
||||||
expect(ctx, TOK_L_BRACKET);
|
if (!expect(ctx, TOK_L_BRACKET))
|
||||||
|
return;
|
||||||
while (peek(ctx)->type != TOK_R_BRACKET && peek(ctx)->type != TOK_EOF)
|
while (peek(ctx)->type != TOK_R_BRACKET && peek(ctx)->type != TOK_EOF)
|
||||||
advance(ctx);
|
advance(ctx);
|
||||||
if (peek(ctx)->type == TOK_R_BRACKET)
|
if (peek(ctx)->type == TOK_R_BRACKET)
|
||||||
@@ -832,7 +963,8 @@ static void parse_extern_decl(spl_comp_t *ctx) {
|
|||||||
spl_tok_copy_name(fname_tok, fn_name, sizeof(fn_name));
|
spl_tok_copy_name(fname_tok, fn_name, sizeof(fn_name));
|
||||||
|
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
expect(ctx, TOK_L_PAREN);
|
if (!expect(ctx, TOK_L_PAREN))
|
||||||
|
return;
|
||||||
|
|
||||||
/* Count params (we don't store them for extern) */
|
/* Count params (we don't store them for extern) */
|
||||||
int nparams = 0;
|
int nparams = 0;
|
||||||
@@ -860,7 +992,8 @@ static void parse_extern_decl(spl_comp_t *ctx) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
expect(ctx, TOK_R_PAREN);
|
if (!expect(ctx, TOK_R_PAREN))
|
||||||
|
return;
|
||||||
skip_nl(ctx);
|
skip_nl(ctx);
|
||||||
|
|
||||||
/* Return type */
|
/* Return type */
|
||||||
@@ -953,21 +1086,27 @@ void spl_parse_stmt(spl_comp_t *ctx) {
|
|||||||
parse_ret_stmt(ctx);
|
parse_ret_stmt(ctx);
|
||||||
break;
|
break;
|
||||||
case KW_VAR:
|
case KW_VAR:
|
||||||
|
snprintf(ctx->parse_context, sizeof(ctx->parse_context), "variable declaration");
|
||||||
parse_var_decl(ctx, 0);
|
parse_var_decl(ctx, 0);
|
||||||
break;
|
break;
|
||||||
case KW_CONST:
|
case KW_CONST:
|
||||||
|
snprintf(ctx->parse_context, sizeof(ctx->parse_context), "constant declaration");
|
||||||
parse_var_decl(ctx, 1);
|
parse_var_decl(ctx, 1);
|
||||||
break;
|
break;
|
||||||
case KW_IF:
|
case KW_IF:
|
||||||
|
snprintf(ctx->parse_context, sizeof(ctx->parse_context), "if statement");
|
||||||
parse_if_stmt(ctx);
|
parse_if_stmt(ctx);
|
||||||
break;
|
break;
|
||||||
case KW_WHILE:
|
case KW_WHILE:
|
||||||
|
snprintf(ctx->parse_context, sizeof(ctx->parse_context), "while statement");
|
||||||
parse_while_stmt(ctx);
|
parse_while_stmt(ctx);
|
||||||
break;
|
break;
|
||||||
case KW_LOOP:
|
case KW_LOOP:
|
||||||
|
snprintf(ctx->parse_context, sizeof(ctx->parse_context), "loop statement");
|
||||||
parse_loop_stmt(ctx);
|
parse_loop_stmt(ctx);
|
||||||
break;
|
break;
|
||||||
case KW_FOR:
|
case KW_FOR:
|
||||||
|
snprintf(ctx->parse_context, sizeof(ctx->parse_context), "for statement");
|
||||||
parse_for_stmt(ctx);
|
parse_for_stmt(ctx);
|
||||||
break;
|
break;
|
||||||
case KW_BREAK:
|
case KW_BREAK:
|
||||||
@@ -980,6 +1119,7 @@ void spl_parse_stmt(spl_comp_t *ctx) {
|
|||||||
parse_defer_stmt(ctx);
|
parse_defer_stmt(ctx);
|
||||||
break;
|
break;
|
||||||
case KW_MATCH:
|
case KW_MATCH:
|
||||||
|
snprintf(ctx->parse_context, sizeof(ctx->parse_context), "match expression");
|
||||||
parse_match_stmt(ctx);
|
parse_match_stmt(ctx);
|
||||||
break;
|
break;
|
||||||
case KW_TYPE:
|
case KW_TYPE:
|
||||||
@@ -1009,6 +1149,7 @@ void spl_parse_stmt(spl_comp_t *ctx) {
|
|||||||
parse_extern_decl(ctx);
|
parse_extern_decl(ctx);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
snprintf(ctx->parse_context, sizeof(ctx->parse_context), "expression statement");
|
||||||
parse_expr_stmt(ctx);
|
parse_expr_stmt(ctx);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -304,6 +304,32 @@ int spl_type_alias(spl_type_ctx_t *tctx, const char *name, int target_type_idx)
|
|||||||
return 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
|
* Item management
|
||||||
* ============================================================ */
|
* ============================================================ */
|
||||||
@@ -317,6 +343,20 @@ 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.type_idx = field_type_idx;
|
||||||
item.aggregate_field.offset = 0;
|
item.aggregate_field.offset = 0;
|
||||||
vec_push(t->items, item);
|
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) {
|
||||||
|
spl_type_info_t *t = &vec_at(tctx->types, type_idx);
|
||||||
|
spl_type_item_t item;
|
||||||
|
memset(&item, 0, sizeof(item));
|
||||||
|
item.name = strdup(name);
|
||||||
|
item.item_kind = ITEM_VAR;
|
||||||
|
item.aggregate_field.type_idx = var_type_idx;
|
||||||
|
item.aggregate_field.offset = 0;
|
||||||
|
vec_push(t->items, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
void spl_type_add_variant(spl_type_ctx_t *tctx, int type_idx, const char *name, int data_type_idx) {
|
void spl_type_add_variant(spl_type_ctx_t *tctx, int type_idx, const char *name, int data_type_idx) {
|
||||||
@@ -328,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.type_idx = data_type_idx;
|
||||||
item.enum_field.value = vec_size(t->items);
|
item.enum_field.value = vec_size(t->items);
|
||||||
vec_push(t->items, item);
|
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) {
|
void spl_type_add_method(spl_type_ctx_t *tctx, int type_idx, const char *name, int func_idx) {
|
||||||
@@ -371,14 +413,22 @@ void spl_type_compute_layout(spl_type_ctx_t *tctx, int type_idx) {
|
|||||||
if (type_idx < 0)
|
if (type_idx < 0)
|
||||||
return;
|
return;
|
||||||
spl_type_info_t *t = &vec_at(tctx->types, type_idx);
|
spl_type_info_t *t = &vec_at(tctx->types, type_idx);
|
||||||
if (t->resolved)
|
if (t->resolved == 1)
|
||||||
return;
|
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) {
|
switch (t->kind) {
|
||||||
case TYPE_STRUCT: {
|
case TYPE_STRUCT: {
|
||||||
usize offset = 0;
|
usize offset = 0;
|
||||||
vec_for(t->items, i) {
|
vec_for(t->items, i) {
|
||||||
spl_type_item_t *it = &vec_at(t->items, i);
|
spl_type_item_t *it = &vec_at(t->items, i);
|
||||||
|
if (it->item_kind == ITEM_VAR)
|
||||||
|
continue;
|
||||||
if (it->item_kind == ITEM_FIELD && it->aggregate_field.type_idx >= 0) {
|
if (it->item_kind == ITEM_FIELD && it->aggregate_field.type_idx >= 0) {
|
||||||
spl_type_compute_layout(tctx, it->aggregate_field.type_idx);
|
spl_type_compute_layout(tctx, it->aggregate_field.type_idx);
|
||||||
spl_type_info_t *ft = &vec_at(tctx->types, it->aggregate_field.type_idx);
|
spl_type_info_t *ft = &vec_at(tctx->types, it->aggregate_field.type_idx);
|
||||||
@@ -398,6 +448,8 @@ void spl_type_compute_layout(spl_type_ctx_t *tctx, int type_idx) {
|
|||||||
usize max_sz = 0;
|
usize max_sz = 0;
|
||||||
vec_for(t->items, i) {
|
vec_for(t->items, i) {
|
||||||
spl_type_item_t *it = &vec_at(t->items, i);
|
spl_type_item_t *it = &vec_at(t->items, i);
|
||||||
|
if (it->item_kind == ITEM_VAR)
|
||||||
|
continue;
|
||||||
if (it->item_kind == ITEM_FIELD && it->aggregate_field.type_idx >= 0) {
|
if (it->item_kind == ITEM_FIELD && it->aggregate_field.type_idx >= 0) {
|
||||||
spl_type_compute_layout(tctx, it->aggregate_field.type_idx);
|
spl_type_compute_layout(tctx, it->aggregate_field.type_idx);
|
||||||
spl_type_info_t *ft = &vec_at(tctx->types, it->aggregate_field.type_idx);
|
spl_type_info_t *ft = &vec_at(tctx->types, it->aggregate_field.type_idx);
|
||||||
@@ -519,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) {
|
int spl_type_is_scalar(spl_type_ctx_t *tctx, int type_idx) {
|
||||||
if (type_idx < 0)
|
if (type_idx < 0)
|
||||||
return 1;
|
return 1;
|
||||||
|
type_idx = spl_type_resolve_underlying(tctx, type_idx);
|
||||||
spl_type_info_t *t = &vec_at(tctx->types, 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;
|
return 1;
|
||||||
if (t->kind == TYPE_ENUM) {
|
if (t->kind == TYPE_ENUM) {
|
||||||
vec_for(t->items, i) {
|
vec_for(t->items, i) {
|
||||||
@@ -560,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) {
|
spl_type_t spl_type_emit_type(spl_type_ctx_t *tctx, int type_idx) {
|
||||||
if (type_idx < 0)
|
if (type_idx < 0)
|
||||||
return SPL_I32;
|
return SPL_I32;
|
||||||
|
type_idx = spl_type_resolve_underlying(tctx, type_idx);
|
||||||
spl_type_info_t *t = &vec_at(tctx->types, type_idx);
|
spl_type_info_t *t = &vec_at(tctx->types, type_idx);
|
||||||
if (t->kind == TYPE_BASIC)
|
if (t->kind == TYPE_BASIC)
|
||||||
return t->basic_type;
|
return t->basic_type;
|
||||||
@@ -575,7 +629,14 @@ spl_type_t spl_type_emit_type(spl_type_ctx_t *tctx, int type_idx) {
|
|||||||
return SPL_PTR;
|
return SPL_PTR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define TYPE_STR_BUF_COUNT 8
|
||||||
|
#define TYPE_STR_BUF_SIZE 64
|
||||||
|
|
||||||
const char *spl_type_str(spl_type_ctx_t *tctx, int type_idx) {
|
const char *spl_type_str(spl_type_ctx_t *tctx, int type_idx) {
|
||||||
|
static char b[TYPE_STR_BUF_COUNT][TYPE_STR_BUF_SIZE];
|
||||||
|
static int bi = 0;
|
||||||
|
int my = (bi++) % TYPE_STR_BUF_COUNT;
|
||||||
|
|
||||||
if (type_idx < 0)
|
if (type_idx < 0)
|
||||||
return "<null>";
|
return "<null>";
|
||||||
spl_type_info_t *t = &vec_at(tctx->types, type_idx);
|
spl_type_info_t *t = &vec_at(tctx->types, type_idx);
|
||||||
@@ -616,28 +677,26 @@ const char *spl_type_str(spl_type_ctx_t *tctx, int type_idx) {
|
|||||||
return "<basic>";
|
return "<basic>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case TYPE_PTR: {
|
case TYPE_PTR:
|
||||||
static char buf[64];
|
snprintf(b[my], TYPE_STR_BUF_SIZE, "*%s",
|
||||||
snprintf(buf, sizeof(buf), "*%s", spl_type_str(tctx, spl_type_elem_type(tctx, type_idx)));
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
case TYPE_ARRAY: {
|
|
||||||
static char buf[64];
|
|
||||||
snprintf(buf, sizeof(buf), "[%zu]%s", spl_type_array_len(tctx, type_idx),
|
|
||||||
spl_type_str(tctx, spl_type_elem_type(tctx, type_idx)));
|
spl_type_str(tctx, spl_type_elem_type(tctx, type_idx)));
|
||||||
return buf;
|
return b[my];
|
||||||
}
|
case TYPE_ARRAY:
|
||||||
case TYPE_SLICE: {
|
snprintf(b[my], TYPE_STR_BUF_SIZE, "[%zu]%s", spl_type_array_len(tctx, type_idx),
|
||||||
static char buf[64];
|
spl_type_str(tctx, spl_type_elem_type(tctx, type_idx)));
|
||||||
snprintf(buf, sizeof(buf), "[]%s", spl_type_str(tctx, spl_type_elem_type(tctx, type_idx)));
|
return b[my];
|
||||||
return buf;
|
case TYPE_SLICE:
|
||||||
}
|
snprintf(b[my], TYPE_STR_BUF_SIZE, "[]%s",
|
||||||
|
spl_type_str(tctx, spl_type_elem_type(tctx, type_idx)));
|
||||||
|
return b[my];
|
||||||
case TYPE_STRUCT:
|
case TYPE_STRUCT:
|
||||||
case TYPE_UNION:
|
case TYPE_UNION:
|
||||||
case TYPE_ENUM:
|
case TYPE_ENUM:
|
||||||
return t->name ? t->name : "<?>";
|
return t->name ? t->name : "<?>";
|
||||||
case TYPE_NAME:
|
case TYPE_NAME:
|
||||||
return t->name ? t->name : "<alias>";
|
return t->name ? t->name : "<alias>";
|
||||||
|
case TYPE_FN:
|
||||||
|
return "fn";
|
||||||
default:
|
default:
|
||||||
return "<?>";
|
return "<?>";
|
||||||
}
|
}
|
||||||
@@ -766,13 +825,13 @@ int spl_type_parse(spl_type_ctx_t *tctx, struct spl_comp *ctx) {
|
|||||||
|
|
||||||
int len_val;
|
int len_val;
|
||||||
if (!spl_parse_int_literal(ctx, &len_val)) {
|
if (!spl_parse_int_literal(ctx, &len_val)) {
|
||||||
spl_comp_error(ctx, "expected array length");
|
spl_comp_err_tok(ctx, peek(ctx), "expected array length");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
usize len = (usize)len_val;
|
usize len = (usize)len_val;
|
||||||
|
|
||||||
if (vec_at(ctx->toks, ctx->tok_idx).type != TOK_R_BRACKET) {
|
if (vec_at(ctx->toks, ctx->tok_idx).type != TOK_R_BRACKET) {
|
||||||
spl_comp_error(ctx, "expected ']'");
|
spl_comp_err_tok(ctx, peek(ctx), "expected ']'");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
ctx->tok_idx++;
|
ctx->tok_idx++;
|
||||||
@@ -839,6 +898,39 @@ int spl_type_parse(spl_type_ctx_t *tctx, struct spl_comp *ctx) {
|
|||||||
return ti;
|
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 */
|
/* Identifier: basic type or named type */
|
||||||
if (tok->type == TOK_IDENT || ((int)tok->type >= (int)KW_AS && (int)tok->type <= (int)KW_ANY)) {
|
if (tok->type == TOK_IDENT || ((int)tok->type >= (int)KW_AS && (int)tok->type <= (int)KW_ANY)) {
|
||||||
const char *name = tok->lexeme;
|
const char *name = tok->lexeme;
|
||||||
@@ -856,6 +948,14 @@ int spl_type_parse(spl_type_ctx_t *tctx, struct spl_comp *ctx) {
|
|||||||
id_buf[cplen] = '\0';
|
id_buf[cplen] = '\0';
|
||||||
|
|
||||||
int found = spl_type_resolve(tctx, id_buf);
|
int found = spl_type_resolve(tctx, id_buf);
|
||||||
|
if (found < 0 && tctx->current_type_idx >= 0) {
|
||||||
|
const char *cur_type_name = spl_type_name(tctx, tctx->current_type_idx);
|
||||||
|
if (cur_type_name) {
|
||||||
|
char qualified[512];
|
||||||
|
snprintf(qualified, sizeof(qualified), "%s.%s", cur_type_name, id_buf);
|
||||||
|
found = spl_type_resolve(tctx, qualified);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (found >= 0) {
|
if (found >= 0) {
|
||||||
ctx->tok_idx++;
|
ctx->tok_idx++;
|
||||||
|
|
||||||
@@ -900,10 +1000,11 @@ int spl_type_parse(spl_type_ctx_t *tctx, struct spl_comp *ctx) {
|
|||||||
return ti;
|
return ti;
|
||||||
}
|
}
|
||||||
|
|
||||||
spl_comp_error(ctx, "unknown type '%s'", id_buf);
|
spl_comp_err_tok(ctx, tok, "unknown type '%s'", id_buf);
|
||||||
|
ctx->tok_idx++;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
spl_comp_error(ctx, "expected type");
|
spl_comp_err_tok(ctx, peek(ctx), "expected type");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ typedef enum {
|
|||||||
TYPE_UNION,
|
TYPE_UNION,
|
||||||
TYPE_ENUM,
|
TYPE_ENUM,
|
||||||
TYPE_NAME,
|
TYPE_NAME,
|
||||||
|
TYPE_FN,
|
||||||
TYPE_COUNT,
|
TYPE_COUNT,
|
||||||
} spl_type_kind_t;
|
} spl_type_kind_t;
|
||||||
|
|
||||||
@@ -32,6 +33,7 @@ typedef enum {
|
|||||||
ITEM_VARIANT, /* enum_field : enum variant */
|
ITEM_VARIANT, /* enum_field : enum variant */
|
||||||
ITEM_METHOD, /* method : type method */
|
ITEM_METHOD, /* method : type method */
|
||||||
ITEM_NESTED_TYPE, /* nested_type : child type decl */
|
ITEM_NESTED_TYPE, /* nested_type : child type decl */
|
||||||
|
ITEM_VAR, /* aggregate_field : type-level var */
|
||||||
} spl_type_item_kind_t;
|
} spl_type_item_kind_t;
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
@@ -122,12 +124,14 @@ 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_union(spl_type_ctx_t *tctx, const char *name);
|
||||||
int spl_type_enum(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_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
|
* Item management — add members to aggregate types
|
||||||
* ============================================================ */
|
* ============================================================ */
|
||||||
|
|
||||||
void spl_type_add_field(spl_type_ctx_t *tctx, int type_idx, const char *name, int field_type_idx);
|
void spl_type_add_field(spl_type_ctx_t *tctx, int type_idx, const char *name, int field_type_idx);
|
||||||
|
void spl_type_add_var(spl_type_ctx_t *tctx, int type_idx, const char *name, int var_type_idx);
|
||||||
void spl_type_add_variant(spl_type_ctx_t *tctx, int type_idx, const char *name, int data_type_idx);
|
void spl_type_add_variant(spl_type_ctx_t *tctx, int type_idx, const char *name, int data_type_idx);
|
||||||
void spl_type_add_method(spl_type_ctx_t *tctx, int type_idx, const char *name, int func_idx);
|
void spl_type_add_method(spl_type_ctx_t *tctx, int type_idx, const char *name, int func_idx);
|
||||||
void spl_type_add_nested(spl_type_ctx_t *tctx, int parent_idx, const char *name,
|
void spl_type_add_nested(spl_type_ctx_t *tctx, int parent_idx, const char *name,
|
||||||
|
|||||||
Reference in New Issue
Block a user