engawa_lisp/lib.rs
1//! Tatara-lisp authoring layer for engawa render graphs.
2//!
3//! Operators write effect declarations in a `.tlisp` file:
4//!
5//! ```text
6//! (defmaterial scanlines
7//! (shader (inline "..."))
8//! (bindings
9//! (binding 0 uniform "frame")
10//! (binding 1 texture "scene")
11//! (binding 2 sampler "scene-sampler")))
12//!
13//! (defresource swap external)
14//! (defresource scene (texture 800 600))
15//! (defresource post (texture 800 600))
16//!
17//! (defgraph mado-pipeline
18//! (input swap)
19//! (output post)
20//! (node clear-scene (kind clear) (output scene))
21//! (node scanlines-pass (kind fullscreen-effect)
22//! (material scanlines)
23//! (input scene)
24//! (output post)))
25//! ```
26//!
27//! …and this crate parses + lowers to `engawa::RenderGraph`.
28//! Pairs with shikumi's notify watcher for live hot-reload —
29//! save the `.tlisp`, the graph rebuilds, the dispatcher picks
30//! up the new topology without a restart.
31//!
32//! ## Why a custom lisp parser
33//!
34//! The full `tatara-lisp` macro engine is heavy. For engawa's
35//! IR — which is intentionally small (8 types) — a minimal
36//! sexpr parser + typed lowering does the job in ~600 LOC.
37//! When the tatara-lisp ecosystem stabilises (caixa-author
38//! lands, the macro engine becomes reusable across consumers),
39//! engawa-lisp migrates to lean on it.
40//!
41//! Today: round-trip-tested sexpr parser, every parse error
42//! carries operator-friendly context (line:col + the form being
43//! parsed), 30+ unit tests.
44
45#![forbid(unsafe_code)]
46#![doc(html_root_url = "https://docs.rs/engawa-lisp/0.1.0")]
47
48pub mod lower;
49pub mod parse;
50pub mod sexpr;
51
52pub use lower::{lower_to_graph, LowerError};
53pub use parse::{ParseError, Span};
54pub use sexpr::{Sexpr, SexprKind};
55
56/// One-call helper: parse a `.tlisp` source string, lower to an
57/// `engawa::RenderGraph`. Returns the graph ready for compile;
58/// the caller chains `.compile()` to get a `CompiledGraph` for
59/// dispatch.
60pub fn parse_and_lower(
61 source: &str,
62) -> Result<engawa::RenderGraph, EngawaLispError> {
63 let forms = parse::parse(source)?;
64 let graph = lower::lower_to_graph(&forms)?;
65 Ok(graph)
66}
67
68#[derive(Debug, thiserror::Error)]
69pub enum EngawaLispError {
70 #[error("parse error: {0}")]
71 Parse(#[from] ParseError),
72 #[error("lower error: {0}")]
73 Lower(#[from] LowerError),
74}