09fb5bb113
Adds a per-fn linearity check that runs on every fn whose param_modes are all explicit (Borrow or Own, no Implicit), tracks each binder's consume/borrow state through the AST, and emits two diagnostics with form-A `suggested_rewrites`: - `use-after-consume` — a binder is referenced after it has already been consumed. - `consume-while-borrowed` — a binder is consumed while a borrow of it is still live (sibling arg slot, or an enclosing-fn Borrow param). Fns with any Implicit param skip the check entirely; that's the back-compat lane that keeps every existing fixture green. Pure diagnostic addition: no IR change, no codegen change, no runtime change. Each diagnostic carries a non-empty `suggested_rewrites` whose `replacement` is form-A AILang text (typically `(clone <name>)`). Adds two new public entrypoints to ailang-surface — `parse_term` and `term_to_form_a` — so the round-trip `parse_term(term_to_form_a(t)) ≡ t` is the contract every emitted replacement must satisfy. Tests assert the contract. Linearity pass runs only on modules that typechecked clean (any upstream typecheck error suppresses it for that module — running on partly-defined IR would produce noise). Tests: 3 unit tests in `linearity::tests`, 3 integration tests in `ailang-check/tests/workspace.rs`, 1 serialisation test in `diagnostic.rs`. `cargo test --workspace` green.
40 lines
1.6 KiB
Rust
40 lines
1.6 KiB
Rust
//! AILang authoring surface — form (A) S-expression projection.
|
|
//!
|
|
//! This crate is **one of potentially many** producers / consumers of
|
|
//! [`ailang_core::ast::Module`] values. The JSON-AST in `ailang-core`
|
|
//! remains the canonical, hashable, content-addressed source of truth;
|
|
//! form (A) is the AI-authoring projection optimised for token-efficient
|
|
//! production by LLMs (see `docs/DESIGN.md` Decision 6).
|
|
//!
|
|
//! ## Modules
|
|
//!
|
|
//! - [`mod@lex`] — hand-written lexer for the 3-rule lexical core
|
|
//! (whitespace / parens / comments delimit tokens; first character
|
|
//! classifies into integer / string / ident).
|
|
//! - [`mod@parse`] — hand-written recursive-descent parser. One Rust
|
|
//! function per EBNF production, line-by-line auditable. No
|
|
//! parser-combinator dependency.
|
|
//! - [`mod@print`] — deterministic pretty-printer that emits form (A).
|
|
//! Round-trip with the parser is the contract that gates the
|
|
//! surface (see the integration test `tests/round_trip.rs`).
|
|
//!
|
|
//! ## Round-trip contract
|
|
//!
|
|
//! For every well-formed [`ailang_core::ast::Module`] value `m`,
|
|
//! [`parse()`] applied to [`print()`]`(m)` produces a module whose
|
|
//! canonical JSON bytes equal those of `m`. This is enforced by the
|
|
//! integration test against every `examples/*.ail.json` fixture.
|
|
//!
|
|
//! ## Architectural pin
|
|
//!
|
|
//! No new AST nodes, no schema changes, no new hashable form.
|
|
//! `ailang-check` and `ailang-codegen` are projection-agnostic — they
|
|
//! consume `Module` values regardless of which front-end produced them.
|
|
|
|
pub mod lex;
|
|
pub mod parse;
|
|
pub mod print;
|
|
|
|
pub use parse::{parse, parse_term, ParseError};
|
|
pub use print::{print, term_to_form_a};
|