Iter 3: ADTs + Pattern Matching

- AST: Def::Type mit Ctors; Term::Ctor (Konstruktion) und Term::Match
  mit Arm/Pattern. Patterns: Wild, Var, Lit, Ctor { ctor, fields } —
  Sub-Patterns im MVP auf Var/Wild beschränkt.
- Typchecker: Type-Registry, ctor_index für O(1)-Resolution, Pattern-
  Bindings, Exhaustiveness-Check gegen volle Konstruktormenge plus
  Negativ-Tests.
- Codegen: Boxed-Heap-Layout via malloc; Tag in Offset 0, Felder ab
  Offset 8 in 8-Byte-Slots. Match lowert zu load tag + switch + Phi
  am Join. Default-Block ist unreachable, wenn vom Typchecker geprüft.
- examples/list.ail.json: rekursive Int-Liste mit sum_list via match.
  E2E-Test + Exhaustiveness-Tests. 19/19 Tests grün.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-07 10:38:07 +02:00
parent 6e6b6a14fb
commit 21606c9340
8 changed files with 1060 additions and 8 deletions
+52 -5
View File
@@ -93,6 +93,29 @@ fn main() -> Result<()> {
ailang_core::pretty::type_to_string(&c.ty),
vec![],
),
ailang_core::Def::Type(t) => {
let s = t
.ctors
.iter()
.map(|c| {
if c.fields.is_empty() {
c.name.clone()
} else {
format!(
"{}({})",
c.name,
c.fields
.iter()
.map(ailang_core::pretty::type_to_string)
.collect::<Vec<_>>()
.join(", ")
)
}
})
.collect::<Vec<_>>()
.join(" | ");
("type", s, vec![])
}
};
serde_json::json!({
"name": d.name(),
@@ -233,11 +256,20 @@ fn main() -> Result<()> {
fn collect_refs(def: &ailang_core::Def) -> std::collections::BTreeSet<String> {
let mut out = std::collections::BTreeSet::new();
let body = match def {
ailang_core::Def::Fn(f) => &f.body,
ailang_core::Def::Const(c) => &c.value,
};
walk_term(body, &mut out);
match def {
ailang_core::Def::Fn(f) => walk_term(&f.body, &mut out),
ailang_core::Def::Const(c) => walk_term(&c.value, &mut out),
ailang_core::Def::Type(td) => {
// Eine Typedef referenziert die Typen ihrer Felder.
for c in &td.ctors {
for ft in &c.fields {
if let ailang_core::Type::Con { name } = ft {
out.insert(format!("type:{name}"));
}
}
}
}
}
out
}
@@ -271,5 +303,20 @@ fn walk_term(t: &ailang_core::Term, out: &mut std::collections::BTreeSet<String>
walk_term(a, out);
}
}
Term::Ctor { type_name, ctor, args } => {
out.insert(format!("ctor:{type_name}/{ctor}"));
for a in args {
walk_term(a, out);
}
}
Term::Match { scrutinee, arms } => {
walk_term(scrutinee, out);
for arm in arms {
if let ailang_core::ast::Pattern::Ctor { ctor, .. } = &arm.pat {
out.insert(format!("ctor:{ctor}"));
}
walk_term(&arm.body, out);
}
}
}
}
+7
View File
@@ -56,3 +56,10 @@ fn hello_world_str_lit() {
let stdout = build_and_run("hello.ail.json");
assert_eq!(stdout.trim(), "Hallo, AILang.");
}
/// Schützt ADT-Codegen + Match: rekursive Liste, sum_list via match auf Cons/Nil.
#[test]
fn list_sum_via_match() {
let stdout = build_and_run("list.ail.json");
assert_eq!(stdout.trim(), "42");
}