lambda-throw-cat 0.1.0

Lambda calculus with records, prototype chains, ref cells, GC, and non-local control flow via throw/try/catch. Outcome::Normal/Thrown is threaded purely-functionally through every reduction. Spike 4 of a web-engine reformulation targeting Tauri.
Documentation
# lambda-throw-cat

Lambda calculus with records, prototype chains, ref cells, a tracing garbage collector, and non-local control flow via `throw` and `try`/`catch`.  Built on [comp-cat-rs](https://crates.io/crates/comp-cat-rs).

This is **spike 4** of a comp-cat-rs reformulation of a web engine targeting Tauri.  Spike 1 ([lambda-cat](https://crates.io/crates/lambda-cat)) was the pure interpreter, spike 2 ([lambda-ref-cat](https://crates.io/crates/lambda-ref-cat)) added a persistent heap and GC, spike 3 ([lambda-obj-cat](https://crates.io/crates/lambda-obj-cat)) added the object model with prototype chains.  Spike 4 adds the last runtime feature distinctive to JavaScript-shaped languages before the spec-scale work of `boa-cat`: throwable exceptions that unwind through every existing reduction form without using mutation, panics, or interior mutability.

## Grammar

```text
expr        ::= seq_expr
seq_expr    ::= right_expr (";" right_expr)*
right_expr  ::= lambda | let | fix | extend | throw_expr | try_expr | assign_expr
lambda      ::= "\" ident "." expr
let         ::= "let" ident "=" expr "in" expr
fix         ::= "fix" ident "." expr
extend      ::= "extend" atom object_lit
throw_expr  ::= "throw" right_expr
try_expr    ::= "try" right_expr "catch" ident "." right_expr
assign_expr ::= app_expr (":=" right_expr)?
app_expr    ::= atom atom*
atom        ::= base_atom ("." ident)*
base_atom   ::= ident | "(" expr ")" | "ref" atom | "!" atom | object_lit
object_lit  ::= "{" props? "}"
props       ::= property ("," property)*
property    ::= ident "=" right_expr

ident       ::= [a-zA-Z_][a-zA-Z0-9_]*
```

- `throw e` evaluates `e` and unwinds with the resulting value.
- `try body catch x. handler` evaluates `body`; if it unwinds, binds `x` to the thrown value and evaluates `handler`.
- An uncaught `throw` at the top surfaces as `Error::UncaughtException`.
- Heap mutations made before a `throw` are visible to the catch handler.

## Usage

```rust
# fn main() -> Result<(), lambda_throw_cat::error::Error> {
use lambda_throw_cat::run;

let source = r"
    try
        throw (\msg. msg)
    catch e. e
";
let value = run(source).run()?;
println!("{value}");
# Ok(())
# }
```

## How exception flow stays pure

Every reduction step returns `Result<Outcome, Error>`, where `Outcome` is `Normal { value, heap, fuel }` or `Thrown { value, heap, fuel }`.  The threading helper `Outcome::and_then` propagates a `Thrown` through every combinator chain without revisiting any state; `eval_try_catch` is the only site that switches a `Thrown` back to `Normal`, by extending the environment with the bound catch parameter and evaluating the handler with the heap and fuel as they were at the moment of the throw.

## Building

```sh
cargo build
cargo test
RUSTFLAGS="-D warnings" cargo clippy --all-targets
```

## License

Licensed under either of [Apache License, Version 2.0](LICENSE-APACHE) or [MIT license](LICENSE-MIT) at your option.