Skip to main content

meta_language/grammar/import/
lark.rs

1use super::{parse_error, unsupported_error, GrammarImportError};
2use crate::grammar::{CharClassItem, Grammar, GrammarExpr, GrammarFormat, GrammarRule, RuleKind};
3
4const FORMAT: GrammarFormat = GrammarFormat::Lark;
5
6/// Parses Lark `.lark` grammar text into the grammar IR.
7///
8/// The importer is a clean-room recursive-descent parser for Lark's structural
9/// EBNF surface: rules, terminals, unordered alternation, grouping, optional
10/// groups, postfix repetition, counted `~` repetition, string literals, and
11/// regex terminals.
12///
13/// # Errors
14///
15/// Returns [`GrammarImportError`] when the source cannot be parsed as the
16/// supported Lark subset or when an explicitly unsupported directive such as
17/// `%import` is encountered.
18pub fn import_lark(text: &str) -> Result<Grammar, GrammarImportError> {
19    Parser::new(Lexer::new(text).tokenize()?).parse_grammar()
20}
21
22#[derive(Clone, Debug)]
23struct Parser {
24    tokens: Vec<Token>,
25    cursor: usize,
26    pending_comments: Vec<String>,
27    ignored: Vec<GrammarExpr>,
28    first_rule: Option<String>,
29}
30
31impl Parser {
32    const fn new(tokens: Vec<Token>) -> Self {
33        Self {
34            tokens,
35            cursor: 0,
36            pending_comments: Vec::new(),
37            ignored: Vec::new(),
38            first_rule: None,
39        }
40    }
41
42    fn parse_grammar(&mut self) -> Result<Grammar, GrammarImportError> {
43        let mut grammar = Grammar::new().with_source_format(FORMAT);
44        while !self.is_end() {
45            self.collect_comments_and_newlines();
46            if self.is_end() {
47                break;
48            }
49            if self.try_consume(|kind| matches!(kind, TokenKind::Percent)) {
50                self.parse_directive()?;
51            } else {
52                let rule = self.parse_rule()?;
53                if self.first_rule.is_none() {
54                    self.first_rule = Some(rule.name.clone());
55                }
56                grammar.add_rule(rule);
57            }
58        }
59
60        self.add_ignore_rules(&mut grammar);
61
62        if grammar.rule("start").is_some() {
63            grammar.set_start("start");
64        } else if let Some(first_rule) = &self.first_rule {
65            grammar.set_start(first_rule.clone());
66        } else {
67            return Err(parse_error(FORMAT, "Lark grammar does not contain rules"));
68        }
69        Ok(grammar)
70    }
71
72    fn parse_rule(&mut self) -> Result<GrammarRule, GrammarImportError> {
73        let comments = std::mem::take(&mut self.pending_comments);
74        let inline = self.try_consume(|kind| matches!(kind, TokenKind::Question));
75        let name = self.expect_ident("rule name")?;
76        let mut notes = Vec::new();
77        if self.try_consume(|kind| matches!(kind, TokenKind::Dot)) {
78            let priority = self.expect_number("rule priority")?;
79            notes.push(format!("priority {priority}"));
80        }
81        self.expect_colon()?;
82
83        let expr = self.parse_choice()?;
84        self.consume_rule_end();
85
86        let kind = if inline {
87            notes.insert(0, "inline".to_string());
88            RuleKind::Silent
89        } else if name.chars().next().is_some_and(char::is_uppercase) {
90            RuleKind::Token
91        } else {
92            RuleKind::Normal
93        };
94
95        let mut rule = GrammarRule::new(name, expr).with_kind(kind);
96        let doc = rule_doc(comments, notes);
97        if let Some(doc) = doc {
98            rule = rule.with_doc(doc);
99        }
100        Ok(rule)
101    }
102
103    fn parse_directive(&mut self) -> Result<(), GrammarImportError> {
104        let directive = self.expect_ident("directive name")?;
105        match directive.as_str() {
106            "ignore" => {
107                let expr = self.parse_choice()?;
108                self.ignored.push(expr);
109                self.consume_rule_end();
110                Ok(())
111            }
112            "declare" => {
113                self.skip_until_newline();
114                Ok(())
115            }
116            "import" => Err(unsupported_error(FORMAT, "%import")),
117            directive => Err(unsupported_error(FORMAT, format!("%{directive}"))),
118        }
119    }
120
121    fn add_ignore_rules(&mut self, grammar: &mut Grammar) {
122        for (index, expr) in self.ignored.drain(..).enumerate() {
123            let name = if index == 0 {
124                "_ignore".to_string()
125            } else {
126                format!("_ignore_{}", index + 1)
127            };
128            grammar.add_rule(
129                GrammarRule::new(name, expr)
130                    .with_kind(RuleKind::Silent)
131                    .with_doc("%ignore"),
132            );
133        }
134    }
135
136    fn parse_choice(&mut self) -> Result<GrammarExpr, GrammarImportError> {
137        let mut alternatives = Vec::new();
138        push_choice_alternative(&mut alternatives, self.parse_sequence()?);
139        while self.try_consume_pipe() {
140            push_choice_alternative(&mut alternatives, self.parse_sequence()?);
141        }
142        Ok(finish_choice(alternatives))
143    }
144
145    fn parse_sequence(&mut self) -> Result<GrammarExpr, GrammarImportError> {
146        let mut items = Vec::new();
147        while !self.is_sequence_end() {
148            push_sequence_item(&mut items, self.parse_postfix()?);
149        }
150        Ok(finish_sequence(items))
151    }
152
153    fn parse_postfix(&mut self) -> Result<GrammarExpr, GrammarImportError> {
154        let mut expr = self.parse_atom()?;
155        loop {
156            expr = match self.peek_kind() {
157                Some(TokenKind::Question) => {
158                    self.advance();
159                    GrammarExpr::optional(expr)
160                }
161                Some(TokenKind::Star) => {
162                    self.advance();
163                    GrammarExpr::zero_or_more(expr)
164                }
165                Some(TokenKind::Plus) => {
166                    self.advance();
167                    GrammarExpr::one_or_more(expr)
168                }
169                Some(TokenKind::Tilde) => {
170                    self.advance();
171                    self.parse_tilde_repeat(expr)?
172                }
173                _ => return Ok(expr),
174            };
175        }
176    }
177
178    fn parse_tilde_repeat(&mut self, expr: GrammarExpr) -> Result<GrammarExpr, GrammarImportError> {
179        let min = self.expect_number("minimum repeat count")?;
180        let max = if self.try_consume(|kind| matches!(kind, TokenKind::Range)) {
181            Some(self.expect_number("maximum repeat count")?)
182        } else {
183            Some(min)
184        };
185        if let Some(max) = max {
186            if min > max {
187                return Err(self.error("repeat minimum exceeds maximum"));
188            }
189        }
190        Ok(GrammarExpr::repeat(expr, min, max))
191    }
192
193    fn parse_atom(&mut self) -> Result<GrammarExpr, GrammarImportError> {
194        let Some(token) = self.peek().cloned() else {
195            return Err(self.expected("expression element"));
196        };
197
198        match token.kind {
199            TokenKind::Ident(name) => {
200                self.advance();
201                Ok(GrammarExpr::NonTerminal(name))
202            }
203            TokenKind::String(value) => {
204                self.advance();
205                Ok(GrammarExpr::Terminal(value))
206            }
207            TokenKind::Regex(value) => {
208                self.advance();
209                lower_regex(&value, token.offset)
210            }
211            TokenKind::Dot => {
212                self.advance();
213                Ok(GrammarExpr::AnyChar)
214            }
215            TokenKind::LParen => {
216                self.advance();
217                let expr = self.parse_choice()?;
218                self.expect_rparen()?;
219                Ok(expr)
220            }
221            TokenKind::LBracket => {
222                self.advance();
223                let expr = self.parse_choice()?;
224                self.expect_rbracket()?;
225                Ok(GrammarExpr::optional(expr))
226            }
227            _ => Err(self.expected("expression element")),
228        }
229    }
230
231    fn collect_comments_and_newlines(&mut self) {
232        loop {
233            match self.peek_kind() {
234                Some(TokenKind::Comment(comment)) => {
235                    self.pending_comments.push(comment.clone());
236                    self.advance();
237                }
238                Some(TokenKind::Newline) => {
239                    self.advance();
240                }
241                _ => break,
242            }
243        }
244    }
245
246    fn consume_rule_end(&mut self) {
247        if matches!(self.peek_kind(), Some(TokenKind::Comment(_))) {
248            self.advance();
249        }
250        if matches!(self.peek_kind(), Some(TokenKind::Newline)) {
251            self.advance();
252        }
253    }
254
255    fn skip_until_newline(&mut self) {
256        while !self.is_end() && !matches!(self.peek_kind(), Some(TokenKind::Newline)) {
257            self.advance();
258        }
259        self.consume_rule_end();
260    }
261
262    fn expect_ident(&mut self, role: &str) -> Result<String, GrammarImportError> {
263        let Some(token) = self.peek().cloned() else {
264            return Err(self.expected(role));
265        };
266        match token.kind {
267            TokenKind::Ident(value) => {
268                self.advance();
269                Ok(value)
270            }
271            _ => Err(self.expected(role)),
272        }
273    }
274
275    fn expect_number(&mut self, role: &str) -> Result<usize, GrammarImportError> {
276        let Some(token) = self.peek().cloned() else {
277            return Err(self.expected(role));
278        };
279        match token.kind {
280            TokenKind::Number(value) => {
281                self.advance();
282                Ok(value)
283            }
284            _ => Err(self.expected(role)),
285        }
286    }
287
288    fn expect_colon(&mut self) -> Result<(), GrammarImportError> {
289        if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
290            self.advance();
291            Ok(())
292        } else {
293            Err(self.expected("':'"))
294        }
295    }
296
297    fn expect_rparen(&mut self) -> Result<(), GrammarImportError> {
298        if matches!(self.peek_kind(), Some(TokenKind::RParen)) {
299            self.advance();
300            Ok(())
301        } else {
302            Err(self.expected("')'"))
303        }
304    }
305
306    fn expect_rbracket(&mut self) -> Result<(), GrammarImportError> {
307        if matches!(self.peek_kind(), Some(TokenKind::RBracket)) {
308            self.advance();
309            Ok(())
310        } else {
311            Err(self.expected("']'"))
312        }
313    }
314
315    fn try_consume(&mut self, predicate: impl FnOnce(&TokenKind) -> bool) -> bool {
316        if self.peek_kind().is_some_and(predicate) {
317            self.advance();
318            true
319        } else {
320            false
321        }
322    }
323
324    fn try_consume_pipe(&mut self) -> bool {
325        let start = self.cursor;
326        while matches!(
327            self.peek_kind(),
328            Some(TokenKind::Newline | TokenKind::Comment(_))
329        ) {
330            self.advance();
331        }
332        if matches!(self.peek_kind(), Some(TokenKind::Pipe)) {
333            self.advance();
334            true
335        } else {
336            self.cursor = start;
337            false
338        }
339    }
340
341    fn is_sequence_end(&self) -> bool {
342        self.is_end()
343            || matches!(
344                self.peek_kind(),
345                Some(
346                    TokenKind::Newline
347                        | TokenKind::Comment(_)
348                        | TokenKind::Pipe
349                        | TokenKind::RParen
350                        | TokenKind::RBracket
351                )
352            )
353    }
354
355    fn expected(&self, expected: &str) -> GrammarImportError {
356        self.error(format!("expected {expected}"))
357    }
358
359    fn error(&self, message: impl Into<String>) -> GrammarImportError {
360        let offset = self.peek().map_or(0, |token| token.offset);
361        error_at(offset, message)
362    }
363
364    fn is_end(&self) -> bool {
365        self.cursor >= self.tokens.len()
366    }
367
368    fn peek(&self) -> Option<&Token> {
369        self.tokens.get(self.cursor)
370    }
371
372    fn peek_kind(&self) -> Option<&TokenKind> {
373        self.peek().map(|token| &token.kind)
374    }
375
376    fn advance(&mut self) -> &Token {
377        let token = &self.tokens[self.cursor];
378        self.cursor += 1;
379        token
380    }
381}
382
383#[derive(Clone, Debug, PartialEq, Eq)]
384struct Token {
385    kind: TokenKind,
386    offset: usize,
387}
388
389#[derive(Clone, Debug, PartialEq, Eq)]
390enum TokenKind {
391    Ident(String),
392    String(String),
393    Regex(String),
394    Number(usize),
395    Comment(String),
396    Percent,
397    Colon,
398    Pipe,
399    LParen,
400    RParen,
401    LBracket,
402    RBracket,
403    Question,
404    Star,
405    Plus,
406    Tilde,
407    Range,
408    Dot,
409    Newline,
410}
411
412#[derive(Clone, Debug)]
413struct Lexer<'text> {
414    text: &'text str,
415    cursor: usize,
416}
417
418impl<'text> Lexer<'text> {
419    const fn new(text: &'text str) -> Self {
420        Self { text, cursor: 0 }
421    }
422
423    fn tokenize(mut self) -> Result<Vec<Token>, GrammarImportError> {
424        let mut tokens = Vec::new();
425        while !self.is_end() {
426            self.skip_horizontal_whitespace();
427            if self.is_end() {
428                break;
429            }
430
431            let offset = self.cursor;
432            let kind = self.next_token_kind()?;
433            tokens.push(Token { kind, offset });
434        }
435        Ok(tokens)
436    }
437
438    fn next_token_kind(&mut self) -> Result<TokenKind, GrammarImportError> {
439        if self.starts_with("//") {
440            return Ok(TokenKind::Comment(self.line_comment()));
441        }
442        if self.starts_with("..") {
443            self.cursor += 2;
444            return Ok(TokenKind::Range);
445        }
446
447        let Some(character) = self.peek_char() else {
448            return Err(error_at(self.cursor, "unexpected end of input"));
449        };
450
451        match character {
452            '\n' => {
453                self.advance_char();
454                Ok(TokenKind::Newline)
455            }
456            '\r' => {
457                self.advance_char();
458                if self.peek_char() == Some('\n') {
459                    self.advance_char();
460                }
461                Ok(TokenKind::Newline)
462            }
463            '"' | '\'' => self.string_literal(character).map(TokenKind::String),
464            '/' => self.regex_literal().map(TokenKind::Regex),
465            '%' => {
466                self.advance_char();
467                Ok(TokenKind::Percent)
468            }
469            ':' => {
470                self.advance_char();
471                Ok(TokenKind::Colon)
472            }
473            '|' => {
474                self.advance_char();
475                Ok(TokenKind::Pipe)
476            }
477            '(' => {
478                self.advance_char();
479                Ok(TokenKind::LParen)
480            }
481            ')' => {
482                self.advance_char();
483                Ok(TokenKind::RParen)
484            }
485            '[' => {
486                self.advance_char();
487                Ok(TokenKind::LBracket)
488            }
489            ']' => {
490                self.advance_char();
491                Ok(TokenKind::RBracket)
492            }
493            '?' => {
494                self.advance_char();
495                Ok(TokenKind::Question)
496            }
497            '*' => {
498                self.advance_char();
499                Ok(TokenKind::Star)
500            }
501            '+' => {
502                self.advance_char();
503                Ok(TokenKind::Plus)
504            }
505            '~' => {
506                self.advance_char();
507                Ok(TokenKind::Tilde)
508            }
509            '.' => {
510                self.advance_char();
511                Ok(TokenKind::Dot)
512            }
513            character if character.is_ascii_digit() => self.number().map(TokenKind::Number),
514            character if is_ident_start(character) => Ok(TokenKind::Ident(self.identifier())),
515            character => Err(error_at(
516                self.cursor,
517                format!("unexpected character {character:?}"),
518            )),
519        }
520    }
521
522    fn line_comment(&mut self) -> String {
523        let start = self.cursor;
524        while let Some(character) = self.peek_char() {
525            if character == '\n' || character == '\r' {
526                break;
527            }
528            self.advance_char();
529        }
530        self.text[start..self.cursor].trim().to_string()
531    }
532
533    fn string_literal(&mut self, quote: char) -> Result<String, GrammarImportError> {
534        let start = self.cursor;
535        self.advance_char();
536        let mut value = String::new();
537        while let Some(character) = self.advance_char() {
538            match character {
539                character if character == quote => return Ok(value),
540                '\\' => value.push(self.escape_sequence(start)?),
541                character => value.push(character),
542            }
543        }
544        Err(error_at(start, "unterminated string literal"))
545    }
546
547    fn regex_literal(&mut self) -> Result<String, GrammarImportError> {
548        let start = self.cursor;
549        self.advance_char();
550        let content_start = self.cursor;
551        let mut escaped = false;
552        while let Some(character) = self.advance_char() {
553            if escaped {
554                escaped = false;
555            } else if character == '\\' {
556                escaped = true;
557            } else if character == '/' {
558                let content_end = self.cursor - character.len_utf8();
559                return Ok(self.text[content_start..content_end].to_string());
560            } else if character == '\n' || character == '\r' {
561                return Err(error_at(start, "unterminated regex literal"));
562            }
563        }
564        Err(error_at(start, "unterminated regex literal"))
565    }
566
567    fn escape_sequence(&mut self, start: usize) -> Result<char, GrammarImportError> {
568        let Some(character) = self.advance_char() else {
569            return Err(error_at(start, "unterminated escape sequence"));
570        };
571        match character {
572            'n' => Ok('\n'),
573            'r' => Ok('\r'),
574            't' => Ok('\t'),
575            'b' => Ok('\u{08}'),
576            'f' => Ok('\u{0c}'),
577            '\\' | '"' | '\'' | '/' | '[' | ']' | '-' | '^' => Ok(character),
578            'x' => self.hex_escape(start, 2),
579            'u' => self.hex_escape(start, 4),
580            'U' => self.hex_escape(start, 8),
581            character => Ok(character),
582        }
583    }
584
585    fn hex_escape(&mut self, start: usize, digits: usize) -> Result<char, GrammarImportError> {
586        let mut value = 0_u32;
587        for _ in 0..digits {
588            let Some(character) = self.advance_char() else {
589                return Err(error_at(start, "unterminated hexadecimal escape"));
590            };
591            let Some(digit) = character.to_digit(16) else {
592                return Err(error_at(
593                    self.cursor - character.len_utf8(),
594                    "hexadecimal escape requires hexadecimal digits",
595                ));
596            };
597            value = (value << 4) | digit;
598        }
599        char::from_u32(value).ok_or_else(|| error_at(start, "invalid hexadecimal escape"))
600    }
601
602    fn number(&mut self) -> Result<usize, GrammarImportError> {
603        let start = self.cursor;
604        let mut value = 0_usize;
605        while let Some(character) = self.peek_char() {
606            let Some(digit) = character.to_digit(10) else {
607                break;
608            };
609            self.advance_char();
610            value = value
611                .checked_mul(10)
612                .and_then(|current| current.checked_add(digit as usize))
613                .ok_or_else(|| error_at(start, "number exceeds usize"))?;
614        }
615        Ok(value)
616    }
617
618    fn identifier(&mut self) -> String {
619        let start = self.cursor;
620        self.advance_char();
621        while self.peek_char().is_some_and(is_ident_continue) {
622            self.advance_char();
623        }
624        self.text[start..self.cursor].to_string()
625    }
626
627    fn skip_horizontal_whitespace(&mut self) {
628        while self.peek_char().is_some_and(|character| {
629            character != '\n' && character != '\r' && character.is_whitespace()
630        }) {
631            self.advance_char();
632        }
633    }
634
635    fn starts_with(&self, prefix: &str) -> bool {
636        self.text[self.cursor..].starts_with(prefix)
637    }
638
639    const fn is_end(&self) -> bool {
640        self.cursor >= self.text.len()
641    }
642
643    fn peek_char(&self) -> Option<char> {
644        self.text[self.cursor..].chars().next()
645    }
646
647    fn advance_char(&mut self) -> Option<char> {
648        let character = self.peek_char()?;
649        self.cursor += character.len_utf8();
650        Some(character)
651    }
652}
653
654fn lower_regex(value: &str, offset: usize) -> Result<GrammarExpr, GrammarImportError> {
655    trivial_regex_char_class(value).map_or_else(
656        || {
657            Ok(GrammarExpr::capture(
658                "regex",
659                GrammarExpr::Terminal(value.to_string()),
660            ))
661        },
662        |content| lower_char_set(content, offset),
663    )
664}
665
666fn trivial_regex_char_class(value: &str) -> Option<&str> {
667    if (!value.starts_with('[') || !value.ends_with(']'))
668        || has_unescaped_inner_closing_bracket(value)
669    {
670        None
671    } else {
672        Some(&value[1..value.len() - 1])
673    }
674}
675
676fn has_unescaped_inner_closing_bracket(value: &str) -> bool {
677    let mut escaped = false;
678    for (index, character) in value.char_indices().skip(1) {
679        if index == value.len() - 1 {
680            return false;
681        }
682        if escaped {
683            escaped = false;
684        } else if character == '\\' {
685            escaped = true;
686        } else if character == ']' {
687            return true;
688        }
689    }
690    false
691}
692
693fn lower_char_set(content: &str, offset: usize) -> Result<GrammarExpr, GrammarImportError> {
694    let mut scanner = ClassScanner::new(content, offset);
695    let negated = scanner.try_consume('^');
696    let mut items = Vec::new();
697    while !scanner.is_end() {
698        let start = scanner.read_char()?;
699        if scanner.try_consume_range_separator() {
700            let end = scanner.read_char()?;
701            if start > end {
702                return Err(error_at(offset, "character class range start exceeds end"));
703            }
704            items.push(CharClassItem::Range(start, end));
705        } else {
706            items.push(CharClassItem::Char(start));
707        }
708    }
709    if items.is_empty() {
710        return Err(error_at(offset, "character class must not be empty"));
711    }
712    Ok(GrammarExpr::CharClass { negated, items })
713}
714
715#[derive(Clone, Debug)]
716struct ClassScanner<'text> {
717    text: &'text str,
718    cursor: usize,
719    offset: usize,
720}
721
722impl<'text> ClassScanner<'text> {
723    const fn new(text: &'text str, offset: usize) -> Self {
724        Self {
725            text,
726            cursor: 0,
727            offset,
728        }
729    }
730
731    fn read_char(&mut self) -> Result<char, GrammarImportError> {
732        let Some(character) = self.advance_char() else {
733            return Err(error_at(self.offset, "unexpected end of character class"));
734        };
735        if character == '\\' {
736            self.read_escape()
737        } else {
738            Ok(character)
739        }
740    }
741
742    fn read_escape(&mut self) -> Result<char, GrammarImportError> {
743        let Some(character) = self.advance_char() else {
744            return Err(error_at(self.offset, "unterminated character class escape"));
745        };
746        match character {
747            'n' => Ok('\n'),
748            'r' => Ok('\r'),
749            't' => Ok('\t'),
750            'b' => Ok('\u{08}'),
751            'f' => Ok('\u{0c}'),
752            '\\' | '"' | '\'' | '/' | '[' | ']' | '-' | '^' => Ok(character),
753            'x' => self.hex_escape(2),
754            'u' => self.hex_escape(4),
755            'U' => self.hex_escape(8),
756            character => Ok(character),
757        }
758    }
759
760    fn hex_escape(&mut self, digits: usize) -> Result<char, GrammarImportError> {
761        let mut value = 0_u32;
762        for _ in 0..digits {
763            let Some(character) = self.advance_char() else {
764                return Err(error_at(self.offset, "unterminated hexadecimal escape"));
765            };
766            let Some(digit) = character.to_digit(16) else {
767                return Err(error_at(
768                    self.offset + self.cursor.saturating_sub(character.len_utf8()),
769                    "hexadecimal escape requires hexadecimal digits",
770                ));
771            };
772            value = (value << 4) | digit;
773        }
774        char::from_u32(value).ok_or_else(|| error_at(self.offset, "invalid hexadecimal escape"))
775    }
776
777    fn try_consume(&mut self, expected: char) -> bool {
778        if self.peek_char() == Some(expected) {
779            self.advance_char();
780            true
781        } else {
782            false
783        }
784    }
785
786    fn try_consume_range_separator(&mut self) -> bool {
787        if self.peek_char() == Some('-') && self.has_char_after_current() {
788            self.advance_char();
789            true
790        } else {
791            false
792        }
793    }
794
795    fn has_char_after_current(&self) -> bool {
796        let mut chars = self.text[self.cursor..].chars();
797        chars.next();
798        chars.next().is_some()
799    }
800
801    const fn is_end(&self) -> bool {
802        self.cursor >= self.text.len()
803    }
804
805    fn peek_char(&self) -> Option<char> {
806        self.text[self.cursor..].chars().next()
807    }
808
809    fn advance_char(&mut self) -> Option<char> {
810        let character = self.peek_char()?;
811        self.cursor += character.len_utf8();
812        Some(character)
813    }
814}
815
816fn finish_sequence(items: Vec<GrammarExpr>) -> GrammarExpr {
817    match items.len() {
818        0 => GrammarExpr::Empty,
819        1 => items.into_iter().next().expect("one sequence item exists"),
820        _ => GrammarExpr::Sequence(items),
821    }
822}
823
824fn push_sequence_item(items: &mut Vec<GrammarExpr>, item: GrammarExpr) {
825    match item {
826        GrammarExpr::Empty => {}
827        GrammarExpr::Sequence(nested) => {
828            for item in nested {
829                push_sequence_item(items, item);
830            }
831        }
832        item => items.push(item),
833    }
834}
835
836fn finish_choice(alternatives: Vec<GrammarExpr>) -> GrammarExpr {
837    if alternatives.iter().all(|expr| expr == &GrammarExpr::Empty) {
838        return GrammarExpr::Empty;
839    }
840    match alternatives.len() {
841        0 => GrammarExpr::Empty,
842        1 => alternatives
843            .into_iter()
844            .next()
845            .expect("one alternative exists"),
846        _ => GrammarExpr::Choice {
847            ordered: false,
848            alternatives,
849        },
850    }
851}
852
853fn push_choice_alternative(alternatives: &mut Vec<GrammarExpr>, alternative: GrammarExpr) {
854    match alternative {
855        GrammarExpr::Choice {
856            ordered: false,
857            alternatives: nested,
858        } => alternatives.extend(nested),
859        alternative => alternatives.push(alternative),
860    }
861}
862
863fn rule_doc(comments: Vec<String>, notes: Vec<String>) -> Option<String> {
864    let mut parts = Vec::new();
865    parts.extend(comments.into_iter().filter(|comment| !comment.is_empty()));
866    parts.extend(notes);
867    if parts.is_empty() {
868        None
869    } else {
870        Some(parts.join("; "))
871    }
872}
873
874fn error_at(offset: usize, message: impl Into<String>) -> GrammarImportError {
875    parse_error(FORMAT, format!("{} at byte {offset}", message.into()))
876}
877
878const fn is_ident_start(character: char) -> bool {
879    character == '_' || character.is_ascii_alphabetic()
880}
881
882const fn is_ident_continue(character: char) -> bool {
883    character == '_' || character.is_ascii_alphanumeric()
884}