polydat_grammar/comprehension/spec/mod.rs
1// Copyright 2024-2026 Jonathan Shook
2// SPDX-License-Identifier: Apache-2.0
3
4//! Comprehension specification surface — author-friendly input
5//! form for YAML / JSON consumers.
6//!
7//! Polydat owns the conversion from a YAML/JSON-native
8//! structural form into the algebra-layer `Comprehension`
9//! AST. Consumers (nb-workload, REPL, tooling) deserialize
10//! into [`ComprehensionSpec`] via serde and call
11//! [`ComprehensionSpec::into_algebra`]; text-block consumers
12//! call [`parse_text`] which routes through serde for them.
13//!
14//! ## Single `for` verb
15//!
16//! Per spec §8.1, the surface has one keyword. The RHS shape
17//! disambiguates which constructor:
18//!
19//! ```yaml
20//! # Single clause
21//! for: "k in 1..10"
22//!
23//! # Multi-clause cartesian (one inline string)
24//! for: "k in 1..10, limit in [10, 100, 1000]"
25//!
26//! # Multi-clause cartesian (list of strings)
27//! for:
28//! - "k in 1..10"
29//! - "limit in [10, 100, 1000]"
30//!
31//! # Union of sub-spaces (list of clause lists)
32//! for:
33//! - ["k in 10", "limit in 1..50"]
34//! - ["k in 100", "limit in 1..500"]
35//! ```
36//!
37//! With optional modifiers:
38//!
39//! ```yaml
40//! for: "k in 1..10, limit in 1..100"
41//! where: "{k} * {limit} <= 1000"
42//! order: "halton/50"
43//! ```
44//!
45//! ## Architecture
46//!
47//! The friendly surface delegates **structural parsing** to
48//! the crate-internal text parser --
49//! `parse_clause_list`,
50//! `parse_comprehension_text`, `parse_order_spec`. Those
51//! parsers produce the flat form with raw
52//! string sources. The [`from_clauses`] module then walks
53//! that form and assembles the algebra-layer AST,
54//! using [`source_parser::parse_source`] for typed-source
55//! classification of each clause's RHS string.
56//!
57//! This is the **single bridge**: every conversion of a
58//! polydat-grammar input to the algebra layer funnels through
59//! [`from_clauses::clauses_to_algebra`].
60
61pub mod algebra_text;
62pub mod from_clauses;
63pub mod serde_form;
64pub mod source_parser;
65pub mod text;
66
67pub use algebra_text::parse_comprehension_algebra;
68pub use from_clauses::ConvertError;
69pub use serde_form::{ComprehensionSpec, ForSpec, SpecConvertError, parse_inline};
70pub use source_parser::{SourceParseError, parse_source};
71pub use text::{TextParseError, parse_text};
72
73/// An `order` specification on its own — `halton/5`, `lex`,
74/// `shuffle(seed=7)` — as the algebra's strategy, truncation, and
75/// seed.
76///
77/// A caller that has an order and nothing else used to reach this by
78/// writing `__o in 0..1 order <spec>` and running the whole
79/// comprehension parser over the result, then matching the algebra
80/// tree it got back for the one node it wanted. The parser has had an
81/// order-spec entry point all along; this exposes it.
82pub fn parse_order(
83 spec: &str,
84) -> Result<
85 (
86 crate::comprehension::strategy::StrategyName,
87 Option<u64>,
88 Option<u64>,
89 ),
90 String,
91> {
92 let form = crate::comprehension::parse::parse_order_spec(spec)?;
93 from_clauses::convert_order(&form).map_err(|e| e.to_string())
94}
95
96// The flat parse form (`crate::comprehension::clause_ast`) and the
97// text parser that produces it are crate-internal: they exist to be
98// lowered here (comprehension_forms.md §14.8). Text reaches the
99// canonical tree through [`parse_comprehension_algebra`], a spec
100// document through [`ComprehensionSpec::into_algebra`] or
101// [`parse_text`], and a source expression through [`parse_source`].
102
103// Leaf grammar utilities — re-exported here so consumers reach
104// them through one module. The implementations live in
105// `crate::comprehension::parse`, which is also public as the full
106// parser.
107//
108// What's re-exported (leaf utilities, no comprehension-build
109// pipeline):
110// - `parse_clause` — one `var in expr` clause text → `Clause`
111// - `parse_clause_list` — comma-separated clauses → `Vec<Clause>`
112// - `parse_order_spec` — order-spec text → `TraversalOrder`
113// - `parse_comprehension_text` — full `for ... where ... order`
114// text → legacy `Comprehension` (used for inline-text shapes
115// where the where/order are not separate keys).
116//
117// Not re-exported here (comprehension-build pipeline — callers
118// route through [`ComprehensionSpec`]; reach them at
119// `crate::comprehension::parse` if needed):
120// - `comprehension_from_subspaces` — used by
121// `ComprehensionSpec::into_legacy` / `into_algebra`.
122// - `split_at_order`, `split_at_where`, `split_respecting_parens` —
123// parser helpers.