329 lines
11 KiB
C
329 lines
11 KiB
C
/* spl_parser.c — Top-level parser: function declarations, type declarations, etc. */
|
|
|
|
#include "spl_comp.h"
|
|
#include <string.h>
|
|
|
|
static spl_tok_t *peek(spl_comp_t *ctx) { return &vec_at(ctx->toks, ctx->tok_idx); }
|
|
|
|
static spl_tok_t *advance(spl_comp_t *ctx) {
|
|
spl_tok_t *t = &vec_at(ctx->toks, ctx->tok_idx);
|
|
if (t->type != TOK_EOF)
|
|
ctx->tok_idx++;
|
|
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
|
|
* fn name(params) ret-type { body }
|
|
* or fn name(params) ret-type; (forward decl, not used for stage1)
|
|
* ============================================================ */
|
|
|
|
static void parse_fn_decl(spl_comp_t *ctx, int is_extern, int is_pub) {
|
|
advance(ctx); /* fn */
|
|
skip_nl(ctx);
|
|
|
|
spl_tok_t *fname_tok = advance(ctx);
|
|
char fn_name[256];
|
|
usize fnl = fname_tok->len < 255 ? fname_tok->len : 255;
|
|
memcpy(fn_name, fname_tok->lexeme, fnl);
|
|
fn_name[fnl] = '\0';
|
|
|
|
skip_nl(ctx);
|
|
expect(ctx, TOK_L_PAREN);
|
|
|
|
/* Parse parameters — collect names and types */
|
|
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;
|
|
}
|
|
if (peek(ctx)->type == TOK_ELLIPSIS) {
|
|
advance(ctx);
|
|
skip_nl(ctx);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
|
|
/* Extern function: register as native, no body */
|
|
if (is_extern) {
|
|
spl_declare_func(ctx, fn_name, ret_type, nparams, 1, is_pub);
|
|
/* Add to prog->natives for NCALL dispatch */
|
|
int found = -1;
|
|
vec_for(ctx->prog.natives, ni) {
|
|
if (strcmp(vec_at(ctx->prog.natives, ni).name, fn_name) == 0) {
|
|
found = (int)ni;
|
|
break;
|
|
}
|
|
}
|
|
if (found < 0) {
|
|
spl_native_t nat;
|
|
memset(&nat, 0, sizeof(nat));
|
|
nat.name = strdup(fn_name);
|
|
nat.idx_of_strtab = 0;
|
|
nat.impl_fn = NULL; /* resolved by VM at runtime */
|
|
vec_push(ctx->prog.natives, nat);
|
|
}
|
|
if (peek(ctx)->type == TOK_SEMICOLON)
|
|
advance(ctx);
|
|
return;
|
|
}
|
|
|
|
/* Check for forward declaration (just semicolon, skip) */
|
|
if (peek(ctx)->type == TOK_SEMICOLON) {
|
|
advance(ctx);
|
|
return;
|
|
}
|
|
|
|
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_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;
|
|
}
|
|
|
|
/* ============================================================
|
|
* Parse type declaration
|
|
* type Name = struct/enum { ... };
|
|
* type Name = ExistingType;
|
|
* ============================================================ */
|
|
|
|
void parse_type_decl(spl_comp_t *ctx) {
|
|
advance(ctx); /* type */
|
|
spl_tok_t *name_tok = advance(ctx);
|
|
char tname[256];
|
|
usize tnl = name_tok->len < 255 ? name_tok->len : 255;
|
|
memcpy(tname, name_tok->lexeme, tnl);
|
|
tname[tnl] = '\0';
|
|
|
|
skip_nl(ctx);
|
|
expect(ctx, TOK_ASSIGN);
|
|
skip_nl(ctx);
|
|
|
|
if (peek(ctx)->type == KW_STRUCT) {
|
|
advance(ctx);
|
|
spl_type_info_t *st = spl_type_struct(tname);
|
|
skip_nl(ctx);
|
|
if (peek(ctx)->type == TOK_L_BRACE) {
|
|
advance(ctx); /* { */
|
|
skip_nl(ctx);
|
|
while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
|
|
spl_tok_t *ftok = advance(ctx);
|
|
skip_nl(ctx);
|
|
if (peek(ctx)->type == TOK_COLON) {
|
|
advance(ctx); /* : */
|
|
skip_nl(ctx);
|
|
spl_type_info_t *ftype = spl_parse_type(ctx);
|
|
char fname[256];
|
|
usize fnl = ftok->len < 255 ? ftok->len : 255;
|
|
memcpy(fname, ftok->lexeme, fnl);
|
|
fname[fnl] = '\0';
|
|
spl_type_add_field(st, fname, ftype);
|
|
}
|
|
skip_nl(ctx);
|
|
if (peek(ctx)->type == TOK_COMMA) {
|
|
advance(ctx);
|
|
skip_nl(ctx);
|
|
}
|
|
}
|
|
if (peek(ctx)->type == TOK_R_BRACE)
|
|
advance(ctx);
|
|
}
|
|
spl_type_compute_layout(st);
|
|
map_put(ctx->type_defs, strdup(tname), st);
|
|
} else if (peek(ctx)->type == KW_ENUM) {
|
|
advance(ctx);
|
|
spl_type_info_t *et = spl_type_enum(tname);
|
|
skip_nl(ctx);
|
|
if (peek(ctx)->type == TOK_L_BRACE) {
|
|
advance(ctx); /* { */
|
|
skip_nl(ctx);
|
|
while (peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
|
|
spl_tok_t *vtok = advance(ctx);
|
|
skip_nl(ctx);
|
|
if (peek(ctx)->type == TOK_COLON) {
|
|
advance(ctx); /* : */
|
|
skip_nl(ctx);
|
|
spl_type_info_t *dtype = spl_parse_type(ctx);
|
|
char vname[256];
|
|
usize vnl = vtok->len < 255 ? vtok->len : 255;
|
|
memcpy(vname, vtok->lexeme, vnl);
|
|
vname[vnl] = '\0';
|
|
spl_type_add_variant(et, vname, dtype);
|
|
} else {
|
|
char vname[256];
|
|
usize vnl = vtok->len < 255 ? vtok->len : 255;
|
|
memcpy(vname, vtok->lexeme, vnl);
|
|
vname[vnl] = '\0';
|
|
spl_type_add_variant(et, vname, NULL);
|
|
}
|
|
skip_nl(ctx);
|
|
if (peek(ctx)->type == TOK_COMMA) {
|
|
advance(ctx);
|
|
skip_nl(ctx);
|
|
}
|
|
}
|
|
if (peek(ctx)->type == TOK_R_BRACE)
|
|
advance(ctx);
|
|
}
|
|
spl_type_compute_layout(et);
|
|
map_put(ctx->type_defs, strdup(tname), et);
|
|
} else if (peek(ctx)->type == TOK_IDENT ||
|
|
(peek(ctx)->type >= KW_AS && peek(ctx)->type <= KW_ANY)) {
|
|
spl_type_info_t *base = spl_parse_type(ctx);
|
|
if (base) {
|
|
spl_type_info_t *alias = spl_type_clone(base);
|
|
alias->name = strdup(tname);
|
|
alias->kind = TYPE_NAME;
|
|
map_put(ctx->type_defs, strdup(tname), alias);
|
|
}
|
|
}
|
|
|
|
skip_nl(ctx);
|
|
if (peek(ctx)->type == TOK_SEMICOLON)
|
|
advance(ctx);
|
|
}
|
|
|
|
/* ============================================================
|
|
* Parse top-level program
|
|
* ============================================================ */
|
|
|
|
void spl_parse_prog(spl_comp_t *ctx) {
|
|
while (peek(ctx)->type != TOK_EOF) {
|
|
skip_nl(ctx);
|
|
if (peek(ctx)->type == TOK_EOF)
|
|
break;
|
|
|
|
switch (peek(ctx)->type) {
|
|
case KW_FN:
|
|
parse_fn_decl(ctx, 0, 0);
|
|
break;
|
|
case KW_TYPE:
|
|
parse_type_decl(ctx);
|
|
break;
|
|
case KW_PUB: {
|
|
advance(ctx); /* skip pub */
|
|
skip_nl(ctx);
|
|
if (peek(ctx)->type == KW_FN)
|
|
parse_fn_decl(ctx, 0, 1);
|
|
else if (peek(ctx)->type == KW_TYPE)
|
|
parse_type_decl(ctx);
|
|
break;
|
|
}
|
|
case TOK_SHARP:
|
|
/* #[extern("vm")] fn ... */
|
|
advance(ctx); /* # */
|
|
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)
|
|
parse_fn_decl(ctx, 1, 0);
|
|
break;
|
|
default: {
|
|
int prev = ctx->tok_idx;
|
|
/* Try to parse as a statement */
|
|
spl_parse_stmt(ctx);
|
|
/* Safety: prevent infinite loop on unrecognized tokens */
|
|
if (ctx->tok_idx == prev)
|
|
advance(ctx);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|