feat(lang): functions are values (L2), and render3d takes its scene as callbacks

fn(int, float) -> bool is a type, fn name is any top-level function's value, and a call through
a local, a global, a record field, a slice element, a parameter or a result of a function type
is an indirect call; two function types mix only when equal, a call checks its argument count,
and a value may be null (examples/functions/values.ludic). Job.parallel_for keeps its worker
check.

render3d's scene is registered rather than required by name: r3d_on_draw, r3d_on_casters and
r3d_on_stream_fill (hooks.ludic). The two rendering examples register theirs - and had defined
scene_draw_casters with no parameter while the renderer passed one, which nothing checked.
render3d declares numbers float itself; smooth.ludic is converted to floats and returns when
r3d_init fails instead of running on into a segfault. Noise.* check their argument count (a call
one short crashed the compiler). selfhost-build says why it failed. The migration tool reads a
declared float as evidence. Seed regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-24 00:27:26 +03:00
parent dc75a5a5ab
commit 0c73287e35
23 changed files with 71430 additions and 68575 deletions

View file

@ -65,8 +65,27 @@ function eat_id() -> pointer {
}
function skipnl() -> void { while toks[pi].kind == TK_NL { pi += 1 } }
# a type: `[]T` slice, or a plain name (int/ptr/str/bool/struct)
# a type: `[]T` slice, `fn(T, U) -> R` function, or a plain name (int/ptr/str/bool/struct)
function ptype() -> pointer {
if (toks[pi].text == "fn") and (toks[pi + 1].text == "(") {
pi += 1
eat_op("(")
var out = "fn("
var first = true
while not is_op(")") {
if not first { out = out + "," }
out = out + ptype()
first = false
if is_op(",") { pi += 1 }
}
eat_op(")")
var r = "void"
if is_op("->") {
pi += 1
r = ptype()
}
return out + ")->" + r
}
if is_op("[") {
pi += 1
eat_op("]")