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

@ -23,6 +23,7 @@ function ck_target(t: Node) -> pointer {
}
function ck_let(s: Node) -> void {
var t: pointer = "?"
ck_expect = s.ty
if s.a != null { t = ck_expr(s.a) }
if s.ty != null {
if s.a != null { ck_give(s.ty, t, s.a, s.s) }
@ -33,6 +34,7 @@ function ck_let(s: Node) -> void {
}
function ck_assign(s: Node) -> void {
let lt = ck_target(s.a)
if (s.s == "=") { ck_expect = lt }
let rt = ck_expr(s.b)
var what: pointer = "this assignment"
if s.a.kind == E_ID { what = s.a.s }
@ -97,6 +99,7 @@ function ck_stmt(s: Node) -> void {
}
if k == S_RETURN {
if s.a == null { return }
ck_expect = ck_ret
let t = ck_expr(s.a)
if (ck_ret != "void") { ck_give(ck_ret, t, s.a, "the result") }
return
@ -132,10 +135,9 @@ function ck_listener(l: Node) -> void {
ck_block(l.a)
ck_pop(m)
}
# LUDIC_CHECK=0 turns the pass off, for bisecting a checker fault; nothing else should need it
# the pass also makes the generics real (check_gen.ludic), so it always runs
function check_program() -> void {
let off = getenv("LUDIC_CHECK")
if off != null and (off == "0") { return }
gen_collect()
ck_index()
var i = 0
while i < g_prog_user_end {
@ -150,6 +152,7 @@ function check_program() -> void {
ck_listener(g_onlisten[j])
j += 1
}
gen_finish()
if ck_errors > 0 and not ck_reporting() {
let m = `{itoa(ck_errors)} type error(s)\n`
file_write(file_stderr(), m, len(m))