stage1 优化代码 完成16测试
This commit is contained in:
@@ -512,6 +512,8 @@ LONG WINAPI UnhandledExceptionFilterImpl(EXCEPTION_POINTERS *pExceptionInfo) {
|
||||
void spl_vm_init_ex(spl_vm_t *vm, int stack_size, int call_depth) {
|
||||
#ifdef _WIN32
|
||||
SetUnhandledExceptionFilter(UnhandledExceptionFilterImpl);
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
SetConsoleCP(CP_UTF8);
|
||||
#endif
|
||||
if (!vm)
|
||||
return;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/* spl_comp.c — SPL compiler main logic and codegen helpers */
|
||||
|
||||
#include "spl_comp.h"
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
/* ---- Compiler context init/drop ---- */
|
||||
|
||||
@@ -17,14 +17,14 @@ void spl_comp_init(spl_comp_t *ctx) {
|
||||
map_init(ctx->const_values, MAP_HASH_STR, MAP_CMP_STR);
|
||||
spl_prog_init(&ctx->prog);
|
||||
ctx->error_msg[0] = '\0';
|
||||
ctx->current_type_name = NULL;
|
||||
}
|
||||
|
||||
void spl_comp_drop(spl_comp_t *ctx) {
|
||||
if (!ctx) return;
|
||||
if (!ctx)
|
||||
return;
|
||||
spl_tok_vec_drop(&ctx->toks);
|
||||
vec_for(ctx->scopes, i) {
|
||||
vec_free(vec_at(ctx->scopes, i).vars);
|
||||
}
|
||||
vec_for(ctx->scopes, i) { vec_free(vec_at(ctx->scopes, i).vars); }
|
||||
vec_free(ctx->scopes);
|
||||
vec_free(ctx->funcs);
|
||||
/* Free type defs — complex, leak for now in bootstrap */
|
||||
@@ -38,9 +38,7 @@ void spl_comp_reset(spl_comp_t *ctx) {
|
||||
/* Keep prog, reset everything else */
|
||||
spl_tok_vec_drop(&ctx->toks);
|
||||
vec_init(ctx->toks);
|
||||
vec_for(ctx->scopes, i) {
|
||||
vec_free(vec_at(ctx->scopes, i).vars);
|
||||
}
|
||||
vec_for(ctx->scopes, i) { vec_free(vec_at(ctx->scopes, i).vars); }
|
||||
vec_free(ctx->scopes);
|
||||
vec_init(ctx->scopes);
|
||||
ctx->scope_depth = 0;
|
||||
@@ -57,6 +55,7 @@ void spl_comp_reset(spl_comp_t *ctx) {
|
||||
ctx->defer_count = 0;
|
||||
ctx->next_gdata_idx = 0;
|
||||
ctx->addr_of_mode = 0;
|
||||
ctx->current_type_name = NULL;
|
||||
free(ctx->break_patches);
|
||||
ctx->break_patches = NULL;
|
||||
}
|
||||
@@ -86,13 +85,9 @@ spl_val_t spl_emit_jmp(spl_comp_t *ctx) {
|
||||
return spl_emit(ctx, SPL_JMP, SPL_VOID, 0);
|
||||
}
|
||||
|
||||
spl_val_t spl_emit_bz(spl_comp_t *ctx) {
|
||||
return spl_emit(ctx, SPL_BZ, SPL_VOID, 0);
|
||||
}
|
||||
spl_val_t spl_emit_bz(spl_comp_t *ctx) { return spl_emit(ctx, SPL_BZ, SPL_VOID, 0); }
|
||||
|
||||
spl_val_t spl_emit_bnz(spl_comp_t *ctx) {
|
||||
return spl_emit(ctx, SPL_BNZ, SPL_VOID, 0);
|
||||
}
|
||||
spl_val_t spl_emit_bnz(spl_comp_t *ctx) { return spl_emit(ctx, SPL_BNZ, SPL_VOID, 0); }
|
||||
|
||||
void spl_patch_to_here(spl_comp_t *ctx, spl_val_t addr) {
|
||||
/* Compute relative offset: target - (source + 1) */
|
||||
@@ -113,9 +108,7 @@ void spl_push_scope(spl_comp_t *ctx) {
|
||||
void spl_pop_scope(spl_comp_t *ctx) {
|
||||
if (vec_size(ctx->scopes) > 0) {
|
||||
spl_scope_t *scope = &vec_at(ctx->scopes, vec_size(ctx->scopes) - 1);
|
||||
vec_for(scope->vars, i) {
|
||||
free(vec_at(scope->vars, i).name);
|
||||
}
|
||||
vec_for(scope->vars, i) { free(vec_at(scope->vars, i).name); }
|
||||
vec_free(scope->vars);
|
||||
ctx->scope_depth--;
|
||||
ctx->scopes.size--;
|
||||
@@ -166,8 +159,8 @@ int spl_get_var_slot(spl_comp_t *ctx, const char *name) {
|
||||
|
||||
/* ---- Function management ---- */
|
||||
|
||||
int spl_declare_func(spl_comp_t *ctx, const char *name, spl_type_info_t *ret_type,
|
||||
int nparams, int is_extern, int is_pub) {
|
||||
int spl_declare_func(spl_comp_t *ctx, const char *name, spl_type_info_t *ret_type, int nparams,
|
||||
int is_extern, int is_pub) {
|
||||
spl_func_info_t fi;
|
||||
memset(&fi, 0, sizeof(fi));
|
||||
fi.name = strdup(name);
|
||||
@@ -295,6 +288,7 @@ int spl_compile(spl_comp_t *ctx, const char *source, const char *fname) {
|
||||
|
||||
/* Phase 2-4: Parse and codegen */
|
||||
spl_parse_prog(ctx);
|
||||
if (ctx->has_error) return -1;
|
||||
if (ctx->has_error)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -79,6 +79,7 @@ spl_type_info_t *spl_type_union(const char *name);
|
||||
spl_type_info_t *spl_type_enum(const char *name);
|
||||
void spl_type_add_field(spl_type_info_t *st, const char *name, spl_type_info_t *ftype);
|
||||
void spl_type_add_variant(spl_type_info_t *et, const char *name, spl_type_info_t *dtype);
|
||||
void spl_type_add_method(spl_type_info_t *t, const char *name, int func_idx);
|
||||
void spl_type_compute_layout(spl_type_info_t *t);
|
||||
usize spl_type_size(spl_type_info_t *t);
|
||||
usize spl_type_slot_count(spl_type_info_t *t);
|
||||
@@ -161,6 +162,7 @@ typedef struct {
|
||||
int current_func_idx;
|
||||
spl_type_info_t *current_ret_type;
|
||||
int current_local_slot; /* next free local slot */
|
||||
const char *current_type_name; /* name of type whose body we're parsing (for method short-name lookup) */
|
||||
|
||||
/* Loop context for break/continue */
|
||||
int in_loop;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* spl_expr.c — Expression parser + codegen (Pratt parser / precedence climbing) */
|
||||
|
||||
#include "spl_comp.h"
|
||||
#include <ctype.h>
|
||||
#include "spl_lex_util.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -10,32 +10,6 @@
|
||||
* Token helpers
|
||||
* ============================================================ */
|
||||
|
||||
/* Token helpers — using shared peek/advance from spl_parser.c */
|
||||
|
||||
static int match(spl_comp_t *ctx, spl_tok_type_t type) {
|
||||
if (peek(ctx)->type == type) {
|
||||
advance(ctx);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int expect(spl_comp_t *ctx, spl_tok_type_t type) {
|
||||
if (peek(ctx)->type == 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;
|
||||
}
|
||||
|
||||
/* Skip newline tokens */
|
||||
static void skip_nl(spl_comp_t *ctx) {
|
||||
while (peek(ctx)->type == TOK_ENDLINE)
|
||||
advance(ctx);
|
||||
}
|
||||
|
||||
/* Check if current token starts a statement */
|
||||
static int is_stmt_start(spl_comp_t *ctx) {
|
||||
spl_tok_type_t t = peek(ctx)->type;
|
||||
@@ -155,8 +129,8 @@ static void emit_slice_index(spl_comp_t *ctx, spl_type_info_t *elem) {
|
||||
* Uses qualified name (TypeName.Member) first for namespace isolation,
|
||||
* then falls back to simple name lookup.
|
||||
* Returns 1 if resolved. */
|
||||
static int spl_resolve_type_member(spl_comp_t *ctx, spl_type_info_t *type,
|
||||
const char *field, spl_expr_result_t *result) {
|
||||
static int spl_resolve_type_member(spl_comp_t *ctx, spl_type_info_t *type, const char *field,
|
||||
spl_expr_result_t *result) {
|
||||
/* For enum: check variants first */
|
||||
if (type->kind == TYPE_ENUM) {
|
||||
vec_for(type->variants, i) {
|
||||
@@ -198,17 +172,28 @@ static int spl_resolve_type_member(spl_comp_t *ctx, spl_type_info_t *type,
|
||||
/* Map assignment operator token (e.g. +=) to corresponding binary operator (e.g. +) */
|
||||
static spl_tok_type_t assign_to_binop(spl_tok_type_t t) {
|
||||
switch (t) {
|
||||
case TOK_ASSIGN_ADD: return TOK_ADD;
|
||||
case TOK_ASSIGN_SUB: return TOK_SUB;
|
||||
case TOK_ASSIGN_MUL: return TOK_MUL;
|
||||
case TOK_ASSIGN_DIV: return TOK_DIV;
|
||||
case TOK_ASSIGN_MOD: return TOK_MOD;
|
||||
case TOK_ASSIGN_AND: return TOK_AND;
|
||||
case TOK_ASSIGN_OR: return TOK_OR;
|
||||
case TOK_ASSIGN_XOR: return TOK_XOR;
|
||||
case TOK_ASSIGN_L_SH: return TOK_L_SH;
|
||||
case TOK_ASSIGN_R_SH: return TOK_R_SH;
|
||||
default: return (spl_tok_type_t)-1;
|
||||
case TOK_ASSIGN_ADD:
|
||||
return TOK_ADD;
|
||||
case TOK_ASSIGN_SUB:
|
||||
return TOK_SUB;
|
||||
case TOK_ASSIGN_MUL:
|
||||
return TOK_MUL;
|
||||
case TOK_ASSIGN_DIV:
|
||||
return TOK_DIV;
|
||||
case TOK_ASSIGN_MOD:
|
||||
return TOK_MOD;
|
||||
case TOK_ASSIGN_AND:
|
||||
return TOK_AND;
|
||||
case TOK_ASSIGN_OR:
|
||||
return TOK_OR;
|
||||
case TOK_ASSIGN_XOR:
|
||||
return TOK_XOR;
|
||||
case TOK_ASSIGN_L_SH:
|
||||
return TOK_L_SH;
|
||||
case TOK_ASSIGN_R_SH:
|
||||
return TOK_R_SH;
|
||||
default:
|
||||
return (spl_tok_type_t)-1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -398,10 +383,165 @@ static spl_expr_result_t parse_array_literal(spl_comp_t *ctx) {
|
||||
return (spl_expr_result_t){arr_type, 0};
|
||||
}
|
||||
|
||||
/* Parse a type name with optional .field suffix for enum variants:
|
||||
* TypeName
|
||||
* TypeName.field
|
||||
*/
|
||||
/* Determine correct STORE type for a type (handles ptr = 8 bytes). */
|
||||
static spl_type_t spl_store_type(spl_type_info_t *type) {
|
||||
if (!type)
|
||||
return SPL_I32;
|
||||
if (type->kind == TYPE_BASIC)
|
||||
return type->basic_type;
|
||||
if (type->kind == TYPE_PTR)
|
||||
return SPL_PTR;
|
||||
if (spl_type_slot_count(type) <= 1)
|
||||
return SPL_PTR;
|
||||
return SPL_I32;
|
||||
}
|
||||
|
||||
/* Parse struct/enum literal: Type { .field = val, ... }
|
||||
* Allocates temp slots for the value, returns lvalue (addr on stack).
|
||||
* For single-slot types, pushes the packed value directly. */
|
||||
static spl_expr_result_t parse_struct_literal(spl_comp_t *ctx, spl_type_info_t *type) {
|
||||
usize sc = spl_type_slot_count(type);
|
||||
int base_slot = ctx->current_local_slot;
|
||||
ctx->current_local_slot += (int)sc;
|
||||
|
||||
advance(ctx); /* { */
|
||||
skip_nl(ctx);
|
||||
|
||||
if (type->kind == TYPE_STRUCT) {
|
||||
while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
|
||||
if (peek(ctx)->type == TOK_COMMA) {
|
||||
advance(ctx);
|
||||
skip_nl(ctx);
|
||||
continue;
|
||||
}
|
||||
if (peek(ctx)->type == TOK_DOT) {
|
||||
advance(ctx);
|
||||
spl_tok_t *ftok = advance(ctx);
|
||||
char fname[256];
|
||||
usize fnl = ftok->len < 255 ? ftok->len : 255;
|
||||
memcpy(fname, ftok->lexeme, fnl);
|
||||
fname[fnl] = '\0';
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_ASSIGN)
|
||||
advance(ctx);
|
||||
skip_nl(ctx);
|
||||
|
||||
vec_for(type->fields, fi) {
|
||||
spl_field_t *f = &vec_at(type->fields, fi);
|
||||
if (strcmp(f->name, fname) == 0) {
|
||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, base_slot);
|
||||
if (f->offset > 0) {
|
||||
spl_emit(ctx, SPL_PUSH, SPL_USIZE, f->offset);
|
||||
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0);
|
||||
}
|
||||
spl_expr_result_t fv = spl_parse_expr(ctx, PREC_MIN);
|
||||
(void)fv;
|
||||
spl_emit(ctx, SPL_STORE, spl_store_type(f->type), 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
skip_nl(ctx);
|
||||
}
|
||||
} else if (type->kind == TYPE_ENUM) {
|
||||
/* Parse .Variant [= value] */
|
||||
if (peek(ctx)->type == TOK_DOT)
|
||||
advance(ctx);
|
||||
spl_tok_t *vtok = advance(ctx);
|
||||
char vname[256];
|
||||
usize vnl = vtok->len < 255 ? vtok->len : 255;
|
||||
memcpy(vname, vtok->lexeme, vnl);
|
||||
vname[vnl] = '\0';
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_ASSIGN)
|
||||
advance(ctx);
|
||||
skip_nl(ctx);
|
||||
|
||||
int found = 0;
|
||||
vec_for(type->variants, vi) {
|
||||
spl_enum_variant_t *v = &vec_at(type->variants, vi);
|
||||
if (strcmp(v->name, vname) == 0) {
|
||||
found = 1;
|
||||
/* Store tag at offset 0 */
|
||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, base_slot);
|
||||
spl_emit(ctx, SPL_PUSH, SPL_I32, v->value);
|
||||
spl_emit(ctx, SPL_STORE, SPL_I32, 0);
|
||||
|
||||
/* Store variant data at offset 4 */
|
||||
const usize DATA_OFFSET = 4;
|
||||
if (v->data_type) {
|
||||
if (v->data_type->kind == TYPE_STRUCT && peek(ctx)->type == TOK_L_BRACE) {
|
||||
/* Struct data: { .field = val, ... } */
|
||||
advance(ctx); /* { */
|
||||
skip_nl(ctx);
|
||||
while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
|
||||
if (peek(ctx)->type == TOK_COMMA) {
|
||||
advance(ctx);
|
||||
skip_nl(ctx);
|
||||
continue;
|
||||
}
|
||||
if (peek(ctx)->type == TOK_DOT)
|
||||
advance(ctx);
|
||||
spl_tok_t *sftok = advance(ctx);
|
||||
char sfname[256];
|
||||
usize sfnl = sftok->len < 255 ? sftok->len : 255;
|
||||
memcpy(sfname, sftok->lexeme, sfnl);
|
||||
sfname[sfnl] = '\0';
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_ASSIGN)
|
||||
advance(ctx);
|
||||
skip_nl(ctx);
|
||||
|
||||
vec_for(v->data_type->fields, sfi) {
|
||||
spl_field_t *sf = &vec_at(v->data_type->fields, sfi);
|
||||
if (strcmp(sf->name, sfname) == 0) {
|
||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, base_slot);
|
||||
usize byte_off = DATA_OFFSET + sf->offset;
|
||||
if (byte_off > 0) {
|
||||
spl_emit(ctx, SPL_PUSH, SPL_USIZE, byte_off);
|
||||
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0);
|
||||
}
|
||||
spl_expr_result_t sfv = spl_parse_expr(ctx, PREC_MIN);
|
||||
(void)sfv;
|
||||
spl_emit(ctx, SPL_STORE, spl_store_type(sf->type), 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
skip_nl(ctx);
|
||||
}
|
||||
expect(ctx, TOK_R_BRACE);
|
||||
} else {
|
||||
/* Simple data: parse expression */
|
||||
spl_expr_result_t dv = spl_parse_expr(ctx, PREC_MIN);
|
||||
(void)dv;
|
||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, base_slot);
|
||||
spl_emit(ctx, SPL_PUSH, SPL_USIZE, DATA_OFFSET);
|
||||
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0);
|
||||
spl_type_t bt = spl_store_type(v->data_type);
|
||||
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
|
||||
spl_emit(ctx, SPL_STORE, bt, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
spl_comp_error(ctx, "unknown enum variant '%s'", vname);
|
||||
}
|
||||
|
||||
skip_nl(ctx);
|
||||
expect(ctx, TOK_R_BRACE);
|
||||
|
||||
/* Return: for single-slot, push packed value. For multi-slot, push address. */
|
||||
if (sc == 1) {
|
||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, base_slot);
|
||||
spl_emit(ctx, SPL_LOAD, SPL_PTR, 0);
|
||||
return (spl_expr_result_t){type, 0}; /* value on stack */
|
||||
} else {
|
||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, base_slot);
|
||||
return (spl_expr_result_t){type, 1}; /* address on stack */
|
||||
}
|
||||
}
|
||||
static spl_type_info_t *parse_qualified_type(spl_comp_t *ctx, const char *name) {
|
||||
/* Check if it's a known type */
|
||||
spl_type_info_t *t = spl_resolve_type(ctx, name);
|
||||
@@ -436,6 +576,12 @@ static spl_expr_result_t parse_ident(spl_comp_t *ctx) {
|
||||
/* Check if it's a function call: ident(...) */
|
||||
if (peek(ctx)->type == TOK_L_PAREN) {
|
||||
int fi = spl_lookup_func(ctx, name);
|
||||
/* Fallback: try qualified name for short-name resolution inside methods */
|
||||
if (fi < 0 && ctx->current_type_name) {
|
||||
char qualified[512];
|
||||
snprintf(qualified, sizeof(qualified), "%s.%s", ctx->current_type_name, name);
|
||||
fi = spl_lookup_func(ctx, qualified);
|
||||
}
|
||||
if (fi < 0) {
|
||||
spl_comp_error(ctx, "unknown function '%s'", name);
|
||||
spl_expr_result_t r = {0};
|
||||
@@ -525,7 +671,12 @@ static spl_expr_result_t parse_ident(spl_comp_t *ctx) {
|
||||
/* Check if it's a type name (for enum variant access like Color.Red) */
|
||||
spl_type_info_t *ttype = spl_resolve_type(ctx, name);
|
||||
if (ttype) {
|
||||
spl_expr_result_t r = {ttype, 1};
|
||||
/* Struct/enum literal: Type { .field = val, ... } */
|
||||
if (peek(ctx)->type == TOK_L_BRACE &&
|
||||
(ttype->kind == TYPE_STRUCT || ttype->kind == TYPE_ENUM)) {
|
||||
return parse_struct_literal(ctx, ttype);
|
||||
}
|
||||
spl_expr_result_t r = {ttype, 0};
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -669,9 +820,7 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) {
|
||||
break;
|
||||
}
|
||||
char bname[256];
|
||||
usize bnlen = tok->len < 255 ? tok->len : 255;
|
||||
memcpy(bname, tok->lexeme, bnlen);
|
||||
bname[bnlen] = '\0';
|
||||
spl_tok_copy_name(tok, bname, sizeof bname);
|
||||
advance(ctx); /* consume builtin name */
|
||||
|
||||
if (strcmp(bname, "dbg") == 0) {
|
||||
@@ -746,6 +895,73 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Method call on type/instance: left.field(args) */
|
||||
if (left.type && peek(ctx)->type == TOK_L_PAREN) {
|
||||
/* Determine the type that owns methods (deref pointer if needed) */
|
||||
spl_type_info_t *methods_type = left.type;
|
||||
int is_ptr_self = 0;
|
||||
if (methods_type->kind == TYPE_PTR && methods_type->elem &&
|
||||
(methods_type->elem->kind == TYPE_STRUCT ||
|
||||
methods_type->elem->kind == TYPE_ENUM)) {
|
||||
is_ptr_self = 1;
|
||||
methods_type = methods_type->elem;
|
||||
}
|
||||
|
||||
int found_method = 0;
|
||||
vec_for(methods_type->methods, mi) {
|
||||
if (strcmp(vec_at(methods_type->methods, mi).name, fname) == 0) {
|
||||
spl_method_info_t *method = &vec_at(methods_type->methods, mi);
|
||||
spl_func_info_t *func = &vec_at(ctx->funcs, method->func_idx);
|
||||
|
||||
advance(ctx); /* ( */
|
||||
found_method = 1;
|
||||
|
||||
int nargs = 0;
|
||||
|
||||
/* Check if instance method: first param is self: *Type */
|
||||
int is_instance = 0;
|
||||
if (func->nparams > 0 && func->param_types[0] &&
|
||||
func->param_types[0]->kind == TYPE_PTR &&
|
||||
func->param_types[0]->elem == methods_type) {
|
||||
is_instance = 1;
|
||||
}
|
||||
|
||||
if (is_instance) {
|
||||
if (!left.is_lvalue && !is_ptr_self) {
|
||||
spl_comp_error(ctx, "cannot call instance method '%s' on type",
|
||||
fname);
|
||||
}
|
||||
nargs = 1; /* self already on stack */
|
||||
}
|
||||
|
||||
/* Parse remaining arguments */
|
||||
if (peek(ctx)->type != TOK_R_PAREN) {
|
||||
for (;;) {
|
||||
spl_expr_result_t arg = spl_parse_expr(ctx, PREC_MIN);
|
||||
(void)arg;
|
||||
nargs++;
|
||||
if (peek(ctx)->type == TOK_COMMA) {
|
||||
advance(ctx);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
expect(ctx, TOK_R_PAREN);
|
||||
|
||||
spl_val_t addr = vec_at(ctx->prog.funcs, func->func_idx).address;
|
||||
spl_emit(ctx, SPL_PUSH, SPL_PTR, addr);
|
||||
spl_emit(ctx, SPL_CALL, SPL_VOID, nargs);
|
||||
|
||||
left = (spl_expr_result_t){func->ret_type, 0};
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found_method)
|
||||
continue;
|
||||
/* If method not found, fall through to field access below */
|
||||
}
|
||||
|
||||
/* Struct field access */
|
||||
if (left.type && left.type->kind == TYPE_STRUCT) {
|
||||
/* The struct address is on stack (as lvalue or from previous computation) */
|
||||
|
||||
@@ -1,9 +1,32 @@
|
||||
/* spl_lex_util.c — Lexer utility functions */
|
||||
|
||||
#include "spl_lexer.h"
|
||||
#include "spl_lex_util.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
int expect(spl_comp_t *ctx, spl_tok_type_t type) {
|
||||
if (peek(ctx)->type == 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;
|
||||
}
|
||||
|
||||
int match(spl_comp_t *ctx, spl_tok_type_t type) {
|
||||
if (peek(ctx)->type == type) {
|
||||
advance(ctx);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void skip_nl(spl_comp_t *ctx) {
|
||||
while (peek(ctx)->type == TOK_ENDLINE)
|
||||
advance(ctx);
|
||||
}
|
||||
|
||||
const char *spl_tok_type_name(spl_tok_type_t type) {
|
||||
switch (type) {
|
||||
#define X(name, enum_name, dummy) \
|
||||
@@ -64,3 +87,12 @@ void spl_tok_vec_drop(spl_tok_vec_t *toks) {
|
||||
vec_free(*toks);
|
||||
}
|
||||
}
|
||||
|
||||
usize spl_tok_copy_name(spl_tok_t *tok, char *buf, usize buf_size) {
|
||||
if (!tok || !buf || buf_size == 0)
|
||||
return 0;
|
||||
usize nlen = tok->len < buf_size - 1 ? tok->len : buf_size - 1;
|
||||
memcpy(buf, tok->lexeme, nlen);
|
||||
buf[nlen] = '\0';
|
||||
return nlen;
|
||||
}
|
||||
|
||||
12
stage1/spl_lex_util.h
Normal file
12
stage1/spl_lex_util.h
Normal file
@@ -0,0 +1,12 @@
|
||||
/* spl_lex_util.h — Shared token helper utilities */
|
||||
#ifndef __SPL_LEX_UTIL_H__
|
||||
#define __SPL_LEX_UTIL_H__
|
||||
|
||||
#include "spl_comp.h"
|
||||
|
||||
/* Token matching helpers */
|
||||
int expect(spl_comp_t *ctx, spl_tok_type_t type);
|
||||
int match(spl_comp_t *ctx, spl_tok_type_t type);
|
||||
void skip_nl(spl_comp_t *ctx);
|
||||
|
||||
#endif /* __SPL_LEX_UTIL_H__ */
|
||||
@@ -135,6 +135,7 @@ const char *spl_tok_type_name(spl_tok_type_t type);
|
||||
void spl_tok_dump(spl_tok_t *tok);
|
||||
void spl_tok_vec_dump(spl_tok_vec_t *toks);
|
||||
void spl_tok_vec_drop(spl_tok_vec_t *toks);
|
||||
usize spl_tok_copy_name(spl_tok_t *tok, char *buf, usize buf_size);
|
||||
|
||||
/* Decode escape sequence, advance *s past it. Returns 0 on success. */
|
||||
int spl_decode_escape(const char **s, char *out);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* spl_parser.c — Top-level parser: function declarations, type declarations, etc. */
|
||||
|
||||
#include "spl_comp.h"
|
||||
#include "spl_lex_util.h"
|
||||
#include <string.h>
|
||||
|
||||
spl_tok_t *peek(spl_comp_t *ctx) { return &vec_at(ctx->toks, ctx->tok_idx); }
|
||||
@@ -12,20 +13,6 @@ spl_tok_t *advance(spl_comp_t *ctx) {
|
||||
return t;
|
||||
}
|
||||
|
||||
static int expect(spl_comp_t *ctx, spl_tok_type_t type) {
|
||||
if (peek(ctx)->type == 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;
|
||||
}
|
||||
|
||||
static void skip_nl(spl_comp_t *ctx) {
|
||||
while (peek(ctx)->type == TOK_ENDLINE)
|
||||
advance(ctx);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* Parse function definition
|
||||
@@ -33,6 +20,48 @@ static void skip_nl(spl_comp_t *ctx) {
|
||||
* or fn name(params) ret-type; (forward decl, not used for stage1)
|
||||
* ============================================================ */
|
||||
|
||||
/* Shared helper: register a function, declare params, parse body, end function.
|
||||
* Used by both top-level fn decl and methods inside type bodies. */
|
||||
static int parse_fn_body(spl_comp_t *ctx, const char *fn_name, spl_type_info_t *ret_type,
|
||||
int nparams, char pnames[][256], spl_type_info_t *ptypes[], int is_pub) {
|
||||
int fi = spl_declare_func(ctx, fn_name, ret_type, nparams, 0, is_pub);
|
||||
ctx->current_func_idx = fi;
|
||||
ctx->current_ret_type = ret_type;
|
||||
ctx->current_local_slot = 0;
|
||||
|
||||
spl_push_scope(ctx);
|
||||
|
||||
for (int i = 0; i < nparams; i++) {
|
||||
spl_declare_var(ctx, pnames[i], ptypes[i], 0);
|
||||
}
|
||||
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_L_BRACE) {
|
||||
advance(ctx); /* { */
|
||||
skip_nl(ctx);
|
||||
|
||||
spl_val_t alloc_addr = vec_size(ctx->prog.insns);
|
||||
spl_emit(ctx, SPL_ALLOC, SPL_VOID, 0);
|
||||
|
||||
while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
|
||||
spl_parse_stmt(ctx);
|
||||
skip_nl(ctx);
|
||||
}
|
||||
|
||||
spl_patch(ctx, alloc_addr, ctx->current_local_slot - nparams);
|
||||
expect(ctx, TOK_R_BRACE);
|
||||
}
|
||||
|
||||
spl_emit_defer_epilogue(ctx, ctx->scope_depth);
|
||||
spl_pop_scope(ctx);
|
||||
|
||||
spl_emit(ctx, SPL_RET, SPL_VOID, 0);
|
||||
spl_prog_end_func(&ctx->prog, fi);
|
||||
ctx->current_func_idx = -1;
|
||||
ctx->current_ret_type = NULL;
|
||||
return fi;
|
||||
}
|
||||
|
||||
static void parse_fn_decl(spl_comp_t *ctx, int is_extern, int is_pub) {
|
||||
advance(ctx); /* fn */
|
||||
skip_nl(ctx);
|
||||
@@ -124,53 +153,8 @@ static void parse_fn_decl(spl_comp_t *ctx, int is_extern, int is_pub) {
|
||||
|
||||
skip_nl(ctx);
|
||||
|
||||
/* Declare function in prog */
|
||||
int fi = spl_declare_func(ctx, fn_name, ret_type, nparams, 0, is_pub);
|
||||
ctx->current_func_idx = fi;
|
||||
ctx->current_ret_type = ret_type;
|
||||
ctx->current_local_slot = 0;
|
||||
|
||||
/* Push function scope for params + locals */
|
||||
spl_push_scope(ctx);
|
||||
|
||||
/* Declare parameters as variables in the function scope */
|
||||
for (int i = 0; i < nparams; i++) {
|
||||
spl_declare_var(ctx, pnames[i], ptypes[i], 0);
|
||||
}
|
||||
|
||||
/* Parse body */
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_L_BRACE) {
|
||||
advance(ctx); /* { */
|
||||
skip_nl(ctx);
|
||||
|
||||
/* Emit ALLOC placeholder to reserve stack space for locals.
|
||||
* Without ALLOC, PUSH in expressions overwrites variable slots
|
||||
* because the stack and locals share the same memory. */
|
||||
spl_val_t alloc_addr = vec_size(ctx->prog.insns);
|
||||
spl_emit(ctx, SPL_ALLOC, SPL_VOID, 0);
|
||||
|
||||
while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
|
||||
spl_parse_stmt(ctx);
|
||||
skip_nl(ctx);
|
||||
}
|
||||
|
||||
/* Patch ALLOC to local slot count only (excludes param slots) */
|
||||
spl_patch(ctx, alloc_addr, ctx->current_local_slot - nparams);
|
||||
|
||||
expect(ctx, TOK_R_BRACE);
|
||||
}
|
||||
|
||||
spl_emit_defer_epilogue(ctx, ctx->scope_depth);
|
||||
spl_pop_scope(ctx);
|
||||
|
||||
/* Emit implicit RET for void functions without explicit return */
|
||||
spl_emit(ctx, SPL_RET, SPL_VOID, 0);
|
||||
|
||||
/* End function */
|
||||
spl_prog_end_func(&ctx->prog, fi);
|
||||
ctx->current_func_idx = -1;
|
||||
ctx->current_ret_type = NULL;
|
||||
/* Use shared helper for function body parsing */
|
||||
parse_fn_body(ctx, fn_name, ret_type, nparams, pnames, ptypes, is_pub);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
@@ -180,7 +164,7 @@ static void parse_fn_decl(spl_comp_t *ctx, int is_extern, int is_pub) {
|
||||
* var name: type; — field declarations
|
||||
* name: type, — field declarations (old-style)
|
||||
* type Name = ...; — nested type declarations
|
||||
* fn name(...) type { } — methods (skipped)
|
||||
* fn name(...) type { } — methods
|
||||
* ============================================================ */
|
||||
|
||||
static void parse_struct_body(spl_comp_t *ctx, spl_type_info_t *st) {
|
||||
@@ -195,9 +179,15 @@ static void parse_struct_body(spl_comp_t *ctx, spl_type_info_t *st) {
|
||||
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++; advance(ctx); }
|
||||
else if (tt == TOK_R_BRACE) { depth--; if (depth == 0) break; advance(ctx); }
|
||||
else if (tt == KW_TYPE && depth == 1) {
|
||||
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) {
|
||||
parse_type_decl(ctx);
|
||||
} else {
|
||||
advance(ctx);
|
||||
@@ -213,13 +203,20 @@ static void parse_struct_body(spl_comp_t *ctx, spl_type_info_t *st) {
|
||||
while (depth > 0 && ctx->tok_idx < vec_size(ctx->toks)) {
|
||||
skip_nl(ctx);
|
||||
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) { advance(ctx); break; } advance(ctx); }
|
||||
else if (tt == KW_TYPE && depth == 1) {
|
||||
if (tt == TOK_L_BRACE) {
|
||||
depth++;
|
||||
advance(ctx);
|
||||
} else if (tt == TOK_R_BRACE) {
|
||||
depth--;
|
||||
if (depth == 0) {
|
||||
advance(ctx);
|
||||
break;
|
||||
}
|
||||
advance(ctx);
|
||||
} else if (tt == KW_TYPE && depth == 1) {
|
||||
/* Nested type — already parsed in pass 1, skip by re-parsing */
|
||||
parse_type_decl(ctx);
|
||||
}
|
||||
else if (tt == KW_VAR && depth == 1) {
|
||||
} else if (tt == KW_VAR && depth == 1) {
|
||||
/* var name: type; */
|
||||
advance(ctx); /* var */
|
||||
skip_nl(ctx);
|
||||
@@ -238,8 +235,7 @@ static void parse_struct_body(spl_comp_t *ctx, spl_type_info_t *st) {
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_COMMA)
|
||||
advance(ctx);
|
||||
}
|
||||
else if (tt == TOK_IDENT && depth == 1) {
|
||||
} else if (tt == TOK_IDENT && depth == 1) {
|
||||
/* Old-style field: name: type, */
|
||||
spl_tok_t *ftok = advance(ctx);
|
||||
skip_nl(ctx);
|
||||
@@ -256,28 +252,86 @@ static void parse_struct_body(spl_comp_t *ctx, spl_type_info_t *st) {
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_COMMA)
|
||||
advance(ctx);
|
||||
}
|
||||
else if (tt == KW_FN && depth == 1) {
|
||||
/* Method — skip over fn name(params) rettype { body } */
|
||||
} else if (tt == KW_FN && depth == 1) {
|
||||
/* Method — parse properly using parse_fn_body */
|
||||
advance(ctx); /* fn */
|
||||
while (ctx->tok_idx < vec_size(ctx->toks)) {
|
||||
spl_tok_type_t mt = peek(ctx)->type;
|
||||
if (mt == TOK_SEMICOLON) { advance(ctx); break; }
|
||||
if (mt == TOK_L_BRACE) {
|
||||
/* Skip function body counting braces */
|
||||
advance(ctx);
|
||||
int bd = 1;
|
||||
while (bd > 0 && ctx->tok_idx < vec_size(ctx->toks)) {
|
||||
if (peek(ctx)->type == TOK_L_BRACE) bd++;
|
||||
else if (peek(ctx)->type == TOK_R_BRACE) bd--;
|
||||
skip_nl(ctx);
|
||||
spl_tok_t *mname_tok = advance(ctx);
|
||||
char mname[256];
|
||||
usize mnl = mname_tok->len < 255 ? mname_tok->len : 255;
|
||||
memcpy(mname, mname_tok->lexeme, mnl);
|
||||
mname[mnl] = '\0';
|
||||
|
||||
/* Build qualified name: TypeName.method_name */
|
||||
char qualified[512];
|
||||
snprintf(qualified, sizeof(qualified), "%s.%s", st->name ? st->name : "anon",
|
||||
mname);
|
||||
|
||||
skip_nl(ctx);
|
||||
expect(ctx, TOK_L_PAREN);
|
||||
|
||||
/* Parse parameters */
|
||||
int nparams = 0;
|
||||
enum { MAX_PARAMS = 64 };
|
||||
char pnames[MAX_PARAMS][256];
|
||||
spl_type_info_t *ptypes[MAX_PARAMS];
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type != TOK_R_PAREN) {
|
||||
while (1) {
|
||||
spl_tok_t *pname = advance(ctx);
|
||||
usize pnl = pname->len < 255 ? pname->len : 255;
|
||||
memcpy(pnames[nparams], pname->lexeme, pnl);
|
||||
pnames[nparams][pnl] = '\0';
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_COLON) {
|
||||
advance(ctx); /* : */
|
||||
skip_nl(ctx);
|
||||
ptypes[nparams] = spl_parse_type(ctx);
|
||||
} else {
|
||||
ptypes[nparams] = spl_type_basic(SPL_I32);
|
||||
}
|
||||
nparams++;
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_COMMA) {
|
||||
advance(ctx);
|
||||
skip_nl(ctx);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
advance(ctx);
|
||||
}
|
||||
expect(ctx, TOK_R_PAREN);
|
||||
skip_nl(ctx);
|
||||
|
||||
/* Return type (default: void) */
|
||||
spl_type_info_t *ret_type = spl_type_basic(SPL_VOID);
|
||||
if (peek(ctx)->type != TOK_SEMICOLON && peek(ctx)->type != TOK_L_BRACE) {
|
||||
ret_type = spl_parse_type(ctx);
|
||||
if (!ret_type)
|
||||
ret_type = spl_type_basic(SPL_VOID);
|
||||
skip_nl(ctx);
|
||||
}
|
||||
|
||||
/* Set current_type_name for short-name resolution inside method body */
|
||||
const char *saved_type_name = ctx->current_type_name;
|
||||
ctx->current_type_name = st->name;
|
||||
|
||||
int fi = parse_fn_body(ctx, qualified, ret_type, nparams, pnames, ptypes, 0);
|
||||
|
||||
{
|
||||
spl_func_info_t *f = &vec_at(ctx->funcs, fi);
|
||||
f->param_types = calloc(nparams, sizeof(spl_type_info_t *));
|
||||
f->param_names = calloc(nparams, sizeof(char *));
|
||||
for (int i = 0; i < nparams; i++) {
|
||||
f->param_types[i] = ptypes[i];
|
||||
f->param_names[i] = strdup(pnames[i]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
ctx->current_type_name = saved_type_name;
|
||||
|
||||
spl_type_add_method(st, mname, fi);
|
||||
} else {
|
||||
advance(ctx);
|
||||
}
|
||||
}
|
||||
@@ -308,9 +362,15 @@ static void parse_enum_body(spl_comp_t *ctx, spl_type_info_t *et) {
|
||||
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++; advance(ctx); }
|
||||
else if (tt == TOK_R_BRACE) { depth--; if (depth == 0) break; advance(ctx); }
|
||||
else if (tt == KW_TYPE && depth == 1) {
|
||||
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) {
|
||||
parse_type_decl(ctx);
|
||||
} else {
|
||||
advance(ctx);
|
||||
@@ -325,13 +385,20 @@ static void parse_enum_body(spl_comp_t *ctx, spl_type_info_t *et) {
|
||||
while (depth > 0 && ctx->tok_idx < vec_size(ctx->toks)) {
|
||||
skip_nl(ctx);
|
||||
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) { advance(ctx); break; } advance(ctx); }
|
||||
else if (tt == KW_TYPE && depth == 1) {
|
||||
if (tt == TOK_L_BRACE) {
|
||||
depth++;
|
||||
advance(ctx);
|
||||
} else if (tt == TOK_R_BRACE) {
|
||||
depth--;
|
||||
if (depth == 0) {
|
||||
advance(ctx);
|
||||
break;
|
||||
}
|
||||
advance(ctx);
|
||||
} else if (tt == KW_TYPE && depth == 1) {
|
||||
/* Already parsed in pass 1, re-parse to skip */
|
||||
parse_type_decl(ctx);
|
||||
}
|
||||
else if (tt == TOK_IDENT && depth == 1) {
|
||||
} else if (tt == TOK_IDENT && depth == 1) {
|
||||
/* Variant: Name or Name: Type */
|
||||
spl_tok_t *vtok = advance(ctx);
|
||||
skip_nl(ctx);
|
||||
@@ -354,27 +421,86 @@ static void parse_enum_body(spl_comp_t *ctx, spl_type_info_t *et) {
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_COMMA)
|
||||
advance(ctx);
|
||||
}
|
||||
else if (tt == KW_FN && depth == 1) {
|
||||
/* Method — skip */
|
||||
} else if (tt == KW_FN && depth == 1) {
|
||||
/* Method — parse properly using parse_fn_body */
|
||||
advance(ctx); /* fn */
|
||||
while (ctx->tok_idx < vec_size(ctx->toks)) {
|
||||
spl_tok_type_t mt = peek(ctx)->type;
|
||||
if (mt == TOK_SEMICOLON) { advance(ctx); break; }
|
||||
if (mt == TOK_L_BRACE) {
|
||||
advance(ctx);
|
||||
int bd = 1;
|
||||
while (bd > 0 && ctx->tok_idx < vec_size(ctx->toks)) {
|
||||
if (peek(ctx)->type == TOK_L_BRACE) bd++;
|
||||
else if (peek(ctx)->type == TOK_R_BRACE) bd--;
|
||||
skip_nl(ctx);
|
||||
spl_tok_t *mname_tok = advance(ctx);
|
||||
char mname[256];
|
||||
usize mnl = mname_tok->len < 255 ? mname_tok->len : 255;
|
||||
memcpy(mname, mname_tok->lexeme, mnl);
|
||||
mname[mnl] = '\0';
|
||||
|
||||
/* Build qualified name: TypeName.method_name */
|
||||
char qualified[512];
|
||||
snprintf(qualified, sizeof(qualified), "%s.%s", et->name ? et->name : "anon",
|
||||
mname);
|
||||
|
||||
skip_nl(ctx);
|
||||
expect(ctx, TOK_L_PAREN);
|
||||
|
||||
/* Parse parameters */
|
||||
int nparams = 0;
|
||||
enum { MAX_PARAMS = 64 };
|
||||
char pnames[MAX_PARAMS][256];
|
||||
spl_type_info_t *ptypes[MAX_PARAMS];
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type != TOK_R_PAREN) {
|
||||
while (1) {
|
||||
spl_tok_t *pname = advance(ctx);
|
||||
usize pnl = pname->len < 255 ? pname->len : 255;
|
||||
memcpy(pnames[nparams], pname->lexeme, pnl);
|
||||
pnames[nparams][pnl] = '\0';
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_COLON) {
|
||||
advance(ctx); /* : */
|
||||
skip_nl(ctx);
|
||||
ptypes[nparams] = spl_parse_type(ctx);
|
||||
} else {
|
||||
ptypes[nparams] = spl_type_basic(SPL_I32);
|
||||
}
|
||||
nparams++;
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_COMMA) {
|
||||
advance(ctx);
|
||||
skip_nl(ctx);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
advance(ctx);
|
||||
}
|
||||
expect(ctx, TOK_R_PAREN);
|
||||
skip_nl(ctx);
|
||||
|
||||
/* Return type (default: void) */
|
||||
spl_type_info_t *ret_type = spl_type_basic(SPL_VOID);
|
||||
if (peek(ctx)->type != TOK_SEMICOLON && peek(ctx)->type != TOK_L_BRACE) {
|
||||
ret_type = spl_parse_type(ctx);
|
||||
if (!ret_type)
|
||||
ret_type = spl_type_basic(SPL_VOID);
|
||||
skip_nl(ctx);
|
||||
}
|
||||
|
||||
/* Set current_type_name for short-name resolution inside method body */
|
||||
const char *saved_type_name = ctx->current_type_name;
|
||||
ctx->current_type_name = et->name;
|
||||
|
||||
int fi = parse_fn_body(ctx, qualified, ret_type, nparams, pnames, ptypes, 0);
|
||||
|
||||
ctx->current_type_name = saved_type_name;
|
||||
|
||||
{
|
||||
spl_func_info_t *f = &vec_at(ctx->funcs, fi);
|
||||
f->param_types = calloc(nparams, sizeof(spl_type_info_t *));
|
||||
f->param_names = calloc(nparams, sizeof(char *));
|
||||
for (int i = 0; i < nparams; i++) {
|
||||
f->param_types[i] = ptypes[i];
|
||||
f->param_names[i] = strdup(pnames[i]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
spl_type_add_method(et, mname, fi);
|
||||
} else {
|
||||
advance(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +1,10 @@
|
||||
/* spl_stmt.c — Statement parser + codegen */
|
||||
|
||||
#include "spl_comp.h"
|
||||
#include "spl_lex_util.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Token helpers — using shared peek/advance from spl_parser.c */
|
||||
|
||||
static int expect(spl_comp_t *ctx, spl_tok_type_t type) {
|
||||
if (peek(ctx)->type == 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;
|
||||
}
|
||||
|
||||
static int match(spl_comp_t *ctx, spl_tok_type_t type) {
|
||||
if (peek(ctx)->type == type) {
|
||||
advance(ctx);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void skip_nl(spl_comp_t *ctx) {
|
||||
while (peek(ctx)->type == TOK_ENDLINE)
|
||||
advance(ctx);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* Return statement: ret expr;
|
||||
* ============================================================ */
|
||||
@@ -49,7 +25,17 @@ static void parse_ret_stmt(spl_comp_t *ctx) {
|
||||
} else {
|
||||
spl_expr_result_t val = spl_parse_expr(ctx, PREC_MIN);
|
||||
(void)val;
|
||||
spl_type_t rt = ctx->current_ret_type ? ctx->current_ret_type->basic_type : SPL_VOID;
|
||||
spl_type_t rt = SPL_VOID;
|
||||
if (ctx->current_ret_type) {
|
||||
if (ctx->current_ret_type->kind == TYPE_BASIC) {
|
||||
rt = ctx->current_ret_type->basic_type;
|
||||
} else if (ctx->current_ret_type->kind == TYPE_PTR) {
|
||||
rt = SPL_PTR;
|
||||
} else if (spl_type_slot_count(ctx->current_ret_type) == 1) {
|
||||
/* 1-slot struct/enum/etc: use PTR type */
|
||||
rt = SPL_PTR;
|
||||
}
|
||||
}
|
||||
spl_emit(ctx, SPL_RET, rt, 0);
|
||||
}
|
||||
if (peek(ctx)->type == TOK_SEMICOLON)
|
||||
@@ -96,19 +82,44 @@ static void parse_var_decl(spl_comp_t *ctx, int is_const) {
|
||||
advance(ctx); /* = */
|
||||
}
|
||||
|
||||
/* Allocate stack slot */
|
||||
if (!var_type)
|
||||
var_type = spl_type_basic(SPL_I32); /* default type */
|
||||
/* Init expression: parse before declare to enable type inference */
|
||||
spl_expr_result_t init = {0};
|
||||
if (has_init) {
|
||||
skip_nl(ctx);
|
||||
init = spl_parse_expr(ctx, PREC_MIN);
|
||||
}
|
||||
|
||||
if (!var_type) {
|
||||
var_type = init.type ? init.type : spl_type_basic(SPL_I32);
|
||||
}
|
||||
int slot = spl_declare_var(ctx, vname, var_type, is_const);
|
||||
|
||||
skip_nl(ctx);
|
||||
|
||||
/* Init expression */
|
||||
/* Store init value */
|
||||
if (has_init) {
|
||||
spl_expr_result_t init = spl_parse_expr(ctx, PREC_MIN);
|
||||
(void)init;
|
||||
|
||||
if (var_type && var_type->kind == TYPE_ARRAY) {
|
||||
if (var_type && (var_type->kind == TYPE_STRUCT || var_type->kind == TYPE_ENUM)) {
|
||||
/* Struct/enum initialization */
|
||||
usize sc = spl_type_slot_count(var_type);
|
||||
if (sc == 1) {
|
||||
/* Single-slot: value on stack, store directly */
|
||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, slot);
|
||||
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
|
||||
spl_emit(ctx, SPL_STORE, SPL_PTR, 0);
|
||||
} else {
|
||||
/* Multi-slot: copy from temp addr to var slots */
|
||||
for (usize i = 0; i < sc; i++) {
|
||||
if (i < sc - 1)
|
||||
spl_emit(ctx, SPL_DUP, SPL_VOID, 0);
|
||||
if (i > 0) {
|
||||
spl_emit(ctx, SPL_PUSH, SPL_USIZE, i * sizeof(spl_val_t));
|
||||
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0);
|
||||
}
|
||||
spl_emit(ctx, SPL_LOAD, SPL_PTR, 0);
|
||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, slot + (int)i);
|
||||
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
|
||||
spl_emit(ctx, SPL_STORE, SPL_PTR, 0);
|
||||
}
|
||||
}
|
||||
} else if (var_type && var_type->kind == TYPE_ARRAY) {
|
||||
/* Array initialization: store each element in reverse stack order */
|
||||
spl_type_info_t *elem = var_type->elem;
|
||||
spl_type_t bt = (elem && elem->kind == TYPE_BASIC) ? elem->basic_type : SPL_I32;
|
||||
@@ -544,6 +555,211 @@ static void parse_defer_stmt(spl_comp_t *ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* Match statement: match expr { .Variant(bindings) => stmt, ... }
|
||||
* ============================================================ */
|
||||
|
||||
static void parse_match_stmt(spl_comp_t *ctx) {
|
||||
advance(ctx); /* match */
|
||||
skip_nl(ctx);
|
||||
|
||||
spl_expr_result_t expr = spl_parse_expr(ctx, PREC_MIN);
|
||||
|
||||
/* Determine the enum type (deref pointer if needed) */
|
||||
spl_type_info_t *enum_type = expr.type;
|
||||
if (enum_type && enum_type->kind == TYPE_PTR && enum_type->elem &&
|
||||
enum_type->elem->kind == TYPE_ENUM) {
|
||||
enum_type = enum_type->elem;
|
||||
}
|
||||
if (!enum_type || enum_type->kind != TYPE_ENUM) {
|
||||
spl_comp_error(ctx, "match expression must be an enum");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Save the enum address (pointer value) to a temp slot */
|
||||
int addr_slot = ctx->current_local_slot;
|
||||
ctx->current_local_slot++;
|
||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, addr_slot);
|
||||
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
|
||||
spl_emit(ctx, SPL_STORE, SPL_PTR, 0);
|
||||
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_L_BRACE)
|
||||
advance(ctx); /* { */
|
||||
|
||||
/* Collect JMP-to-end addresses for patching */
|
||||
enum { MAX_MATCH_ARMS = 32 };
|
||||
spl_val_t jmp_to_end[MAX_MATCH_ARMS];
|
||||
int n_jmps = 0;
|
||||
|
||||
skip_nl(ctx);
|
||||
while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_COMMA) {
|
||||
advance(ctx);
|
||||
skip_nl(ctx);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Parse .VariantName */
|
||||
if (peek(ctx)->type == TOK_DOT)
|
||||
advance(ctx);
|
||||
spl_tok_t *vtok = advance(ctx);
|
||||
char vname[256];
|
||||
usize vnl = vtok->len < 255 ? vtok->len : 255;
|
||||
memcpy(vname, vtok->lexeme, vnl);
|
||||
vname[vnl] = '\0';
|
||||
|
||||
/* Find the variant in the enum type */
|
||||
spl_enum_variant_t *variant = NULL;
|
||||
int vi;
|
||||
vec_for(enum_type->variants, vi) {
|
||||
if (strcmp(vec_at(enum_type->variants, vi).name, vname) == 0) {
|
||||
variant = &vec_at(enum_type->variants, vi);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!variant) {
|
||||
spl_comp_error(ctx, "unknown variant '%s' in match", vname);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Compare tag: load [addr_slot+0] == variant->value */
|
||||
/* Load tag and compare */
|
||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, addr_slot);
|
||||
spl_emit(ctx, SPL_LOAD, SPL_PTR, 0);
|
||||
spl_emit(ctx, SPL_PUSH, SPL_USIZE, 0);
|
||||
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0);
|
||||
spl_emit(ctx, SPL_LOAD, SPL_I32, 0);
|
||||
spl_emit(ctx, SPL_PUSH, SPL_I32, variant->value);
|
||||
spl_emit(ctx, SPL_EQ, SPL_I32, 0);
|
||||
spl_val_t bz_addr = spl_emit_bz(ctx);
|
||||
|
||||
/* This arm matches — parse bindings and body */
|
||||
skip_nl(ctx);
|
||||
|
||||
/* Parse binding list: (name, name, ...) */
|
||||
int has_parens = 0;
|
||||
if (peek(ctx)->type == TOK_L_PAREN) {
|
||||
has_parens = 1;
|
||||
advance(ctx); /* ( */
|
||||
skip_nl(ctx);
|
||||
}
|
||||
|
||||
/* Push scope for bindings if there are any */
|
||||
int scope_pushed = 0;
|
||||
if (has_parens && peek(ctx)->type != TOK_R_PAREN) {
|
||||
spl_push_scope(ctx);
|
||||
scope_pushed = 1;
|
||||
|
||||
if (variant->data_type && variant->data_type->kind == TYPE_STRUCT) {
|
||||
/* Struct data: each struct field is a binding */
|
||||
int bi = 0;
|
||||
for (;;) {
|
||||
spl_tok_t *btok = advance(ctx);
|
||||
char bname[256];
|
||||
usize bnl = btok->len < 255 ? btok->len : 255;
|
||||
memcpy(bname, btok->lexeme, bnl);
|
||||
bname[bnl] = '\0';
|
||||
|
||||
spl_type_info_t *btype = spl_type_basic(SPL_I32);
|
||||
usize field_byte_off = 4;
|
||||
if ((usize)bi < vec_size(variant->data_type->fields)) {
|
||||
btype = vec_at(variant->data_type->fields, bi).type;
|
||||
field_byte_off = 4 + vec_at(variant->data_type->fields, bi).offset;
|
||||
}
|
||||
|
||||
int bslot = spl_declare_var(ctx, bname, btype, 0);
|
||||
|
||||
/* Load from enum data and store to variable */
|
||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, addr_slot);
|
||||
spl_emit(ctx, SPL_LOAD, SPL_PTR, 0);
|
||||
spl_emit(ctx, SPL_PUSH, SPL_USIZE, field_byte_off);
|
||||
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0);
|
||||
spl_type_t bt = (btype->kind == TYPE_BASIC) ? btype->basic_type
|
||||
: (btype->kind == TYPE_PTR) ? SPL_PTR
|
||||
: SPL_I32;
|
||||
spl_emit(ctx, SPL_LOAD, bt, 0);
|
||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, bslot);
|
||||
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
|
||||
spl_emit(ctx, SPL_STORE, bt, 0);
|
||||
|
||||
bi++;
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_COMMA) {
|
||||
advance(ctx);
|
||||
skip_nl(ctx);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else if (variant->data_type) {
|
||||
/* Simple data type: one binding */
|
||||
spl_tok_t *btok = advance(ctx);
|
||||
char bname[256];
|
||||
usize bnl = btok->len < 255 ? btok->len : 255;
|
||||
memcpy(bname, btok->lexeme, bnl);
|
||||
bname[bnl] = '\0';
|
||||
|
||||
spl_type_info_t *btype = variant->data_type;
|
||||
int bslot = spl_declare_var(ctx, bname, btype, 0);
|
||||
|
||||
/* Load from enum data at offset 4 */
|
||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, addr_slot);
|
||||
spl_emit(ctx, SPL_LOAD, SPL_PTR, 0);
|
||||
spl_emit(ctx, SPL_PUSH, SPL_USIZE, 4);
|
||||
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0);
|
||||
spl_type_t bt = (btype->kind == TYPE_BASIC) ? btype->basic_type
|
||||
: (btype->kind == TYPE_PTR) ? SPL_PTR
|
||||
: SPL_I32;
|
||||
spl_emit(ctx, SPL_LOAD, bt, 0);
|
||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, bslot);
|
||||
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
|
||||
spl_emit(ctx, SPL_STORE, bt, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (has_parens)
|
||||
expect(ctx, TOK_R_PAREN);
|
||||
skip_nl(ctx);
|
||||
|
||||
/* => (two tokens: = >) */
|
||||
if (peek(ctx)->type == TOK_ASSIGN) {
|
||||
advance(ctx);
|
||||
if (peek(ctx)->type == TOK_GT)
|
||||
advance(ctx);
|
||||
}
|
||||
skip_nl(ctx);
|
||||
|
||||
/* Parse arm body statement */
|
||||
spl_parse_stmt(ctx);
|
||||
|
||||
/* Pop scope if we pushed one */
|
||||
if (scope_pushed) {
|
||||
spl_emit_defer_epilogue(ctx, ctx->scope_depth);
|
||||
spl_pop_scope(ctx);
|
||||
}
|
||||
|
||||
/* JMP to end (skip remaining arms) */
|
||||
if (n_jmps < MAX_MATCH_ARMS)
|
||||
jmp_to_end[n_jmps++] = spl_emit_jmp(ctx);
|
||||
|
||||
/* Patch BZ to here (next arm or end) */
|
||||
spl_patch_to_here(ctx, bz_addr);
|
||||
|
||||
skip_nl(ctx);
|
||||
}
|
||||
|
||||
expect(ctx, TOK_R_BRACE);
|
||||
|
||||
/* Patch all JMPs to end */
|
||||
for (int i = 0; i < n_jmps; i++)
|
||||
spl_patch_to_here(ctx, jmp_to_end[i]);
|
||||
|
||||
/* Release temp slot */
|
||||
ctx->current_local_slot--;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* Extern declaration: #[extern("vm")] fn name(...) ret;
|
||||
* ============================================================ */
|
||||
@@ -662,7 +878,8 @@ static void parse_expr_stmt(spl_comp_t *ctx) {
|
||||
if (is_assign)
|
||||
ctx->addr_of_mode = 0;
|
||||
|
||||
if (!is_assign && expr.type && expr.type->kind == TYPE_BASIC && expr.type->basic_type != SPL_VOID)
|
||||
if (!is_assign && expr.type && expr.type->kind == TYPE_BASIC &&
|
||||
expr.type->basic_type != SPL_VOID)
|
||||
spl_emit(ctx, SPL_DROP, SPL_VOID, 0);
|
||||
if (peek(ctx)->type == TOK_SEMICOLON)
|
||||
advance(ctx);
|
||||
@@ -712,6 +929,9 @@ void spl_parse_stmt(spl_comp_t *ctx) {
|
||||
case KW_DEFER:
|
||||
parse_defer_stmt(ctx);
|
||||
break;
|
||||
case KW_MATCH:
|
||||
parse_match_stmt(ctx);
|
||||
break;
|
||||
case KW_TYPE:
|
||||
parse_type_decl(ctx);
|
||||
break;
|
||||
|
||||
@@ -132,6 +132,7 @@ spl_type_info_t *spl_type_struct(const char *name) {
|
||||
if (name)
|
||||
t->name = strdup(name);
|
||||
vec_init(t->fields);
|
||||
vec_init(t->methods);
|
||||
t->resolved = 0;
|
||||
return t;
|
||||
}
|
||||
@@ -142,6 +143,7 @@ spl_type_info_t *spl_type_union(const char *name) {
|
||||
if (name)
|
||||
t->name = strdup(name);
|
||||
vec_init(t->fields);
|
||||
vec_init(t->methods);
|
||||
t->resolved = 0;
|
||||
return t;
|
||||
}
|
||||
@@ -152,6 +154,7 @@ spl_type_info_t *spl_type_enum(const char *name) {
|
||||
if (name)
|
||||
t->name = strdup(name);
|
||||
vec_init(t->variants);
|
||||
vec_init(t->methods);
|
||||
t->byte_size = 4;
|
||||
t->slot_count = 1;
|
||||
t->resolved = 0;
|
||||
@@ -174,6 +177,13 @@ void spl_type_add_variant(spl_type_info_t *et, const char *name, spl_type_info_t
|
||||
vec_push(et->variants, v);
|
||||
}
|
||||
|
||||
void spl_type_add_method(spl_type_info_t *t, const char *name, int func_idx) {
|
||||
spl_method_info_t m;
|
||||
m.name = strdup(name);
|
||||
m.func_idx = func_idx;
|
||||
vec_push(t->methods, m);
|
||||
}
|
||||
|
||||
void spl_type_compute_layout(spl_type_info_t *t) {
|
||||
if (!t || t->resolved)
|
||||
return;
|
||||
@@ -205,7 +215,8 @@ void spl_type_compute_layout(spl_type_info_t *t) {
|
||||
/* Round up to slot alignment */
|
||||
usize slot_sz = sizeof(spl_val_t);
|
||||
t->slot_count = (t->byte_size + slot_sz - 1) / slot_sz;
|
||||
if (t->slot_count < 1) t->slot_count = 1;
|
||||
if (t->slot_count < 1)
|
||||
t->slot_count = 1;
|
||||
t->resolved = 1;
|
||||
} else if (t->kind == TYPE_ENUM) {
|
||||
/* Enums with data need special handling */
|
||||
|
||||
@@ -34,8 +34,5 @@ fn main() i32 {
|
||||
}
|
||||
if s != 20 { ret 6; }
|
||||
|
||||
/* 多维效果:数组元素为数组 */
|
||||
/* 目前只测试一维 */
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,10 @@ fn identity(x: i32) i32 {
|
||||
ret x;
|
||||
}
|
||||
|
||||
fn addr(x: *i32) *i32 {
|
||||
ret x;
|
||||
}
|
||||
|
||||
fn main() i32 {
|
||||
/* 函数调用 */
|
||||
var r1 := add(3, 4);
|
||||
@@ -42,5 +46,10 @@ fn main() i32 {
|
||||
var r6 := add(add(1, 2), add(3, 4));
|
||||
if r6 != 10 { ret 6; }
|
||||
|
||||
/* 返回地址 */
|
||||
var r7 := addr(&r6);
|
||||
if r7 != &r6 { ret 7; }
|
||||
if r7.* != 10 { ret 8; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ fn main() i32 {
|
||||
var expr_l := Expr { .Int = 3 };
|
||||
var expr_r := Expr { .Int = 4 };
|
||||
var expr := Expr { .Add = { .left = expr_l, .right = expr_r } };
|
||||
var result := expr.eval();
|
||||
var result := expr.eval(&expr);
|
||||
vm_printf("eval result: %d\n", result);
|
||||
if result != 7 { ret 1; }
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
// TODO
|
||||
Reference in New Issue
Block a user