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

@ -123,7 +123,9 @@ function ptype() -> pointer {
out[2 + i] = 0
return out
}
return eat_id()
let tn = eat_id()
if is_op("<") { return gen_type_args(tn) }
return tn
}
# ---- expressions -----------------------------------------------------------
@ -535,7 +537,10 @@ function is_reserved_word(w: pointer) -> bool {
}
function parse_fn() -> Node {
pi += 1; let n = node(N_FN); n.s = eat_id(); eat_op("(")
pi += 1; let n = node(N_FN); n.s = eat_id()
if is_op("<") { n.tps = gen_params() } # L5: function first<T>(xs: []T) -> T
gen_enter(n.tps)
eat_op("(")
if is_reserved_word(n.s) { perr(`'{n.s}' is a reserved word and cannot name a function`) }
while not is_op(")") {
let p = node(N_PARAM); p.s = eat_id(); eat_op(":"); p.ty = ptype(); push(n.kids, p)
@ -545,6 +550,7 @@ function parse_fn() -> Node {
n.ty = "void"
if is_op("->") { pi += 1; n.ty = ptype() }
n.a = block()
gen_enter(null)
return n
}
function parse_main() -> Node { pi += 1; let n = node(N_MAIN); n.a = block(); return n }