From 3988fb53810452953540867b914b74505ccf38d8 Mon Sep 17 00:00:00 2001 From: cassowarii Date: Wed, 1 Jul 2026 21:55:03 -0700 Subject: [PATCH] split out ir printing; mutual + recursive function calling --- src/compile/emit.c | 10 +++- src/compile/ir.c | 151 +++++-------------------------------------------- src/compile/print_ir.c | 139 +++++++++++++++++++++++++++++++++++++++++++++ src/vm/bytecode.h | 1 + src/vm/exec.c | 10 ++++ 5 files changed, 172 insertions(+), 139 deletions(-) create mode 100644 src/compile/print_ir.c diff --git a/src/compile/emit.c b/src/compile/emit.c index 249daa8..071edfd 100644 --- a/src/compile/emit.c +++ b/src/compile/emit.c @@ -255,11 +255,17 @@ void compile_expr(sbVmCompiler *cm, sbIrExpr *expr) { * a second time */ EMIT(BC_LD_UPREF); } else if ((*var)->closed_over) { - /* note: BC_LD_VAR here, not BC_LD_IND! this will push + /* note: BC_LD_REF here, not BC_LD_IND! this will push * the pointer value, not the variable itself, because * we want to put the pointer (well, the sbRef) inside * the closure, not the value */ - EMIT(BC_LD_VAR); + /* BC_LD_REF is actually almost the same as BC_LD_VAR, + * except that if a variable hasn't been initialized + * it will create a new reference to nil. (this is + * necessary for mutually calling functions, who want to + * close over each other before all of them have been + * defined.) */ + EMIT(BC_LD_REF); } else { PANIC("cannot have a direct variable in closure!"); } diff --git a/src/compile/ir.c b/src/compile/ir.c index 16aca5d..79d7cee 100644 --- a/src/compile/ir.c +++ b/src/compile/ir.c @@ -16,7 +16,6 @@ typedef struct varmapentry { static sbIrChunk *compile_ast_function(hIrProgram ir, sbAst params, sbAst seqast); static sbIrChunk *new_chunk(hIrProgram ir); static void chunk_deinitialize(hIrChunk ck); -static void print_stmt(sbIrStmt *s); void sbIrProgram_initialize(hIrProgram ir, usize initial_arena_size) { *ir = (sbIrProgram) {0}; @@ -43,27 +42,6 @@ void sbIrProgram_compile_ast(hIrProgram ir, sbAst ast) { compile_ast_function(ir, NO_NODE, ast); } -void sbIrProgram_print(hIrProgram ir) { - usize nchunks = ir->chunks.size / sizeof(sbIrChunk*); - - for (usize i = 0; i < nchunks; i++) { - sbIrChunk *ck = ((sbIrChunk**)ir->chunks.data)[i]; - debug("\nCHUNK %d with %d variables", ck->id, ck->variable_count); - if (ck->num_upvalues > 0) { - debug(" and %d upvalues", ck->num_upvalues); - } - debug("\n"); - - usize nstmts = ck->stmts.size / sizeof(sbIrStmt*); - for (usize j = 0; j < nstmts; j++) { - sbIrStmt *s = ((sbIrStmt**)ck->stmts.data)[j]; - print_stmt(s); - } - - debug("\n"); - } -} - /* --- */ static sbIrExpr SENTINEL_NIL_EXPR = { @@ -446,7 +424,6 @@ static void put_expr(hIrChunk ck, sbIrExpr *expr) { }); } -static void print_expr(sbIrExpr *e); static void put_assign(hIrChunk ck, sbIrVariable *assign_to, sbIrExpr *expr) { assign_to->initialized = TRUE; put_ir_stmt(ck, &(sbIrStmt) { @@ -535,6 +512,19 @@ static void compile_ast_stmtseq(hIrChunk ck, sbAst seqast, flag implicit_return) sbAst considering = seqast; while (considering != NO_NODE) { + sbAst node = considering->seq.left; + if (considering->seq.left->type == AST_NODE_DEF) { + /* DEF is guaranteed to have a value bound to it always. to allow functions + * to call each other forward and backward, we'll scan each scope for DEF nodes + * and make sure those names are available through the whole scope. */ + const char *vname = sbSymbol_name(node->seq.left->symb); + create_var(ck, vname, strlen(vname)); + } + considering = considering->seq.right; + } + + considering = seqast; + while (considering != NO_NODE) { /* walk down the tree a line at a time */ if (considering->type != AST_NODE_SEQ) { PANIC("compile_ast_stmtseq expects a SEQ node! (received instead #%d)", seqast->type); @@ -731,7 +721,7 @@ static void compile_ast_stmt(hIrChunk ck, sbAst node, flag implicit_return) { if (body->type != AST_NODE_SEQ) PANIC("expected SEQ node as function body!"); const char *vname = sbSymbol_name(node->seq.left->symb); - V1 = create_var(ck, vname, strlen(vname)); + V1 = var_name(ck, vname, strlen(vname)); C1 = compile_ast_function(ck->program, params, body); E1 = expr_func(ck, C1); put_assign(ck, V1, E1); @@ -928,116 +918,3 @@ static sbIrExpr *compile_ast_expr(hIrChunk ck, sbAst node) { PANIC("expr todo! (%d)", node->type); } } - -static void print_var(sbIrVariable *v) { - if (v->closed_over) { - debug("special "); - } - - if (v->is_upvalue) { - debug("upvalue %zu", v->slot_id); - } else { - debug("variable %zu", v->slot_id); - } -} - -static void print_expr(sbIrExpr *e) { - debug("("); - switch(e->type) { - case IR_E_VAR: - print_var(e->var); - break; - case IR_E_FUNC: - debug("{ chunk %d ", e->func.chunk->id); - if (e->func.bound.size > 0) { - debug("closing over "); - BUFFER_ITER(e->func.bound, sbIrVariable*, var) { - print_var(*var); - debug(", "); - } - } - debug("}"); - break; - case IR_E_VALUE: - if (e->value.type == IT_NIL) { - debug("nil"); - } else if (e->value.type == IT_INTEGER) { - debug("%lld", e->value.integer); - } else { - debug("some value"); - } - break; - case IR_E_OP: - print_expr(e->op.left); - debug(" %c ", e->op.type); - if (e->op.right) { - print_expr(e->op.right); - } - break; - case IR_E_CALL: - debug("CALL: "); - print_expr(e->call.func); - debug(" with params "); - sbIrExpr *param = e->call.param; - while (param) { - print_expr(param->list.this); - debug(","); - param = param->list.next; - } - break; - default: - debug("something"); - } - debug(")"); -} - -static void print_stmt(sbIrStmt *s) { - switch (s->type) { - case IR_S_ARG: - debug(" bind function argument to "); - print_var(s->arg.var); - debug("\n"); - if (s->arg.last) { - debug(" (no function arguments left on the stack now)\n"); - } - break; - case IR_S_LABEL: - debug("label %zu:\n", s->label->id); - break; - case IR_S_JUMP: - debug(" jump to label %zu", s->jump.label->id); - if (s->jump.condition) { - if (s->jump.inverted) { - debug(" unless "); - } else { - debug(" if "); - } - print_expr(s->jump.condition); - } - debug("\n"); - break; - case IR_S_RETURN: - debug(" return "); - if (s->expr) { - print_expr(s->expr); - } else { - debug("NOTHING"); - } - debug("\n"); - break; - case IR_S_EXPR: - debug(" "); - print_expr(s->expr); - debug("\n"); - break; - case IR_S_ASSIGN: - debug(" "); - print_var(s->assign.var); - debug(" = "); - print_expr(s->assign.expr); - debug("\n"); - break; - default: - debug(" (do something)\n"); - } -} diff --git a/src/compile/print_ir.c b/src/compile/print_ir.c new file mode 100644 index 0000000..bb5cbb8 --- /dev/null +++ b/src/compile/print_ir.c @@ -0,0 +1,139 @@ +#include "compile/ir.h" + +static void print_stmt(sbIrStmt *s); + +void sbIrProgram_print(hIrProgram ir) { + usize nchunks = ir->chunks.size / sizeof(sbIrChunk*); + + for (usize i = 0; i < nchunks; i++) { + sbIrChunk *ck = ((sbIrChunk**)ir->chunks.data)[i]; + debug("\nCHUNK %d with %d variables", ck->id, ck->variable_count); + if (ck->num_upvalues > 0) { + debug(" and %d upvalues", ck->num_upvalues); + } + debug("\n"); + + usize nstmts = ck->stmts.size / sizeof(sbIrStmt*); + for (usize j = 0; j < nstmts; j++) { + sbIrStmt *s = ((sbIrStmt**)ck->stmts.data)[j]; + print_stmt(s); + } + + debug("\n"); + } +} + +/* --- */ + +static void print_var(sbIrVariable *v) { + if (v->closed_over) { + debug("special "); + } + + if (v->is_upvalue) { + debug("upvalue %zu", v->slot_id); + } else { + debug("variable %zu", v->slot_id); + } +} + +static void print_expr(sbIrExpr *e) { + debug("("); + switch(e->type) { + case IR_E_VAR: + print_var(e->var); + break; + case IR_E_FUNC: + debug("{ chunk %d ", e->func.chunk->id); + if (e->func.bound.size > 0) { + debug("closing over "); + BUFFER_ITER(e->func.bound, sbIrVariable*, var) { + print_var(*var); + debug(", "); + } + } + debug("}"); + break; + case IR_E_VALUE: + if (e->value.type == IT_NIL) { + debug("nil"); + } else if (e->value.type == IT_INTEGER) { + debug("%lld", e->value.integer); + } else { + debug("some value"); + } + break; + case IR_E_OP: + print_expr(e->op.left); + debug(" %c ", e->op.type); + if (e->op.right) { + print_expr(e->op.right); + } + break; + case IR_E_CALL: + debug("CALL: "); + print_expr(e->call.func); + debug(" with params "); + sbIrExpr *param = e->call.param; + while (param) { + print_expr(param->list.this); + debug(","); + param = param->list.next; + } + break; + default: + debug("something"); + } + debug(")"); +} + +static void print_stmt(sbIrStmt *s) { + switch (s->type) { + case IR_S_ARG: + debug(" bind function argument to "); + print_var(s->arg.var); + debug("\n"); + if (s->arg.last) { + debug(" (no function arguments left on the stack now)\n"); + } + break; + case IR_S_LABEL: + debug("label %zu:\n", s->label->id); + break; + case IR_S_JUMP: + debug(" jump to label %zu", s->jump.label->id); + if (s->jump.condition) { + if (s->jump.inverted) { + debug(" unless "); + } else { + debug(" if "); + } + print_expr(s->jump.condition); + } + debug("\n"); + break; + case IR_S_RETURN: + debug(" return "); + if (s->expr) { + print_expr(s->expr); + } else { + debug("NOTHING"); + } + debug("\n"); + break; + case IR_S_EXPR: + debug(" "); + print_expr(s->expr); + debug("\n"); + break; + case IR_S_ASSIGN: + debug(" "); + print_var(s->assign.var); + debug(" = "); + print_expr(s->assign.expr); + debug("\n"); + break; + default: + debug(" (do something)\n"); + } +} diff --git a/src/vm/bytecode.h b/src/vm/bytecode.h index 6eb6cb5..d6ae47b 100644 --- a/src/vm/bytecode.h +++ b/src/vm/bytecode.h @@ -9,6 +9,7 @@ typedef enum sbOpcode { BC_LD_CONST, // push constants[index] to stack BC_LD_CTX, // look up key in context object and push result BC_LD_VAR, // push value onto stack from variable + BC_LD_REF, // push reference value onto stack from variable BC_LD_UPVAL, // push value onto stack from closure BC_LD_UPREF, // push pointer to closure value onto stack (to close over again) BC_LD_BLK, // push reference to function onto stack diff --git a/src/vm/exec.c b/src/vm/exec.c index fd2f688..bef5fa2 100644 --- a/src/vm/exec.c +++ b/src/vm/exec.c @@ -199,6 +199,16 @@ void execute_instruction(hVm vm) { param = get_param(vm); push_stack(vm, &vm->fp->locals[param]); break; + case BC_LD_REF: + param = get_param(vm); + w = &vm->fp->locals[param]; + if (w->type == IT_NOTHING) { + /* create new ref */ + store_local(vm, param, &HVREF(sbRef_create(&HVNIL))); + w = &vm->fp->locals[param]; + } + push_stack(vm, w); + break; case BC_LD_UPVAL: /* Hey, was there upval in there? */ param = get_param(vm); -- 1.8.3.1