1use super::{parse_error, unsupported_error, GrammarImportError};
2use crate::grammar::{CharClassItem, Grammar, GrammarExpr, GrammarFormat, GrammarRule, RuleKind};
3
4const FORMAT: GrammarFormat = GrammarFormat::Lark;
5
6pub 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}