Skip to main content

meta_language/grammar/
mod.rs

1//! Grammar intermediate representation and links encoding.
2//!
3//! The grammar IR is a small expression algebra that can hold PEG, BNF, EBNF,
4//! ABNF, and inferred grammars without committing to one textual surface
5//! syntax. Values can be encoded as first-class grammar links in a
6//! [`LinkNetwork`](crate::LinkNetwork).
7//!
8//! # Example
9//!
10//! ```
11//! use meta_language::{
12//!     FromLinks, Grammar, LinkType, LinksDecoder, LinksEncoder, ToLinks,
13//! };
14//!
15//! let expr = Grammar::expr();
16//! let grammar = Grammar::builder().start("word").rule("word", expr.rep1(expr.char_range('a', 'z'))).build();
17//!
18//! let mut encoder = LinksEncoder::new();
19//! let root = grammar.to_links(&mut encoder);
20//! let network = encoder.into_network();
21//! assert!(network.links().any(|link| link.metadata().link_type() == Some(LinkType::Grammar)));
22//! let mut decoder = LinksDecoder::new(&network);
23//! assert_eq!(Grammar::from_links(&mut decoder, root).expect("grammar decodes"), grammar);
24//! ```
25
26pub mod concepts;
27pub mod emit;
28pub mod fidelity;
29pub mod import;
30pub mod inference;
31mod links;
32pub mod runtime;
33pub mod surface;
34pub mod translate;
35pub mod validate;
36
37pub use concepts::{
38    annotate_grammar_concepts, grammar_expr_concept_id, rule_concept_id, GrammarConcept,
39    GRAMMAR_CONCEPTS,
40};
41pub use emit::{
42    emit_abnf, emit_bnf, emit_ebnf, emit_gbnf, emit_javascript_parser, emit_peggy, emit_pest,
43    emit_rust_parser, emit_tree_sitter_grammar_js, emit_tree_sitter_grammar_js_with_report,
44    render_rust_type, EmitReport, GrammarEmitError, JsParserArtifacts, RustParserArtifacts,
45};
46pub use fidelity::{
47    canonical_grammar_format, grammar_format_profile, GrammarFidelityLevel, GrammarFormatProfile,
48    GRAMMAR_CONSTRUCTS, GRAMMAR_FORMATS,
49};
50pub use import::{
51    import_abnf, import_antlr, import_bnf, import_ebnf, import_gbnf, import_lark, import_pest,
52    import_tree_sitter_json, GrammarImportError,
53};
54pub use inference::active::{
55    clean_structural_acceptance, learn_dfa, learn_grammar, ActiveLearningConfig,
56    ActiveLearningError, Dfa, GrammarAcceptorOracle, Oracle as ActiveLearningOracle,
57    ParserAcceptancePredicate, ParserMembershipOracle, SamplingEquivalenceOracle,
58    Symbol as ActiveSymbol,
59};
60pub use inference::advisor::{
61    AdviceDecision, AdviceDecisionKind, AdviceSource, ConceptNamingAdvisor, FallbackAdvisor,
62    MdlMergeAdvisor, MergeAdvisor, MergeCandidate, MergeRequest, MergeScore, NameCandidate,
63    NamingAdvisor, NamingRequest,
64};
65#[cfg(feature = "llm-assist")]
66pub use inference::advisor::{LlmClient, LlmError, LlmMergeAdvisor, LlmNamingAdvisor};
67pub use inference::cfg::{
68    infer_cfg, infer_cfg_with_advisors, InferenceOptions, InferenceReport, InferenceResult, Oracle,
69    PositiveOnlyOracle,
70};
71pub use inference::eval::{
72    evaluate, mdl, run_corpus, run_named_corpus, sample, size_symbols, BenchmarkReport, EvalError,
73    GoldenCorpus, GrammarOracle, MembershipOracle, MetricScores, SampleConfig, ScoringMode,
74    GOLDEN_CORPORA,
75};
76pub use inference::lexical::{
77    categorise, infer_lexical_classes, CharCategory, LexicalConfig, LexicalModel, Token,
78};
79pub use inference::minimize::{
80    mdl_cost, minimize, Mdl, MinimizeOptions, MinimizeReport, MinimizeResult,
81};
82pub use inference::prior::{
83    build_structural_prior, ByteSpan, Delimiter, LeafKind, PriorOptions, SeedNode, SeedTree,
84    StructuralPrior, WhitespacePolicy,
85};
86pub use inference::semantic::{
87    default_pattern_catalog, evaluate_atom, evaluate_clause, evaluate_constraint,
88    evaluate_probabilistic, mine_semantic_constraints, ConstraintAtom, ConstraintClause,
89    ConstraintPattern, LengthUnit, NonTerminalRef, SemanticConstraint, SemanticInferenceConfig,
90};
91pub use inference::sequitur::{run_sequitur, Symbol};
92pub use inference::state_merging::{infer_dfa, InferredAutomaton, MergeStrategy, Sample};
93pub use runtime::{register_grammar, with_grammar, GrammarParser};
94pub use surface::{
95    grammar_from_lino, grammar_to_lino, parse_grammar_surface, write_grammar_surface,
96    GrammarSurfaceError,
97};
98pub use translate::{
99    grammar_concept_translation_rules, translate_grammar_surface, GrammarTranslateError,
100};
101pub use validate::{validate, DiagnosticKind, GrammarDiagnostic, RuleSpan, Severity};
102
103use std::collections::BTreeSet;
104use std::fmt;
105
106/// One node of the grammar expression algebra.
107#[derive(Clone, Debug, PartialEq, Eq)]
108pub enum GrammarExpr {
109    /// Matches the empty string.
110    Empty,
111    /// Literal string terminal, for example `"fn"`.
112    Terminal(String),
113    /// Case-insensitive literal string terminal.
114    TerminalInsensitive(String),
115    /// Inclusive character range, for example `'a'..='z'`.
116    CharRange(char, char),
117    /// Explicit set of characters or ranges.
118    CharClass {
119        /// Whether the class is negated.
120        negated: bool,
121        /// Characters and ranges accepted by the class.
122        items: Vec<CharClassItem>,
123    },
124    /// The any-character wildcard.
125    AnyChar,
126    /// Reference to another grammar rule by name.
127    NonTerminal(String),
128    /// Alternation between expressions.
129    Choice {
130        /// Whether alternatives are ordered, as in PEG choice.
131        ordered: bool,
132        /// Alternative expressions.
133        alternatives: Vec<Self>,
134    },
135    /// Concatenation of expressions.
136    Sequence(Vec<Self>),
137    /// Optional expression.
138    Optional(Box<Self>),
139    /// Zero-or-more repetition.
140    ZeroOrMore(Box<Self>),
141    /// One-or-more repetition.
142    OneOrMore(Box<Self>),
143    /// Counted repetition.
144    Repeat {
145        /// Repeated expression.
146        expr: Box<Self>,
147        /// Minimum number of repetitions.
148        min: usize,
149        /// Maximum number of repetitions, or `None` for unbounded.
150        max: Option<usize>,
151    },
152    /// Positive lookahead predicate.
153    And(Box<Self>),
154    /// Negative lookahead predicate.
155    Not(Box<Self>),
156    /// Labelled or anonymous capture.
157    Capture {
158        /// Optional capture label.
159        label: Option<String>,
160        /// Captured expression.
161        expr: Box<Self>,
162    },
163}
164
165impl GrammarExpr {
166    /// Builds an empty-string expression.
167    #[must_use]
168    pub const fn empty() -> Self {
169        Self::Empty
170    }
171
172    /// Builds a literal terminal expression.
173    #[must_use]
174    pub fn terminal(value: impl Into<String>) -> Self {
175        Self::Terminal(value.into())
176    }
177
178    /// Builds a case-insensitive literal terminal expression.
179    #[must_use]
180    pub fn terminal_insensitive(value: impl Into<String>) -> Self {
181        Self::TerminalInsensitive(value.into())
182    }
183
184    /// Builds an inclusive character range expression.
185    #[must_use]
186    pub const fn char_range(start: char, end: char) -> Self {
187        Self::CharRange(start, end)
188    }
189
190    /// Builds a character class expression.
191    #[must_use]
192    pub fn char_class<I>(negated: bool, items: I) -> Self
193    where
194        I: IntoIterator<Item = CharClassItem>,
195    {
196        Self::CharClass {
197            negated,
198            items: items.into_iter().collect(),
199        }
200    }
201
202    /// Builds an any-character wildcard expression.
203    #[must_use]
204    pub const fn any_char() -> Self {
205        Self::AnyChar
206    }
207
208    /// Builds a non-terminal reference expression.
209    #[must_use]
210    pub fn non_terminal(value: impl Into<String>) -> Self {
211        Self::NonTerminal(value.into())
212    }
213
214    /// Builds a choice expression.
215    #[must_use]
216    pub fn choice<I>(ordered: bool, alternatives: I) -> Self
217    where
218        I: IntoIterator<Item = Self>,
219    {
220        Self::Choice {
221            ordered,
222            alternatives: alternatives.into_iter().collect(),
223        }
224    }
225
226    /// Builds a sequence expression.
227    #[must_use]
228    pub fn sequence<I>(items: I) -> Self
229    where
230        I: IntoIterator<Item = Self>,
231    {
232        Self::Sequence(items.into_iter().collect())
233    }
234
235    /// Builds an optional expression.
236    #[must_use]
237    pub fn optional(expr: Self) -> Self {
238        Self::Optional(Box::new(expr))
239    }
240
241    /// Builds a zero-or-more repetition expression.
242    #[must_use]
243    pub fn zero_or_more(expr: Self) -> Self {
244        Self::ZeroOrMore(Box::new(expr))
245    }
246
247    /// Builds a one-or-more repetition expression.
248    #[must_use]
249    pub fn one_or_more(expr: Self) -> Self {
250        Self::OneOrMore(Box::new(expr))
251    }
252
253    /// Builds a counted repetition expression.
254    #[must_use]
255    pub fn repeat(expr: Self, min: usize, max: Option<usize>) -> Self {
256        Self::Repeat {
257            expr: Box::new(expr),
258            min,
259            max,
260        }
261    }
262
263    /// Builds a positive lookahead expression.
264    #[must_use]
265    pub fn and(expr: Self) -> Self {
266        Self::And(Box::new(expr))
267    }
268
269    /// Builds a negative lookahead expression.
270    #[must_use]
271    #[allow(clippy::should_implement_trait)]
272    pub fn not(expr: Self) -> Self {
273        Self::Not(Box::new(expr))
274    }
275
276    /// Builds a labelled capture expression.
277    #[must_use]
278    pub fn capture(label: impl Into<String>, expr: Self) -> Self {
279        Self::Capture {
280            label: Some(label.into()),
281            expr: Box::new(expr),
282        }
283    }
284
285    /// Builds an anonymous capture expression.
286    #[must_use]
287    pub fn capture_unlabeled(expr: Self) -> Self {
288        Self::Capture {
289            label: None,
290            expr: Box::new(expr),
291        }
292    }
293
294    fn collect_nonterminals(&self, names: &mut BTreeSet<String>) {
295        match self {
296            Self::NonTerminal(name) => {
297                names.insert(name.clone());
298            }
299            Self::Choice { alternatives, .. } => {
300                for alternative in alternatives {
301                    alternative.collect_nonterminals(names);
302                }
303            }
304            Self::Sequence(items) => {
305                for item in items {
306                    item.collect_nonterminals(names);
307                }
308            }
309            Self::Optional(expr)
310            | Self::ZeroOrMore(expr)
311            | Self::OneOrMore(expr)
312            | Self::And(expr)
313            | Self::Not(expr)
314            | Self::Capture { expr, .. }
315            | Self::Repeat { expr, .. } => expr.collect_nonterminals(names),
316            Self::Empty
317            | Self::Terminal(_)
318            | Self::TerminalInsensitive(_)
319            | Self::CharRange(_, _)
320            | Self::CharClass { .. }
321            | Self::AnyChar => {}
322        }
323    }
324}
325
326impl fmt::Display for GrammarExpr {
327    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
328        match self {
329            Self::Empty => formatter.write_str("empty"),
330            Self::Terminal(value) => write!(formatter, "{value:?}"),
331            Self::TerminalInsensitive(value) => write!(formatter, "i{value:?}"),
332            Self::CharRange(start, end) => write!(formatter, "{start:?}..={end:?}"),
333            Self::CharClass { negated, items } => {
334                let marker = if *negated { "^" } else { "" };
335                write!(formatter, "[{marker}")?;
336                for item in items {
337                    write!(formatter, "{item}")?;
338                }
339                formatter.write_str("]")
340            }
341            Self::AnyChar => formatter.write_str("."),
342            Self::NonTerminal(name) => formatter.write_str(name),
343            Self::Choice {
344                ordered,
345                alternatives,
346            } => {
347                let separator = if *ordered { " / " } else { " | " };
348                write_joined(formatter, alternatives, separator)
349            }
350            Self::Sequence(items) => write_joined(formatter, items, " "),
351            Self::Optional(expr) => write!(formatter, "({expr})?"),
352            Self::ZeroOrMore(expr) => write!(formatter, "({expr})*"),
353            Self::OneOrMore(expr) => write!(formatter, "({expr})+"),
354            Self::Repeat { expr, min, max } => match max {
355                Some(max) => write!(formatter, "({expr}){{{min},{max}}}"),
356                None => write!(formatter, "({expr}){{{min},}}"),
357            },
358            Self::And(expr) => write!(formatter, "&({expr})"),
359            Self::Not(expr) => write!(formatter, "!({expr})"),
360            Self::Capture { label, expr } => match label {
361                Some(label) => write!(formatter, "{label}:({expr})"),
362                None => write!(formatter, "capture({expr})"),
363            },
364        }
365    }
366}
367
368/// One item inside a character class.
369#[derive(Clone, Debug, PartialEq, Eq)]
370pub enum CharClassItem {
371    /// A single character.
372    Char(char),
373    /// An inclusive character range.
374    Range(char, char),
375}
376
377impl CharClassItem {
378    /// Builds a single-character class item.
379    #[must_use]
380    pub const fn char(value: char) -> Self {
381        Self::Char(value)
382    }
383
384    /// Builds an inclusive character range class item.
385    #[must_use]
386    pub const fn range(start: char, end: char) -> Self {
387        Self::Range(start, end)
388    }
389}
390
391impl fmt::Display for CharClassItem {
392    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
393        match self {
394            Self::Char(value) => write!(formatter, "{}", value.escape_default()),
395            Self::Range(start, end) => {
396                write!(
397                    formatter,
398                    "{}-{}",
399                    start.escape_default(),
400                    end.escape_default()
401                )
402            }
403        }
404    }
405}
406
407/// How a rule participates in parsing.
408#[derive(Clone, Copy, Debug, PartialEq, Eq)]
409pub enum RuleKind {
410    /// A normal rule that participates in the parse tree.
411    Normal,
412    /// An atomic rule whose inner expression is treated as an indivisible token.
413    Atomic,
414    /// A silent rule that can be omitted from visible parse output.
415    Silent,
416    /// A token-level rule.
417    Token,
418}
419
420impl RuleKind {
421    /// Stable tag used in links encoding and display output.
422    #[must_use]
423    pub const fn as_str(self) -> &'static str {
424        match self {
425            Self::Normal => "normal",
426            Self::Atomic => "atomic",
427            Self::Silent => "silent",
428            Self::Token => "token",
429        }
430    }
431
432    pub(crate) fn from_tag(value: &str) -> Option<Self> {
433        match value {
434            "normal" => Some(Self::Normal),
435            "atomic" => Some(Self::Atomic),
436            "silent" => Some(Self::Silent),
437            "token" => Some(Self::Token),
438            _ => None,
439        }
440    }
441}
442
443impl fmt::Display for RuleKind {
444    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
445        formatter.write_str(self.as_str())
446    }
447}
448
449/// A named grammar rule.
450#[derive(Clone, Debug, PartialEq, Eq)]
451pub struct GrammarRule {
452    /// Rule name.
453    pub name: String,
454    /// Rule expression.
455    pub expr: GrammarExpr,
456    /// Rule participation kind.
457    pub kind: RuleKind,
458    /// Optional concept-ontology alignment.
459    pub concept: Option<String>,
460    /// Optional free-text documentation or comment.
461    pub doc: Option<String>,
462}
463
464impl GrammarRule {
465    /// Builds a normal grammar rule.
466    #[must_use]
467    pub fn new(name: impl Into<String>, expr: GrammarExpr) -> Self {
468        Self {
469            name: name.into(),
470            expr,
471            kind: RuleKind::Normal,
472            concept: None,
473            doc: None,
474        }
475    }
476
477    /// Returns this rule with a different rule kind.
478    #[must_use]
479    pub const fn with_kind(mut self, kind: RuleKind) -> Self {
480        self.kind = kind;
481        self
482    }
483
484    /// Returns this rule with concept-ontology alignment.
485    #[must_use]
486    pub fn with_concept(mut self, concept: impl Into<String>) -> Self {
487        self.concept = Some(concept.into());
488        self
489    }
490
491    /// Returns this rule with documentation text.
492    #[must_use]
493    pub fn with_doc(mut self, doc: impl Into<String>) -> Self {
494        self.doc = Some(doc.into());
495        self
496    }
497
498    /// Rule name.
499    #[must_use]
500    pub fn name(&self) -> &str {
501        &self.name
502    }
503
504    /// Rule expression.
505    #[must_use]
506    pub const fn expr(&self) -> &GrammarExpr {
507        &self.expr
508    }
509
510    /// Rule participation kind.
511    #[must_use]
512    pub const fn kind(&self) -> RuleKind {
513        self.kind
514    }
515
516    /// Concept-ontology alignment, when present.
517    #[must_use]
518    pub fn concept(&self) -> Option<&str> {
519        self.concept.as_deref()
520    }
521
522    /// Rule documentation, when present.
523    #[must_use]
524    pub fn doc(&self) -> Option<&str> {
525        self.doc.as_deref()
526    }
527}
528
529/// Origin grammar format.
530#[derive(Clone, Copy, Debug, PartialEq, Eq)]
531pub enum GrammarFormat {
532    /// The meta-language's own grammar notation.
533    MetaLanguage,
534    /// Backus-Naur Form.
535    Bnf,
536    /// Extended Backus-Naur Form.
537    Ebnf,
538    /// Augmented Backus-Naur Form.
539    Abnf,
540    /// Parsing Expression Grammar.
541    Peg,
542    /// ANTLR grammar.
543    Antlr,
544    /// Lark grammar.
545    Lark,
546    /// GBNF grammar.
547    Gbnf,
548    /// Tree-sitter grammar.
549    TreeSitter,
550    /// Grammar inferred from examples or observations.
551    Inferred,
552}
553
554impl GrammarFormat {
555    /// Stable tag used in links encoding and display output.
556    #[must_use]
557    pub const fn as_str(self) -> &'static str {
558        match self {
559            Self::MetaLanguage => "meta-language",
560            Self::Bnf => "bnf",
561            Self::Ebnf => "ebnf",
562            Self::Abnf => "abnf",
563            Self::Peg => "peg",
564            Self::Antlr => "antlr",
565            Self::Lark => "lark",
566            Self::Gbnf => "gbnf",
567            Self::TreeSitter => "tree-sitter",
568            Self::Inferred => "inferred",
569        }
570    }
571
572    pub(crate) fn from_tag(value: &str) -> Option<Self> {
573        match value {
574            "meta-language" => Some(Self::MetaLanguage),
575            "bnf" => Some(Self::Bnf),
576            "ebnf" => Some(Self::Ebnf),
577            "abnf" => Some(Self::Abnf),
578            "peg" => Some(Self::Peg),
579            "antlr" => Some(Self::Antlr),
580            "lark" => Some(Self::Lark),
581            "gbnf" => Some(Self::Gbnf),
582            "tree-sitter" => Some(Self::TreeSitter),
583            "inferred" => Some(Self::Inferred),
584            _ => None,
585        }
586    }
587}
588
589impl fmt::Display for GrammarFormat {
590    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
591        formatter.write_str(self.as_str())
592    }
593}
594
595/// Order-preserving grammar.
596#[derive(Clone, Debug, Default, PartialEq, Eq)]
597pub struct Grammar {
598    rules: Vec<GrammarRule>,
599    start: Option<String>,
600    source_format: Option<GrammarFormat>,
601}
602
603impl Grammar {
604    /// Builds an empty grammar.
605    #[must_use]
606    pub const fn new() -> Self {
607        Self {
608            rules: Vec::new(),
609            start: None,
610            source_format: None,
611        }
612    }
613
614    /// Builds a fluent grammar builder.
615    #[must_use]
616    pub const fn builder() -> GrammarBuilder {
617        GrammarBuilder::new()
618    }
619
620    /// Builds an expression builder.
621    #[must_use]
622    pub const fn expr() -> ExprBuilder {
623        ExprBuilder
624    }
625
626    /// Returns this grammar with an additional rule.
627    #[must_use]
628    pub fn with_rule(mut self, rule: GrammarRule) -> Self {
629        self.rules.push(rule);
630        self
631    }
632
633    /// Returns this grammar with a start rule name.
634    #[must_use]
635    pub fn with_start(mut self, start: impl Into<String>) -> Self {
636        self.start = Some(start.into());
637        self
638    }
639
640    /// Returns this grammar with a source format.
641    #[must_use]
642    pub const fn with_source_format(mut self, source_format: GrammarFormat) -> Self {
643        self.source_format = Some(source_format);
644        self
645    }
646
647    /// Adds a rule to the grammar.
648    pub fn add_rule(&mut self, rule: GrammarRule) {
649        self.rules.push(rule);
650    }
651
652    /// Sets the grammar start rule name.
653    pub fn set_start(&mut self, start: impl Into<String>) {
654        self.start = Some(start.into());
655    }
656
657    /// Clears the explicit grammar start rule.
658    pub fn clear_start(&mut self) {
659        self.start = None;
660    }
661
662    /// Sets the grammar source format.
663    pub const fn set_source_format(&mut self, source_format: GrammarFormat) {
664        self.source_format = Some(source_format);
665    }
666
667    /// Returns all rules in source order.
668    #[must_use]
669    pub fn rules(&self) -> &[GrammarRule] {
670        &self.rules
671    }
672
673    /// Returns the rule with `name`, when present.
674    #[must_use]
675    pub fn rule(&self, name: &str) -> Option<&GrammarRule> {
676        self.rules.iter().find(|rule| rule.name == name)
677    }
678
679    /// Returns the explicitly configured start symbol, if present.
680    #[must_use]
681    pub fn start(&self) -> Option<&str> {
682        self.start.as_deref()
683    }
684
685    /// Returns the start rule, defaulting to the first rule when unset.
686    #[must_use]
687    pub fn start_rule(&self) -> Option<&GrammarRule> {
688        self.start
689            .as_deref()
690            .map_or_else(|| self.rules.first(), |start| self.rule(start))
691    }
692
693    /// Returns the source format, if known.
694    #[must_use]
695    pub const fn source_format(&self) -> Option<GrammarFormat> {
696        self.source_format
697    }
698
699    /// Returns rule names in source order.
700    #[must_use]
701    pub fn rule_names(&self) -> Vec<&str> {
702        self.rules.iter().map(GrammarRule::name).collect()
703    }
704
705    /// Returns non-terminal names referenced from every rule expression.
706    #[must_use]
707    pub fn referenced_nonterminals(&self) -> BTreeSet<String> {
708        let mut names = BTreeSet::new();
709        for rule in &self.rules {
710            rule.expr.collect_nonterminals(&mut names);
711        }
712        names
713    }
714
715    /// Returns referenced non-terminals that do not have a local rule.
716    #[must_use]
717    pub fn undefined_nonterminals(&self) -> BTreeSet<String> {
718        let defined = self
719            .rules
720            .iter()
721            .map(|rule| rule.name.clone())
722            .collect::<BTreeSet<_>>();
723        self.referenced_nonterminals()
724            .difference(&defined)
725            .cloned()
726            .collect()
727    }
728}
729
730/// Fluent builder for order-preserving grammars.
731#[derive(Clone, Debug, Default, PartialEq, Eq)]
732pub struct GrammarBuilder {
733    grammar: Grammar,
734}
735
736impl GrammarBuilder {
737    /// Builds an empty grammar builder.
738    #[must_use]
739    pub const fn new() -> Self {
740        Self {
741            grammar: Grammar::new(),
742        }
743    }
744
745    /// Returns this builder with a source format.
746    #[must_use]
747    pub const fn source_format(mut self, source_format: GrammarFormat) -> Self {
748        self.grammar.source_format = Some(source_format);
749        self
750    }
751
752    /// Returns this builder with a start rule name.
753    #[must_use]
754    pub fn start(mut self, start: impl Into<String>) -> Self {
755        self.grammar.start = Some(start.into());
756        self
757    }
758
759    /// Adds a normal rule from a name and expression.
760    #[must_use]
761    pub fn rule(mut self, name: impl Into<String>, expr: GrammarExpr) -> Self {
762        self.grammar.rules.push(GrammarRule::new(name, expr));
763        self
764    }
765
766    /// Adds a complete rule.
767    #[must_use]
768    pub fn grammar_rule(mut self, rule: GrammarRule) -> Self {
769        self.grammar.rules.push(rule);
770        self
771    }
772
773    /// Adds a rule with an explicit kind.
774    #[must_use]
775    pub fn rule_with_kind(
776        mut self,
777        name: impl Into<String>,
778        expr: GrammarExpr,
779        kind: RuleKind,
780    ) -> Self {
781        self.grammar
782            .rules
783            .push(GrammarRule::new(name, expr).with_kind(kind));
784        self
785    }
786
787    /// Finishes the builder.
788    #[must_use]
789    pub fn build(self) -> Grammar {
790        self.grammar
791    }
792}
793
794/// Ergonomic constructor for grammar expressions.
795#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
796pub struct ExprBuilder;
797
798impl ExprBuilder {
799    /// Builds an empty-string expression.
800    #[must_use]
801    pub const fn empty(self) -> GrammarExpr {
802        GrammarExpr::Empty
803    }
804
805    /// Builds a literal terminal.
806    #[must_use]
807    pub fn term(self, value: impl Into<String>) -> GrammarExpr {
808        GrammarExpr::terminal(value)
809    }
810
811    /// Builds a literal terminal.
812    #[must_use]
813    pub fn terminal(self, value: impl Into<String>) -> GrammarExpr {
814        GrammarExpr::terminal(value)
815    }
816
817    /// Builds a case-insensitive literal terminal.
818    #[must_use]
819    pub fn terminal_insensitive(self, value: impl Into<String>) -> GrammarExpr {
820        GrammarExpr::terminal_insensitive(value)
821    }
822
823    /// Builds a single-character range expression.
824    #[must_use]
825    pub const fn char(self, value: char) -> GrammarExpr {
826        GrammarExpr::CharRange(value, value)
827    }
828
829    /// Builds an inclusive character range expression.
830    #[must_use]
831    pub const fn char_range(self, start: char, end: char) -> GrammarExpr {
832        GrammarExpr::CharRange(start, end)
833    }
834
835    /// Builds a character class.
836    #[must_use]
837    pub fn char_class<I>(self, negated: bool, items: I) -> GrammarExpr
838    where
839        I: IntoIterator<Item = CharClassItem>,
840    {
841        GrammarExpr::char_class(negated, items)
842    }
843
844    /// Builds an any-character wildcard.
845    #[must_use]
846    pub const fn any(self) -> GrammarExpr {
847        GrammarExpr::AnyChar
848    }
849
850    /// Builds a non-terminal reference.
851    #[must_use]
852    pub fn nt(self, value: impl Into<String>) -> GrammarExpr {
853        GrammarExpr::non_terminal(value)
854    }
855
856    /// Builds a non-terminal reference.
857    #[must_use]
858    pub fn non_terminal(self, value: impl Into<String>) -> GrammarExpr {
859        GrammarExpr::non_terminal(value)
860    }
861
862    /// Builds a choice expression.
863    #[must_use]
864    pub fn choice<I>(self, ordered: bool, alternatives: I) -> GrammarExpr
865    where
866        I: IntoIterator<Item = GrammarExpr>,
867    {
868        GrammarExpr::choice(ordered, alternatives)
869    }
870
871    /// Builds an ordered choice expression.
872    #[must_use]
873    pub fn choice_ordered<I>(self, alternatives: I) -> GrammarExpr
874    where
875        I: IntoIterator<Item = GrammarExpr>,
876    {
877        GrammarExpr::choice(true, alternatives)
878    }
879
880    /// Builds an unordered choice expression.
881    #[must_use]
882    pub fn choice_unordered<I>(self, alternatives: I) -> GrammarExpr
883    where
884        I: IntoIterator<Item = GrammarExpr>,
885    {
886        GrammarExpr::choice(false, alternatives)
887    }
888
889    /// Builds a sequence expression.
890    #[must_use]
891    pub fn seq<I>(self, items: I) -> GrammarExpr
892    where
893        I: IntoIterator<Item = GrammarExpr>,
894    {
895        GrammarExpr::sequence(items)
896    }
897
898    /// Builds an optional expression.
899    #[must_use]
900    pub fn opt(self, expr: GrammarExpr) -> GrammarExpr {
901        GrammarExpr::optional(expr)
902    }
903
904    /// Builds a zero-or-more repetition expression.
905    #[must_use]
906    pub fn rep0(self, expr: GrammarExpr) -> GrammarExpr {
907        GrammarExpr::zero_or_more(expr)
908    }
909
910    /// Builds a one-or-more repetition expression.
911    #[must_use]
912    pub fn rep1(self, expr: GrammarExpr) -> GrammarExpr {
913        GrammarExpr::one_or_more(expr)
914    }
915
916    /// Builds a counted repetition expression.
917    #[must_use]
918    pub fn repeat(self, expr: GrammarExpr, min: usize, max: Option<usize>) -> GrammarExpr {
919        GrammarExpr::repeat(expr, min, max)
920    }
921
922    /// Builds a positive lookahead expression.
923    #[must_use]
924    pub fn and(self, expr: GrammarExpr) -> GrammarExpr {
925        GrammarExpr::and(expr)
926    }
927
928    /// Builds a negative lookahead expression.
929    #[must_use]
930    pub fn not(self, expr: GrammarExpr) -> GrammarExpr {
931        GrammarExpr::not(expr)
932    }
933
934    /// Builds a labelled capture expression.
935    #[must_use]
936    pub fn capture(self, label: Option<impl Into<String>>, expr: GrammarExpr) -> GrammarExpr {
937        match label {
938            Some(label) => GrammarExpr::capture(label, expr),
939            None => GrammarExpr::capture_unlabeled(expr),
940        }
941    }
942
943    /// Builds an anonymous capture expression.
944    #[must_use]
945    pub fn capture_unlabeled(self, expr: GrammarExpr) -> GrammarExpr {
946        GrammarExpr::capture_unlabeled(expr)
947    }
948}
949
950fn write_joined(
951    formatter: &mut fmt::Formatter<'_>,
952    expressions: &[GrammarExpr],
953    separator: &str,
954) -> fmt::Result {
955    if let Some((first, rest)) = expressions.split_first() {
956        write!(formatter, "{first}")?;
957        for expression in rest {
958            formatter.write_str(separator)?;
959            write!(formatter, "{expression}")?;
960        }
961    }
962    Ok(())
963}