|
|
|
@@ -1,39 +1,46 @@
|
|
|
|
/* spl_expr.c — Expression parser + codegen (Pratt parser / precedence climbing) */
|
|
|
|
/* spl_expr.c — Expression parser + codegen (Pratt parser / precedence climbing) */
|
|
|
|
|
|
|
|
|
|
|
|
#include "spl_comp.h"
|
|
|
|
#include "spl_comp.h"
|
|
|
|
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ============================================================
|
|
|
|
/* ============================================================
|
|
|
|
* Token helpers
|
|
|
|
* Token helpers
|
|
|
|
* ============================================================ */
|
|
|
|
* ============================================================ */
|
|
|
|
|
|
|
|
|
|
|
|
static spl_tok_t *peek(spl_comp_t *ctx) {
|
|
|
|
static spl_tok_t *peek(spl_comp_t *ctx) { return &vec_at(ctx->toks, ctx->tok_idx); }
|
|
|
|
return &vec_at(ctx->toks, ctx->tok_idx);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static spl_tok_t *advance(spl_comp_t *ctx) {
|
|
|
|
static spl_tok_t *advance(spl_comp_t *ctx) {
|
|
|
|
spl_tok_t *t = &vec_at(ctx->toks, ctx->tok_idx);
|
|
|
|
spl_tok_t *t = &vec_at(ctx->toks, ctx->tok_idx);
|
|
|
|
if (t->type != TOK_EOF) ctx->tok_idx++;
|
|
|
|
if (t->type != TOK_EOF)
|
|
|
|
|
|
|
|
ctx->tok_idx++;
|
|
|
|
return t;
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int match(spl_comp_t *ctx, spl_tok_type_t type) {
|
|
|
|
static int match(spl_comp_t *ctx, spl_tok_type_t type) {
|
|
|
|
if (peek(ctx)->type == type) { advance(ctx); return 1; }
|
|
|
|
if (peek(ctx)->type == type) {
|
|
|
|
|
|
|
|
advance(ctx);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int expect(spl_comp_t *ctx, spl_tok_type_t type) {
|
|
|
|
static int expect(spl_comp_t *ctx, spl_tok_type_t type) {
|
|
|
|
if (peek(ctx)->type == type) { advance(ctx); return 1; }
|
|
|
|
if (peek(ctx)->type == type) {
|
|
|
|
spl_comp_error(ctx, "expected '%s', got '%s'", spl_tok_type_name(type), spl_tok_type_name(peek(ctx)->type));
|
|
|
|
advance(ctx);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
spl_comp_error(ctx, "expected '%s', got '%s'", spl_tok_type_name(type),
|
|
|
|
|
|
|
|
spl_tok_type_name(peek(ctx)->type));
|
|
|
|
return 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Skip newline tokens */
|
|
|
|
/* Skip newline tokens */
|
|
|
|
static void skip_nl(spl_comp_t *ctx) {
|
|
|
|
static void skip_nl(spl_comp_t *ctx) {
|
|
|
|
while (peek(ctx)->type == TOK_ENDLINE) advance(ctx);
|
|
|
|
while (peek(ctx)->type == TOK_ENDLINE)
|
|
|
|
|
|
|
|
advance(ctx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Check if current token starts a statement */
|
|
|
|
/* Check if current token starts a statement */
|
|
|
|
@@ -48,22 +55,48 @@ static int is_stmt_start(spl_comp_t *ctx) {
|
|
|
|
|
|
|
|
|
|
|
|
static int tok_prec(spl_tok_type_t t) {
|
|
|
|
static int tok_prec(spl_tok_type_t t) {
|
|
|
|
switch (t) {
|
|
|
|
switch (t) {
|
|
|
|
case TOK_OR_OR: return PREC_LOGOR;
|
|
|
|
case TOK_OR_OR:
|
|
|
|
case TOK_AND_AND: return PREC_LOGAND;
|
|
|
|
return PREC_LOGOR;
|
|
|
|
case TOK_OR: return PREC_OR;
|
|
|
|
case TOK_AND_AND:
|
|
|
|
case TOK_XOR: return PREC_XOR;
|
|
|
|
return PREC_LOGAND;
|
|
|
|
case TOK_AND: return PREC_AND;
|
|
|
|
case TOK_OR:
|
|
|
|
case TOK_EQ: case TOK_NEQ: return PREC_CMPEQ;
|
|
|
|
return PREC_OR;
|
|
|
|
case TOK_LT: case TOK_LE: case TOK_GT: case TOK_GE: return PREC_CMP;
|
|
|
|
case TOK_XOR:
|
|
|
|
case TOK_L_SH: case TOK_R_SH: return PREC_SHIFT;
|
|
|
|
return PREC_XOR;
|
|
|
|
case TOK_ADD: case TOK_SUB: return PREC_ADD;
|
|
|
|
case TOK_AND:
|
|
|
|
case TOK_MUL: case TOK_DIV: case TOK_MOD: return PREC_MUL;
|
|
|
|
return PREC_AND;
|
|
|
|
case TOK_ASSIGN: case TOK_ASSIGN_ADD: case TOK_ASSIGN_SUB:
|
|
|
|
case TOK_EQ:
|
|
|
|
case TOK_ASSIGN_MUL: case TOK_ASSIGN_DIV: case TOK_ASSIGN_MOD:
|
|
|
|
case TOK_NEQ:
|
|
|
|
case TOK_ASSIGN_AND: case TOK_ASSIGN_OR: case TOK_ASSIGN_XOR:
|
|
|
|
return PREC_CMPEQ;
|
|
|
|
case TOK_ASSIGN_L_SH: case TOK_ASSIGN_R_SH:
|
|
|
|
case TOK_LT:
|
|
|
|
|
|
|
|
case TOK_LE:
|
|
|
|
|
|
|
|
case TOK_GT:
|
|
|
|
|
|
|
|
case TOK_GE:
|
|
|
|
|
|
|
|
return PREC_CMP;
|
|
|
|
|
|
|
|
case TOK_L_SH:
|
|
|
|
|
|
|
|
case TOK_R_SH:
|
|
|
|
|
|
|
|
return PREC_SHIFT;
|
|
|
|
|
|
|
|
case TOK_ADD:
|
|
|
|
|
|
|
|
case TOK_SUB:
|
|
|
|
|
|
|
|
return PREC_ADD;
|
|
|
|
|
|
|
|
case TOK_MUL:
|
|
|
|
|
|
|
|
case TOK_DIV:
|
|
|
|
|
|
|
|
case TOK_MOD:
|
|
|
|
|
|
|
|
return PREC_MUL;
|
|
|
|
|
|
|
|
case TOK_ASSIGN:
|
|
|
|
|
|
|
|
case TOK_ASSIGN_ADD:
|
|
|
|
|
|
|
|
case TOK_ASSIGN_SUB:
|
|
|
|
|
|
|
|
case TOK_ASSIGN_MUL:
|
|
|
|
|
|
|
|
case TOK_ASSIGN_DIV:
|
|
|
|
|
|
|
|
case TOK_ASSIGN_MOD:
|
|
|
|
|
|
|
|
case TOK_ASSIGN_AND:
|
|
|
|
|
|
|
|
case TOK_ASSIGN_OR:
|
|
|
|
|
|
|
|
case TOK_ASSIGN_XOR:
|
|
|
|
|
|
|
|
case TOK_ASSIGN_L_SH:
|
|
|
|
|
|
|
|
case TOK_ASSIGN_R_SH:
|
|
|
|
return PREC_ASSIGN;
|
|
|
|
return PREC_ASSIGN;
|
|
|
|
default: return PREC_MIN;
|
|
|
|
default:
|
|
|
|
|
|
|
|
return PREC_MIN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -114,7 +147,7 @@ static void emit_slice_create(spl_comp_t *ctx, usize stride) {
|
|
|
|
* Stack in: [slice_struct_addr, index]
|
|
|
|
* Stack in: [slice_struct_addr, index]
|
|
|
|
* Stack out: [element_addr] */
|
|
|
|
* Stack out: [element_addr] */
|
|
|
|
static void emit_slice_index(spl_comp_t *ctx, spl_type_info_t *elem) {
|
|
|
|
static void emit_slice_index(spl_comp_t *ctx, spl_type_info_t *elem) {
|
|
|
|
usize elem_size = elem ? elem->byte_size : 4;
|
|
|
|
usize elem_size = spl_type_elem_stride(elem);
|
|
|
|
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); /* [index, slice_struct_addr] */
|
|
|
|
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); /* [index, slice_struct_addr] */
|
|
|
|
spl_emit(ctx, SPL_LOAD, SPL_PTR, 0); /* [index, data_ptr] */
|
|
|
|
spl_emit(ctx, SPL_LOAD, SPL_PTR, 0); /* [index, data_ptr] */
|
|
|
|
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); /* [data_ptr, index] */
|
|
|
|
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0); /* [data_ptr, index] */
|
|
|
|
@@ -130,23 +163,40 @@ static void emit_slice_index(spl_comp_t *ctx, spl_type_info_t *elem) {
|
|
|
|
static int binop_to_sir(spl_tok_type_t t, spl_type_t bt) {
|
|
|
|
static int binop_to_sir(spl_tok_type_t t, spl_type_t bt) {
|
|
|
|
int is_signed = (bt == SPL_I32 || bt == SPL_I64 || bt == SPL_I8 || bt == SPL_I16);
|
|
|
|
int is_signed = (bt == SPL_I32 || bt == SPL_I64 || bt == SPL_I8 || bt == SPL_I16);
|
|
|
|
switch (t) {
|
|
|
|
switch (t) {
|
|
|
|
case TOK_ADD: return SPL_ADD;
|
|
|
|
case TOK_ADD:
|
|
|
|
case TOK_SUB: return SPL_SUB;
|
|
|
|
return SPL_ADD;
|
|
|
|
case TOK_MUL: return SPL_MUL;
|
|
|
|
case TOK_SUB:
|
|
|
|
case TOK_DIV: return is_signed ? SPL_DIV_S : SPL_DIV_U;
|
|
|
|
return SPL_SUB;
|
|
|
|
case TOK_MOD: return is_signed ? SPL_REM_S : SPL_REM_U;
|
|
|
|
case TOK_MUL:
|
|
|
|
case TOK_AND: return SPL_AND;
|
|
|
|
return SPL_MUL;
|
|
|
|
case TOK_OR: return SPL_OR;
|
|
|
|
case TOK_DIV:
|
|
|
|
case TOK_XOR: return SPL_XOR;
|
|
|
|
return is_signed ? SPL_DIV_S : SPL_DIV_U;
|
|
|
|
case TOK_L_SH: return SPL_SHL;
|
|
|
|
case TOK_MOD:
|
|
|
|
case TOK_R_SH: return is_signed ? SPL_SHR_S : SPL_SHR_U;
|
|
|
|
return is_signed ? SPL_REM_S : SPL_REM_U;
|
|
|
|
case TOK_EQ: return SPL_EQ;
|
|
|
|
case TOK_AND:
|
|
|
|
case TOK_NEQ: return SPL_NE;
|
|
|
|
return SPL_AND;
|
|
|
|
case TOK_LT: return is_signed ? SPL_SLT : SPL_ULT;
|
|
|
|
case TOK_OR:
|
|
|
|
case TOK_LE: return is_signed ? SPL_SLE : SPL_ULE;
|
|
|
|
return SPL_OR;
|
|
|
|
case TOK_GT: return is_signed ? SPL_SGT : SPL_UGT;
|
|
|
|
case TOK_XOR:
|
|
|
|
case TOK_GE: return is_signed ? SPL_SGE : SPL_UGE;
|
|
|
|
return SPL_XOR;
|
|
|
|
default: return -1;
|
|
|
|
case TOK_L_SH:
|
|
|
|
|
|
|
|
return SPL_SHL;
|
|
|
|
|
|
|
|
case TOK_R_SH:
|
|
|
|
|
|
|
|
return is_signed ? SPL_SHR_S : SPL_SHR_U;
|
|
|
|
|
|
|
|
case TOK_EQ:
|
|
|
|
|
|
|
|
return SPL_EQ;
|
|
|
|
|
|
|
|
case TOK_NEQ:
|
|
|
|
|
|
|
|
return SPL_NE;
|
|
|
|
|
|
|
|
case TOK_LT:
|
|
|
|
|
|
|
|
return is_signed ? SPL_SLT : SPL_ULT;
|
|
|
|
|
|
|
|
case TOK_LE:
|
|
|
|
|
|
|
|
return is_signed ? SPL_SLE : SPL_ULE;
|
|
|
|
|
|
|
|
case TOK_GT:
|
|
|
|
|
|
|
|
return is_signed ? SPL_SGT : SPL_UGT;
|
|
|
|
|
|
|
|
case TOK_GE:
|
|
|
|
|
|
|
|
return is_signed ? SPL_SGE : SPL_UGE;
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -157,11 +207,15 @@ static int binop_to_sir(spl_tok_type_t t, spl_type_t bt) {
|
|
|
|
static int64_t parse_int(const char *s, usize len) {
|
|
|
|
static int64_t parse_int(const char *s, usize len) {
|
|
|
|
char buf[64];
|
|
|
|
char buf[64];
|
|
|
|
usize clen = len < 63 ? len : 63;
|
|
|
|
usize clen = len < 63 ? len : 63;
|
|
|
|
memcpy(buf, s, clen); buf[clen] = '\0';
|
|
|
|
memcpy(buf, s, clen);
|
|
|
|
|
|
|
|
buf[clen] = '\0';
|
|
|
|
if (clen > 2 && buf[0] == '0') {
|
|
|
|
if (clen > 2 && buf[0] == '0') {
|
|
|
|
if (buf[1] == 'x' || buf[1] == 'X') return (int64_t)strtoll(buf, NULL, 16);
|
|
|
|
if (buf[1] == 'x' || buf[1] == 'X')
|
|
|
|
if (buf[1] == 'b' || buf[1] == 'B') return (int64_t)strtoll(buf + 2, NULL, 2);
|
|
|
|
return (int64_t)strtoll(buf, NULL, 16);
|
|
|
|
if (buf[1] == 'o' || buf[1] == 'O') return (int64_t)strtoll(buf + 2, NULL, 8);
|
|
|
|
if (buf[1] == 'b' || buf[1] == 'B')
|
|
|
|
|
|
|
|
return (int64_t)strtoll(buf + 2, NULL, 2);
|
|
|
|
|
|
|
|
if (buf[1] == 'o' || buf[1] == 'O')
|
|
|
|
|
|
|
|
return (int64_t)strtoll(buf + 2, NULL, 8);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (int64_t)strtoll(buf, NULL, 10);
|
|
|
|
return (int64_t)strtoll(buf, NULL, 10);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -182,7 +236,8 @@ static spl_expr_result_t parse_float_literal(spl_comp_t *ctx) {
|
|
|
|
spl_tok_t *t = advance(ctx);
|
|
|
|
spl_tok_t *t = advance(ctx);
|
|
|
|
char buf[64];
|
|
|
|
char buf[64];
|
|
|
|
usize clen = t->len < 63 ? t->len : 63;
|
|
|
|
usize clen = t->len < 63 ? t->len : 63;
|
|
|
|
memcpy(buf, t->lexeme, clen); buf[clen] = '\0';
|
|
|
|
memcpy(buf, t->lexeme, clen);
|
|
|
|
|
|
|
|
buf[clen] = '\0';
|
|
|
|
double val = strtod(buf, NULL);
|
|
|
|
double val = strtod(buf, NULL);
|
|
|
|
spl_emit(ctx, SPL_PUSH, SPL_F64, (spl_val_t)(int64_t)val);
|
|
|
|
spl_emit(ctx, SPL_PUSH, SPL_F64, (spl_val_t)(int64_t)val);
|
|
|
|
(void)val;
|
|
|
|
(void)val;
|
|
|
|
@@ -251,7 +306,8 @@ static spl_expr_result_t parse_array_literal(spl_comp_t *ctx) {
|
|
|
|
spl_tok_t *len_tok = advance(ctx);
|
|
|
|
spl_tok_t *len_tok = advance(ctx);
|
|
|
|
if (len_tok->type != TOK_INT_LITERAL) {
|
|
|
|
if (len_tok->type != TOK_INT_LITERAL) {
|
|
|
|
spl_comp_error(ctx, "expected array length");
|
|
|
|
spl_comp_error(ctx, "expected array length");
|
|
|
|
spl_expr_result_t r = {0}; return r;
|
|
|
|
spl_expr_result_t r = {0};
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
usize len = (usize)strtoull(len_tok->lexeme, NULL, 0);
|
|
|
|
usize len = (usize)strtoull(len_tok->lexeme, NULL, 0);
|
|
|
|
|
|
|
|
|
|
|
|
@@ -262,7 +318,8 @@ static spl_expr_result_t parse_array_literal(spl_comp_t *ctx) {
|
|
|
|
/* Parse element type */
|
|
|
|
/* Parse element type */
|
|
|
|
spl_type_info_t *elem_type = spl_parse_type(ctx);
|
|
|
|
spl_type_info_t *elem_type = spl_parse_type(ctx);
|
|
|
|
if (!elem_type) {
|
|
|
|
if (!elem_type) {
|
|
|
|
spl_expr_result_t r = {0}; return r;
|
|
|
|
spl_expr_result_t r = {0};
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
skip_nl(ctx);
|
|
|
|
skip_nl(ctx);
|
|
|
|
@@ -272,13 +329,15 @@ static spl_expr_result_t parse_array_literal(spl_comp_t *ctx) {
|
|
|
|
/* Parse each element value */
|
|
|
|
/* Parse each element value */
|
|
|
|
for (usize i = 0; i < len; i++) {
|
|
|
|
for (usize i = 0; i < len; i++) {
|
|
|
|
if (i > 0) {
|
|
|
|
if (i > 0) {
|
|
|
|
if (peek(ctx)->type == TOK_COMMA) advance(ctx);
|
|
|
|
if (peek(ctx)->type == TOK_COMMA)
|
|
|
|
|
|
|
|
advance(ctx);
|
|
|
|
skip_nl(ctx);
|
|
|
|
skip_nl(ctx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
spl_parse_expr(ctx, PREC_MIN);
|
|
|
|
spl_parse_expr(ctx, PREC_MIN);
|
|
|
|
skip_nl(ctx);
|
|
|
|
skip_nl(ctx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (peek(ctx)->type == TOK_COMMA) advance(ctx); /* trailing comma */
|
|
|
|
if (peek(ctx)->type == TOK_COMMA)
|
|
|
|
|
|
|
|
advance(ctx); /* trailing comma */
|
|
|
|
|
|
|
|
|
|
|
|
skip_nl(ctx);
|
|
|
|
skip_nl(ctx);
|
|
|
|
expect(ctx, TOK_R_BRACE);
|
|
|
|
expect(ctx, TOK_R_BRACE);
|
|
|
|
@@ -300,7 +359,8 @@ static spl_type_info_t *parse_qualified_type(spl_comp_t *ctx, const char *name)
|
|
|
|
spl_tok_t *vtok = advance(ctx);
|
|
|
|
spl_tok_t *vtok = advance(ctx);
|
|
|
|
char vname[256];
|
|
|
|
char vname[256];
|
|
|
|
usize vn = vtok->len < 255 ? vtok->len : 255;
|
|
|
|
usize vn = vtok->len < 255 ? vtok->len : 255;
|
|
|
|
memcpy(vname, vtok->lexeme, vn); vname[vn] = '\0';
|
|
|
|
memcpy(vname, vtok->lexeme, vn);
|
|
|
|
|
|
|
|
vname[vn] = '\0';
|
|
|
|
/* For enum values, just emit the tag as integer */
|
|
|
|
/* For enum values, just emit the tag as integer */
|
|
|
|
if (t->kind == TYPE_ENUM) {
|
|
|
|
if (t->kind == TYPE_ENUM) {
|
|
|
|
vec_for(t->variants, vi) {
|
|
|
|
vec_for(t->variants, vi) {
|
|
|
|
@@ -318,14 +378,16 @@ static spl_expr_result_t parse_ident(spl_comp_t *ctx) {
|
|
|
|
spl_tok_t *t = advance(ctx);
|
|
|
|
spl_tok_t *t = advance(ctx);
|
|
|
|
char name[256];
|
|
|
|
char name[256];
|
|
|
|
usize nlen = t->len < 255 ? t->len : 255;
|
|
|
|
usize nlen = t->len < 255 ? t->len : 255;
|
|
|
|
memcpy(name, t->lexeme, nlen); name[nlen] = '\0';
|
|
|
|
memcpy(name, t->lexeme, nlen);
|
|
|
|
|
|
|
|
name[nlen] = '\0';
|
|
|
|
|
|
|
|
|
|
|
|
/* Check if it's a function call: ident(...) */
|
|
|
|
/* Check if it's a function call: ident(...) */
|
|
|
|
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);
|
|
|
|
spl_comp_error(ctx, "unknown function '%s'", name);
|
|
|
|
spl_expr_result_t r = {0}; return r;
|
|
|
|
spl_expr_result_t r = {0};
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
spl_func_info_t *f = &vec_at(ctx->funcs, fi);
|
|
|
|
spl_func_info_t *f = &vec_at(ctx->funcs, fi);
|
|
|
|
advance(ctx); /* skip ( */
|
|
|
|
advance(ctx); /* skip ( */
|
|
|
|
@@ -336,7 +398,10 @@ static spl_expr_result_t parse_ident(spl_comp_t *ctx) {
|
|
|
|
spl_expr_result_t arg = spl_parse_expr(ctx, PREC_MIN);
|
|
|
|
spl_expr_result_t arg = spl_parse_expr(ctx, PREC_MIN);
|
|
|
|
(void)arg;
|
|
|
|
(void)arg;
|
|
|
|
nargs++;
|
|
|
|
nargs++;
|
|
|
|
if (peek(ctx)->type == TOK_COMMA) { advance(ctx); continue; }
|
|
|
|
if (peek(ctx)->type == TOK_COMMA) {
|
|
|
|
|
|
|
|
advance(ctx);
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -347,7 +412,8 @@ static spl_expr_result_t parse_ident(spl_comp_t *ctx) {
|
|
|
|
int nidx = -1;
|
|
|
|
int nidx = -1;
|
|
|
|
vec_for(ctx->prog.natives, ni) {
|
|
|
|
vec_for(ctx->prog.natives, ni) {
|
|
|
|
if (strcmp(vec_at(ctx->prog.natives, ni).name, f->name) == 0) {
|
|
|
|
if (strcmp(vec_at(ctx->prog.natives, ni).name, f->name) == 0) {
|
|
|
|
nidx = (int)ni; break;
|
|
|
|
nidx = (int)ni;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (nidx < 0) {
|
|
|
|
if (nidx < 0) {
|
|
|
|
@@ -419,9 +485,11 @@ static spl_expr_result_t parse_group(spl_comp_t *ctx) {
|
|
|
|
static spl_expr_result_t parse_prefix_op(spl_comp_t *ctx) {
|
|
|
|
static spl_expr_result_t parse_prefix_op(spl_comp_t *ctx) {
|
|
|
|
spl_tok_t *op = advance(ctx);
|
|
|
|
spl_tok_t *op = advance(ctx);
|
|
|
|
|
|
|
|
|
|
|
|
if (op->type == TOK_AND) ctx->addr_of_mode = 1;
|
|
|
|
if (op->type == TOK_AND)
|
|
|
|
|
|
|
|
ctx->addr_of_mode = 1;
|
|
|
|
spl_expr_result_t right = spl_parse_expr(ctx, PREC_PREFIX);
|
|
|
|
spl_expr_result_t right = spl_parse_expr(ctx, PREC_PREFIX);
|
|
|
|
if (op->type == TOK_AND) ctx->addr_of_mode = 0;
|
|
|
|
if (op->type == TOK_AND)
|
|
|
|
|
|
|
|
ctx->addr_of_mode = 0;
|
|
|
|
|
|
|
|
|
|
|
|
switch (op->type) {
|
|
|
|
switch (op->type) {
|
|
|
|
case TOK_SUB:
|
|
|
|
case TOK_SUB:
|
|
|
|
@@ -478,15 +546,26 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) {
|
|
|
|
skip_nl(ctx);
|
|
|
|
skip_nl(ctx);
|
|
|
|
|
|
|
|
|
|
|
|
spl_tok_t *tok = peek(ctx);
|
|
|
|
spl_tok_t *tok = peek(ctx);
|
|
|
|
if (!tok) { spl_expr_result_t r = {0}; return r; }
|
|
|
|
if (!tok) {
|
|
|
|
|
|
|
|
spl_expr_result_t r = {0};
|
|
|
|
|
|
|
|
return r;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
spl_expr_result_t left = {0};
|
|
|
|
spl_expr_result_t left = {0};
|
|
|
|
|
|
|
|
|
|
|
|
switch (tok->type) {
|
|
|
|
switch (tok->type) {
|
|
|
|
case TOK_INT_LITERAL: left = parse_int_literal(ctx); break;
|
|
|
|
case TOK_INT_LITERAL:
|
|
|
|
case TOK_FLOAT_LITERAL: left = parse_float_literal(ctx); break;
|
|
|
|
left = parse_int_literal(ctx);
|
|
|
|
case TOK_CHAR_LITERAL: left = parse_char_literal(ctx); break;
|
|
|
|
break;
|
|
|
|
case TOK_STRING_LITERAL: left = parse_string_literal(ctx); break;
|
|
|
|
case TOK_FLOAT_LITERAL:
|
|
|
|
|
|
|
|
left = parse_float_literal(ctx);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TOK_CHAR_LITERAL:
|
|
|
|
|
|
|
|
left = parse_char_literal(ctx);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case TOK_STRING_LITERAL:
|
|
|
|
|
|
|
|
left = parse_string_literal(ctx);
|
|
|
|
|
|
|
|
break;
|
|
|
|
case KW_TRUE:
|
|
|
|
case KW_TRUE:
|
|
|
|
advance(ctx);
|
|
|
|
advance(ctx);
|
|
|
|
spl_emit(ctx, SPL_PUSH, SPL_I32, 1);
|
|
|
|
spl_emit(ctx, SPL_PUSH, SPL_I32, 1);
|
|
|
|
@@ -503,13 +582,19 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) {
|
|
|
|
left = (spl_expr_result_t){spl_type_basic(SPL_PTR), 0};
|
|
|
|
left = (spl_expr_result_t){spl_type_basic(SPL_PTR), 0};
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case TOK_IDENT:
|
|
|
|
case TOK_IDENT:
|
|
|
|
case KW_BOOL: case KW_VOID: case KW_ANY:
|
|
|
|
case KW_BOOL:
|
|
|
|
|
|
|
|
case KW_VOID:
|
|
|
|
|
|
|
|
case KW_ANY:
|
|
|
|
left = parse_ident(ctx);
|
|
|
|
left = parse_ident(ctx);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case TOK_L_PAREN:
|
|
|
|
case TOK_L_PAREN:
|
|
|
|
left = parse_group(ctx);
|
|
|
|
left = parse_group(ctx);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case TOK_SUB: case TOK_NOT: case TOK_BIT_NOT: case TOK_AND: case TOK_MUL:
|
|
|
|
case TOK_SUB:
|
|
|
|
|
|
|
|
case TOK_NOT:
|
|
|
|
|
|
|
|
case TOK_BIT_NOT:
|
|
|
|
|
|
|
|
case TOK_AND:
|
|
|
|
|
|
|
|
case TOK_MUL:
|
|
|
|
left = parse_prefix_op(ctx);
|
|
|
|
left = parse_prefix_op(ctx);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case TOK_L_BRACKET:
|
|
|
|
case TOK_L_BRACKET:
|
|
|
|
@@ -526,7 +611,8 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
char bname[256];
|
|
|
|
char bname[256];
|
|
|
|
usize bnlen = tok->len < 255 ? tok->len : 255;
|
|
|
|
usize bnlen = tok->len < 255 ? tok->len : 255;
|
|
|
|
memcpy(bname, tok->lexeme, bnlen); bname[bnlen] = '\0';
|
|
|
|
memcpy(bname, tok->lexeme, bnlen);
|
|
|
|
|
|
|
|
bname[bnlen] = '\0';
|
|
|
|
advance(ctx); /* consume builtin name */
|
|
|
|
advance(ctx); /* consume builtin name */
|
|
|
|
|
|
|
|
|
|
|
|
if (strcmp(bname, "dbg") == 0) {
|
|
|
|
if (strcmp(bname, "dbg") == 0) {
|
|
|
|
@@ -538,7 +624,10 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) {
|
|
|
|
for (;;) {
|
|
|
|
for (;;) {
|
|
|
|
spl_parse_expr(ctx, PREC_MIN);
|
|
|
|
spl_parse_expr(ctx, PREC_MIN);
|
|
|
|
nargs++;
|
|
|
|
nargs++;
|
|
|
|
if (peek(ctx)->type == TOK_COMMA) { advance(ctx); continue; }
|
|
|
|
if (peek(ctx)->type == TOK_COMMA) {
|
|
|
|
|
|
|
|
advance(ctx);
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -564,7 +653,8 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) {
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
default:
|
|
|
|
/* If it's a keyword-as-type (i32, u8, etc.), parse as function call target or type constructor */
|
|
|
|
/* If it's a keyword-as-type (i32, u8, etc.), parse as function call target or type
|
|
|
|
|
|
|
|
* constructor */
|
|
|
|
if (peek(ctx)->type >= KW_AS && peek(ctx)->type <= KW_ANY) {
|
|
|
|
if (peek(ctx)->type >= KW_AS && peek(ctx)->type <= KW_ANY) {
|
|
|
|
left = parse_ident(ctx);
|
|
|
|
left = parse_ident(ctx);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
@@ -585,7 +675,8 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) {
|
|
|
|
spl_tok_t *field = advance(ctx);
|
|
|
|
spl_tok_t *field = advance(ctx);
|
|
|
|
char fname[256];
|
|
|
|
char fname[256];
|
|
|
|
usize fnl = field->len < 255 ? field->len : 255;
|
|
|
|
usize fnl = field->len < 255 ? field->len : 255;
|
|
|
|
memcpy(fname, field->lexeme, fnl); fname[fnl] = '\0';
|
|
|
|
memcpy(fname, field->lexeme, fnl);
|
|
|
|
|
|
|
|
fname[fnl] = '\0';
|
|
|
|
|
|
|
|
|
|
|
|
/* Check for enum variant: Type.Variant */
|
|
|
|
/* Check for enum variant: Type.Variant */
|
|
|
|
if (left.type && left.type->kind == TYPE_ENUM) {
|
|
|
|
if (left.type && left.type->kind == TYPE_ENUM) {
|
|
|
|
@@ -674,13 +765,16 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) {
|
|
|
|
} else if (strcmp(fname, "ptr") == 0) {
|
|
|
|
} else if (strcmp(fname, "ptr") == 0) {
|
|
|
|
/* ptr is at offset 0 */
|
|
|
|
/* ptr is at offset 0 */
|
|
|
|
spl_emit(ctx, SPL_LOAD, SPL_PTR, 0);
|
|
|
|
spl_emit(ctx, SPL_LOAD, SPL_PTR, 0);
|
|
|
|
left = (spl_expr_result_t){ left.type->elem ? spl_type_ptr(left.type->elem) : spl_type_basic(SPL_PTR), 0 };
|
|
|
|
left = (spl_expr_result_t){left.type->elem ? spl_type_ptr(left.type->elem)
|
|
|
|
|
|
|
|
: spl_type_basic(SPL_PTR),
|
|
|
|
|
|
|
|
0};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Postfix dereference: expr.* */
|
|
|
|
/* Postfix dereference: expr.* */
|
|
|
|
if (strcmp(fname, "*") == 0 && left.type && left.type->kind == TYPE_PTR && left.type->elem) {
|
|
|
|
if (strcmp(fname, "*") == 0 && left.type && left.type->kind == TYPE_PTR &&
|
|
|
|
|
|
|
|
left.type->elem) {
|
|
|
|
if (left.is_lvalue) {
|
|
|
|
if (left.is_lvalue) {
|
|
|
|
spl_emit(ctx, SPL_LOAD, SPL_PTR, 0);
|
|
|
|
spl_emit(ctx, SPL_LOAD, SPL_PTR, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -741,7 +835,7 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) {
|
|
|
|
|
|
|
|
|
|
|
|
/* Generate slice: compute ptr = base + begin * stride, len = end - begin */
|
|
|
|
/* Generate slice: compute ptr = base + begin * stride, len = end - begin */
|
|
|
|
if (left.type && (left.type->kind == TYPE_ARRAY || left.type->kind == TYPE_SLICE)) {
|
|
|
|
if (left.type && (left.type->kind == TYPE_ARRAY || left.type->kind == TYPE_SLICE)) {
|
|
|
|
usize stride = (left.type->kind == TYPE_ARRAY) ? sizeof(spl_val_t) : (left.type->elem ? left.type->elem->byte_size : 4);
|
|
|
|
usize stride = spl_type_elem_stride(left.type->elem);
|
|
|
|
|
|
|
|
|
|
|
|
/* For TYPE_SLICE: convert [struct_addr, begin, end] → [data_ptr, begin, end] */
|
|
|
|
/* For TYPE_SLICE: convert [struct_addr, begin, end] → [data_ptr, begin, end] */
|
|
|
|
if (left.type->kind == TYPE_SLICE) {
|
|
|
|
if (left.type->kind == TYPE_SLICE) {
|
|
|
|
@@ -761,7 +855,8 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) {
|
|
|
|
expect(ctx, TOK_R_BRACKET);
|
|
|
|
expect(ctx, TOK_R_BRACKET);
|
|
|
|
|
|
|
|
|
|
|
|
/* Array/slice/pointer indexing */
|
|
|
|
/* Array/slice/pointer indexing */
|
|
|
|
if (left.type && (left.type->kind == TYPE_ARRAY || left.type->kind == TYPE_PTR || left.type->kind == TYPE_SLICE)) {
|
|
|
|
if (left.type && (left.type->kind == TYPE_ARRAY || left.type->kind == TYPE_PTR ||
|
|
|
|
|
|
|
|
left.type->kind == TYPE_SLICE)) {
|
|
|
|
spl_type_info_t *elem = left.type->elem;
|
|
|
|
spl_type_info_t *elem = left.type->elem;
|
|
|
|
|
|
|
|
|
|
|
|
if (left.type->kind == TYPE_SLICE) {
|
|
|
|
if (left.type->kind == TYPE_SLICE) {
|
|
|
|
@@ -769,9 +864,9 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) {
|
|
|
|
* Load data ptr first, then compute element address. */
|
|
|
|
* Load data ptr first, then compute element address. */
|
|
|
|
emit_slice_index(ctx, elem);
|
|
|
|
emit_slice_index(ctx, elem);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
/* Stack arrays: each element occupies a full slot (sizeof(spl_val_t)) */
|
|
|
|
/* Stack arrays and pointer indexing */
|
|
|
|
usize elem_size = (left.type->kind == TYPE_ARRAY) ? sizeof(spl_val_t) : (elem ? elem->byte_size : 4);
|
|
|
|
usize stride = spl_type_elem_stride(elem);
|
|
|
|
spl_emit(ctx, SPL_PUSH, SPL_U64, elem_size);
|
|
|
|
spl_emit(ctx, SPL_PUSH, SPL_U64, stride);
|
|
|
|
spl_emit(ctx, SPL_MUL, SPL_U64, 0);
|
|
|
|
spl_emit(ctx, SPL_MUL, SPL_U64, 0);
|
|
|
|
spl_emit(ctx, SPL_ADD, SPL_U64, 0);
|
|
|
|
spl_emit(ctx, SPL_ADD, SPL_U64, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -792,7 +887,8 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) {
|
|
|
|
|
|
|
|
|
|
|
|
/* Binary operators */
|
|
|
|
/* Binary operators */
|
|
|
|
int prec = tok_prec(opt);
|
|
|
|
int prec = tok_prec(opt);
|
|
|
|
if (prec == 0 || prec < min_prec) break;
|
|
|
|
if (prec == 0 || prec < min_prec)
|
|
|
|
|
|
|
|
break;
|
|
|
|
advance(ctx);
|
|
|
|
advance(ctx);
|
|
|
|
left = parse_infix(ctx, left, opt);
|
|
|
|
left = parse_infix(ctx, left, opt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -825,10 +921,10 @@ static spl_expr_result_t parse_infix(spl_comp_t *ctx, spl_expr_result_t left, sp
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Assignment operators */
|
|
|
|
/* Assignment operators */
|
|
|
|
if (op == TOK_ASSIGN || op == TOK_ASSIGN_ADD || op == TOK_ASSIGN_SUB ||
|
|
|
|
if (op == TOK_ASSIGN || op == TOK_ASSIGN_ADD || op == TOK_ASSIGN_SUB || op == TOK_ASSIGN_MUL ||
|
|
|
|
op == TOK_ASSIGN_MUL || op == TOK_ASSIGN_DIV || op == TOK_ASSIGN_MOD ||
|
|
|
|
op == TOK_ASSIGN_DIV || op == TOK_ASSIGN_MOD || op == TOK_ASSIGN_AND ||
|
|
|
|
op == TOK_ASSIGN_AND || op == TOK_ASSIGN_OR || op == TOK_ASSIGN_XOR ||
|
|
|
|
op == TOK_ASSIGN_OR || op == TOK_ASSIGN_XOR || op == TOK_ASSIGN_L_SH ||
|
|
|
|
op == TOK_ASSIGN_L_SH || op == TOK_ASSIGN_R_SH) {
|
|
|
|
op == TOK_ASSIGN_R_SH) {
|
|
|
|
|
|
|
|
|
|
|
|
/* RHS must not inherit addr_of_mode from LHS */
|
|
|
|
/* RHS must not inherit addr_of_mode from LHS */
|
|
|
|
int saved_addr_of_mode = ctx->addr_of_mode;
|
|
|
|
int saved_addr_of_mode = ctx->addr_of_mode;
|
|
|
|
@@ -837,9 +933,10 @@ static spl_expr_result_t parse_infix(spl_comp_t *ctx, spl_expr_result_t left, sp
|
|
|
|
ctx->addr_of_mode = saved_addr_of_mode;
|
|
|
|
ctx->addr_of_mode = saved_addr_of_mode;
|
|
|
|
|
|
|
|
|
|
|
|
if (left.is_lvalue) {
|
|
|
|
if (left.is_lvalue) {
|
|
|
|
spl_type_t bt = left.type ?
|
|
|
|
spl_type_t bt = left.type ? (left.type->kind == TYPE_BASIC ? left.type->basic_type
|
|
|
|
(left.type->kind == TYPE_BASIC ? left.type->basic_type :
|
|
|
|
: left.type->kind == TYPE_PTR ? SPL_PTR
|
|
|
|
left.type->kind == TYPE_PTR ? SPL_PTR : SPL_I32) : SPL_I32;
|
|
|
|
: SPL_I32)
|
|
|
|
|
|
|
|
: SPL_I32;
|
|
|
|
if (op == TOK_ASSIGN) {
|
|
|
|
if (op == TOK_ASSIGN) {
|
|
|
|
/* Simple assignment: stack is [addr, rhs] */
|
|
|
|
/* Simple assignment: stack is [addr, rhs] */
|
|
|
|
/* STORE pops TOS=value, TOS-1=address — already correct order */
|
|
|
|
/* STORE pops TOS=value, TOS-1=address — already correct order */
|
|
|
|
|