Iter 16a: nested constructor patterns via AST desugaring
Lifts the gate that rejected `(pat-ctor Cons a (pat-ctor Cons b _))`.
Approach: a pure AST → AST pass `ailang_core::desugar::desugar_module`
that flattens nested-Ctor sub-patterns into chains of single-level
matches with let-bound fresh vars and duplicate fall-through. Both
ailang-check and ailang-codegen call the pass at pipeline entry.
Hash-relevant canonical bytes are untouched: the pass runs after
load_module, in memory only. `check`'s returned CheckedModule.symbols
still carries hashes of the original defs so `ail diff` / `ail manifest`
see on-disk identities.
Lit sub-patterns inside a Ctor still rejected — that's the narrower
scope of `nested-ctor-pattern-not-allowed` post-16a; the variant doc
reflects the new meaning.
Fresh-name safety: `$` is a valid ident character in form (A), so
the Desugarer pre-walks every Term::Var / Pattern::Var name into a
BTreeSet and bumps its counter past any user-spelled `$mp_N`.
Already-flat matches go through `is_flat()` and emit identical AST
shapes; every existing fixture produces the same output as before.
New: examples/nested_pat.{ailx,ail.json} prints 30 from
first_two_sum on [10, 20, 30]. e2e test
`nested_ctor_pattern_first_two_sum` guards the path.
Tests: 92/92 (e2e 32→33, ailang-core unit 10→12).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -129,6 +129,9 @@ type Result<T> = std::result::Result<T, CodegenError>;
|
||||
/// modules (cross-module calls, transitive imports) — `emit_ir` is the
|
||||
/// short-cut for the single-file demo case.
|
||||
pub fn emit_ir(m: &Module) -> Result<String> {
|
||||
// Iter 16a: nested ctor patterns are desugared inside
|
||||
// `lower_workspace`, so single-module callers go through the
|
||||
// same code path with no extra work here.
|
||||
let mut modules = BTreeMap::new();
|
||||
modules.insert(m.name.clone(), m.clone());
|
||||
let ws = Workspace {
|
||||
@@ -165,6 +168,20 @@ pub fn emit_ir(m: &Module) -> Result<String> {
|
||||
/// Use [`emit_ir`] for the single-file shortcut when there are no
|
||||
/// imports.
|
||||
pub fn lower_workspace(ws: &Workspace) -> Result<String> {
|
||||
// Iter 16a: desugar every module before any lowering work runs.
|
||||
// The pass is idempotent and structurally identical to what
|
||||
// `ailang-check` runs at its public entries, so the codegen
|
||||
// sees the same flat-pattern AST as the typechecker.
|
||||
let ws_owned = Workspace {
|
||||
entry: ws.entry.clone(),
|
||||
modules: ws
|
||||
.modules
|
||||
.iter()
|
||||
.map(|(k, m)| (k.clone(), ailang_core::desugar::desugar_module(m)))
|
||||
.collect(),
|
||||
root_dir: ws.root_dir.clone(),
|
||||
};
|
||||
let ws = &ws_owned;
|
||||
let mut header = String::new();
|
||||
let mut body = String::new();
|
||||
let mut all_strings: BTreeMap<String, Vec<(String, String)>> = BTreeMap::new();
|
||||
|
||||
Reference in New Issue
Block a user