stage1 重构代码
This commit is contained in:
@@ -3,7 +3,6 @@
|
||||
#include "spl_comp.h"
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* ---- Compiler context init/drop ---- */
|
||||
@@ -13,13 +12,12 @@ void spl_comp_init(spl_comp_t *ctx) {
|
||||
vec_init(ctx->toks);
|
||||
vec_init(ctx->scopes);
|
||||
vec_init(ctx->funcs);
|
||||
vec_init(ctx->ns_chain);
|
||||
map_init(ctx->type_defs, MAP_HASH_STR, MAP_CMP_STR);
|
||||
map_init(ctx->const_values, MAP_HASH_STR, MAP_CMP_STR);
|
||||
vec_init(ctx->call_fixups);
|
||||
vec_init(ctx->call_fixup_funcs);
|
||||
spl_prog_init(&ctx->prog);
|
||||
ctx->error_msg[0] = '\0';
|
||||
spl_emit_init(&ctx->emit, &ctx->prog);
|
||||
spl_type_ctx_init(&ctx->tctx);
|
||||
ctx->current_ret_type_idx = -1;
|
||||
}
|
||||
|
||||
void spl_comp_drop(spl_comp_t *ctx) {
|
||||
@@ -28,34 +26,32 @@ void spl_comp_drop(spl_comp_t *ctx) {
|
||||
spl_tok_vec_drop(&ctx->toks);
|
||||
vec_for(ctx->scopes, i) { vec_free(vec_at(ctx->scopes, i).vars); }
|
||||
vec_free(ctx->scopes);
|
||||
vec_for(ctx->funcs, i) {
|
||||
free(ctx->funcs.data[i].name);
|
||||
free(ctx->funcs.data[i].param_type_indices);
|
||||
free(ctx->funcs.data[i].param_names);
|
||||
}
|
||||
vec_free(ctx->funcs);
|
||||
vec_free(ctx->ns_chain);
|
||||
/* Free type defs — complex, leak for now in bootstrap */
|
||||
map_free(ctx->type_defs);
|
||||
map_free(ctx->const_values);
|
||||
vec_free(ctx->call_fixups);
|
||||
vec_free(ctx->call_fixup_funcs);
|
||||
spl_prog_drop(&ctx->prog);
|
||||
free(ctx->break_patches);
|
||||
spl_emit_drop(&ctx->emit);
|
||||
spl_type_ctx_drop(&ctx->tctx);
|
||||
}
|
||||
|
||||
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_free(ctx->scopes);
|
||||
vec_init(ctx->scopes);
|
||||
vec_free(ctx->ns_chain);
|
||||
vec_init(ctx->ns_chain);
|
||||
ctx->scope_depth = 0;
|
||||
ctx->tok_idx = 0;
|
||||
ctx->has_error = 0;
|
||||
ctx->error_msg[0] = '\0';
|
||||
ctx->current_func_idx = -1;
|
||||
ctx->current_ret_type = NULL;
|
||||
ctx->current_local_bytes = 0;
|
||||
ctx->peak_local_bytes = 0;
|
||||
ctx->current_ret_type_idx = -1;
|
||||
fa_init(&ctx->emit.frame);
|
||||
ctx->in_loop = 0;
|
||||
ctx->break_patch_count = 0;
|
||||
ctx->break_patch_cap = 0;
|
||||
@@ -65,10 +61,10 @@ void spl_comp_reset(spl_comp_t *ctx) {
|
||||
ctx->addr_of_mode = 0;
|
||||
free(ctx->break_patches);
|
||||
ctx->break_patches = NULL;
|
||||
vec_free(ctx->call_fixups);
|
||||
vec_init(ctx->call_fixups);
|
||||
vec_free(ctx->call_fixup_funcs);
|
||||
vec_init(ctx->call_fixup_funcs);
|
||||
vec_free(ctx->emit.call_fixups);
|
||||
vec_init(ctx->emit.call_fixups);
|
||||
vec_free(ctx->emit.call_fixup_funcs);
|
||||
vec_init(ctx->emit.call_fixup_funcs);
|
||||
}
|
||||
|
||||
void spl_comp_error(spl_comp_t *ctx, const char *fmt, ...) {
|
||||
@@ -79,135 +75,6 @@ void spl_comp_error(spl_comp_t *ctx, const char *fmt, ...) {
|
||||
ctx->has_error = 1;
|
||||
}
|
||||
|
||||
/* ---- Codegen helpers ---- */
|
||||
|
||||
spl_val_t spl_emit(spl_comp_t *ctx, uint16_t opcode, uint16_t type, spl_val_t imm) {
|
||||
return spl_prog_emit(&ctx->prog, opcode, type, imm);
|
||||
}
|
||||
|
||||
void spl_patch(spl_comp_t *ctx, spl_val_t addr, spl_val_t target) {
|
||||
if (addr < vec_size(ctx->prog.insns)) {
|
||||
vec_at(ctx->prog.insns, addr).imm = target;
|
||||
}
|
||||
}
|
||||
|
||||
spl_val_t spl_emit_jmp(spl_comp_t *ctx) {
|
||||
/* Emit JMP with placeholder 0, return address to patch */
|
||||
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_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) */
|
||||
spl_val_t here = vec_size(ctx->prog.insns);
|
||||
spl_val_t offset = here - addr - 1;
|
||||
spl_patch(ctx, addr, offset);
|
||||
}
|
||||
|
||||
/* ---- Multi-slot copy helpers ---- */
|
||||
|
||||
void spl_emit_copy_slots(spl_comp_t *ctx, int dest_offset, usize nslots) {
|
||||
for (usize i = 0; i < nslots; i++) {
|
||||
if (i < nslots - 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, dest_offset + (int)(i * sizeof(spl_val_t)));
|
||||
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
|
||||
spl_emit(ctx, SPL_STORE, SPL_PTR, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy nslots from source address to destination address.
|
||||
* Stack before: [..., depth_items..., dest_addr, src_addr] (src TOS)
|
||||
* Stack after: [..., depth_items...]
|
||||
* depth: number of items below dest_addr that must be preserved (e.g. 1 if
|
||||
* there's a base pointer between the rest of the stack and dest_addr). */
|
||||
void spl_emit_copy_addr_to_addr(spl_comp_t *ctx, usize nslots, int depth) {
|
||||
for (usize i = 0; i < nslots; i++) {
|
||||
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_PICK, SPL_VOID, 2 + depth);
|
||||
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_SWAP, SPL_VOID, 0);
|
||||
spl_emit(ctx, SPL_STORE, SPL_PTR, 0);
|
||||
}
|
||||
spl_emit(ctx, SPL_DROP, SPL_VOID, 0);
|
||||
spl_emit(ctx, SPL_DROP, SPL_VOID, 0);
|
||||
}
|
||||
|
||||
/* ---- Uniform LOAD/STORE type helper ---- */
|
||||
|
||||
spl_type_t spl_type_emit_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 (type->kind == TYPE_ENUM) {
|
||||
/* Simple enum (no data variants): tag is I32 */
|
||||
vec_for(type->variants, i) {
|
||||
if (vec_at(type->variants, i).data_type)
|
||||
return SPL_PTR; /* has data — treat as aggregate */
|
||||
}
|
||||
return SPL_I32;
|
||||
}
|
||||
return SPL_PTR;
|
||||
}
|
||||
|
||||
/* Scalar types fit in one slot, are auto-loaded on field access,
|
||||
* and support == / != comparison directly. */
|
||||
int spl_type_is_scalar(spl_type_info_t *type) {
|
||||
if (!type)
|
||||
return 1;
|
||||
if (type->kind == TYPE_BASIC || type->kind == TYPE_PTR)
|
||||
return 1;
|
||||
if (type->kind == TYPE_ENUM) {
|
||||
vec_for(type->variants, i) {
|
||||
if (vec_at(type->variants, i).data_type)
|
||||
return 0; /* has data — aggregate */
|
||||
}
|
||||
return 1; /* simple enum — scalar */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---- Load from [saved_ptr+offset], store to local var ---- */
|
||||
|
||||
void spl_emit_load_to_var(spl_comp_t *ctx, int ptr_slot_offset, usize byte_offset,
|
||||
spl_type_info_t *data_type, int var_offset) {
|
||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, ptr_slot_offset);
|
||||
spl_emit(ctx, SPL_LOAD, SPL_PTR, 0);
|
||||
if (byte_offset > 0) {
|
||||
spl_emit(ctx, SPL_PUSH, SPL_USIZE, byte_offset);
|
||||
spl_emit(ctx, SPL_ADD, SPL_USIZE, 0);
|
||||
}
|
||||
if (data_type && !spl_type_is_scalar(data_type) &&
|
||||
spl_type_size(data_type) > sizeof(spl_val_t)) {
|
||||
spl_emit_copy_slots(ctx, var_offset, spl_type_slot_count(data_type));
|
||||
} else {
|
||||
spl_type_t bt = spl_type_emit_type(data_type);
|
||||
spl_emit(ctx, SPL_LOAD, bt, 0);
|
||||
spl_emit(ctx, SPL_LADDR, SPL_PTR, var_offset);
|
||||
spl_emit(ctx, SPL_SWAP, SPL_VOID, 0);
|
||||
spl_emit(ctx, SPL_STORE, bt, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- Scope management ---- */
|
||||
|
||||
void spl_push_scope(spl_comp_t *ctx) {
|
||||
@@ -229,39 +96,17 @@ void spl_pop_scope(spl_comp_t *ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- Namespace chain management ---- */
|
||||
|
||||
void spl_ns_push(spl_comp_t *ctx, spl_type_info_t *type) {
|
||||
if (type)
|
||||
vec_push(ctx->ns_chain, type);
|
||||
}
|
||||
|
||||
void spl_ns_pop(spl_comp_t *ctx) {
|
||||
if (vec_size(ctx->ns_chain) > 0)
|
||||
ctx->ns_chain.size--;
|
||||
}
|
||||
|
||||
/* ---- Variable management ---- */
|
||||
|
||||
int spl_declare_var(spl_comp_t *ctx, const char *name, spl_type_info_t *type, int is_const) {
|
||||
int spl_declare_var(spl_comp_t *ctx, const char *name, int type_idx, int is_const) {
|
||||
spl_var_info_t var;
|
||||
memset(&var, 0, sizeof(var));
|
||||
var.name = strdup(name);
|
||||
var.type = type;
|
||||
var.type_idx = type_idx;
|
||||
var.is_const = is_const;
|
||||
var.depth = ctx->scope_depth;
|
||||
var.offset = ctx->current_local_bytes;
|
||||
var.offset = fa_alloc(&ctx->emit.frame, spl_type_size(&ctx->tctx, type_idx));
|
||||
|
||||
usize var_size = spl_type_size(type);
|
||||
/* Round up to sizeof(spl_val_t) alignment so params (pushed as spl_val_t) align */
|
||||
usize aligned = (var_size + sizeof(spl_val_t) - 1) & ~(sizeof(spl_val_t) - 1);
|
||||
if (aligned < sizeof(spl_val_t))
|
||||
aligned = sizeof(spl_val_t);
|
||||
ctx->current_local_bytes += (int)aligned;
|
||||
if (ctx->current_local_bytes > ctx->peak_local_bytes)
|
||||
ctx->peak_local_bytes = ctx->current_local_bytes;
|
||||
|
||||
/* Add to current scope */
|
||||
if (vec_size(ctx->scopes) > 0) {
|
||||
spl_scope_t *scope = &vec_at(ctx->scopes, vec_size(ctx->scopes) - 1);
|
||||
vec_push(scope->vars, var);
|
||||
@@ -272,11 +117,9 @@ int spl_declare_var(spl_comp_t *ctx, const char *name, spl_type_info_t *type, in
|
||||
spl_var_info_t *spl_lookup_var(spl_comp_t *ctx, const char *name) {
|
||||
for (int i = (int)vec_size(ctx->scopes) - 1; i >= 0; i--) {
|
||||
spl_scope_t *scope = &vec_at(ctx->scopes, i);
|
||||
/* Search in reverse order so later declarations shadow earlier ones */
|
||||
for (int j = (int)vec_size(scope->vars) - 1; j >= 0; j--) {
|
||||
if (strcmp(vec_at(scope->vars, j).name, name) == 0) {
|
||||
if (strcmp(vec_at(scope->vars, j).name, name) == 0)
|
||||
return &vec_at(scope->vars, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
@@ -289,13 +132,12 @@ int spl_get_var_offset(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 spl_declare_func(spl_comp_t *ctx, const char *name, int ret_type_idx, int nparams,
|
||||
int is_extern, int is_pub) {
|
||||
/* Check if already declared — allows pre-registration */
|
||||
vec_for(ctx->funcs, i) {
|
||||
spl_func_info_t *existing = &vec_at(ctx->funcs, i);
|
||||
if (strcmp(existing->name, name) == 0) {
|
||||
existing->ret_type = ret_type;
|
||||
existing->ret_type_idx = ret_type_idx;
|
||||
existing->nparams = nparams;
|
||||
existing->is_extern = is_extern;
|
||||
existing->is_pub = is_pub;
|
||||
@@ -308,32 +150,37 @@ int spl_declare_func(spl_comp_t *ctx, const char *name, spl_type_info_t *ret_typ
|
||||
spl_func_info_t fi;
|
||||
memset(&fi, 0, sizeof(fi));
|
||||
fi.name = strdup(name);
|
||||
fi.ret_type = ret_type;
|
||||
fi.ret_type_idx = ret_type_idx;
|
||||
fi.nparams = nparams;
|
||||
fi.is_extern = is_extern;
|
||||
fi.is_pub = is_pub;
|
||||
|
||||
if (is_extern) {
|
||||
if (is_extern)
|
||||
fi.func_idx = -1;
|
||||
} else {
|
||||
else
|
||||
fi.func_idx = spl_prog_add_func_simple(&ctx->prog, name, nparams);
|
||||
}
|
||||
|
||||
vec_push(ctx->funcs, fi);
|
||||
return (int)vec_size(ctx->funcs) - 1;
|
||||
}
|
||||
|
||||
int spl_lookup_func(spl_comp_t *ctx, const char *name) {
|
||||
/* 1. Walk ns_chain from innermost to outermost, check each type's methods */
|
||||
for (usize i = vec_size(ctx->ns_chain); i > 0; i--) {
|
||||
spl_type_info_t *type = vec_at(ctx->ns_chain, i - 1);
|
||||
vec_for(type->methods, j) {
|
||||
if (strcmp(vec_at(type->methods, j).name, name) == 0)
|
||||
return vec_at(type->methods, j).func_idx;
|
||||
{
|
||||
int current = ctx->tctx.current_type_idx;
|
||||
while (current >= 0) {
|
||||
spl_type_item_vec_t *items = spl_type_items(&ctx->tctx, current);
|
||||
if (items) {
|
||||
vec_for(*items, j) {
|
||||
spl_type_item_t *it = &vec_at(*items, j);
|
||||
if (it->item_kind == ITEM_METHOD && it->name &&
|
||||
strcmp(it->name, name) == 0)
|
||||
return it->method.func_idx;
|
||||
}
|
||||
}
|
||||
current = vec_at(ctx->tctx.types, current).parent_type_idx;
|
||||
}
|
||||
}
|
||||
|
||||
/* 2. Fallback to flat func table (top-level functions and qualified names) */
|
||||
vec_for(ctx->funcs, j) {
|
||||
if (strcmp(vec_at(ctx->funcs, j).name, name) == 0)
|
||||
return (int)j;
|
||||
@@ -341,8 +188,6 @@ int spl_lookup_func(spl_comp_t *ctx, const char *name) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Ensure a native function is registered for NCALL dispatch.
|
||||
* Returns the native index. */
|
||||
int spl_ensure_native(spl_comp_t *ctx, const char *name) {
|
||||
vec_for(ctx->prog.natives, ni) {
|
||||
if (strcmp(vec_at(ctx->prog.natives, ni).name, name) == 0)
|
||||
@@ -372,109 +217,69 @@ void spl_emit_defer(spl_comp_t *ctx) {
|
||||
if (ctx->defer_count >= DEFER_MAX)
|
||||
return;
|
||||
|
||||
/* Emit JMP placeholder (will skip defer body during normal execution).
|
||||
* This JMP is at the current position. The defer body starts right after. */
|
||||
spl_val_t jmp_skip = spl_emit_jmp(ctx);
|
||||
spl_val_t jmp_skip = emit_jmp_here(&ctx->emit);
|
||||
|
||||
spl_defer_entry_t *e = &ctx->defer_stack[ctx->defer_count];
|
||||
e->body_start = jmp_skip + 1; /* instruction right after the JMP = body start */
|
||||
e->jmp_exit = 0; /* set after body is parsed */
|
||||
e->body_start = jmp_skip + 1;
|
||||
e->jmp_exit = 0;
|
||||
e->depth = ctx->scope_depth;
|
||||
e->count_at_decl = ctx->defer_count;
|
||||
ctx->defer_count++;
|
||||
}
|
||||
|
||||
void spl_emit_defer_epilogue(spl_comp_t *ctx, int depth) {
|
||||
/* === Pass 1: patch skip JMPs to jump past their defer body ===
|
||||
* During normal execution, the skip JMP at body_start-1 must jump
|
||||
* over the defer body (to jmp_exit + 1 = the code after the defer). */
|
||||
for (int i = 0; i < ctx->defer_count; i++) {
|
||||
if (ctx->defer_stack[i].depth != depth)
|
||||
continue;
|
||||
spl_defer_entry_t *e = &ctx->defer_stack[i];
|
||||
|
||||
spl_val_t skip_addr = e->body_start - 1; /* the JMP L_skip instruction */
|
||||
spl_val_t skip_target = e->jmp_exit + 1; /* instruction right after defer body */
|
||||
spl_val_t skip_offset = skip_target - skip_addr - 1;
|
||||
spl_patch(ctx, skip_addr, skip_offset);
|
||||
spl_val_t skip_addr = e->body_start - 1;
|
||||
spl_val_t skip_target = e->jmp_exit + 1;
|
||||
emit_patch(&ctx->emit, skip_addr, skip_target - skip_addr - 1);
|
||||
}
|
||||
|
||||
/* === Pass 2: at scope exit, emit backwards JMPs to each defer body ===
|
||||
* Process in reverse order so the LAST declared defer runs FIRST at scope exit.
|
||||
* Each defer body's trailing JMP_exit gets patched to jump to right after
|
||||
* the scope-exit JMP we just emitted, so control chains through correctly. */
|
||||
for (int i = ctx->defer_count - 1; i >= 0; i--) {
|
||||
if (ctx->defer_stack[i].depth != depth)
|
||||
continue;
|
||||
spl_defer_entry_t *e = &ctx->defer_stack[i];
|
||||
|
||||
/* Emit JMP backwards to the defer body */
|
||||
spl_val_t here = vec_size(ctx->prog.insns);
|
||||
spl_val_t jmp_offset = (spl_val_t)((isize)e->body_start - (isize)here - 1);
|
||||
spl_emit(ctx, SPL_JMP, SPL_VOID, jmp_offset);
|
||||
emit_jmp(&ctx->emit, jmp_offset);
|
||||
|
||||
/* Patch the body's trailing JMP_exit to jump to here+1 (right after the
|
||||
* backwards JMP we just emitted). This chains to the next defer or exits. */
|
||||
if (e->jmp_exit > 0) {
|
||||
spl_val_t exit_target = here + 1;
|
||||
spl_val_t offset = exit_target - e->jmp_exit - 1;
|
||||
spl_patch(ctx, e->jmp_exit, offset);
|
||||
}
|
||||
if (e->jmp_exit > 0)
|
||||
emit_patch(&ctx->emit, e->jmp_exit, here + 1 - e->jmp_exit - 1);
|
||||
}
|
||||
|
||||
/* Remove processed defers from stack */
|
||||
int new_count = 0;
|
||||
for (int i = 0; i < ctx->defer_count; i++) {
|
||||
if (ctx->defer_stack[i].depth != depth) {
|
||||
if (ctx->defer_stack[i].depth != depth)
|
||||
ctx->defer_stack[new_count++] = ctx->defer_stack[i];
|
||||
}
|
||||
}
|
||||
ctx->defer_count = new_count;
|
||||
}
|
||||
|
||||
/* ---- Register runtime natives ---- */
|
||||
|
||||
void spl_comp_register(spl_prog_t *prog) {
|
||||
/* Runtime support functions for compiled SPL programs go here.
|
||||
* For stage1 bootstrap, most operations are handled inline.
|
||||
* We register basic runtime helpers if needed. */
|
||||
(void)prog;
|
||||
}
|
||||
|
||||
/* ---- Patch call fixups ---- */
|
||||
|
||||
void spl_patch_call_fixups(spl_comp_t *ctx) {
|
||||
for (usize i = 0; i < vec_size(ctx->call_fixups); i++) {
|
||||
spl_val_t insn_idx = vec_at(ctx->call_fixups, i);
|
||||
int pfi = vec_at(ctx->call_fixup_funcs, i);
|
||||
if (pfi >= 0 && pfi < (int)vec_size(ctx->prog.funcs)) {
|
||||
spl_val_t addr = vec_at(ctx->prog.funcs, pfi).address;
|
||||
vec_at(ctx->prog.insns, insn_idx).imm = addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
void spl_comp_register(spl_prog_t *prog) { (void)prog; }
|
||||
|
||||
/* ---- Main compilation ---- */
|
||||
|
||||
int spl_compile(spl_comp_t *ctx, const char *source, const char *fname) {
|
||||
/* Phase 1: Lex */
|
||||
ctx->toks = spl_lex(source, fname);
|
||||
ctx->tok_idx = 0;
|
||||
ctx->fname = fname;
|
||||
ctx->source = source;
|
||||
|
||||
/* Skip initial newlines */
|
||||
while (ctx->tok_idx < vec_size(ctx->toks) &&
|
||||
vec_at(ctx->toks, ctx->tok_idx).type == TOK_ENDLINE)
|
||||
ctx->tok_idx++;
|
||||
|
||||
/* Phase 2-4: Parse and codegen */
|
||||
spl_parse_prog(ctx);
|
||||
if (ctx->has_error)
|
||||
return -1;
|
||||
|
||||
/* Phase 5: Patch all function call fixups (forward references) */
|
||||
spl_patch_call_fixups(ctx);
|
||||
emit_patch_call_fixups(&ctx->emit, &ctx->prog);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user