1mod char_set;
2mod commands;
3mod lexer;
4
5use char_set::{has_case, lower_char_set, negate_expr, with_case_variants};
6use commands::{apply_retypes, command_channel, command_type};
7use lexer::{Lexer, Token, TokenKind};
8
9use super::{GrammarImportError, parse_error, unsupported_error};
10use crate::grammar::feature::{FeatureExpr, FeatureForm, FieldValue, class_expression};
11use crate::grammar::{
12 Grammar, GrammarExpr, GrammarFormat, GrammarRule, RuleAttributes, RuleKind, UnicodeClassItem,
13};
14
15const FORMAT: GrammarFormat = GrammarFormat::Antlr;
16
17pub fn import_antlr(text: &str) -> Result<Grammar, GrammarImportError> {
33 Parser::new(Lexer::new(text).tokenize()?).parse_grammar()
34}
35
36#[derive(Clone, Debug)]
37struct Parser {
38 tokens: Vec<Token>,
39 cursor: usize,
40 pending_comments: Vec<String>,
41 case_insensitive: bool,
44 rule_case_insensitive: bool,
45}
46
47impl Parser {
48 const fn new(tokens: Vec<Token>) -> Self {
49 Self {
50 tokens,
51 cursor: 0,
52 pending_comments: Vec::new(),
53 case_insensitive: false,
54 rule_case_insensitive: false,
55 }
56 }
57
58 fn parse_grammar(&mut self) -> Result<Grammar, GrammarImportError> {
59 let mut grammar = Grammar::new().with_source_format(FORMAT);
60 let mut rules = Vec::new();
61 while !self.is_end() {
62 self.collect_comments();
63 if self.is_end() {
64 break;
65 }
66 if self.parse_header()? || self.parse_skipped_directive()? {
67 continue;
68 }
69 rules.push(self.parse_rule()?);
70 }
71 for rule in apply_retypes(rules) {
72 grammar.add_rule(rule);
73 }
74
75 let Some(start) = grammar
76 .rules()
77 .iter()
78 .find(|rule| rule.kind() == RuleKind::Normal)
79 .or_else(|| grammar.rules().first())
80 .map(|rule| rule.name.clone())
81 else {
82 return Err(parse_error(FORMAT, "ANTLR grammar does not contain rules"));
83 };
84 grammar.set_start(start);
85 Ok(grammar)
86 }
87
88 fn parse_header(&mut self) -> Result<bool, GrammarImportError> {
89 let matched = if self.check_keyword("grammar") {
90 self.advance();
91 true
92 } else if (self.check_keyword("lexer") || self.check_keyword("parser"))
93 && self.check_next_keyword("grammar")
94 {
95 self.advance();
96 self.advance();
97 true
98 } else {
99 false
100 };
101
102 if !matched {
103 return Ok(false);
104 }
105 self.expect_ident("grammar name")?;
106 self.expect_semicolon()?;
107 self.pending_comments.clear();
108 self.case_insensitive = false;
109 Ok(true)
110 }
111
112 fn parse_skipped_directive(&mut self) -> Result<bool, GrammarImportError> {
113 if self.check_any_keyword(&["options", "tokens", "channels"]) {
114 let is_options = self.check_keyword("options");
115 self.advance();
116 if let Some(TokenKind::Action(options)) = self.peek_kind() {
117 if is_options {
118 self.case_insensitive = parse_options(options)
119 .iter()
120 .rev()
121 .find(|(key, _)| key == "caseInsensitive")
122 .is_some_and(|(_, value)| value == "true");
123 }
124 self.advance();
125 self.try_consume_semicolon();
126 self.pending_comments.clear();
127 return Ok(true);
128 }
129 self.skip_until_semicolon()?;
130 self.pending_comments.clear();
131 return Ok(true);
132 }
133
134 if self.check_any_keyword(&["import", "mode"]) {
135 self.advance();
136 self.skip_until_semicolon()?;
137 self.pending_comments.clear();
138 return Ok(true);
139 }
140
141 Ok(false)
142 }
143
144 fn parse_rule(&mut self) -> Result<(GrammarRule, Option<String>), GrammarImportError> {
146 let comments = std::mem::take(&mut self.pending_comments);
147 let fragment = self.try_consume_keyword("fragment");
148 let name = self.expect_ident("rule name")?;
149 self.collect_comments();
151 let mut comments = comments;
152 comments.append(&mut self.pending_comments);
153 let mut notes = Vec::new();
154 self.parse_rule_prelude(&mut notes)?;
155 self.expect_colon()?;
156
157 let alternatives = self.parse_alternatives(&mut notes)?;
158 let expr = precedence_climbing(&name, alternatives);
159 let command = if self.try_consume_arrow() {
160 Some(self.parse_lexer_command()?)
161 } else {
162 None
163 };
164 self.expect_semicolon()?;
165
166 let kind = if fragment {
167 RuleKind::Silent
168 } else if name.chars().next().is_some_and(char::is_uppercase) {
169 RuleKind::Token
170 } else {
171 RuleKind::Normal
172 };
173
174 let channel = command.as_deref().and_then(command_channel);
175 let retype = if channel.is_none() {
176 command.as_deref().and_then(command_type)
177 } else {
178 None
179 };
180 let mut rule = GrammarRule::new(name, expr).with_kind(kind);
181 if channel.is_some() {
182 rule = rule.with_attributes(RuleAttributes {
183 channel,
184 ..RuleAttributes::default()
185 });
186 }
187 if let Some(doc) = rule_doc(comments, notes, command) {
188 rule = rule.with_doc(doc);
189 }
190 Ok((rule, retype))
191 }
192
193 fn parse_rule_prelude(&mut self, notes: &mut Vec<String>) -> Result<(), GrammarImportError> {
197 self.rule_case_insensitive = self.case_insensitive;
198 if matches!(self.peek_kind(), Some(TokenKind::CharSet(_))) {
199 return Err(unsupported_error(FORMAT, "rule arguments"));
200 }
201 while !self.check_colon() {
202 if (self.check_keyword("returns") || self.check_keyword("locals"))
203 && let Some(TokenKind::CharSet(text)) = self.peek_next_kind()
204 {
205 let note = format!(
206 "dropped {} [{text}]",
207 self.peek().map(Token::text).unwrap_or_default()
208 );
209 self.advance();
210 self.advance();
211 notes.push(note);
212 } else if self.check_keyword("throws") {
213 self.advance();
214 let mut names = vec![self.expect_ident("exception name")?];
215 while matches!(self.peek_kind(), Some(TokenKind::Comma)) {
216 self.advance();
217 names.push(self.expect_ident("exception name")?);
218 }
219 notes.push(format!("dropped throws {}", names.join(", ")));
220 } else if self.check_keyword("options")
221 && let Some(TokenKind::Action(text)) = self.peek_next_kind()
222 {
223 let options = parse_options(text);
224 self.advance();
225 self.advance();
226 for (key, value) in options {
227 if key == "caseInsensitive" {
228 self.rule_case_insensitive = value == "true";
229 } else {
230 notes.push(format!("rule option {key}={value}"));
231 }
232 }
233 } else {
234 return Err(self.expected("':' before rule body"));
235 }
236 self.skip_inline_comments();
237 }
238 Ok(())
239 }
240
241 fn parse_choice(&mut self, notes: &mut Vec<String>) -> Result<GrammarExpr, GrammarImportError> {
242 let mut alternatives = Vec::new();
243 for (expr, _) in self.parse_alternatives(notes)? {
244 push_choice_alternative(&mut alternatives, expr);
245 }
246 Ok(finish_choice(alternatives))
247 }
248
249 fn parse_alternatives(
250 &mut self,
251 notes: &mut Vec<String>,
252 ) -> Result<Vec<(GrammarExpr, &'static str)>, GrammarImportError> {
253 let mut alternatives = vec![self.parse_alternative(notes)?];
254 while self.try_consume_pipe() {
255 alternatives.push(self.parse_alternative(notes)?);
256 }
257 Ok(alternatives)
258 }
259
260 fn parse_alternative(
265 &mut self,
266 notes: &mut Vec<String>,
267 ) -> Result<(GrammarExpr, &'static str), GrammarImportError> {
268 self.skip_inline_comments();
269 let options = if matches!(self.peek_kind(), Some(TokenKind::LAngle)) {
270 self.advance();
271 self.parse_element_options()?
272 } else {
273 Vec::new()
274 };
275 let mut assoc = None;
276 for (key, value) in options {
277 if key == "assoc" {
278 assoc = Some(value);
279 } else {
280 notes.push(format!("alternative option {key}={value}"));
281 }
282 }
283 let sequence = self.parse_sequence(notes)?;
284 if matches!(self.peek_kind(), Some(TokenKind::Hash)) {
285 self.advance();
286 let label = self.expect_ident("alternative label")?;
287 notes.push(format!("alternative {label}"));
288 self.skip_inline_comments();
289 }
290 let associativity = match assoc.as_deref() {
291 None | Some("left") => "left",
292 Some("right") => "right",
293 Some(other) => {
294 return Err(unsupported_error(FORMAT, format!("associativity {other}")));
295 }
296 };
297 Ok((sequence, associativity))
298 }
299
300 fn parse_element_options(&mut self) -> Result<Vec<(String, String)>, GrammarImportError> {
301 let mut options: Vec<(String, String)> = Vec::new();
302 loop {
303 let key = self.expect_ident("element option")?;
304 let mut value = "true".to_owned();
305 if matches!(self.peek_kind(), Some(TokenKind::Equal)) {
306 self.advance();
307 value = match self.peek_kind() {
308 Some(TokenKind::Ident(text) | TokenKind::String(text)) => text.clone(),
309 _ => return Err(self.expected("element option value")),
310 };
311 self.advance();
312 }
313 options.retain(|(existing, _)| *existing != key);
314 options.push((key, value));
315 if matches!(self.peek_kind(), Some(TokenKind::Comma)) {
316 self.advance();
317 } else {
318 break;
319 }
320 }
321 if !matches!(self.peek_kind(), Some(TokenKind::RAngle)) {
322 return Err(self.expected("'>'"));
323 }
324 self.advance();
325 Ok(options)
326 }
327
328 fn parse_sequence(
329 &mut self,
330 notes: &mut Vec<String>,
331 ) -> Result<GrammarExpr, GrammarImportError> {
332 let mut items = Vec::new();
333 loop {
334 self.skip_inline_comments();
335 if self.is_sequence_end() {
336 break;
337 }
338 push_sequence_item(&mut items, self.parse_element(notes)?);
339 }
340 Ok(finish_sequence(items))
341 }
342
343 fn parse_element(
344 &mut self,
345 notes: &mut Vec<String>,
346 ) -> Result<GrammarExpr, GrammarImportError> {
347 self.skip_inline_comments();
348 if matches!(self.peek_kind(), Some(TokenKind::Action(_))) {
349 return Ok(self.parse_action(notes));
350 }
351
352 if let Some(label) = self.label_ahead() {
353 self.advance();
354 self.advance();
355 let expr = self.parse_prefixed(notes)?;
356 return Ok(GrammarExpr::capture(label, expr));
357 }
358
359 self.parse_prefixed(notes)
360 }
361
362 fn parse_prefixed(
363 &mut self,
364 notes: &mut Vec<String>,
365 ) -> Result<GrammarExpr, GrammarImportError> {
366 self.skip_inline_comments();
367 let expr = if self.try_consume_tilde() {
368 negate_expr(self.parse_atom(notes)?)
369 } else {
370 self.parse_atom(notes)?
371 };
372 self.parse_suffixes(expr)
373 }
374
375 fn parse_atom(&mut self, notes: &mut Vec<String>) -> Result<GrammarExpr, GrammarImportError> {
376 self.skip_inline_comments();
377 let Some(token) = self.peek().cloned() else {
378 return Err(self.expected("expression element"));
379 };
380
381 match token.kind {
382 TokenKind::Ident(name) => {
383 self.advance();
384 if name == "EOF" {
386 return Ok(GrammarExpr::Not(Box::new(GrammarExpr::AnyChar)));
387 }
388 Ok(GrammarExpr::NonTerminal(name))
389 }
390 TokenKind::String(value) => {
391 self.advance();
392 if self.try_consume_range() {
393 let end = self.expect_string("range end")?;
394 let start = single_char(&value, "range start", token.offset)?;
395 let end = single_char(&end, "range end", token.offset)?;
396 if start > end {
397 return Err(error_at(token.offset, "literal range start exceeds end"));
398 }
399 if self.rule_case_insensitive {
400 let items = with_case_variants(&[UnicodeClassItem::Range(start, end)]);
401 if items.len() > 1 {
402 return Ok(class_expression(false, items));
403 }
404 }
405 Ok(GrammarExpr::CharRange(start, end))
406 } else if self.rule_case_insensitive && has_case(&value) {
407 Ok(GrammarExpr::TerminalInsensitive(value))
408 } else {
409 Ok(GrammarExpr::Terminal(value))
410 }
411 }
412 TokenKind::CharSet(content) => {
413 self.advance();
414 lower_char_set(&content, token.offset, self.rule_case_insensitive)
415 }
416 TokenKind::Dot => {
417 self.advance();
418 Ok(GrammarExpr::AnyChar)
419 }
420 TokenKind::LParen => {
421 self.advance();
422 let expr = self.parse_choice(notes)?;
423 self.expect_rparen()?;
424 Ok(expr)
425 }
426 TokenKind::Action(_) => Ok(self.parse_action(notes)),
427 _ => Err(self.expected("expression element")),
428 }
429 }
430
431 fn parse_action(&mut self, notes: &mut Vec<String>) -> GrammarExpr {
432 self.advance();
433 if self.try_consume_question() {
434 notes.push("dropped predicate".to_string());
435 } else {
436 notes.push("dropped action".to_string());
437 }
438 GrammarExpr::Empty
439 }
440
441 fn parse_suffixes(&mut self, mut expr: GrammarExpr) -> Result<GrammarExpr, GrammarImportError> {
442 loop {
443 let Some(suffix) = self.consume_suffix() else {
444 return Ok(expr);
445 };
446 expr = match suffix {
447 Suffix::Optional => GrammarExpr::optional(expr),
448 Suffix::ZeroOrMore => GrammarExpr::zero_or_more(expr),
449 Suffix::OneOrMore => GrammarExpr::one_or_more(expr),
450 };
451 if self.try_consume_question() {
452 expr = GrammarExpr::capture("non_greedy", expr);
453 }
454 }
455 }
456
457 fn parse_lexer_command(&mut self) -> Result<String, GrammarImportError> {
458 let mut tokens = Vec::new();
459 while !self.is_end() && !self.check_semicolon() {
460 if matches!(self.peek_kind(), Some(TokenKind::Comment(_))) {
461 self.advance();
462 } else {
463 tokens.push(self.advance().clone());
464 }
465 }
466 if tokens.is_empty() {
467 return Err(self.expected("lexer command"));
468 }
469 Ok(format_command(&tokens))
470 }
471
472 fn skip_until_semicolon(&mut self) -> Result<(), GrammarImportError> {
473 while !self.is_end() {
474 if self.try_consume_semicolon() {
475 return Ok(());
476 }
477 self.advance();
478 }
479 Err(self.expected("';' after directive"))
480 }
481
482 fn collect_comments(&mut self) {
483 while let Some(TokenKind::Comment(comment)) = self.peek_kind() {
484 self.pending_comments.push(comment.clone());
485 self.advance();
486 }
487 }
488
489 fn skip_inline_comments(&mut self) {
490 while matches!(self.peek_kind(), Some(TokenKind::Comment(_))) {
491 self.advance();
492 }
493 }
494
495 fn label_ahead(&self) -> Option<String> {
496 let label = self.peek()?.ident()?;
497 if matches!(
498 self.tokens.get(self.cursor + 1).map(|token| &token.kind),
499 Some(TokenKind::Equal | TokenKind::PlusEqual)
500 ) {
501 Some(label.to_string())
502 } else {
503 None
504 }
505 }
506
507 fn consume_suffix(&mut self) -> Option<Suffix> {
508 match self.peek_kind() {
509 Some(TokenKind::Question) => {
510 self.advance();
511 Some(Suffix::Optional)
512 }
513 Some(TokenKind::Star) => {
514 self.advance();
515 Some(Suffix::ZeroOrMore)
516 }
517 Some(TokenKind::Plus) => {
518 self.advance();
519 Some(Suffix::OneOrMore)
520 }
521 _ => None,
522 }
523 }
524
525 fn is_sequence_end(&self) -> bool {
526 self.is_end()
527 || matches!(
528 self.peek_kind(),
529 Some(
530 TokenKind::Pipe
531 | TokenKind::Semicolon
532 | TokenKind::Arrow
533 | TokenKind::RParen
534 | TokenKind::Hash
535 )
536 )
537 }
538
539 fn expect_ident(&mut self, role: &str) -> Result<String, GrammarImportError> {
540 let Some(token) = self.peek().cloned() else {
541 return Err(self.expected(role));
542 };
543 match token.kind {
544 TokenKind::Ident(value) => {
545 self.advance();
546 Ok(value)
547 }
548 _ => Err(self.expected(role)),
549 }
550 }
551
552 fn expect_string(&mut self, role: &str) -> Result<String, GrammarImportError> {
553 let Some(token) = self.peek().cloned() else {
554 return Err(self.expected(role));
555 };
556 match token.kind {
557 TokenKind::String(value) => {
558 self.advance();
559 Ok(value)
560 }
561 _ => Err(self.expected(role)),
562 }
563 }
564
565 fn expect_colon(&mut self) -> Result<(), GrammarImportError> {
566 if self.check_colon() {
567 self.advance();
568 Ok(())
569 } else {
570 Err(self.expected("':'"))
571 }
572 }
573
574 fn expect_semicolon(&mut self) -> Result<(), GrammarImportError> {
575 if self.check_semicolon() {
576 self.advance();
577 Ok(())
578 } else {
579 Err(self.expected("';'"))
580 }
581 }
582
583 fn expect_rparen(&mut self) -> Result<(), GrammarImportError> {
584 if matches!(self.peek_kind(), Some(TokenKind::RParen)) {
585 self.advance();
586 Ok(())
587 } else {
588 Err(self.expected("')'"))
589 }
590 }
591
592 fn expected(&self, expected: &str) -> GrammarImportError {
593 let offset = self.peek().map_or(0, |token| token.offset);
594 error_at(offset, format!("expected {expected}"))
595 }
596
597 fn try_consume_keyword(&mut self, keyword: &str) -> bool {
598 if self.check_keyword(keyword) {
599 self.advance();
600 true
601 } else {
602 false
603 }
604 }
605
606 fn try_consume_arrow(&mut self) -> bool {
607 if matches!(self.peek_kind(), Some(TokenKind::Arrow)) {
608 self.advance();
609 true
610 } else {
611 false
612 }
613 }
614
615 fn try_consume_pipe(&mut self) -> bool {
616 if matches!(self.peek_kind(), Some(TokenKind::Pipe)) {
617 self.advance();
618 true
619 } else {
620 false
621 }
622 }
623
624 fn try_consume_question(&mut self) -> bool {
625 if matches!(self.peek_kind(), Some(TokenKind::Question)) {
626 self.advance();
627 true
628 } else {
629 false
630 }
631 }
632
633 fn try_consume_tilde(&mut self) -> bool {
634 if matches!(self.peek_kind(), Some(TokenKind::Tilde)) {
635 self.advance();
636 true
637 } else {
638 false
639 }
640 }
641
642 fn try_consume_range(&mut self) -> bool {
643 if matches!(self.peek_kind(), Some(TokenKind::Range)) {
644 self.advance();
645 true
646 } else {
647 false
648 }
649 }
650
651 fn try_consume_semicolon(&mut self) -> bool {
652 if self.check_semicolon() {
653 self.advance();
654 true
655 } else {
656 false
657 }
658 }
659
660 fn check_keyword(&self, keyword: &str) -> bool {
661 self.peek().is_some_and(|token| token.is_keyword(keyword))
662 }
663
664 fn check_next_keyword(&self, keyword: &str) -> bool {
665 self.tokens
666 .get(self.cursor + 1)
667 .is_some_and(|token| token.is_keyword(keyword))
668 }
669
670 fn check_any_keyword(&self, keywords: &[&str]) -> bool {
671 self.peek()
672 .and_then(Token::ident)
673 .is_some_and(|value| keywords.contains(&value))
674 }
675
676 fn check_colon(&self) -> bool {
677 matches!(self.peek_kind(), Some(TokenKind::Colon))
678 }
679
680 fn check_semicolon(&self) -> bool {
681 matches!(self.peek_kind(), Some(TokenKind::Semicolon))
682 }
683
684 const fn is_end(&self) -> bool {
685 self.cursor >= self.tokens.len()
686 }
687
688 fn peek(&self) -> Option<&Token> {
689 self.tokens.get(self.cursor)
690 }
691
692 fn peek_kind(&self) -> Option<&TokenKind> {
693 self.peek().map(|token| &token.kind)
694 }
695
696 fn peek_next_kind(&self) -> Option<&TokenKind> {
697 self.tokens.get(self.cursor + 1).map(|token| &token.kind)
698 }
699
700 fn advance(&mut self) -> &Token {
701 let token = &self.tokens[self.cursor];
702 self.cursor += 1;
703 token
704 }
705}
706
707#[derive(Clone, Copy, Debug, PartialEq, Eq)]
708enum Suffix {
709 Optional,
710 ZeroOrMore,
711 OneOrMore,
712}
713
714fn parse_options(text: &str) -> Vec<(String, String)> {
716 let pairs = text.rsplit_once(';').map_or("", |(pairs, _)| pairs);
718 pairs
719 .split(';')
720 .filter_map(|part| {
721 let (key, value) = part.split_once('=')?;
722 let key = key.trim_end();
723 let start = key
724 .rfind(|character: char| !(character.is_ascii_alphanumeric() || character == '_'))
725 .map_or(0, |index| index + 1);
726 let key = &key[start..];
727 is_identifier(key).then(|| (key.to_owned(), value.trim().to_owned()))
728 })
729 .collect()
730}
731
732fn is_identifier(text: &str) -> bool {
733 let mut chars = text.chars();
734 chars
735 .next()
736 .is_some_and(|first| first.is_ascii_alphabetic() || first == '_')
737 && chars.all(|next| next.is_ascii_alphanumeric() || next == '_')
738}
739
740fn is_channel_number(text: &str) -> bool {
742 !text.starts_with('0') && !text.is_empty() && text.chars().all(|digit| digit.is_ascii_digit())
743}
744
745fn precedence_climbing(name: &str, alternatives: Vec<(GrammarExpr, &'static str)>) -> GrammarExpr {
749 let left_recursive = alternatives
750 .iter()
751 .any(|(expr, _)| edge_ref(expr, Edge::Left) == Some(name));
752 let count = alternatives.len();
753 let mut choice = Vec::new();
754 for (index, (expr, associativity)) in alternatives.into_iter().enumerate() {
755 let climbs = left_recursive
756 && (edge_ref(&expr, Edge::Left) == Some(name)
757 || edge_ref(&expr, Edge::Right) == Some(name));
758 let expr = if climbs {
759 let level = i64::try_from(count - index).unwrap_or(i64::MAX);
760 GrammarExpr::feature(FeatureExpr::Form(FeatureForm::new(
761 "precedence",
762 vec![
763 FieldValue::Integer(level),
764 FieldValue::Word(associativity.into()),
765 FieldValue::Expression(expr),
766 ],
767 )))
768 } else {
769 expr
770 };
771 push_choice_alternative(&mut choice, expr);
772 }
773 finish_choice(choice)
774}
775
776#[derive(Clone, Copy)]
777enum Edge {
778 Left,
779 Right,
780}
781
782fn edge_ref(expr: &GrammarExpr, edge: Edge) -> Option<&str> {
783 match expr {
784 GrammarExpr::NonTerminal(name) => Some(name),
785 GrammarExpr::Capture { expr, .. } => edge_ref(expr, edge),
786 GrammarExpr::Sequence(items) => match edge {
787 Edge::Left => items.first(),
788 Edge::Right => items.last(),
789 }
790 .and_then(|item| edge_ref(item, edge)),
791 _ => None,
792 }
793}
794
795fn finish_sequence(items: Vec<GrammarExpr>) -> GrammarExpr {
796 match items.len() {
797 0 => GrammarExpr::Empty,
798 1 => items.into_iter().next().expect("one sequence item exists"),
799 _ => GrammarExpr::Sequence(items),
800 }
801}
802
803fn push_sequence_item(items: &mut Vec<GrammarExpr>, item: GrammarExpr) {
804 match item {
805 GrammarExpr::Empty => {}
806 GrammarExpr::Sequence(nested) => {
807 for item in nested {
808 push_sequence_item(items, item);
809 }
810 }
811 item => items.push(item),
812 }
813}
814
815fn finish_choice(alternatives: Vec<GrammarExpr>) -> GrammarExpr {
816 if alternatives.iter().all(|expr| expr == &GrammarExpr::Empty) {
817 return GrammarExpr::Empty;
818 }
819 match alternatives.len() {
820 0 => GrammarExpr::Empty,
821 1 => alternatives
822 .into_iter()
823 .next()
824 .expect("one alternative exists"),
825 _ => GrammarExpr::Choice {
826 ordered: false,
827 alternatives,
828 },
829 }
830}
831
832fn push_choice_alternative(alternatives: &mut Vec<GrammarExpr>, alternative: GrammarExpr) {
833 match alternative {
834 GrammarExpr::Choice {
835 ordered: false,
836 alternatives: nested,
837 } => alternatives.extend(nested),
838 alternative => alternatives.push(alternative),
839 }
840}
841
842fn single_char(value: &str, role: &str, offset: usize) -> Result<char, GrammarImportError> {
843 let mut chars = value.chars();
844 let Some(character) = chars.next() else {
845 return Err(error_at(
846 offset,
847 format!("{role} must contain one character"),
848 ));
849 };
850 if chars.next().is_some() {
851 return Err(error_at(
852 offset,
853 format!("{role} {value:?} must contain one character"),
854 ));
855 }
856 Ok(character)
857}
858
859fn rule_doc(comments: Vec<String>, notes: Vec<String>, command: Option<String>) -> Option<String> {
860 let mut parts = Vec::new();
861 parts.extend(comments.into_iter().filter(|comment| !comment.is_empty()));
862 parts.extend(notes);
863 parts.extend(command);
864 if parts.is_empty() {
865 None
866 } else {
867 Some(parts.join("; "))
868 }
869}
870
871fn format_command(tokens: &[Token]) -> String {
872 let mut output = "->".to_string();
873 for token in tokens {
874 match token.kind {
875 TokenKind::LParen => output.push('('),
876 TokenKind::RParen => output.push(')'),
877 TokenKind::Comma => output.push_str(", "),
878 _ => {
879 if output == "->" || (!output.ends_with('(') && !output.ends_with(", ")) {
880 output.push(' ');
881 }
882 output.push_str(&token.text());
883 }
884 }
885 }
886 output
887}
888
889fn error_at(offset: usize, message: impl Into<String>) -> GrammarImportError {
890 parse_error(FORMAT, format!("{} at byte {offset}", message.into()))
891}