Skip to main content

meta_language/grammar/
builder.rs

1//! The ergonomic expression builder of [`Grammar::expr`](super::Grammar::expr).
2
3use std::fmt;
4
5use super::{CharClassItem, Grammar, GrammarExpr, GrammarFormat, GrammarRule, RuleKind};
6
7/// Largest integer a JavaScript number holds exactly (`Number.MAX_SAFE_INTEGER`).
8const MAX_SAFE_INTEGER: u64 = (1 << 53) - 1;
9
10/// Ergonomic constructor for grammar expressions.
11#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
12pub struct ExprBuilder;
13
14impl ExprBuilder {
15    /// Builds an empty-string expression.
16    #[must_use]
17    pub const fn empty(self) -> GrammarExpr {
18        GrammarExpr::Empty
19    }
20
21    /// Builds a literal terminal.
22    #[must_use]
23    pub fn term(self, value: impl Into<String>) -> GrammarExpr {
24        GrammarExpr::terminal(value)
25    }
26
27    /// Builds a literal terminal.
28    #[must_use]
29    pub fn terminal(self, value: impl Into<String>) -> GrammarExpr {
30        GrammarExpr::terminal(value)
31    }
32
33    /// Builds a case-insensitive literal terminal.
34    #[must_use]
35    pub fn terminal_insensitive(self, value: impl Into<String>) -> GrammarExpr {
36        GrammarExpr::terminal_insensitive(value)
37    }
38
39    /// Builds a single-character range expression.
40    #[must_use]
41    pub const fn char(self, value: char) -> GrammarExpr {
42        GrammarExpr::CharRange(value, value)
43    }
44
45    /// Builds an inclusive character range expression.
46    #[must_use]
47    pub const fn char_range(self, start: char, end: char) -> GrammarExpr {
48        GrammarExpr::CharRange(start, end)
49    }
50
51    /// Builds a character class.
52    #[must_use]
53    pub fn char_class<I>(self, negated: bool, items: I) -> GrammarExpr
54    where
55        I: IntoIterator<Item = CharClassItem>,
56    {
57        GrammarExpr::char_class(negated, items)
58    }
59
60    /// Builds an any-character wildcard.
61    #[must_use]
62    pub const fn any(self) -> GrammarExpr {
63        GrammarExpr::AnyChar
64    }
65
66    /// Builds a non-terminal reference.
67    #[must_use]
68    pub fn nt(self, value: impl Into<String>) -> GrammarExpr {
69        GrammarExpr::non_terminal(value)
70    }
71
72    /// Builds a non-terminal reference.
73    #[must_use]
74    pub fn non_terminal(self, value: impl Into<String>) -> GrammarExpr {
75        GrammarExpr::non_terminal(value)
76    }
77
78    /// Builds a choice expression.
79    #[must_use]
80    pub fn choice<I>(self, ordered: bool, alternatives: I) -> GrammarExpr
81    where
82        I: IntoIterator<Item = GrammarExpr>,
83    {
84        GrammarExpr::choice(ordered, alternatives)
85    }
86
87    /// Builds an ordered choice expression.
88    #[must_use]
89    pub fn choice_ordered<I>(self, alternatives: I) -> GrammarExpr
90    where
91        I: IntoIterator<Item = GrammarExpr>,
92    {
93        GrammarExpr::choice(true, alternatives)
94    }
95
96    /// Builds an unordered choice expression.
97    #[must_use]
98    pub fn choice_unordered<I>(self, alternatives: I) -> GrammarExpr
99    where
100        I: IntoIterator<Item = GrammarExpr>,
101    {
102        GrammarExpr::choice(false, alternatives)
103    }
104
105    /// Builds a sequence expression.
106    #[must_use]
107    pub fn seq<I>(self, items: I) -> GrammarExpr
108    where
109        I: IntoIterator<Item = GrammarExpr>,
110    {
111        GrammarExpr::sequence(items)
112    }
113
114    /// Builds an optional expression.
115    #[must_use]
116    pub fn opt(self, expr: GrammarExpr) -> GrammarExpr {
117        GrammarExpr::optional(expr)
118    }
119
120    /// Builds a zero-or-more repetition expression.
121    #[must_use]
122    pub fn rep0(self, expr: GrammarExpr) -> GrammarExpr {
123        GrammarExpr::zero_or_more(expr)
124    }
125
126    /// Builds a one-or-more repetition expression.
127    #[must_use]
128    pub fn rep1(self, expr: GrammarExpr) -> GrammarExpr {
129        GrammarExpr::one_or_more(expr)
130    }
131
132    /// Builds a counted repetition expression.
133    #[must_use]
134    pub fn repeat(self, expr: GrammarExpr, min: usize, max: Option<usize>) -> GrammarExpr {
135        GrammarExpr::repeat(expr, min, max)
136    }
137
138    /// Builds a positive lookahead expression.
139    #[must_use]
140    pub fn and(self, expr: GrammarExpr) -> GrammarExpr {
141        GrammarExpr::and(expr)
142    }
143
144    /// Builds a negative lookahead expression.
145    #[must_use]
146    pub fn not(self, expr: GrammarExpr) -> GrammarExpr {
147        GrammarExpr::not(expr)
148    }
149
150    /// Builds a labelled capture expression.
151    #[must_use]
152    pub fn capture(self, label: Option<impl Into<String>>, expr: GrammarExpr) -> GrammarExpr {
153        match label {
154            Some(label) => GrammarExpr::capture(label, expr),
155            None => GrammarExpr::capture_unlabeled(expr),
156        }
157    }
158
159    /// Builds an anonymous capture expression.
160    #[must_use]
161    pub fn capture_unlabeled(self, expr: GrammarExpr) -> GrammarExpr {
162        GrammarExpr::capture_unlabeled(expr)
163    }
164}
165
166/// Flattens nested sequences and drops empty items, as the JavaScript
167/// `sequence` builder does: no items build [`GrammarExpr::Empty`] and one item
168/// builds itself.
169#[must_use]
170pub fn sequence(items: Vec<GrammarExpr>) -> GrammarExpr {
171    let mut flattened = Vec::with_capacity(items.len());
172    for item in items {
173        match item {
174            GrammarExpr::Empty => {}
175            GrammarExpr::Sequence(nested) => flattened.extend(nested),
176            item => flattened.push(item),
177        }
178    }
179    match flattened.len() {
180        0 => GrammarExpr::Empty,
181        1 => flattened.remove(0),
182        _ => GrammarExpr::Sequence(flattened),
183    }
184}
185
186/// Flattens nested choices of the same ordering, as the JavaScript `choice`
187/// builder does: no alternatives build [`GrammarExpr::Empty`] and one
188/// alternative builds itself.
189#[must_use]
190pub fn choice(alternatives: Vec<GrammarExpr>, ordered: bool) -> GrammarExpr {
191    let mut flattened = Vec::with_capacity(alternatives.len());
192    for alternative in alternatives {
193        match alternative {
194            GrammarExpr::Choice {
195                ordered: nested_ordered,
196                alternatives: nested,
197            } if nested_ordered == ordered => flattened.extend(nested),
198            alternative => flattened.push(alternative),
199        }
200    }
201    match flattened.len() {
202        0 => GrammarExpr::Empty,
203        1 => flattened.remove(0),
204        _ => GrammarExpr::Choice {
205            ordered,
206            alternatives: flattened,
207        },
208    }
209}
210
211/// Counted repetition bounds [`canonical_repeat`] rejects: an upper bound
212/// below the lower one, or a bound past the JavaScript safe-integer range.
213#[derive(Clone, Copy, Debug, PartialEq, Eq)]
214pub struct RepetitionBoundsError {
215    /// The lower bound.
216    pub min: u64,
217    /// The upper bound, `None` when unbounded.
218    pub max: Option<u64>,
219}
220
221impl fmt::Display for RepetitionBoundsError {
222    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
223        let max = self.max.map_or_else(String::new, |max| max.to_string());
224        write!(formatter, "invalid repetition bounds {}..{max}", self.min)
225    }
226}
227
228impl std::error::Error for RepetitionBoundsError {}
229
230/// Lowers counted repetition to its most specific expression variant.
231///
232/// As the JavaScript `canonicalRepeat` builder does, `0..` is zero-or-more,
233/// `1..` is one-or-more, `0..1` is optional, and every other range is a
234/// counted repeat.
235///
236/// # Errors
237///
238/// Returns [`RepetitionBoundsError`] when `max` is below `min` or either
239/// bound exceeds the JavaScript safe-integer range.
240pub fn canonical_repeat(
241    expr: GrammarExpr,
242    min: u64,
243    max: Option<u64>,
244) -> Result<GrammarExpr, RepetitionBoundsError> {
245    let invalid = RepetitionBoundsError { min, max };
246    if min > MAX_SAFE_INTEGER || max.is_some_and(|max| max > MAX_SAFE_INTEGER || max < min) {
247        return Err(invalid);
248    }
249    let bound = |value: u64| usize::try_from(value).map_err(|_| invalid);
250    Ok(match (min, max) {
251        (0, None) => GrammarExpr::zero_or_more(expr),
252        (1, None) => GrammarExpr::one_or_more(expr),
253        (0, Some(1)) => GrammarExpr::optional(expr),
254        (min, max) => GrammarExpr::repeat(expr, bound(min)?, max.map(bound).transpose()?),
255    })
256}
257
258/// Fluent builder for order-preserving grammars.
259#[derive(Clone, Debug, Default, PartialEq, Eq)]
260pub struct GrammarBuilder {
261    grammar: Grammar,
262}
263
264impl GrammarBuilder {
265    /// Builds an empty grammar builder.
266    #[must_use]
267    pub const fn new() -> Self {
268        Self {
269            grammar: Grammar::new(),
270        }
271    }
272
273    /// Returns this builder with a source format.
274    #[must_use]
275    pub const fn source_format(mut self, source_format: GrammarFormat) -> Self {
276        self.grammar.source_format = Some(source_format);
277        self
278    }
279
280    /// Returns this builder with a start rule name.
281    #[must_use]
282    pub fn start(mut self, start: impl Into<String>) -> Self {
283        self.grammar.start = Some(start.into());
284        self
285    }
286
287    /// Adds a normal rule from a name and expression.
288    #[must_use]
289    pub fn rule(mut self, name: impl Into<String>, expr: GrammarExpr) -> Self {
290        self.grammar.rules.push(GrammarRule::new(name, expr));
291        self
292    }
293
294    /// Adds a complete rule.
295    #[must_use]
296    pub fn grammar_rule(mut self, rule: GrammarRule) -> Self {
297        self.grammar.rules.push(rule);
298        self
299    }
300
301    /// Adds a rule with an explicit kind.
302    #[must_use]
303    pub fn rule_with_kind(
304        mut self,
305        name: impl Into<String>,
306        expr: GrammarExpr,
307        kind: RuleKind,
308    ) -> Self {
309        self.grammar
310            .rules
311            .push(GrammarRule::new(name, expr).with_kind(kind));
312        self
313    }
314
315    /// Finishes the builder.
316    #[must_use]
317    pub fn build(self) -> Grammar {
318        self.grammar
319    }
320}