prose: human-readable projection renderer (iter 20a)

New crate ailang-prose with one public fn module_to_prose(&Module)
-> String. Rust-flavour with braces, =>-match-arms, mode keywords
(own/borrow), effects as trailing 'with IO', Cons(1, Nil) ctor
form, /// doc strings.

Lossy projection where the LLM can re-derive: (con T) wrap,
(fn-type ...) wrap, (term-ctor ...) wrap. Load-bearing semantic
detail stays visible (modes, effects, clone, reuse-as, doc strings,
type annotations, tail flag).

CLI: 'ail prose <file.ail.json>' prints the projection.

3 snapshot fixtures + 28 unit tests. 215-line .ail.json reduces
to ~18 lines of legible source.

20b queued: infix arithmetic, paren elision, let-inlining, do
prettify.
This commit is contained in:
2026-05-08 18:06:16 +02:00
parent d8e80cb9c5
commit a9d57c5c81
11 changed files with 1171 additions and 0 deletions
@@ -0,0 +1,24 @@
// module rc_app_let_partial_drop_leak
data Wrap = MkWrap(Int)
data Cell = MkCell(Wrap, Wrap)
data Pair = MkPair(Cell, Cell)
fn build_pair(n: Int) -> own Pair {
MkPair(MkCell(MkWrap(n), MkWrap(2)), MkCell(MkWrap(3), MkWrap(4)))
}
fn use_cell(c: own Cell) -> Int {
match c {
MkCell(w1, w2) => 1
}
}
fn main() -> Unit with IO {
let p = build_pair(1);
do io/print_int(match p {
MkPair(a, _) => use_cell(a)
})
}
@@ -0,0 +1,23 @@
// module rc_match_arm_partial_drop_leak
data Wrap = MkWrap(Int)
data Cell = MkCell(Wrap, Wrap)
data Pair = MkPair(Cell, Cell)
fn build_pair(n: Int) -> own Pair {
MkPair(MkCell(MkWrap(n), MkWrap(2)), MkCell(MkWrap(3), MkWrap(4)))
}
fn use_first(p: own Pair) -> Int {
match p {
MkPair(a, b) => match a {
MkCell(w1, _) => 1
}
}
}
fn main() -> Unit with IO {
do io/print_int(use_first(build_pair(1)))
}
+18
View File
@@ -0,0 +1,18 @@
// module rc_own_param_drop
/// Recursive Int list — boxed.
data IntList = Nil | Cons(Int, IntList)
/// Take ownership of an IntList; return its head if Cons, else 0. The tail is loaded as a pattern binder but never consumed — iter A dec's it at arm close. The outer cell is dec'd at fn return via iter B's Own-param emission.
fn head_or_zero(xs: own IntList) -> Int {
match xs {
Nil => 0,
Cons(h, t) => h
}
}
/// Build a 5-element IntList; pass to head_or_zero (transferring ownership); print the result.
fn main() -> Unit with IO {
let xs = Cons(11, Cons(22, Cons(33, Cons(44, Cons(55, Nil)))));
do io/print_int(head_or_zero(xs))
}