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:
@@ -8879,3 +8879,88 @@ is the form-B *projection*).
|
||||
- Whether snapshot tests live in `crates/ailang-prose/tests/`
|
||||
or `examples/` (as `.prose.txt` siblings to the .ail.json).
|
||||
|
||||
|
||||
## 2026-05-08 — Iter 20a: prose renderer skeleton shipped
|
||||
|
||||
The renderer-side of the family-20 design pinning. New crate
|
||||
`ailang-prose` with one public fn `module_to_prose(&Module) -> String`,
|
||||
plus a `ail prose <file.ail.json>` CLI subcommand that prints the
|
||||
projection to stdout.
|
||||
|
||||
### Style commitments (orchestrator-fixed for 20a, not up to renderer)
|
||||
|
||||
- Rust-flavour with braces and `=>` for match arms.
|
||||
- Mode keywords `own T` / `borrow T` (not sigils — Decision-10 consistency).
|
||||
- Effects trailing the return type: `with IO`.
|
||||
- Constructor application as `Cons(1, Nil)` (no `(term-ctor ...)` wrap).
|
||||
- Types as bare names: `Int`, `List<Int>` (no `(con ...)` wrap).
|
||||
- Doc strings as `///` lines above the def.
|
||||
- `tail` flag on calls renders as a `tail ` prefix.
|
||||
- All other load-bearing semantic detail stays visible: `clone`,
|
||||
`reuse-as`, effects, doc strings, type annotations on signatures
|
||||
and lambdas.
|
||||
- No infix yet; arithmetic primitives stay as `i64-add(a, b)`.
|
||||
That's 20b.
|
||||
|
||||
### Snapshot coverage
|
||||
|
||||
Three fixtures got `.prose.txt` siblings, asserted by
|
||||
`crates/ailang-prose/tests/snapshot.rs`:
|
||||
|
||||
- `examples/rc_own_param_drop.prose.txt`
|
||||
- `examples/rc_match_arm_partial_drop_leak.prose.txt`
|
||||
- `examples/rc_app_let_partial_drop_leak.prose.txt`
|
||||
|
||||
Sample (`rc_own_param_drop.prose.txt`):
|
||||
|
||||
```
|
||||
fn head_or_zero(xs: own IntList) -> Int {
|
||||
match xs {
|
||||
Nil => 0,
|
||||
Cons(h, t) => h
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
vs. the 215-line `.ail.json` it projects from.
|
||||
|
||||
The `head_or_zero` body is exactly the kind of thing the user
|
||||
named: dramatically less syntactic noise, immediately legible to
|
||||
a human, with `own` mode and `Cons(h, t)` as conventional source
|
||||
forms.
|
||||
|
||||
### Visible nits queued for 20b
|
||||
|
||||
- Long doc strings stay on one wrap-less line.
|
||||
- `do io/print_int(x)` could be `print(x)` for the IO/Int-print
|
||||
default.
|
||||
- Nested match arms could break across lines.
|
||||
- Arithmetic primitives are still prefix.
|
||||
- No precedence-aware paren elision yet.
|
||||
|
||||
All explicitly out of 20a's scope; pinned for 20b.
|
||||
|
||||
### Files
|
||||
|
||||
- New crate `crates/ailang-prose/` (`Cargo.toml`, `src/lib.rs`
|
||||
~924 lines, `tests/snapshot.rs` 64 lines).
|
||||
- `Cargo.toml` (root) + `crates/ail/Cargo.toml`: workspace + dep wiring.
|
||||
- `crates/ail/src/main.rs`: `Cmd::Prose { path }` variant + dispatch.
|
||||
- Three new `examples/*.prose.txt` snapshots.
|
||||
|
||||
### Build/test status
|
||||
|
||||
- `cargo build --workspace` — green, no warnings.
|
||||
- `cargo test --workspace` — 28 prose-unit + 3 prose-snapshot pass;
|
||||
existing 69 e2e + 53 ailang-check + others all unchanged-green.
|
||||
|
||||
### What stays queued
|
||||
|
||||
- **Iter 20b** — formatting polish: infix for arithmetic, paren
|
||||
elision by precedence, let-inlining, prettier `do`, line-wrap
|
||||
for long doc strings.
|
||||
- **Iter 20c** — the CLI subcommand was bundled into 20a (one
|
||||
natural unit). Originally pinned as a separate iter; collapsed.
|
||||
- **Iter 20d** — the round-trip mediator (prose-edit → updated
|
||||
AIL via LLM call). Specification + prompt template, not a
|
||||
compiler pass.
|
||||
|
||||
Reference in New Issue
Block a user