polydat-grammar 0.5.0

The Polydat language: lexer, parser, AST, and pretty-printer, without the runtime
Documentation
// Copyright 2024-2026 Jonathan Shook
// SPDX-License-Identifier: Apache-2.0

//! Comprehension specification surface — author-friendly input
//! form for YAML / JSON consumers.
//!
//! Polydat owns the conversion from a YAML/JSON-native
//! structural form into the algebra-layer `Comprehension`
//! AST. Consumers (nb-workload, REPL, tooling) deserialize
//! into [`ComprehensionSpec`] via serde and call
//! [`ComprehensionSpec::into_algebra`]; text-block consumers
//! call [`parse_text`] which routes through serde for them.
//!
//! ## Single `for` verb
//!
//! Per spec §8.1, the surface has one keyword. The RHS shape
//! disambiguates which constructor:
//!
//! ```yaml
//! # Single clause
//! for: "k in 1..10"
//!
//! # Multi-clause cartesian (one inline string)
//! for: "k in 1..10, limit in [10, 100, 1000]"
//!
//! # Multi-clause cartesian (list of strings)
//! for:
//!   - "k in 1..10"
//!   - "limit in [10, 100, 1000]"
//!
//! # Union of sub-spaces (list of clause lists)
//! for:
//!   - ["k in 10",  "limit in 1..50"]
//!   - ["k in 100", "limit in 1..500"]
//! ```
//!
//! With optional modifiers:
//!
//! ```yaml
//! for: "k in 1..10, limit in 1..100"
//! where: "{k} * {limit} <= 1000"
//! order: "halton/50"
//! ```
//!
//! ## Architecture
//!
//! The friendly surface delegates **structural parsing** to
//! the crate-internal text parser --
//! `parse_clause_list`,
//! `parse_comprehension_text`, `parse_order_spec`. Those
//! parsers produce the flat form with raw
//! string sources. The [`from_clauses`] module then walks
//! that form and assembles the algebra-layer AST,
//! using [`source_parser::parse_source`] for typed-source
//! classification of each clause's RHS string.
//!
//! This is the **single bridge**: every conversion of a
//! polydat-grammar input to the algebra layer funnels through
//! [`from_clauses::clauses_to_algebra`].

pub mod algebra_text;
pub mod from_clauses;
pub mod serde_form;
pub mod source_parser;
pub mod text;

pub use algebra_text::parse_comprehension_algebra;
pub use from_clauses::ConvertError;
pub use serde_form::{ComprehensionSpec, ForSpec, SpecConvertError, parse_inline};
pub use source_parser::{SourceParseError, parse_source};
pub use text::{TextParseError, parse_text};

/// An `order` specification on its own — `halton/5`, `lex`,
/// `shuffle(seed=7)` — as the algebra's strategy, truncation, and
/// seed.
///
/// A caller that has an order and nothing else used to reach this by
/// writing `__o in 0..1 order <spec>` and running the whole
/// comprehension parser over the result, then matching the algebra
/// tree it got back for the one node it wanted. The parser has had an
/// order-spec entry point all along; this exposes it.
pub fn parse_order(
    spec: &str,
) -> Result<
    (
        crate::comprehension::strategy::StrategyName,
        Option<u64>,
        Option<u64>,
    ),
    String,
> {
    let form = crate::comprehension::parse::parse_order_spec(spec)?;
    from_clauses::convert_order(&form).map_err(|e| e.to_string())
}

// The flat parse form (`crate::comprehension::clause_ast`) and the
// text parser that produces it are crate-internal: they exist to be
// lowered here (comprehension_forms.md §14.8). Text reaches the
// canonical tree through [`parse_comprehension_algebra`], a spec
// document through [`ComprehensionSpec::into_algebra`] or
// [`parse_text`], and a source expression through [`parse_source`].

// Leaf grammar utilities — re-exported here so consumers reach
// them through one module. The implementations live in
// `crate::comprehension::parse`, which is also public as the full
// parser.
//
// What's re-exported (leaf utilities, no comprehension-build
// pipeline):
// - `parse_clause` — one `var in expr` clause text → `Clause`
// - `parse_clause_list` — comma-separated clauses → `Vec<Clause>`
// - `parse_order_spec` — order-spec text → `TraversalOrder`
// - `parse_comprehension_text` — full `for ... where ... order`
//   text → legacy `Comprehension` (used for inline-text shapes
//   where the where/order are not separate keys).
//
// Not re-exported here (comprehension-build pipeline — callers
// route through [`ComprehensionSpec`]; reach them at
// `crate::comprehension::parse` if needed):
// - `comprehension_from_subspaces` — used by
//   `ComprehensionSpec::into_legacy` / `into_algebra`.
// - `split_at_order`, `split_at_where`, `split_respecting_parens` —
//   parser helpers.