feat(lang): L5 generic records and functions

property Pool<T> { ... }, function first<T>(xs: []T) -> T, map<T, U> over fn
types; a type writes an instance as Pool<Thing>, nested as deep as needed. The
parser names an instance Pool$Thing and remembers its generic and arguments; the
checker takes the generic declarations out, infers a call's type arguments from
its arguments or its result's declared slot, and makes each instance once as an
ordinary record or function, checked like any other. Errors print Pool<Thing>.
An instance keeps its generic's module and export (L3). ludic-fmt keeps type
arguments together while spacing comparisons and shifts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-24 02:21:09 +03:00
parent 57b66bdf47
commit 6a24b14f0e
26 changed files with 61796 additions and 51949 deletions

View file

@ -2,7 +2,10 @@
# enum declarations, `for (vars) in query [terms] where cond`, spawn and prefab.
function parse_component() -> Node {
pi += 1; let n = node(N_COMP); n.s = eat_id(); skipnl(); eat_op("{")
pi += 1; let n = node(N_COMP); n.s = eat_id()
if is_op("<") { n.tps = gen_params() } # L5: property Pool<T> { ... }
gen_enter(n.tps)
skipnl(); eat_op("{")
while true { skipnl(); if is_op("}") { break }
var is_computed = false
var is_sync = false # @Sync — this field replicates (NETWORKING N2)
@ -13,7 +16,9 @@ function parse_component() -> Node {
if is_computed { register_computed(n.s, f.s, f.ty, f.a) } # derived: no storage
else { push(n.kids, f) }
if is_op(",") { pi += 1 } }
eat_op("}"); return n
eat_op("}")
gen_enter(null)
return n
}
# event Name { field: T = default, ... } — a public event's POD payload. Same