wip(0.S3): the runtime migrated - ludic migrate state --runtime <every program>: 331 vars into 25 states (RtInputState, RtGlState, ...), 2 lets; its states are made before it boots; no module-level var is let through outside --globals

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-25 16:02:12 +03:00
parent 7b17b4a1b9
commit 02448e176c
38 changed files with 53877 additions and 53251 deletions

View file

@ -500,6 +500,10 @@ function emit_ns_call(ns: pointer, meth: pointer, e: Node) -> Val {
if (vdecl != null) {
let vnames = param_labels(vdecl)
var vi = 0
if vdecl.kind == N_FN {
nsfn = vdecl # 0.S: its states are the runtime's to give
vi = state_lead(vdecl)
}
while vi < len(vnames) { push(labels, vnames[vi]); vi += 1 }
}
}
@ -510,6 +514,10 @@ function emit_ns_call(ns: pointer, meth: pointer, e: Node) -> Val {
if (gdecl != null) {
let gnames = param_labels(gdecl)
var gi = 0
if gdecl.kind == N_FN {
nsfn = gdecl # 0.S: its states are the runtime's to give
gi = state_lead(gdecl)
}
while gi < len(gnames) { push(labels, gnames[gi]); gi += 1 }
}
}

View file

@ -60,6 +60,7 @@ function emit_main(d: Node) -> void {
# game that renders (drives tick_render, draws, uses the engine light pass) has
# its framebuffer allocated. Headless it only allocates — no window, no output —
# so a non-rendering entry game is unchanged.
emit(" call void @L_init_runtime()\n")
if (find_fn("rt_init") != null) { emit(" call void " + fn_sym("rt_init") + "()\n") }
emit(" call void @L_init_globals()\n")
emit_block(d.a)
@ -146,6 +147,7 @@ function emit_test_runner() -> void {
}
emit(" ret i32 0\n")
emit(`{lrun0}:\n`)
emit(" call void @L_init_runtime()\n")
if has_ecs() { emit(" call void @rt_init()\n") }
emit(" call void @L_init_globals()\n")
i = 0

View file

@ -64,6 +64,12 @@ function global_needs_init_code(d: Node) -> bool {
# (`var run: Progress = new Progress`, `var speed: int = BASE * 2`, a call), in
# declaration order, once at startup — after the runtime boots, before Start.
function emit_global_init_fn() -> void {
emit_init_fn("L_init_runtime", true)
emit_init_fn("L_init_globals", false)
}
# 0.S: the runtime's states are made first, before it boots (rt_init reads them); the rest after
function is_runtime_state_var(d: Node) -> bool { return d.uns == 1 and is_state_ty(d.ty) and is_runtime_file(d.file) }
function emit_init_fn(name: pointer, runtime: bool) -> void {
ll_t = 0; ll_lbl = 0; g_term = false; loc_reset(); nloop = 0; nself = 0
ret_ty = "void"
let fbody = buf_new()
@ -73,7 +79,7 @@ function emit_global_init_fn() -> void {
var i = 0
while i < len(prog) {
let d = prog[i]
if d.kind == N_VAR and global_needs_init_code(d) {
if d.kind == N_VAR and global_needs_init_code(d) and is_runtime_state_var(d) == runtime {
# an initializer is the global's own file's code: its errors, and what its module
# may see (L3), are that file's - not whichever statement was lowered last
g_err_file = d.file
@ -93,7 +99,7 @@ function emit_global_init_fn() -> void {
}
emit(" ret void\n")
code = saved
emit("define void @L_init_globals() {\nentry:\n")
emit(`define void @{name}() {{\nentry:\n`)
emit(buf_str(falloc))
emit(buf_str(fbody))
emit("}\n\n")

View file

@ -445,6 +445,7 @@ function emit_game_main() -> void {
emit("define i32 @main(i32 %argc, ptr %argv) {\nentry:\n")
emit(" store i32 %argc, ptr @L_argc\n")
emit(" store ptr %argv, ptr @L_argv\n")
emit(" call void @L_init_runtime()\n")
if (find_fn("rt_init") != null) { emit(" call void " + fn_sym("rt_init") + "()\n") }
emit(" call void @L_init_globals()\n")
emit_calls_for_phase("Start")

View file

@ -53,6 +53,15 @@ function ns_alias_labels(i: int) -> []pointer {
if d != null { return param_labels(d) }
return out
}
# 0.S: a target's leading states come first, by their own names (they are supplied, not written)
let t = find_fn(g_al_target[i])
if t != null {
var k = 0
while k < len(t.kids) and t.kids[k].kind == N_PARAM and is_state_ty(t.kids[k].ty) {
push(out, t.kids[k].s)
k += 1
}
}
var a = 0
var j = 0
let n = len(ls)

View file

@ -1027,7 +1027,7 @@ function parse_one_decl() -> void {
if is_id("component") and (toks[pi + 1].kind == TK_ID) and (toks[pi + 2].text == "{") { parse_ui_component(); return } # L11
if is_id("state") and (toks[pi + 1].kind == TK_ID) and (toks[pi + 2].text == "{") { parse_state(); return } # 0.S
if is_id("var") {
if not g_allow_globals and not has_sub(g_parse_file, "runtime/native/") { # the runtime: until it is migrated (0.S3)
if not g_allow_globals {
perr(`a module-level var is refused: a module's changing data is its state (state Name {{ ... }}), passed to the functions that use it - or, if it never changes, a let`)
}
push(prog, parse_var())

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff