Skip to main content

meta_language/grammar/interchange/
native.rs

1//! The native grammar listing written and read by the `meta-language grammar`
2//! commands.
3//!
4//! An optional `format TAG` line, a `start NAME` line for the start rule, then
5//! one `rule NAME = KIND EXPRESSION` line per rule in grammar order, with
6//! expressions spelled as the shared grammar parity fixtures spell them
7//! (`seq(ref(a), literal("b"))`). Mirrors the native listing of
8//! `js/src/grammar-interchange.js`.
9
10use super::super::feature::class_expression;
11use super::super::{
12    CharClassItem, FeatureExpr, Grammar, GrammarDeclarations, GrammarExpr, GrammarFormat,
13    GrammarImportError, GrammarMacro, GrammarRule, GrammarScanner, MATCHING_MODES, Operation,
14    PrecedenceEntry, RuleAttributes, RuleKind, UnicodeClassItem, settling_problem,
15};
16use super::feature_forms::{FormCodec, render_feature_form, render_operation};
17
18/// The longest repetition bound a listing may spell, in decimal digits.
19const MAX_BOUND_DIGITS: usize = 9;
20
21fn native_error(line: Option<usize>, detail: impl Into<String>) -> GrammarImportError {
22    let detail = detail.into();
23    GrammarImportError::Parse {
24        format: GrammarFormat::MetaLanguage,
25        message: line.map_or_else(|| detail.clone(), |line| format!("line {line}: {detail}")),
26    }
27}
28
29/// Renders the native grammar listing of `grammar`.
30///
31/// The listing holds the `format` and `start` lines, the feature union declarations (`matching`, `import`, `mode`,
32/// `extra`, `conflict`, `macro` and `scanner` lines, in that order), then one
33/// rule line per rule, with its parameters and its `channel(...)`,
34/// `modes(...)` and `action(...)` attributes.
35#[must_use]
36pub fn render_native_grammar(grammar: &Grammar) -> String {
37    let mut lines = Vec::new();
38    if let Some(format) = grammar.source_format() {
39        lines.push(format!("format {}", format.as_str()));
40    }
41    if let Some(start) = grammar.start_rule() {
42        lines.push(format!("start {}", render_name(&start.name)));
43    }
44    let declarations = grammar.declarations();
45    let names = |names: &[String], separator: &str| {
46        names
47            .iter()
48            .map(|name| render_name(name))
49            .collect::<Vec<_>>()
50            .join(separator)
51    };
52    let parameters = |parameters: &[String]| {
53        if parameters.is_empty() {
54            String::new()
55        } else {
56            format!("({})", names(parameters, ", "))
57        }
58    };
59    let operations = |operations: &[Operation]| {
60        operations
61            .iter()
62            .map(|operation| render_operation(operation, &LineCodec))
63            .collect::<Vec<_>>()
64            .join(", ")
65    };
66    if let Some(matching) = &declarations.matching {
67        lines.push(format!("matching {matching}"));
68    }
69    if let Some(settling) = &declarations.settling {
70        lines.push(format!("settling {}", settling.join(" ")));
71    }
72    for name in &declarations.imports {
73        lines.push(format!("import {}", render_name(name)));
74    }
75    for name in &declarations.modes {
76        lines.push(format!("mode {}", render_name(name)));
77    }
78    for extra in &declarations.extras {
79        lines.push(format!("extra {}", render_native_expression(extra)));
80    }
81    for group in &declarations.conflicts {
82        lines.push(format!("conflict {}", names(group, " ")));
83    }
84    for order in &declarations.precedences {
85        let entries = order
86            .iter()
87            .map(|entry| format!("{}({})", entry.kind(), render_name(entry.value())))
88            .collect::<Vec<_>>();
89        lines.push(format!("precedences {}", entries.join(" ")));
90    }
91    for declared in &declarations.macros {
92        lines.push(format!(
93            "macro {}{} = {}",
94            render_name(&declared.name),
95            parameters(&declared.parameters),
96            render_native_expression(&declared.expression)
97        ));
98    }
99    for scanner in &declarations.scanners {
100        lines.push(format!(
101            "scanner {} tokens({}) operations({})",
102            render_name(&scanner.name),
103            names(&scanner.tokens, ", "),
104            operations(&scanner.operations)
105        ));
106    }
107    for rule in grammar.rules() {
108        let attributes = &rule.attributes;
109        let mut line = format!(
110            "rule {}{} = {} {}",
111            render_name(&rule.name),
112            parameters(&attributes.parameters),
113            rule.kind().as_str(),
114            render_native_expression(rule.expr())
115        );
116        let mut attribute = |head: &str, body: String| {
117            line.push(' ');
118            line.push_str(head);
119            line.push('(');
120            line.push_str(&body);
121            line.push(')');
122        };
123        if let Some(channel) = &attributes.channel {
124            attribute("channel", render_name(channel));
125        }
126        if let Some(modes) = &attributes.modes {
127            attribute("modes", names(modes, ", "));
128        }
129        if let Some(action) = &attributes.action {
130            attribute("action", operations(action));
131        }
132        lines.push(line);
133    }
134    lines.into_iter().map(|line| line + "\n").collect()
135}
136
137/// The native listing spelling of the feature union forms.
138struct LineCodec;
139
140impl FormCodec for LineCodec {
141    fn name(&self, value: &str) -> String {
142        render_name(value)
143    }
144
145    fn text(&self, value: &str) -> String {
146        quote(value)
147    }
148
149    fn expression(&self, expr: &GrammarExpr) -> String {
150        render_native_expression(expr)
151    }
152
153    fn call(&self, head: &str, parts: Vec<String>) -> String {
154        if parts.is_empty() {
155            head.to_owned()
156        } else {
157            format!("{head}({})", parts.join(", "))
158        }
159    }
160
161    fn block(&self, word: &str, parts: Vec<String>) -> String {
162        format!("{word}({})", parts.join(", "))
163    }
164
165    fn byte_class(&self, negated: bool, items: Vec<String>) -> String {
166        format!(
167            "{}({})",
168            if negated { "notByteClass" } else { "byteClass" },
169            items.join(", ")
170        )
171    }
172}
173
174/// Renders one feature union expression with the native listing spelling.
175#[must_use]
176pub fn render_native_feature(feature: &FeatureExpr) -> String {
177    match feature {
178        FeatureExpr::UnicodeClass { negated, items } => {
179            let items = items
180                .iter()
181                .map(|item| match item {
182                    UnicodeClassItem::Char(value) => format!("char({})", quote_char(*value)),
183                    UnicodeClassItem::Range(start, end) => render_range(*start, *end),
184                    UnicodeClassItem::Category(value) => format!("category({})", quote(value)),
185                    UnicodeClassItem::Script(value) => format!("script({})", quote(value)),
186                })
187                .collect::<Vec<_>>()
188                .join(", ");
189            format!("{}({items})", if *negated { "notClass" } else { "class" })
190        }
191        FeatureExpr::Call { name, arguments } => format!(
192            "ref({}, {})",
193            render_name(name),
194            arguments
195                .iter()
196                .map(render_native_expression)
197                .collect::<Vec<_>>()
198                .join(", ")
199        ),
200        FeatureExpr::Form(_) | FeatureExpr::ByteClass { .. } => {
201            render_feature_form(feature, &LineCodec).unwrap_or_default()
202        }
203    }
204}
205
206/// Renders one expression with the native listing spelling.
207#[must_use]
208pub fn render_native_expression(expr: &GrammarExpr) -> String {
209    let list = |items: &[GrammarExpr]| {
210        items
211            .iter()
212            .map(render_native_expression)
213            .collect::<Vec<_>>()
214            .join(", ")
215    };
216    match expr {
217        GrammarExpr::Empty => "empty".to_owned(),
218        GrammarExpr::AnyChar => "any".to_owned(),
219        GrammarExpr::Terminal(value) => format!("literal({})", quote(value)),
220        GrammarExpr::TerminalInsensitive(value) => format!("literalInsensitive({})", quote(value)),
221        GrammarExpr::CharRange(start, end) => render_range(*start, *end),
222        GrammarExpr::CharClass { negated, items } => {
223            let items = items
224                .iter()
225                .map(|item| match item {
226                    CharClassItem::Range(start, end) => render_range(*start, *end),
227                    CharClassItem::Char(value) => format!("char({})", quote_char(*value)),
228                })
229                .collect::<Vec<_>>()
230                .join(", ");
231            format!("{}({items})", if *negated { "notClass" } else { "class" })
232        }
233        GrammarExpr::NonTerminal(name) => format!("ref({})", render_name(name)),
234        GrammarExpr::Choice {
235            ordered,
236            alternatives,
237        } => format!(
238            "{}({})",
239            if *ordered { "orderedChoice" } else { "choice" },
240            list(alternatives)
241        ),
242        GrammarExpr::Sequence(items) => format!("seq({})", list(items)),
243        GrammarExpr::Optional(inner) => format!("optional({})", render_native_expression(inner)),
244        GrammarExpr::ZeroOrMore(inner) => format!("repeat0({})", render_native_expression(inner)),
245        GrammarExpr::OneOrMore(inner) => format!("repeat1({})", render_native_expression(inner)),
246        GrammarExpr::And(inner) => format!("and({})", render_native_expression(inner)),
247        GrammarExpr::Not(inner) => format!("not({})", render_native_expression(inner)),
248        GrammarExpr::Repeat { expr, min, max } => format!(
249            "repeat({}, {min}, {})",
250            render_native_expression(expr),
251            max.map_or_else(|| "unbounded".to_owned(), |max| max.to_string())
252        ),
253        GrammarExpr::Capture { label, expr } => format!(
254            "capture({}, {})",
255            label.as_deref().map_or_else(|| "null".to_owned(), quote),
256            render_native_expression(expr)
257        ),
258        GrammarExpr::Feature(feature) => render_native_feature(feature),
259    }
260}
261
262fn render_range(start: char, end: char) -> String {
263    format!("range({}, {})", quote_char(start), quote_char(end))
264}
265
266pub(super) fn quote(value: &str) -> String {
267    serde_json::to_string(value).unwrap_or_default()
268}
269
270fn quote_char(value: char) -> String {
271    quote(&value.to_string())
272}
273
274/// Whether `value` ends a bare rule name.
275const fn is_name_stop(value: char) -> bool {
276    value.is_whitespace() || matches!(value, '(' | ')' | ',' | '"' | '=')
277}
278
279pub(super) fn render_name(name: &str) -> String {
280    if !name.is_empty() && !name.chars().any(is_name_stop) {
281        name.to_owned()
282    } else {
283        quote(name)
284    }
285}
286
287/// Parses a native grammar listing written by [`render_native_grammar`].
288///
289/// # Errors
290///
291/// Returns a [`GrammarImportError`] of the meta-language format naming the
292/// first line that does not parse.
293pub fn parse_native_grammar(source: &str) -> Result<Grammar, GrammarImportError> {
294    let mut rules: Vec<GrammarRule> = Vec::new();
295    let mut start: Option<(String, usize)> = None;
296    let mut source_format: Option<GrammarFormat> = None;
297    let mut declarations = GrammarDeclarations::default();
298    for (index, text) in source.split('\n').enumerate() {
299        let line = index + 1;
300        let text = text.strip_suffix('\r').unwrap_or(text);
301        let mut cursor = Cursor {
302            chars: text.chars().collect(),
303            line,
304            position: 0,
305        };
306        cursor.skip_spaces();
307        if cursor.done() || cursor.peek() == Some('#') {
308            continue;
309        }
310        let directive = cursor.word()?;
311        cursor.require_space()?;
312        match directive.as_str() {
313            "format" => {
314                if source_format.is_some() {
315                    return Err(cursor.fail("the format is given twice"));
316                }
317                let tag = cursor.word()?;
318                source_format = Some(
319                    GrammarFormat::from_tag(&tag)
320                        .ok_or_else(|| cursor.fail(format!("unknown source format {tag}")))?,
321                );
322            }
323            "start" => {
324                if start.is_some() {
325                    return Err(cursor.fail("the start rule is given twice"));
326                }
327                start = Some((cursor.name()?, line));
328            }
329            "rule" => {
330                let name = cursor.name()?;
331                let parameters = cursor.parameters()?;
332                cursor.skip_spaces();
333                cursor.expect('=')?;
334                cursor.skip_spaces();
335                let kind = cursor.word()?;
336                let kind = RuleKind::from_tag(&kind)
337                    .ok_or_else(|| cursor.fail(format!("unknown rule kind {kind}")))?;
338                cursor.require_space()?;
339                let expr = cursor.expression()?;
340                if rules.iter().any(|rule| rule.name == name) {
341                    return Err(cursor.fail(format!("rule {name} is defined twice")));
342                }
343                let mut attributes = cursor.rule_attributes()?;
344                attributes.parameters = parameters;
345                rules.push(
346                    GrammarRule::new(name, expr)
347                        .with_kind(kind)
348                        .with_attributes(attributes),
349                );
350            }
351            "matching" => {
352                if declarations.matching.is_some() {
353                    return Err(cursor.fail("the matching is given twice"));
354                }
355                let matching = cursor.word()?;
356                if !MATCHING_MODES.contains(&matching.as_str()) {
357                    return Err(cursor.fail(format!("unknown matching {matching}")));
358                }
359                declarations.matching = Some(matching);
360            }
361            "settling" => {
362                if declarations.settling.is_some() {
363                    return Err(cursor.fail("the settling is given twice"));
364                }
365                let mut steps = Vec::new();
366                while !cursor.done() {
367                    steps.push(cursor.word()?);
368                    cursor.skip_spaces();
369                }
370                if let Some(problem) = settling_problem(&steps) {
371                    return Err(cursor.fail(problem));
372                }
373                declarations.settling = Some(steps);
374            }
375            "import" => declarations.imports.push(cursor.name()?),
376            "mode" => declarations.modes.push(cursor.name()?),
377            "extra" => declarations.extras.push(cursor.expression()?),
378            "conflict" => {
379                let mut group = vec![cursor.name()?];
380                cursor.skip_spaces();
381                while !cursor.done() {
382                    group.push(cursor.name()?);
383                    cursor.skip_spaces();
384                }
385                declarations.conflicts.push(group);
386            }
387            "precedences" => {
388                let mut order = Vec::new();
389                while !cursor.done() {
390                    let kind = cursor.word()?;
391                    let mut values = cursor.list(Cursor::name)?;
392                    let Some(entry) = (values.len() == 1)
393                        .then(|| values.pop())
394                        .flatten()
395                        .and_then(|value| PrecedenceEntry::from_kind(&kind, value))
396                    else {
397                        return Err(cursor.fail(if kind == "name" || kind == "rule" {
398                            format!("{kind}(...) names one entry")
399                        } else {
400                            format!("a precedence entry is name(NAME) or rule(NAME), not {kind}")
401                        }));
402                    };
403                    order.push(entry);
404                    cursor.skip_spaces();
405                }
406                if order.len() < 2 {
407                    return Err(cursor.fail("precedences orders at least two entries"));
408                }
409                declarations.precedences.push(order);
410            }
411            "macro" => {
412                let name = cursor.name()?;
413                let parameters = cursor.parameters()?;
414                cursor.skip_spaces();
415                cursor.expect('=')?;
416                cursor.skip_spaces();
417                let expression = cursor.expression()?;
418                declarations.macros.push(GrammarMacro {
419                    name,
420                    parameters,
421                    expression,
422                });
423            }
424            "scanner" => {
425                let name = cursor.name()?;
426                cursor.require_space()?;
427                if cursor.word()? != "tokens" {
428                    return Err(cursor.fail("expected tokens(...)"));
429                }
430                let tokens = cursor.list(Cursor::name)?;
431                cursor.require_space()?;
432                if cursor.word()? != "operations" {
433                    return Err(cursor.fail("expected operations(...)"));
434                }
435                let operations = cursor.statements()?;
436                declarations.scanners.push(GrammarScanner {
437                    name,
438                    tokens,
439                    operations,
440                });
441            }
442            other => return Err(cursor.fail(format!("unknown directive {other}"))),
443        }
444        cursor.skip_spaces();
445        if !cursor.done() {
446            return Err(cursor.fail("unexpected text at the end of the line"));
447        }
448    }
449    let Some(first) = rules.first().map(|rule| rule.name.clone()) else {
450        return Err(native_error(None, "the listing defines no rules"));
451    };
452    let start = match start {
453        Some((name, line)) => {
454            if !rules.iter().any(|rule| rule.name == name) {
455                return Err(native_error(
456                    Some(line),
457                    format!("start rule {name} is not defined"),
458                ));
459            }
460            name
461        }
462        None => first,
463    };
464    let mut grammar = Grammar::new();
465    for rule in rules {
466        grammar.add_rule(rule);
467    }
468    grammar.set_start(start);
469    if let Some(format) = source_format {
470        grammar.set_source_format(format);
471    }
472    grammar.set_declarations(declarations);
473    Ok(grammar)
474}
475
476/// Parses one expression written by [`render_native_expression`].
477///
478/// # Errors
479///
480/// Returns a [`GrammarImportError`] of the meta-language format when the text
481/// is not exactly one expression.
482pub fn parse_native_expression(text: &str) -> Result<GrammarExpr, GrammarImportError> {
483    let mut cursor = Cursor {
484        chars: text.chars().collect(),
485        line: 1,
486        position: 0,
487    };
488    cursor.skip_spaces();
489    let expr = cursor.expression()?;
490    cursor.skip_spaces();
491    if cursor.done() {
492        Ok(expr)
493    } else {
494        Err(cursor.fail("unexpected text after the expression"))
495    }
496}
497
498pub(super) struct Cursor {
499    pub(super) chars: Vec<char>,
500    pub(super) line: usize,
501    pub(super) position: usize,
502}
503
504impl Cursor {
505    pub(super) fn fail(&self, detail: impl Into<String>) -> GrammarImportError {
506        native_error(Some(self.line), detail)
507    }
508
509    pub(super) const fn done(&self) -> bool {
510        self.position >= self.chars.len()
511    }
512
513    pub(super) fn peek(&self) -> Option<char> {
514        self.chars.get(self.position).copied()
515    }
516
517    pub(super) fn text(&self, begin: usize) -> String {
518        self.chars[begin..self.position].iter().collect()
519    }
520
521    pub(super) fn skip_spaces(&mut self) {
522        while matches!(self.peek(), Some(' ' | '\t')) {
523            self.position += 1;
524        }
525    }
526
527    pub(super) fn require_space(&mut self) -> Result<(), GrammarImportError> {
528        if !matches!(self.peek(), Some(' ' | '\t')) {
529            return Err(self.fail("expected a space"));
530        }
531        self.skip_spaces();
532        Ok(())
533    }
534
535    pub(super) fn expect(&mut self, expected: char) -> Result<(), GrammarImportError> {
536        if self.peek() != Some(expected) {
537            return Err(self.fail(format!("expected {expected}")));
538        }
539        self.position += 1;
540        Ok(())
541    }
542
543    pub(super) fn word(&mut self) -> Result<String, GrammarImportError> {
544        let begin = self.position;
545        while self
546            .peek()
547            .is_some_and(|value| value.is_ascii_alphanumeric() || matches!(value, '_' | '-'))
548        {
549            self.position += 1;
550        }
551        if begin == self.position {
552            return Err(self.fail("expected a word"));
553        }
554        Ok(self.text(begin))
555    }
556
557    pub(super) fn name(&mut self) -> Result<String, GrammarImportError> {
558        if self.peek() == Some('"') {
559            return self.string();
560        }
561        let begin = self.position;
562        while self.peek().is_some_and(|value| !is_name_stop(value)) {
563            self.position += 1;
564        }
565        if begin == self.position {
566            return Err(self.fail("expected a name"));
567        }
568        Ok(self.text(begin))
569    }
570
571    pub(super) fn string(&mut self) -> Result<String, GrammarImportError> {
572        if self.peek() != Some('"') {
573            return Err(self.fail("expected a string"));
574        }
575        let begin = self.position;
576        self.position += 1;
577        while let Some(value) = self.peek() {
578            if value == '"' {
579                break;
580            }
581            self.position += if value == '\\' { 2 } else { 1 };
582        }
583        if self.done() {
584            return Err(self.fail("unterminated string"));
585        }
586        self.position += 1;
587        serde_json::from_str::<String>(&self.text(begin)).map_err(|_| self.fail("invalid string"))
588    }
589
590    pub(super) fn character(&mut self) -> Result<char, GrammarImportError> {
591        let value = self.string()?;
592        let mut chars = value.chars();
593        match (chars.next(), chars.next()) {
594            (Some(value), None) => Ok(value),
595            _ => Err(self.fail("a character must be one code point")),
596        }
597    }
598
599    fn bound(&mut self, allow_unbounded: bool) -> Result<Option<usize>, GrammarImportError> {
600        if allow_unbounded && self.peek() == Some('u') {
601            if self.word()? != "unbounded" {
602                return Err(self.fail("expected a repetition bound"));
603            }
604            return Ok(None);
605        }
606        let begin = self.position;
607        while self.peek().is_some_and(|value| value.is_ascii_digit()) {
608            self.position += 1;
609        }
610        let digits = self.text(begin);
611        if digits.is_empty() {
612            return Err(self.fail("expected a repetition bound"));
613        }
614        if digits.len() > MAX_BOUND_DIGITS {
615            return Err(self.fail(format!("repetition bound {digits} is too large")));
616        }
617        digits
618            .parse()
619            .map(Some)
620            .map_err(|_| self.fail("expected a repetition bound"))
621    }
622
623    pub(super) fn separator(&mut self) -> Result<(), GrammarImportError> {
624        self.skip_spaces();
625        self.expect(',')?;
626        self.skip_spaces();
627        Ok(())
628    }
629
630    pub(super) fn open(&mut self) -> Result<(), GrammarImportError> {
631        self.expect('(')?;
632        self.skip_spaces();
633        Ok(())
634    }
635
636    pub(super) fn close(&mut self) -> Result<(), GrammarImportError> {
637        self.skip_spaces();
638        self.expect(')')
639    }
640
641    pub(super) fn list<T>(
642        &mut self,
643        item: fn(&mut Self) -> Result<T, GrammarImportError>,
644    ) -> Result<Vec<T>, GrammarImportError> {
645        self.open()?;
646        let mut items = Vec::new();
647        self.skip_spaces();
648        while self.peek() != Some(')') {
649            if !items.is_empty() {
650                self.separator()?;
651            }
652            items.push(item(self)?);
653            self.skip_spaces();
654            if self.done() {
655                return Err(self.fail("expected )"));
656            }
657        }
658        self.close()?;
659        Ok(items)
660    }
661
662    fn inner(&mut self) -> Result<Box<GrammarExpr>, GrammarImportError> {
663        self.open()?;
664        let expr = self.expression()?;
665        self.close()?;
666        Ok(Box::new(expr))
667    }
668
669    pub(super) fn expression(&mut self) -> Result<GrammarExpr, GrammarImportError> {
670        let kind = self.word()?;
671        Ok(match kind.as_str() {
672            "empty" => GrammarExpr::Empty,
673            "any" => GrammarExpr::AnyChar,
674            "literal" | "literalInsensitive" => {
675                self.open()?;
676                let value = self.string()?;
677                self.close()?;
678                if kind == "literal" {
679                    GrammarExpr::Terminal(value)
680                } else {
681                    GrammarExpr::TerminalInsensitive(value)
682                }
683            }
684            "range" => {
685                self.open()?;
686                let start = self.character()?;
687                self.separator()?;
688                let end = self.character()?;
689                self.close()?;
690                GrammarExpr::CharRange(start, end)
691            }
692            "class" | "notClass" => {
693                let negated = kind == "notClass";
694                class_expression(negated, self.list(Self::class_item)?)
695            }
696            "ref" => {
697                self.open()?;
698                let name = self.name()?;
699                let mut arguments = Vec::new();
700                self.skip_spaces();
701                while self.peek() == Some(',') {
702                    self.separator()?;
703                    arguments.push(self.expression()?);
704                    self.skip_spaces();
705                }
706                self.close()?;
707                if arguments.is_empty() {
708                    GrammarExpr::NonTerminal(name)
709                } else {
710                    GrammarExpr::feature(FeatureExpr::Call { name, arguments })
711                }
712            }
713            "choice" | "orderedChoice" => GrammarExpr::Choice {
714                ordered: kind == "orderedChoice",
715                alternatives: self.list(Self::expression)?,
716            },
717            "seq" => GrammarExpr::Sequence(self.list(Self::expression)?),
718            "repeat" => {
719                self.open()?;
720                let expr = Box::new(self.expression()?);
721                self.separator()?;
722                let min = self.bound(false)?.unwrap_or_default();
723                self.separator()?;
724                let max = self.bound(true)?;
725                self.close()?;
726                if max.is_some_and(|max| max < min) {
727                    let max = max.unwrap_or_default();
728                    return Err(self.fail(format!("repetition bounds {min}, {max} are reversed")));
729                }
730                GrammarExpr::Repeat { expr, min, max }
731            }
732            "capture" => {
733                self.open()?;
734                let label = if self.peek() == Some('"') {
735                    Some(self.string()?)
736                } else if self.word()? == "null" {
737                    None
738                } else {
739                    return Err(self.fail("expected a capture label or null"));
740                };
741                self.separator()?;
742                let expr = Box::new(self.expression()?);
743                self.close()?;
744                GrammarExpr::Capture { label, expr }
745            }
746            other => {
747                if let Some(feature) = self.feature(other)? {
748                    return Ok(feature);
749                }
750                match other {
751                    "optional" => GrammarExpr::Optional(self.inner()?),
752                    "repeat0" => GrammarExpr::ZeroOrMore(self.inner()?),
753                    "repeat1" => GrammarExpr::OneOrMore(self.inner()?),
754                    "and" => GrammarExpr::And(self.inner()?),
755                    "not" => GrammarExpr::Not(self.inner()?),
756                    _ => return Err(self.fail(format!("unknown expression {other}"))),
757                }
758            }
759        })
760    }
761
762    fn class_item(&mut self) -> Result<UnicodeClassItem, GrammarImportError> {
763        let kind = self.word()?;
764        self.open()?;
765        let item = match kind.as_str() {
766            "char" => UnicodeClassItem::Char(self.character()?),
767            "range" => {
768                let start = self.character()?;
769                self.separator()?;
770                UnicodeClassItem::Range(start, self.character()?)
771            }
772            "category" => UnicodeClassItem::Category(self.string()?),
773            "script" => UnicodeClassItem::Script(self.string()?),
774            other => return Err(self.fail(format!("unknown class item {other}"))),
775        };
776        self.close()?;
777        Ok(item)
778    }
779
780    /// The optional `(NAME, ...)` parameter list after a rule or macro name.
781    fn parameters(&mut self) -> Result<Vec<String>, GrammarImportError> {
782        if self.peek() != Some('(') {
783            return Ok(Vec::new());
784        }
785        let names = self.list(Self::name)?;
786        if names.is_empty() {
787            return Err(self.fail("a parameter list names at least one parameter"));
788        }
789        let mut seen = std::collections::BTreeSet::new();
790        if !names.iter().all(|name| seen.insert(name)) {
791            return Err(self.fail("a parameter is named twice"));
792        }
793        Ok(names)
794    }
795
796    /// The `channel(NAME)`, `modes(MODE, ...)` and `action(OPERATION, ...)`
797    /// attributes after a rule expression; other text is trailing text.
798    fn rule_attributes(&mut self) -> Result<RuleAttributes, GrammarImportError> {
799        const ORDER: [&str; 3] = ["channel", "modes", "action"];
800        let mut attributes = RuleAttributes::default();
801        let mut next = 0;
802        self.skip_spaces();
803        while !self.done() {
804            let begin = self.position;
805            while self.peek().is_some_and(|value| value.is_ascii_alphabetic()) {
806                self.position += 1;
807            }
808            let attribute = self.text(begin);
809            let position = ORDER.iter().position(|word| *word == attribute);
810            let Some(position) = position.filter(|_| self.peek() == Some('(')) else {
811                self.position = begin;
812                return Err(self.fail("unexpected text at the end of the line"));
813            };
814            if position < next {
815                return Err(self.fail(format!("rule attribute {attribute} is out of order")));
816            }
817            next = position + 1;
818            match position {
819                0 => {
820                    self.open()?;
821                    attributes.channel = Some(self.name()?);
822                    self.close()?;
823                }
824                1 => attributes.modes = Some(self.list(Self::name)?),
825                _ => attributes.action = Some(self.statements()?),
826            }
827            self.skip_spaces();
828        }
829        Ok(attributes)
830    }
831}