Archive Rust bootstrap — El compiler is now self-hosting

This commit is contained in:
Will Anderson
2026-04-29 22:21:31 -05:00
parent 9a0747aa13
commit 4f3543b068
139 changed files with 8980 additions and 1778 deletions
+308 -309
View File
@@ -7,6 +7,11 @@
// See the el-compiler/src/bytecode.rs for the canonical enum.
//
// Entry point: fn codegen(stmts: [Map<String, Any>], source: String) -> String
//
// Performance: instruction accumulation uses the VM-native global buffer
// (native_instr_push / native_instr_len / native_instr_patch / native_instr_all)
// instead of passing a growing list through function arguments. This avoids O(n²)
// cloning and keeps compilation time linear in the number of instructions.
// JSON helpers
@@ -59,265 +64,237 @@ fn json_bool(b: Bool) -> String {
// Codegen state
//
// el has no mutable globals. We thread a "ctx" map through all codegen
// functions. The context holds:
// "instrs" -> [String] JSON strings for each emitted instruction
// "patches" -> [Map<String,Any>] pending forward-jump patches
// each patch: { "idx": Int, "kind": String }
// (kind is "JumpIfNot" / "Jump" we store the instruction index and
// patch it at the end of the construct)
fn ctx_new() -> Map<String, Any> {
{ "instrs": native_list_empty(), "patches": native_list_empty() }
}
fn ctx_emit(ctx: Map<String, Any>, instr_json: String) -> Map<String, Any> {
let instrs = ctx["instrs"]
let instrs = native_list_append(instrs, instr_json)
{ "instrs": instrs, "patches": ctx["patches"] }
}
fn ctx_len(ctx: Map<String, Any>) -> Int {
let instrs = ctx["instrs"]
native_list_len(instrs)
}
// Patch a previously-emitted placeholder instruction at index idx.
// For Jump/JumpIf/JumpIfNot, we need to replace the entry in instrs.
// We rebuild the list with the patched value at position idx.
fn ctx_patch(ctx: Map<String, Any>, idx: Int, dest: Int) -> Map<String, Any> {
// offset = dest - (idx + 1)
let offset = dest - (idx + 1)
let instrs = ctx["instrs"]
let total = native_list_len(instrs)
let new_instrs: [String] = native_list_empty()
let i = 0
while i < total {
let instr: String = native_list_get(instrs, i)
if i == idx {
// Replace: determine kind from original instruction string
if native_string_contains(instr, "JumpIfNot") {
let new_instrs = native_list_append(new_instrs, "{\"JumpIfNot\":" + json_int(offset) + "}")
} else {
if native_string_contains(instr, "JumpIf") {
let new_instrs = native_list_append(new_instrs, "{\"JumpIf\":" + json_int(offset) + "}")
} else {
let new_instrs = native_list_append(new_instrs, "{\"Jump\":" + json_int(offset) + "}")
}
}
} else {
let new_instrs = native_list_append(new_instrs, instr)
}
let i = i + 1
}
{ "instrs": new_instrs, "patches": ctx["patches"] }
}
// The instruction accumulator is stored in the VM's global instr_buf.
// native_instr_push(s) append JSON instruction string
// native_instr_len() current instruction count
// native_instr_get(i) get instruction at index i
// native_instr_patch(i,s) replace instruction at index i
// native_instr_all() return full list as [String]
// Instruction emitters
fn emit_halt(ctx: Map<String, Any>) -> Map<String, Any> {
ctx_emit(ctx, "\"Halt\"")
fn emit_halt() -> Void {
native_instr_push("\"Halt\"")
}
fn emit_nop(ctx: Map<String, Any>) -> Map<String, Any> {
ctx_emit(ctx, "\"Nop\"")
fn emit_pop() -> Void {
native_instr_push("\"Pop\"")
}
fn emit_pop(ctx: Map<String, Any>) -> Map<String, Any> {
ctx_emit(ctx, "\"Pop\"")
fn emit_return() -> Void {
native_instr_push("\"Return\"")
}
fn emit_return(ctx: Map<String, Any>) -> Map<String, Any> {
ctx_emit(ctx, "\"Return\"")
fn emit_add() -> Void {
native_instr_push("\"Add\"")
}
fn emit_add(ctx: Map<String, Any>) -> Map<String, Any> {
ctx_emit(ctx, "\"Add\"")
fn emit_sub() -> Void {
native_instr_push("\"Sub\"")
}
fn emit_sub(ctx: Map<String, Any>) -> Map<String, Any> {
ctx_emit(ctx, "\"Sub\"")
fn emit_mul() -> Void {
native_instr_push("\"Mul\"")
}
fn emit_mul(ctx: Map<String, Any>) -> Map<String, Any> {
ctx_emit(ctx, "\"Mul\"")
fn emit_div() -> Void {
native_instr_push("\"Div\"")
}
fn emit_div(ctx: Map<String, Any>) -> Map<String, Any> {
ctx_emit(ctx, "\"Div\"")
fn emit_eq() -> Void {
native_instr_push("\"Eq\"")
}
fn emit_eq(ctx: Map<String, Any>) -> Map<String, Any> {
ctx_emit(ctx, "\"Eq\"")
fn emit_not_eq() -> Void {
native_instr_push("\"NotEq\"")
}
fn emit_not_eq(ctx: Map<String, Any>) -> Map<String, Any> {
ctx_emit(ctx, "\"NotEq\"")
fn emit_lt() -> Void {
native_instr_push("\"Lt\"")
}
fn emit_lt(ctx: Map<String, Any>) -> Map<String, Any> {
ctx_emit(ctx, "\"Lt\"")
fn emit_gt() -> Void {
native_instr_push("\"Gt\"")
}
fn emit_gt(ctx: Map<String, Any>) -> Map<String, Any> {
ctx_emit(ctx, "\"Gt\"")
fn emit_lt_eq() -> Void {
native_instr_push("\"LtEq\"")
}
fn emit_lt_eq(ctx: Map<String, Any>) -> Map<String, Any> {
ctx_emit(ctx, "\"LtEq\"")
fn emit_gt_eq() -> Void {
native_instr_push("\"GtEq\"")
}
fn emit_gt_eq(ctx: Map<String, Any>) -> Map<String, Any> {
ctx_emit(ctx, "\"GtEq\"")
fn emit_and() -> Void {
native_instr_push("\"And\"")
}
fn emit_and(ctx: Map<String, Any>) -> Map<String, Any> {
ctx_emit(ctx, "\"And\"")
fn emit_or() -> Void {
native_instr_push("\"Or\"")
}
fn emit_or(ctx: Map<String, Any>) -> Map<String, Any> {
ctx_emit(ctx, "\"Or\"")
fn emit_not() -> Void {
native_instr_push("\"Not\"")
}
fn emit_not(ctx: Map<String, Any>) -> Map<String, Any> {
ctx_emit(ctx, "\"Not\"")
fn emit_get_index() -> Void {
native_instr_push("\"GetIndex\"")
}
fn emit_get_index(ctx: Map<String, Any>) -> Map<String, Any> {
ctx_emit(ctx, "\"GetIndex\"")
fn emit_push_nil() -> Void {
native_instr_push("{\"Push\":\"Nil\"}")
}
fn emit_push_nil(ctx: Map<String, Any>) -> Map<String, Any> {
ctx_emit(ctx, "{\"Push\":\"Nil\"}")
fn emit_push_int(n: Int) -> Void {
native_instr_push("{\"Push\":{\"Int\":" + json_int(n) + "}}")
}
fn emit_push_int(ctx: Map<String, Any>, n: Int) -> Map<String, Any> {
ctx_emit(ctx, "{\"Push\":{\"Int\":" + json_int(n) + "}}")
fn emit_push_bool(b: Bool) -> Void {
native_instr_push("{\"Push\":{\"Bool\":" + json_bool(b) + "}}")
}
fn emit_push_bool(ctx: Map<String, Any>, b: Bool) -> Map<String, Any> {
ctx_emit(ctx, "{\"Push\":{\"Bool\":" + json_bool(b) + "}}")
fn emit_push_str(s: String) -> Void {
native_instr_push("{\"Push\":{\"Str\":" + json_str(s) + "}}")
}
fn emit_push_str(ctx: Map<String, Any>, s: String) -> Map<String, Any> {
ctx_emit(ctx, "{\"Push\":{\"Str\":" + json_str(s) + "}}")
fn emit_load(name: String) -> Void {
native_instr_push("{\"LoadLocal\":" + json_str(name) + "}")
}
fn emit_load(ctx: Map<String, Any>, name: String) -> Map<String, Any> {
ctx_emit(ctx, "{\"LoadLocal\":" + json_str(name) + "}")
fn emit_store(name: String) -> Void {
native_instr_push("{\"StoreLocal\":" + json_str(name) + "}")
}
fn emit_store(ctx: Map<String, Any>, name: String) -> Map<String, Any> {
ctx_emit(ctx, "{\"StoreLocal\":" + json_str(name) + "}")
fn emit_call(name: String, arity: Int) -> Void {
native_instr_push("{\"Call\":{\"name\":" + json_str(name) + ",\"arity\":" + json_int(arity) + "}}")
}
fn emit_call(ctx: Map<String, Any>, name: String, arity: Int) -> Map<String, Any> {
ctx_emit(ctx, "{\"Call\":{\"name\":" + json_str(name) + ",\"arity\":" + json_int(arity) + "}}")
fn emit_get_field(field: String) -> Void {
native_instr_push("{\"GetField\":" + json_str(field) + "}")
}
fn emit_get_field(ctx: Map<String, Any>, field: String) -> Map<String, Any> {
ctx_emit(ctx, "{\"GetField\":" + json_str(field) + "}")
fn emit_build_map(n: Int) -> Void {
native_instr_push("{\"BuildMap\":" + json_int(n) + "}")
}
fn emit_build_map(ctx: Map<String, Any>, n: Int) -> Map<String, Any> {
ctx_emit(ctx, "{\"BuildMap\":" + json_int(n) + "}")
fn emit_build_list(n: Int) -> Void {
native_instr_push("{\"BuildList\":" + json_int(n) + "}")
}
fn emit_build_list(ctx: Map<String, Any>, n: Int) -> Map<String, Any> {
ctx_emit(ctx, "{\"BuildList\":" + json_int(n) + "}")
// Emit a placeholder Jump and return its index (for later patching).
fn emit_jump_placeholder() -> Int {
let idx: Int = native_instr_len()
native_instr_push("{\"Jump\":0}")
idx
}
// Emit a placeholder Jump and return its index (for later patching)
fn emit_jump_placeholder(ctx: Map<String, Any>) -> Map<String, Any> {
let idx = ctx_len(ctx)
let ctx = ctx_emit(ctx, "{\"Jump\":0}")
{ "ctx": ctx, "idx": idx }
fn emit_jump_if_not_placeholder() -> Int {
let idx: Int = native_instr_len()
native_instr_push("{\"JumpIfNot\":0}")
idx
}
fn emit_jump_if_not_placeholder(ctx: Map<String, Any>) -> Map<String, Any> {
let idx = ctx_len(ctx)
let ctx = ctx_emit(ctx, "{\"JumpIfNot\":0}")
{ "ctx": ctx, "idx": idx }
fn emit_jump_to(dest: Int) -> Void {
let here: Int = native_instr_len()
let offset: Int = dest - (here + 1)
native_instr_push("{\"Jump\":" + json_int(offset) + "}")
}
fn emit_jump_to(ctx: Map<String, Any>, dest: Int) -> Map<String, Any> {
let here = ctx_len(ctx)
let offset = dest - (here + 1)
ctx_emit(ctx, "{\"Jump\":" + json_int(offset) + "}")
// Patch a previously-emitted placeholder jump instruction.
fn patch_instr(idx: Int, dest: Int) -> Void {
let offset: Int = dest - (idx + 1)
let original: String = native_instr_get(idx)
if native_string_contains(original, "JumpIfNot") {
native_instr_patch(idx, "{\"JumpIfNot\":" + json_int(offset) + "}")
} else {
if native_string_contains(original, "JumpIf") {
native_instr_patch(idx, "{\"JumpIf\":" + json_int(offset) + "}")
} else {
native_instr_patch(idx, "{\"Jump\":" + json_int(offset) + "}")
}
}
}
// Expression codegen
fn cg_expr(ctx: Map<String, Any>, expr: Map<String, Any>) -> Map<String, Any> {
fn cg_expr(expr: Map<String, Any>) -> Void {
let kind: String = expr["expr"]
if kind == "Int" {
let v: String = expr["value"]
let n: Int = native_str_to_int(v)
return emit_push_int(ctx, n)
emit_push_int(n)
return
}
if kind == "Float" {
let v: String = expr["value"]
// Push as string for now VM handles float parsing from Str
return emit_push_str(ctx, v)
emit_push_str(v)
return
}
if kind == "Str" {
let v: String = expr["value"]
return emit_push_str(ctx, v)
emit_push_str(v)
return
}
if kind == "Bool" {
let v: String = expr["value"]
if v == "true" {
return emit_push_bool(ctx, true)
emit_push_bool(true)
} else {
emit_push_bool(false)
}
return emit_push_bool(ctx, false)
return
}
if kind == "Nil" {
return emit_push_nil(ctx)
emit_push_nil()
return
}
if kind == "Ident" {
let name: String = expr["name"]
return emit_load(ctx, name)
emit_load(name)
return
}
if kind == "Not" {
let inner = expr["inner"]
let ctx = cg_expr(ctx, inner)
return emit_not(ctx)
cg_expr(inner)
emit_not()
return
}
if kind == "Neg" {
// unary minus: push 0, push inner, sub
let ctx = emit_push_int(ctx, 0)
emit_push_int(0)
let inner = expr["inner"]
let ctx = cg_expr(ctx, inner)
return emit_sub(ctx)
cg_expr(inner)
emit_sub()
return
}
if kind == "BinOp" {
let op: String = expr["op"]
let left = expr["left"]
let right = expr["right"]
let ctx = cg_expr(ctx, left)
let ctx = cg_expr(ctx, right)
if op == "Plus" { return emit_add(ctx) }
if op == "Minus" { return emit_sub(ctx) }
if op == "Star" { return emit_mul(ctx) }
if op == "Slash" { return emit_div(ctx) }
if op == "EqEq" { return emit_eq(ctx) }
if op == "NotEq" { return emit_not_eq(ctx) }
if op == "Lt" { return emit_lt(ctx) }
if op == "Gt" { return emit_gt(ctx) }
if op == "LtEq" { return emit_lt_eq(ctx) }
if op == "GtEq" { return emit_gt_eq(ctx) }
if op == "And" { return emit_and(ctx) }
if op == "Or" { return emit_or(ctx) }
return ctx
cg_expr(left)
cg_expr(right)
if op == "Plus" { emit_add() }
if op == "Minus" { emit_sub() }
if op == "Star" { emit_mul() }
if op == "Slash" { emit_div() }
if op == "EqEq" { emit_eq() }
if op == "NotEq" { emit_not_eq() }
if op == "Lt" { emit_lt() }
if op == "Gt" { emit_gt() }
if op == "LtEq" { emit_lt_eq() }
if op == "GtEq" { emit_gt_eq() }
if op == "And" { emit_and() }
if op == "Or" { emit_or() }
return
}
if kind == "Call" {
@@ -328,37 +305,42 @@ fn cg_expr(ctx: Map<String, Any>, expr: Map<String, Any>) -> Map<String, Any> {
let i = 0
while i < arity {
let arg = native_list_get(args, i)
let ctx = cg_expr(ctx, arg)
cg_expr(arg)
let i = i + 1
}
// get function name from func expr
let func_kind: String = func["expr"]
if func_kind == "Ident" {
let fn_name: String = func["name"]
return emit_call(ctx, fn_name, arity)
emit_call(fn_name, arity)
return
}
if func_kind == "Field" {
let obj = func["object"]
let field: String = func["field"]
let ctx = cg_expr(ctx, obj)
return emit_call(ctx, field, arity + 1)
cg_expr(obj)
emit_call(field, arity + 1)
return
}
return emit_call(ctx, "__dynamic__", arity)
emit_call("__dynamic__", arity)
return
}
if kind == "Field" {
let obj = expr["object"]
let field: String = expr["field"]
let ctx = cg_expr(ctx, obj)
return emit_get_field(ctx, field)
cg_expr(obj)
emit_get_field(field)
return
}
if kind == "Index" {
let obj = expr["object"]
let idx = expr["index"]
let ctx = cg_expr(ctx, obj)
let ctx = cg_expr(ctx, idx)
return emit_get_index(ctx)
cg_expr(obj)
cg_expr(idx)
emit_get_index()
return
}
if kind == "Array" {
@@ -367,10 +349,11 @@ fn cg_expr(ctx: Map<String, Any>, expr: Map<String, Any>) -> Map<String, Any> {
let i = 0
while i < n {
let elem = native_list_get(elems, i)
let ctx = cg_expr(ctx, elem)
cg_expr(elem)
let i = i + 1
}
return emit_build_list(ctx, n)
emit_build_list(n)
return
}
if kind == "Map" {
@@ -381,11 +364,12 @@ fn cg_expr(ctx: Map<String, Any>, expr: Map<String, Any>) -> Map<String, Any> {
let pair = native_list_get(pairs, i)
let key: String = pair["key"]
let val = pair["value"]
let ctx = emit_push_str(ctx, key)
let ctx = cg_expr(ctx, val)
emit_push_str(key)
cg_expr(val)
let i = i + 1
}
return emit_build_map(ctx, n)
emit_build_map(n)
return
}
if kind == "If" {
@@ -394,41 +378,36 @@ fn cg_expr(ctx: Map<String, Any>, expr: Map<String, Any>) -> Map<String, Any> {
let else_stmts = expr["else"]
let has_else: Bool = expr["has_else"]
// cond
let ctx = cg_expr(ctx, cond)
cg_expr(cond)
// JumpIfNot placeholder
let r = emit_jump_if_not_placeholder(ctx)
let ctx = r["ctx"]
let jump_false_idx: Int = r["idx"]
let jump_false_idx: Int = emit_jump_if_not_placeholder()
// then body
let ctx = cg_stmts(ctx, then_stmts)
cg_stmts(then_stmts)
if has_else {
// jump over else
let r2 = emit_jump_placeholder(ctx)
let ctx = r2["ctx"]
let jump_end_idx: Int = r2["idx"]
let jump_end_idx: Int = emit_jump_placeholder()
// patch jump_false to here
let else_start = ctx_len(ctx)
let ctx = ctx_patch(ctx, jump_false_idx, else_start)
let else_start: Int = native_instr_len()
patch_instr(jump_false_idx, else_start)
// else body
let ctx = cg_stmts(ctx, else_stmts)
cg_stmts(else_stmts)
// patch jump_end to here
let after_else = ctx_len(ctx)
let ctx = ctx_patch(ctx, jump_end_idx, after_else)
return ctx
let after_else: Int = native_instr_len()
patch_instr(jump_end_idx, after_else)
} else {
let after_then = ctx_len(ctx)
let ctx = ctx_patch(ctx, jump_false_idx, after_then)
return ctx
let after_then: Int = native_instr_len()
patch_instr(jump_false_idx, after_then)
}
return
}
if kind == "Match" {
let subject = expr["subject"]
let arms = expr["arms"]
let n_arms: Int = native_list_len(arms)
let ctx = cg_expr(ctx, subject)
cg_expr(subject)
// store subject in temp var
let ctx = emit_store(ctx, "__match_subj__")
emit_store("__match_subj__")
let end_jump_idxs: [Int] = native_list_empty()
let i = 0
while i < n_arms {
@@ -438,155 +417,187 @@ fn cg_expr(ctx: Map<String, Any>, expr: Map<String, Any>) -> Map<String, Any> {
let pat_kind: String = pattern["pattern"]
if pat_kind == "Wildcard" {
// always matches just emit body
let ctx = cg_expr(ctx, body)
let r = emit_jump_placeholder(ctx)
let ctx = r["ctx"]
let jidx: Int = r["idx"]
cg_expr(body)
let jidx: Int = emit_jump_placeholder()
let end_jump_idxs = native_list_append(end_jump_idxs, jidx)
} else {
if pat_kind == "Binding" {
let bind_name: String = pattern["name"]
let ctx = emit_load(ctx, "__match_subj__")
let ctx = emit_store(ctx, bind_name)
let ctx = cg_expr(ctx, body)
let r = emit_jump_placeholder(ctx)
let ctx = r["ctx"]
let jidx: Int = r["idx"]
emit_load("__match_subj__")
emit_store(bind_name)
cg_expr(body)
let jidx: Int = emit_jump_placeholder()
let end_jump_idxs = native_list_append(end_jump_idxs, jidx)
} else {
// literal pattern: compare subject to literal
let ctx = emit_load(ctx, "__match_subj__")
emit_load("__match_subj__")
if pat_kind == "LitInt" {
let v: String = pattern["value"]
let n: Int = native_str_to_int(v)
let ctx = emit_push_int(ctx, n)
emit_push_int(n)
} else {
if pat_kind == "LitStr" {
let v: String = pattern["value"]
let ctx = emit_push_str(ctx, v)
emit_push_str(v)
} else {
if pat_kind == "LitBool" {
let v: String = pattern["value"]
if v == "true" {
let ctx = emit_push_bool(ctx, true)
emit_push_bool(true)
} else {
let ctx = emit_push_bool(ctx, false)
emit_push_bool(false)
}
} else {
let ctx = emit_push_nil(ctx)
emit_push_nil()
}
}
}
let ctx = emit_eq(ctx)
let r = emit_jump_if_not_placeholder(ctx)
let ctx = r["ctx"]
let no_match_idx: Int = r["idx"]
let ctx = cg_expr(ctx, body)
let r2 = emit_jump_placeholder(ctx)
let ctx = r2["ctx"]
let jidx: Int = r2["idx"]
emit_eq()
let no_match_idx: Int = emit_jump_if_not_placeholder()
cg_expr(body)
let jidx: Int = emit_jump_placeholder()
let end_jump_idxs = native_list_append(end_jump_idxs, jidx)
let next_arm = ctx_len(ctx)
let ctx = ctx_patch(ctx, no_match_idx, next_arm)
let next_arm: Int = native_instr_len()
patch_instr(no_match_idx, next_arm)
}
}
let i = i + 1
}
// default: push nil
let ctx = emit_push_nil(ctx)
let end_pos = ctx_len(ctx)
emit_push_nil()
let end_pos: Int = native_instr_len()
// patch all end jumps
let n_end: Int = native_list_len(end_jump_idxs)
let j = 0
while j < n_end {
let jidx: Int = native_list_get(end_jump_idxs, j)
let ctx = ctx_patch(ctx, jidx, end_pos)
patch_instr(jidx, end_pos)
let j = j + 1
}
return ctx
return
}
if kind == "For" {
// for item in list { body }
// Implementation:
// __for_list__ = list
// __for_len__ = len(list)
// __for_i__ = 0
// loop_start:
// if __for_i__ >= __for_len__ goto done
// item = __for_list__[__for_i__]
// body
// __for_i__ = __for_i__ + 1
// goto loop_start
// done:
let item: String = expr["item"]
let list_expr = expr["list"]
let body = expr["body"]
// emit list, store it
let ctx = cg_expr(ctx, list_expr)
let ctx = emit_store(ctx, "__for_list__")
cg_expr(list_expr)
emit_store("__for_list__")
// compute length
let ctx = emit_load(ctx, "__for_list__")
let ctx = emit_call(ctx, "native_list_len", 1)
let ctx = emit_store(ctx, "__for_len__")
emit_load("__for_list__")
emit_call("native_list_len", 1)
emit_store("__for_len__")
// init counter
let ctx = emit_push_int(ctx, 0)
let ctx = emit_store(ctx, "__for_i__")
emit_push_int(0)
emit_store("__for_i__")
// loop start
let loop_start = ctx_len(ctx)
let loop_start: Int = native_instr_len()
// condition: __for_i__ < __for_len__
let ctx = emit_load(ctx, "__for_i__")
let ctx = emit_load(ctx, "__for_len__")
let ctx = emit_lt(ctx)
let r = emit_jump_if_not_placeholder(ctx)
let ctx = r["ctx"]
let to_done_idx: Int = r["idx"]
emit_load("__for_i__")
emit_load("__for_len__")
emit_lt()
let to_done_idx: Int = emit_jump_if_not_placeholder()
// get current element
let ctx = emit_load(ctx, "__for_list__")
let ctx = emit_load(ctx, "__for_i__")
let ctx = emit_get_index(ctx)
let ctx = emit_store(ctx, item)
emit_load("__for_list__")
emit_load("__for_i__")
emit_get_index()
emit_store(item)
// body
let ctx = cg_stmts(ctx, body)
cg_stmts(body)
// increment counter
let ctx = emit_load(ctx, "__for_i__")
let ctx = emit_push_int(ctx, 1)
let ctx = emit_add(ctx)
let ctx = emit_store(ctx, "__for_i__")
emit_load("__for_i__")
emit_push_int(1)
emit_add()
emit_store("__for_i__")
// jump back
let ctx = emit_jump_to(ctx, loop_start)
emit_jump_to(loop_start)
// patch done
let done_pos = ctx_len(ctx)
let ctx = ctx_patch(ctx, to_done_idx, done_pos)
return ctx
let done_pos: Int = native_instr_len()
patch_instr(to_done_idx, done_pos)
return
}
if kind == "Try" {
// Just emit the inner expression error propagation handled at runtime
// Just emit the inner expression
let inner = expr["inner"]
return cg_expr(ctx, inner)
cg_expr(inner)
return
}
// Fallback
emit_push_nil(ctx)
emit_push_nil()
}
// Statement codegen
fn cg_stmt(ctx: Map<String, Any>, stmt: Map<String, Any>) -> Map<String, Any> {
// cg_stmt_tail emit a statement in tail position (last stmt of fn body).
// For Expr statements, the result is left on the stack (not popped).
// For all other statement kinds, we delegate to cg_stmt and push Nil.
fn cg_stmt_tail(stmt: Map<String, Any>) -> Void {
let kind: String = stmt["stmt"]
if kind == "Expr" {
let val = stmt["value"]
// Emit the expression leave result on stack (tail position).
cg_expr(val)
return
}
if kind == "Return" {
// Explicit return same as cg_stmt
let val = stmt["value"]
cg_expr(val)
emit_return()
return
}
// All other statement kinds (Let, FnDef, While, etc.) don't produce
// a natural return value fall through to cg_stmt then push Nil.
cg_stmt(stmt)
emit_push_nil()
}
// cg_stmts_body emit function body statements.
// All statements except the last use cg_stmt (pops results).
// The last statement uses cg_stmt_tail (leaves value on stack).
fn cg_stmts_body(stmts: [Map<String, Any>]) -> Void {
let n: Int = native_list_len(stmts)
if n == 0 {
// Empty body push Nil as return value.
emit_push_nil()
return
}
let i = 0
while i < n {
let stmt = native_list_get(stmts, i)
let is_last: Bool = (i == n - 1)
if is_last {
cg_stmt_tail(stmt)
} else {
cg_stmt(stmt)
}
let i = i + 1
}
}
fn cg_stmt(stmt: Map<String, Any>) -> Void {
let kind: String = stmt["stmt"]
if kind == "Let" {
let name: String = stmt["name"]
let val = stmt["value"]
let ctx = cg_expr(ctx, val)
return emit_store(ctx, name)
cg_expr(val)
emit_store(name)
return
}
if kind == "Return" {
let val = stmt["value"]
let ctx = cg_expr(ctx, val)
return emit_return(ctx)
cg_expr(val)
emit_return()
return
}
if kind == "FnDef" {
@@ -595,45 +606,40 @@ fn cg_stmt(ctx: Map<String, Any>, stmt: Map<String, Any>) -> Map<String, Any> {
let body = stmt["body"]
let n_params: Int = native_list_len(params)
// emit a Jump to skip over the function body
let r = emit_jump_placeholder(ctx)
let ctx = r["ctx"]
let skip_jump_idx: Int = r["idx"]
let skip_jump_idx: Int = emit_jump_placeholder()
// function body entry: store params in reverse order (caller pushes LR, so pop RL)
let pi = n_params - 1
while pi >= 0 {
let param = native_list_get(params, pi)
let pname: String = param["name"]
let ctx = emit_store(ctx, pname)
emit_store(pname)
let pi = pi - 1
}
// emit body statements
let ctx = cg_stmts(ctx, body)
// implicit nil return
let ctx = emit_push_nil(ctx)
let ctx = emit_return(ctx)
// emit body statements using tail-aware emit (last stmt leaves value on stack)
cg_stmts_body(body)
// return the top-of-stack value
emit_return()
// patch skip jump
let after_fn = ctx_len(ctx)
let ctx = ctx_patch(ctx, skip_jump_idx, after_fn)
let after_fn: Int = native_instr_len()
patch_instr(skip_jump_idx, after_fn)
// register function entry point
let entry_ip = skip_jump_idx + 1
let ctx = emit_push_int(ctx, entry_ip)
let ctx = emit_store(ctx, "__fn_" + fn_name)
return ctx
let entry_ip: Int = skip_jump_idx + 1
emit_push_int(entry_ip)
emit_store("__fn_" + fn_name)
return
}
if kind == "While" {
let cond = stmt["cond"]
let body = stmt["body"]
let loop_start = ctx_len(ctx)
let ctx = cg_expr(ctx, cond)
let r = emit_jump_if_not_placeholder(ctx)
let ctx = r["ctx"]
let to_done_idx: Int = r["idx"]
let ctx = cg_stmts(ctx, body)
let ctx = emit_jump_to(ctx, loop_start)
let done_pos = ctx_len(ctx)
let ctx = ctx_patch(ctx, to_done_idx, done_pos)
return ctx
let loop_start: Int = native_instr_len()
cg_expr(cond)
let to_done_idx: Int = emit_jump_if_not_placeholder()
cg_stmts(body)
emit_jump_to(loop_start)
let done_pos: Int = native_instr_len()
patch_instr(to_done_idx, done_pos)
return
}
if kind == "For" {
@@ -642,53 +648,46 @@ fn cg_stmt(ctx: Map<String, Any>, stmt: Map<String, Any>) -> Map<String, Any> {
let list_expr = stmt["list"]
let body = stmt["body"]
let for_expr = { "expr": "For", "item": item, "list": list_expr, "body": body }
return cg_expr(ctx, for_expr)
cg_expr(for_expr)
return
}
if kind == "Expr" {
let val = stmt["value"]
let val_kind: String = val["expr"]
let ctx = cg_expr(ctx, val)
// Discard result unless it's a control-flow expression
if val_kind == "If" {
return ctx
}
cg_expr(val)
// Discard result unless it's a control-flow expression that doesn't push a value
if val_kind == "For" {
return ctx
return
}
if val_kind == "Match" {
return ctx
}
return emit_pop(ctx)
emit_pop()
return
}
if kind == "TypeDef" {
// compile-time only; no runtime code
return ctx
return
}
if kind == "EnumDef" {
// compile-time only; no runtime code
return ctx
return
}
if kind == "Import" {
// handled at a higher level; skip
return ctx
return
}
ctx
}
fn cg_stmts(ctx: Map<String, Any>, stmts: [Map<String, Any>]) -> Map<String, Any> {
fn cg_stmts(stmts: [Map<String, Any>]) -> Void {
let n: Int = native_list_len(stmts)
let i = 0
while i < n {
let stmt = native_list_get(stmts, i)
let ctx = cg_stmt(ctx, stmt)
cg_stmt(stmt)
let i = i + 1
}
ctx
}
// JSON serialisation
@@ -711,9 +710,9 @@ fn instrs_to_json(instrs: [String]) -> String {
// Entry point
fn codegen(stmts: [Map<String, Any>], source: String) -> String {
let ctx = ctx_new()
let ctx = cg_stmts(ctx, stmts)
let ctx = emit_halt(ctx)
let instrs = ctx["instrs"]
native_instr_reset()
cg_stmts(stmts)
emit_halt()
let instrs: [String] = native_instr_all()
instrs_to_json(instrs)
}