praxis_input_parser/lib.rs
1//! The unified input parser DSL (§7, §14.1).
2//!
3//! Responsibility (per the design): the parser-expression typed AST, the backtick
4//! template scanner, static validation, compile-time result-type synthesis, and
5//! parser-plan construction. The DSL has its own typed AST (§7.9) and is **not**
6//! lowered immediately into string-splitting calls.
7//!
8//! The crate covers the atomics ([`AtomicKind::ALL`] — `int`, `uint`, `float`,
9//! `byte`, `char`, `digit`, `word`, `identifier`, `text`, `rest`), the
10//! constructors ([`Constructor::ALL`] — `lines`, `sections`, `csv`, `ws`,
11//! `sep`, `grid`, `matrix`, `chars`, `one_of`, `block`, `choice`, `optional`,
12//! `scan`, `repeated`), backtick templates, type synthesis, and plan lowering.
13//! The runtime interpreter lives in `praxis-runtime::parser`.
14
15pub mod ast;
16pub mod body;
17pub mod call;
18pub mod plan;
19pub mod scan;
20pub mod synthesize;
21pub mod validate;
22
23pub use ast::{
24 ArgShape, AtomicKind, BlockItem, CaptureName, Constructor, EmptySeparator, InvalidCaptureName,
25 InvalidRepeatCount, ParserAst, RepeatCount, SectionItem, Separator, SkipPolicy, TemplatePart,
26 WsPolicy,
27};
28pub use call::{CallArg, build_call, build_repeated_tail};
29pub use plan::{
30 BlockItemNode, CompiledPlan, FieldOrder, MAX_PLANS, ParserPlan, PlanId, PlanNode,
31 SectionItemNode, SourceOrder, TemplatePartNode, TemplateShape, TooManyPlans, get_plan,
32 lower_to_plan, plan_count, register_plan, retire_all_plans,
33};
34pub use scan::{MAX_NESTING, ScanError, scan_template};
35pub use synthesize::{synthesize, synthesize_indexed};
36pub use validate::{ArgKind, ValidationError, check_call, validate};
37
38/// Marker documenting that this crate is filled at Milestone 6.
39pub const FILLED_AT_MILESTONE: u32 = 6;
40
41/// Every name a parser expression may begin with: §7.4's atomics and §7.5's
42/// constructors, in their own tables' order.
43///
44/// The two are one list because they are one thing to a *user* — the word after
45/// `read` or inside `{…}` — and the two diagnostics for getting it wrong
46/// (`I010`, `I013`) are the same mistake seen from two tables. A "did you mean"
47/// that only knew one of them would answer `int` for `intt` and nothing for
48/// `line`.
49pub fn parser_names() -> impl Iterator<Item = &'static str> {
50 AtomicKind::ALL
51 .iter()
52 .map(|a| a.keyword())
53 .chain(Constructor::ALL.iter().map(|c| c.keyword()))
54}
55
56/// The atomic or constructor `name` was probably meant to be (ADR-132).
57///
58/// §15.3's own example: `line` answers `lines`. The threshold is
59/// [`praxis_source::nearest`]'s, shared with every other did-you-mean in the
60/// compiler, so one place decides when a near miss is near enough.
61#[must_use]
62pub fn nearest_parser_name(name: &str) -> Option<&'static str> {
63 praxis_source::nearest(name, parser_names())
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69
70 #[test]
71 fn milestone_marker_is_six() {
72 assert_eq!(FILLED_AT_MILESTONE, 6);
73 }
74}