cddl/
parser.rs

1use super::{
2  ast::*,
3  error::{
4    ErrorMsg,
5    MsgType::{self, *},
6  },
7  lexer::{self, Position},
8  token::{self, SocketPlug, Token},
9};
10
11use std::{cmp::Ordering, marker::PhantomData, mem, result};
12
13use codespan_reporting::{
14  diagnostic::{Diagnostic, Label},
15  files::SimpleFiles,
16  term,
17};
18use displaydoc::Display;
19
20#[cfg(feature = "std")]
21use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
22#[cfg(feature = "std")]
23use std::{borrow::Cow, collections::BTreeSet, rc::Rc};
24
25#[cfg(not(feature = "std"))]
26use alloc::{
27  borrow::{Cow, ToOwned},
28  boxed::Box,
29  collections::BTreeSet,
30  rc::Rc,
31  string::{String, ToString},
32  vec::Vec,
33};
34
35#[cfg(target_arch = "wasm32")]
36use wasm_bindgen::prelude::*;
37
38#[cfg(target_arch = "wasm32")]
39use serde::Serialize;
40
41/// Alias for `Result` with an error of type `cddl::ParserError`
42pub type Result<T> = result::Result<T, Error>;
43
44/// Parser type
45pub struct Parser<'a> {
46  tokens: Box<dyn Iterator<Item = lexer::Item<'a>> + 'a>,
47  str_input: &'a str,
48  cur_token: Token<'a>,
49  peek_token: Token<'a>,
50  lexer_position: Position,
51  peek_lexer_position: Position,
52  #[cfg(feature = "ast-span")]
53  parser_position: Position,
54  /// Vec of collected parsing errors
55  pub errors: Vec<Error>,
56  current_rule_generic_param_idents: Option<Vec<&'a str>>,
57  typenames: Rc<BTreeSet<&'a str>>,
58  groupnames: Rc<BTreeSet<&'a str>>,
59  #[cfg(feature = "ast-span")]
60  unknown_rule_idents: Vec<(&'a str, Span)>,
61  #[cfg(not(feature = "ast-span"))]
62  unknown_rule_idents: Vec<&'a str>,
63  is_guaranteed: bool,
64}
65
66/// Parsing error types
67#[derive(Debug, Display)]
68pub enum Error {
69  /// Parsing errors
70  #[displaydoc("{0}")]
71  CDDL(String),
72  #[cfg_attr(
73    feature = "ast-span",
74    displaydoc("parsing error: position {position:?}, msg: {msg}")
75  )]
76  #[cfg_attr(not(feature = "ast-span"), displaydoc("parsing error: msg: {msg}"))]
77  /// Parsing error occurred
78  PARSER {
79    /// Error position
80    #[cfg(feature = "ast-span")]
81    position: Position,
82    /// Error message
83    msg: ErrorMsg,
84  },
85  #[displaydoc("{0}")]
86  /// Lexing error
87  LEXER(lexer::Error),
88  /// Regex error
89  #[displaydoc("regex parsing error: {0}")]
90  REGEX(regex::Error),
91  #[displaydoc("incremental parsing error")]
92  /// Incremental parsing error
93  INCREMENTAL,
94  #[displaydoc("defer parsing error")]
95  /// Incremental parsing error
96  GROUP,
97}
98
99#[cfg(feature = "std")]
100impl std::error::Error for Error {}
101
102impl<'a> Parser<'a> {
103  /// Create a new `Parser` from a given str input and iterator over
104  /// `lexer::Item`.
105  ///
106  /// # Example
107  ///
108  /// ```
109  /// use cddl::parser::Parser;
110  /// use cddl::lexer::Lexer;
111  ///
112  /// let input = r#"mycddl = ( int / float )"#;
113  /// let p = Parser::new(input, Box::new(Lexer::new(input).iter()));
114  /// ```
115  pub fn new(
116    str_input: &'a str,
117    tokens: Box<dyn Iterator<Item = lexer::Item<'a>> + 'a>,
118  ) -> Result<Parser<'a>> {
119    let mut p = Parser {
120      tokens,
121      str_input,
122      cur_token: Token::EOF,
123      peek_token: Token::EOF,
124      errors: Vec::default(),
125      lexer_position: Position::default(),
126      peek_lexer_position: Position::default(),
127      #[cfg(feature = "ast-span")]
128      parser_position: Position::default(),
129      current_rule_generic_param_idents: None,
130      typenames: Rc::new(BTreeSet::from([
131        "any",
132        "uint",
133        "nint",
134        "int",
135        "bstr",
136        "bytes",
137        "tstr",
138        "text",
139        "tdate",
140        "time",
141        "number",
142        "biguint",
143        "bignint",
144        "bigint",
145        "integer",
146        "unsigned",
147        "decfrac",
148        "bigfloat",
149        "eb64url",
150        "eb64legacy",
151        "eb16",
152        "encoded-cbor",
153        "uri",
154        "b64url",
155        "b64legacy",
156        "regexp",
157        "mime-message",
158        "cbor-any",
159        "float16",
160        "float32",
161        "float64",
162        "float16-32",
163        "float32-64",
164        "float",
165        "false",
166        "true",
167        "bool",
168        "nil",
169        "null",
170        "undefined",
171      ])),
172      groupnames: Rc::new(BTreeSet::default()),
173      unknown_rule_idents: Vec::default(),
174      is_guaranteed: false,
175    };
176
177    p.next_token()?;
178    p.next_token()?;
179
180    Ok(p)
181  }
182
183  /// Print parser errors if there are any. Used with the `Error::PARSER`
184  /// variant
185  ///
186  /// # Arguments
187  ///
188  /// * `to_stderr` - When true, outputs formatted errors to stderr
189  ///
190  /// # Example
191  ///
192  /// ```
193  /// use cddl::parser::{Error, Parser};
194  /// use cddl::lexer::Lexer;
195  ///
196  /// let input = r#"mycddl = ( int / float )"#;
197  /// if let Ok(mut p) = Parser::new(input, Box::new(Lexer::new(input).iter())) {
198  ///   if let Err(Error::INCREMENTAL) = p.parse_cddl() {
199  ///     let _ = p.report_errors(true);
200  ///   }
201  /// }
202  /// ```
203  #[cfg(feature = "std")]
204  pub fn report_errors(
205    &self,
206    to_stderr: bool,
207  ) -> std::result::Result<Option<String>, Box<dyn std::error::Error>> {
208    if self.errors.is_empty() {
209      return Ok(None);
210    }
211
212    let mut files = SimpleFiles::new();
213
214    let file_id = files.add("input", self.str_input);
215
216    let mut labels = Vec::new();
217    for error in self.errors.iter() {
218      if let Error::PARSER {
219        #[cfg(feature = "ast-span")]
220        position,
221        msg,
222      } = error
223      {
224        labels.push(
225          #[cfg(feature = "ast-span")]
226          Label::primary(file_id, position.range.0..position.range.1).with_message(msg.to_string()),
227          #[cfg(not(feature = "ast-span"))]
228          Label::primary(file_id, 0..0).with_message(msg.to_string()),
229        );
230      }
231    }
232
233    let diagnostic = Diagnostic::error()
234      .with_message("parser errors")
235      .with_labels(labels);
236
237    let config = term::Config::default();
238
239    if to_stderr {
240      let writer = StandardStream::stderr(ColorChoice::Auto);
241      // TODO: Use `map_or_else()` once it is determined this crate should set
242      // its minimum version to 1.41
243      match term::emit(&mut writer.lock(), &config, &files, &diagnostic) {
244        Ok(_) => return Ok(None),
245        Err(e) => return Err(Box::from(e)),
246      };
247    }
248
249    let mut buffer = Vec::new();
250    let mut writer = term::termcolor::NoColor::new(&mut buffer);
251
252    term::emit(&mut writer, &config, &files, &diagnostic)?;
253
254    Ok(Some(String::from_utf8(buffer)?))
255  }
256
257  /// Print parser errors if there are any. Used with the `Error::PARSER`
258  /// variant
259  ///
260  /// # Example
261  ///
262  /// ```
263  /// use cddl::parser::{Error, Parser};
264  /// use cddl::lexer::Lexer;
265  ///
266  /// let input = r#"mycddl = ( int / float )"#;
267  /// if let Ok(mut p) = Parser::new(Lexer::new(input).iter(), input) {
268  ///   if let Err(Error::PARSER) = p.parse_cddl() {
269  ///     let _ = p.report_errors();
270  ///   }
271  /// }
272  /// ```
273  #[cfg(not(feature = "std"))]
274  pub fn report_errors(&self) -> Option<String> {
275    if self.errors.is_empty() {
276      return None;
277    }
278
279    let mut files = SimpleFiles::new();
280
281    let file_id = files.add("input", self.str_input);
282
283    let mut labels = Vec::new();
284    for error in self.errors.iter() {
285      if let Error::PARSER {
286        #[cfg(feature = "ast-span")]
287        position,
288        msg,
289      } = error
290      {
291        labels.push(
292          #[cfg(feature = "ast-span")]
293          Label::primary(file_id, position.range.0..position.range.1).with_message(msg.to_string()),
294          #[cfg(not(feature = "ast-span"))]
295          Label::primary(file_id, 0..0).with_message(msg.to_string()),
296        );
297      }
298    }
299
300    let diagnostic = Diagnostic::error()
301      .with_message("parser errors")
302      .with_labels(labels);
303
304    let config = term::Config::default();
305
306    let mut buffer = Vec::new();
307    let mut writer = term::termcolor::NoColor::new(&mut buffer);
308
309    term::emit(&mut writer, &config, &files, &diagnostic).ok()?;
310
311    String::from_utf8(buffer).ok()
312  }
313
314  fn next_token(&mut self) -> Result<()> {
315    mem::swap(&mut self.cur_token, &mut self.peek_token);
316    mem::swap(&mut self.lexer_position, &mut self.peek_lexer_position);
317
318    if let Some(next_token) = self.tokens.next() {
319      let nt = next_token.map_err(Error::LEXER)?;
320      self.peek_token = nt.1;
321      self.peek_lexer_position = nt.0;
322    }
323
324    Ok(())
325  }
326
327  fn advance_to_next_rule(&mut self) -> Result<()> {
328    let mut is_possible_rule = false;
329
330    while !is_possible_rule {
331      self.next_token()?;
332      if let Token::IDENT(..) = self.cur_token {
333        match self.peek_token {
334          Token::ASSIGN | Token::TCHOICEALT | Token::GCHOICEALT => is_possible_rule = true,
335          _ => continue,
336        }
337      } else if let Token::EOF = self.cur_token {
338        is_possible_rule = true;
339      }
340    }
341
342    Ok(())
343  }
344
345  #[cfg(feature = "ast-comments")]
346  fn collect_comments(&mut self) -> Result<Option<Comments<'a>>> {
347    #[cfg_attr(not(feature = "lsp"), allow(unused_mut))]
348    let mut comments: Option<Comments> = None;
349
350    while let Token::COMMENT(_comment) = self.cur_token {
351      #[cfg(not(feature = "lsp"))]
352      comments.get_or_insert(Comments::default()).0.push(_comment);
353
354      self.next_token()?;
355    }
356
357    while let Token::NEWLINE = self.cur_token {
358      #[cfg(feature = "lsp")]
359      comments.get_or_insert(Comments::default()).0.push("\n");
360
361      self.next_token()?;
362    }
363
364    if let Token::COMMENT(_) = self.cur_token {
365      if let Some(c) = self.collect_comments()? {
366        #[cfg_attr(not(feature = "lsp"), allow(unused_mut))]
367        for comment in c.0.iter() {
368          comments.get_or_insert(Comments::default()).0.push(comment);
369        }
370      }
371    }
372
373    Ok(comments)
374  }
375
376  #[cfg(not(feature = "ast-comments"))]
377  fn advance_newline(&mut self) -> Result<()> {
378    while let Token::NEWLINE = self.cur_token {
379      #[cfg(feature = "lsp")]
380      comments.get_or_insert(Comments::default()).0.push("\n");
381
382      self.next_token()?;
383    }
384
385    Ok(())
386  }
387
388  fn register_rule(&mut self, rule: &Rule<'a>) {
389    match &rule {
390      Rule::Type { rule, .. } => Rc::make_mut(&mut self.typenames).insert(rule.name.ident),
391      Rule::Group { rule, .. } => Rc::make_mut(&mut self.groupnames).insert(rule.name.ident),
392    };
393  }
394
395  /// Parses into a `CDDL` AST
396  pub fn parse_cddl(&mut self) -> Result<CDDL<'a>> {
397    #[cfg(not(feature = "ast-comments"))]
398    self.advance_newline()?;
399
400    let mut c = CDDL {
401      #[cfg(feature = "ast-comments")]
402      comments: self.collect_comments()?,
403      ..Default::default()
404    };
405
406    struct UnknownRule<'a> {
407      rule: Rule<'a>,
408      index: usize,
409      range: (usize, usize),
410    }
411
412    // First pass: Parse all rules and register their names without checking for unknown identifiers
413    let mut all_rules = Vec::default();
414    // let mut rule_ranges = Vec::default();
415
416    while self.cur_token != Token::EOF {
417      let begin_rule_range = self.lexer_position.range.0;
418
419      match self.parse_rule(false) {
420        Ok(r) => {
421          let rule_exists =
422            |existing_rule: &Rule| r.name() == existing_rule.name() && !r.is_choice_alternate();
423
424          if c.rules.iter().any(rule_exists) || all_rules.iter().any(|(rule, _)| rule_exists(rule))
425          {
426            #[cfg(feature = "ast-span")]
427            {
428              self.parser_position.range = (r.span().0, r.span().1);
429              self.parser_position.line = r.span().2;
430            }
431
432            self.errors.push(Error::PARSER {
433              #[cfg(feature = "ast-span")]
434              position: self.parser_position,
435              msg: DuplicateRuleIdentifier.into(),
436            });
437
438            continue;
439          }
440
441          // Register the rule name immediately
442          self.register_rule(&r);
443
444          all_rules.push((r, begin_rule_range));
445          self.is_guaranteed = false;
446        }
447        Err(Error::INCREMENTAL) => {
448          if !self.cur_token_is(Token::EOF) {
449            self.advance_to_next_rule()?;
450          }
451        }
452        Err(e) => return Err(e),
453      }
454    }
455
456    // Second pass: Add all rules to the CDDL
457    let mut unknown_rules = Vec::default();
458
459    for (rule, begin_rule_range) in all_rules {
460      // Check if the rule still has unknown identifiers
461      if !self.unknown_rule_idents.is_empty() {
462        unknown_rules.push(UnknownRule {
463          rule,
464          index: c.rules.len(),
465          range: (begin_rule_range, self.lexer_position.range.1),
466        });
467        self.unknown_rule_idents = Vec::default();
468      } else {
469        c.rules.push(rule);
470      }
471    }
472
473    // In practice unknown rules usually are declared backwards, so we reverse
474    // it here.
475    unknown_rules.reverse();
476
477    // Try to specialize unknown rules until the set of them stabilizes.
478    {
479      let mut errors;
480      let mut known_rules = Vec::default();
481      loop {
482        let mut resolved_rules = Vec::default();
483        let mut unresolved_rules = Vec::default();
484
485        errors = Vec::default();
486        for unknown_rule in unknown_rules {
487          match self.resolve_rule(unknown_rule.range, false) {
488            Ok(rule) => resolved_rules.push((unknown_rule.index, rule)),
489            Err(_) => match self.resolve_rule(unknown_rule.range, true) {
490              Ok(rule) => resolved_rules.push((unknown_rule.index, rule)),
491              Err(mut error) => {
492                errors.append(&mut error);
493                unresolved_rules.push(unknown_rule);
494              }
495            },
496          }
497        }
498        if resolved_rules.is_empty() {
499          break;
500        }
501        for (_, rule) in &resolved_rules {
502          self.register_rule(rule);
503        }
504        known_rules.append(&mut resolved_rules);
505        unknown_rules = unresolved_rules;
506      }
507      self.errors.append(&mut errors);
508      known_rules.sort_by(|(a, _), (b, _)| b.partial_cmp(a).unwrap());
509      for (index, rule) in known_rules {
510        c.rules.insert(index, rule);
511      }
512    }
513
514    if !self.errors.is_empty() {
515      return Err(Error::INCREMENTAL);
516    }
517
518    // RFC 9682 Section 3.1: Empty data models are now allowed
519    // The requirement for at least one rule is now a semantic constraint
520    // to be fulfilled after processing of all directives.
521
522    Ok(c)
523  }
524
525  fn resolve_rule(
526    &mut self,
527    range: (usize, usize),
528    parse_group_rule: bool,
529  ) -> result::Result<Rule<'a>, Vec<Error>> {
530    let tokens = Box::new(lexer::Lexer::new(&self.str_input[range.0..range.1]).iter());
531    let mut parser = Parser::new(self.str_input, tokens).map_err(|err| vec![err])?;
532    parser.groupnames = self.groupnames.clone();
533    parser.typenames = self.typenames.clone();
534    let rule = parser
535      .parse_rule(parse_group_rule)
536      .map_err(|err| vec![err])?;
537    if !parser.unknown_rule_idents.is_empty() {
538      Err(
539        #[cfg(feature = "ast-span")]
540        parser
541          .unknown_rule_idents
542          .into_iter()
543          .map(|(ident, span)| Error::PARSER {
544            position: Position {
545              column: 0,
546              index: span.0,
547              line: span.2,
548              range: (span.0 + range.0, span.1 + range.0),
549            },
550            msg: ErrorMsg {
551              short: format!("missing definition for rule {}", ident),
552              extended: None,
553            },
554          })
555          .collect(),
556        #[cfg(not(feature = "ast-span"))]
557        parser
558          .unknown_rule_idents
559          .into_iter()
560          .map(|ident| Error::PARSER {
561            msg: ErrorMsg {
562              short: format!("missing definition for rule {}", ident),
563              extended: None,
564            },
565          })
566          .collect(),
567      )
568    } else {
569      Ok(rule)
570    }
571  }
572
573  #[allow(missing_docs)]
574  pub fn parse_rule(&mut self, parse_group_rule: bool) -> Result<Rule<'a>> {
575    #[cfg(feature = "ast-span")]
576    let begin_rule_range = self.lexer_position.range.0;
577    #[cfg(feature = "ast-span")]
578    let begin_rule_line = self.lexer_position.line;
579    #[cfg(feature = "ast-span")]
580    let begin_rule_col = self.lexer_position.column;
581
582    let ident = match &self.cur_token {
583      Token::IDENT(i, s) => self.identifier_from_ident_token(i, *s),
584      _ => {
585        #[cfg(feature = "ast-span")]
586        {
587          self.parser_position.range = self.lexer_position.range;
588          self.parser_position.line = self.lexer_position.line;
589        }
590
591        self.errors.push(Error::PARSER {
592          #[cfg(feature = "ast-span")]
593          position: self.parser_position,
594          msg: InvalidRuleIdentifier.into(),
595        });
596
597        return Err(Error::INCREMENTAL);
598      }
599    };
600
601    let gp = if self.peek_token_is(&Token::LANGLEBRACKET) {
602      self.next_token()?;
603
604      let params = self.parse_genericparm()?;
605      let mut param_list = Vec::default();
606
607      for param in params.params.iter() {
608        param_list.push(param.param.ident);
609      }
610
611      self.current_rule_generic_param_idents = Some(param_list);
612
613      Some(params)
614    } else {
615      None
616    };
617
618    #[cfg(feature = "ast-comments")]
619    let comments_before_assign = self.collect_comments()?;
620    #[cfg(not(feature = "ast-comments"))]
621    self.advance_newline()?;
622
623    if !self.expect_peek(&Token::ASSIGN)?
624      && !self.expect_peek(&Token::TCHOICEALT)?
625      && !self.expect_peek(&Token::GCHOICEALT)?
626    {
627      #[cfg(feature = "ast-span")]
628      {
629        self.parser_position.range = (begin_rule_range, self.lexer_position.range.1);
630        self.parser_position.line = self.lexer_position.line;
631      }
632
633      self.errors.push(Error::PARSER {
634        #[cfg(feature = "ast-span")]
635        position: self.parser_position,
636        msg: MsgType::MissingAssignmentToken.into(),
637      });
638
639      return Err(Error::INCREMENTAL);
640    }
641
642    let mut is_type_choice_alternate = false;
643    let mut is_group_choice_alternate = false;
644
645    if let Token::TCHOICEALT = &self.cur_token {
646      is_type_choice_alternate = true;
647    } else if let Token::GCHOICEALT = &self.cur_token {
648      is_group_choice_alternate = true;
649    }
650
651    if let Some(socket) = &ident.socket {
652      match socket {
653        SocketPlug::TYPE if !is_type_choice_alternate => {
654          #[cfg(feature = "ast-span")]
655          {
656            self.parser_position.range = (begin_rule_range, self.lexer_position.range.1);
657            self.parser_position.line = self.lexer_position.line;
658          }
659
660          self.errors.push(Error::PARSER {
661            #[cfg(feature = "ast-span")]
662            position: self.parser_position,
663            msg: MsgType::TypeSocketNamesMustBeTypeAugmentations.into(),
664          });
665
666          return Err(Error::INCREMENTAL);
667        }
668        SocketPlug::GROUP if !is_group_choice_alternate => {
669          #[cfg(feature = "ast-span")]
670          {
671            self.parser_position.range = (begin_rule_range, self.lexer_position.range.1);
672            self.parser_position.line = self.lexer_position.line;
673          }
674
675          self.errors.push(Error::PARSER {
676            #[cfg(feature = "ast-span")]
677            position: self.parser_position,
678            msg: MsgType::GroupSocketNamesMustBeGroupAugmentations.into(),
679          });
680
681          return Err(Error::INCREMENTAL);
682        }
683        _ => (),
684      }
685    }
686
687    self.next_token()?;
688
689    #[cfg(feature = "ast-comments")]
690    let comments_after_assign = self.collect_comments()?;
691    #[cfg(not(feature = "ast-comments"))]
692    self.advance_newline()?;
693
694    // If token is group socket or rule is a group plug alternative, parse
695    // as group rule
696    if matches!(self.cur_token, Token::IDENT(_, Some(SocketPlug::GROUP)))
697      || is_group_choice_alternate
698      || parse_group_rule
699    {
700      let ge = self.parse_grpent(true)?;
701
702      #[cfg(feature = "ast-comments")]
703      let comments_after_rule = self.collect_comments()?;
704      #[cfg(not(feature = "ast-comments"))]
705      self.advance_newline()?;
706
707      #[cfg(feature = "ast-span")]
708      let span = (
709        begin_rule_range,
710        self.parser_position.range.1,
711        begin_rule_line,
712      );
713
714      self.current_rule_generic_param_idents = None;
715      self.is_guaranteed = true;
716
717      return Ok(Rule::Group {
718        rule: Box::from(GroupRule {
719          name: ident,
720          generic_params: gp,
721          is_group_choice_alternate,
722          entry: ge,
723          #[cfg(feature = "ast-comments")]
724          comments_before_assigng: comments_before_assign,
725          #[cfg(feature = "ast-comments")]
726          comments_after_assigng: comments_after_assign,
727        }),
728        #[cfg(feature = "ast-comments")]
729        comments_after_rule,
730        #[cfg(feature = "ast-span")]
731        span,
732      });
733    }
734
735    match self.cur_token {
736      Token::LPAREN | Token::ASTERISK | Token::ONEORMORE | Token::OPTIONAL => {
737        #[cfg(feature = "ast-span")]
738        let begin_pt_range = self.lexer_position.range.0;
739
740        let ge = self.parse_grpent(true)?;
741
742        #[cfg(feature = "ast-span")]
743        let mut end_rule_range = self.parser_position.range.1;
744
745        #[cfg(feature = "ast-comments")]
746        let comments_after_rule = self.collect_comments()?;
747        #[cfg(not(feature = "ast-comments"))]
748        self.advance_newline()?;
749
750        // If a group entry is an inline group with no leading occurrence
751        // indicator, and its group has only a single element that is not
752        // preceded by an occurrence indicator nor member key, then there are
753        // two valid interpretations: either it's a parenthesized inline group
754        // with a type or a parenthesized type. Both cases are interpreted in
755        // the same way, but according to the BNF, the parenthesized type takes
756        // priority.
757        //
758        // A priori, we coerce this group into a parenthesized type. This is one
759        // of the few situations where `clone` is required
760        if let GroupEntry::InlineGroup {
761          occur: None,
762          group,
763          #[cfg(feature = "ast-comments")]
764          comments_before_group,
765          #[cfg(feature = "ast-comments")]
766          comments_after_group,
767          ..
768        } = &ge
769        {
770          if group.group_choices.len() == 1 {
771            if let Some(gc) = group.group_choices.first() {
772              if gc.group_entries.len() == 1 {
773                if let Some(group_entry) = gc.group_entries.first() {
774                  // Check that there is no trailing comma
775                  if !group_entry.1.optional_comma {
776                    // EXAMPLE: non-empty<M> = (M) .and ({ + any => any })
777                    if let GroupEntry::TypeGroupname {
778                      ge,
779                      #[cfg(feature = "ast-comments")]
780                      leading_comments,
781                      #[cfg(feature = "ast-comments")]
782                      trailing_comments,
783                      ..
784                    } = &group_entry.0
785                    {
786                      if ge.occur.is_none() && matches!(self.cur_token, Token::ControlOperator(_)) {
787                        let value = self.parse_type(Some(Type2::ParenthesizedType {
788                          #[cfg(feature = "ast-comments")]
789                          comments_before_type: comments_before_group.clone(),
790                          pt: Type {
791                            type_choices: vec![TypeChoice {
792                              #[cfg(feature = "ast-comments")]
793                              comments_before_type: leading_comments.clone(),
794                              #[cfg(feature = "ast-comments")]
795                              comments_after_type: trailing_comments.clone(),
796                              type1: Type1 {
797                                type2: Type2::Typename {
798                                  ident: ge.name.clone(),
799                                  generic_args: ge.generic_args.clone(),
800                                  #[cfg(feature = "ast-span")]
801                                  span: ge.name.span,
802                                },
803                                operator: None,
804                                #[cfg(feature = "ast-span")]
805                                span: ge.name.span,
806                                #[cfg(feature = "ast-comments")]
807                                comments_after_type: None,
808                              },
809                            }],
810                            #[cfg(feature = "ast-span")]
811                            span: ge.name.span,
812                          },
813                          #[cfg(feature = "ast-comments")]
814                          comments_after_type: comments_after_group.clone(),
815                          #[cfg(feature = "ast-span")]
816                          span: (
817                            begin_pt_range,
818                            self.parser_position.range.1,
819                            begin_rule_line,
820                          ),
821                        }))?;
822
823                        #[cfg(feature = "ast-span")]
824                        {
825                          end_rule_range = self.parser_position.range.1;
826                        }
827
828                        self.current_rule_generic_param_idents = None;
829
830                        return Ok(Rule::Type {
831                          rule: TypeRule {
832                            name: ident,
833                            generic_params: gp,
834                            is_type_choice_alternate,
835                            value,
836                            #[cfg(feature = "ast-comments")]
837                            comments_before_assignt: comments_before_assign,
838                            #[cfg(feature = "ast-comments")]
839                            comments_after_assignt: comments_after_assign,
840                          },
841                          #[cfg(feature = "ast-comments")]
842                          comments_after_rule,
843                          #[cfg(feature = "ast-span")]
844                          span: (begin_rule_range, end_rule_range, begin_rule_line),
845                        });
846                      }
847                    }
848
849                    // TODO: Replace with box pattern destructuring once supported in stable
850                    if let GroupEntry::ValueMemberKey { ge, .. } = &group_entry.0 {
851                      if ge.occur.is_none() && ge.member_key.is_none() {
852                        let value = self.parse_type(Some(Type2::ParenthesizedType {
853                          #[cfg(feature = "ast-comments")]
854                          comments_before_type: comments_before_group.clone(),
855                          pt: ge.entry_type.clone(),
856                          #[cfg(feature = "ast-comments")]
857                          comments_after_type: comments_after_group.clone(),
858                          #[cfg(feature = "ast-span")]
859                          span: (
860                            begin_pt_range,
861                            self.parser_position.range.1,
862                            begin_rule_line,
863                          ),
864                        }))?;
865
866                        #[cfg(feature = "ast-span")]
867                        {
868                          end_rule_range = self.parser_position.range.1;
869                        }
870
871                        self.current_rule_generic_param_idents = None;
872
873                        return Ok(Rule::Type {
874                          rule: TypeRule {
875                            name: ident,
876                            generic_params: gp,
877                            is_type_choice_alternate,
878                            value,
879                            #[cfg(feature = "ast-comments")]
880                            comments_before_assignt: comments_before_assign,
881                            #[cfg(feature = "ast-comments")]
882                            comments_after_assignt: comments_after_assign,
883                          },
884                          #[cfg(feature = "ast-comments")]
885                          comments_after_rule,
886                          #[cfg(feature = "ast-span")]
887                          span: (begin_rule_range, end_rule_range, begin_rule_line),
888                        });
889                      }
890                    }
891                  }
892                }
893              }
894            }
895          }
896        }
897
898        self.current_rule_generic_param_idents = None;
899
900        Ok(Rule::Group {
901          rule: Box::from(GroupRule {
902            name: ident,
903            generic_params: gp,
904            is_group_choice_alternate,
905            entry: ge,
906            #[cfg(feature = "ast-comments")]
907            comments_before_assigng: comments_before_assign,
908            #[cfg(feature = "ast-comments")]
909            comments_after_assigng: comments_after_assign,
910          }),
911          #[cfg(feature = "ast-comments")]
912          comments_after_rule,
913          #[cfg(feature = "ast-span")]
914          span: (begin_rule_range, end_rule_range, begin_rule_line),
915        })
916      }
917      _ => {
918        // If type rule is an unwrap type, advance token after parsing type
919        let advance_token = matches!(self.cur_token, Token::UNWRAP);
920
921        #[cfg(feature = "ast-comments")]
922        let mut t = self.parse_type(None)?;
923        #[cfg(not(feature = "ast-comments"))]
924        let t = self.parse_type(None)?;
925
926        if advance_token {
927          self.next_token()?;
928        }
929
930        #[cfg(feature = "ast-comments")]
931        let comments_after_rule = if let Some(comments) = t.split_comments_after_type() {
932          Some(comments)
933        } else {
934          self.collect_comments()?
935        };
936
937        #[cfg(not(feature = "ast-comments"))]
938        self.advance_newline()?;
939
940        if let Token::ASSIGN | Token::TCHOICEALT | Token::GCHOICEALT = &self.cur_token {
941          self.errors.push(Error::PARSER {
942            #[cfg(feature = "ast-span")]
943            position: Position {
944              line: begin_rule_line,
945              column: begin_rule_col,
946              range: (ident.span.0, ident.span.1),
947              index: self.parser_position.range.0,
948            },
949            msg: IncompleteRuleEntry.into(),
950          });
951
952          return Err(Error::INCREMENTAL);
953        }
954
955        #[cfg(feature = "ast-span")]
956        let span = (
957          begin_rule_range,
958          self.parser_position.range.1,
959          begin_rule_line,
960        );
961
962        self.current_rule_generic_param_idents = None;
963
964        if t.type_choices.len() > 1
965          || !matches!(
966            t.type_choices[0].type1.type2,
967            Type2::ParenthesizedType { .. } | Type2::Typename { .. }
968          )
969        {
970          self.is_guaranteed = true;
971        }
972
973        Ok(Rule::Type {
974          rule: TypeRule {
975            name: ident,
976            generic_params: gp,
977            is_type_choice_alternate,
978            value: t,
979            #[cfg(feature = "ast-comments")]
980            comments_before_assignt: comments_before_assign,
981            #[cfg(feature = "ast-comments")]
982            comments_after_assignt: comments_after_assign,
983          },
984          #[cfg(feature = "ast-comments")]
985          comments_after_rule,
986          #[cfg(feature = "ast-span")]
987          span,
988        })
989      }
990    }
991  }
992
993  #[allow(missing_docs)]
994  pub fn parse_genericparm(&mut self) -> Result<GenericParams<'a>> {
995    #[cfg(feature = "ast-span")]
996    let begin_range = self.lexer_position.range.0;
997
998    if let Token::LANGLEBRACKET = &self.cur_token {
999      self.next_token()?;
1000    }
1001
1002    let mut generic_params = GenericParams::default();
1003
1004    while !self.cur_token_is(Token::RANGLEBRACKET) {
1005      #[cfg(feature = "ast-comments")]
1006      let comments_before_ident = self.collect_comments()?;
1007      #[cfg(not(feature = "ast-comments"))]
1008      self.advance_newline()?;
1009
1010      match &self.cur_token {
1011        Token::IDENT(ident, socket) => {
1012          let param = self.identifier_from_ident_token(ident, *socket);
1013
1014          self.next_token()?;
1015
1016          #[cfg(feature = "ast-comments")]
1017          let comments_after_ident = self.collect_comments()?;
1018          #[cfg(not(feature = "ast-comments"))]
1019          self.advance_newline()?;
1020
1021          generic_params.params.push(GenericParam {
1022            param,
1023            #[cfg(feature = "ast-comments")]
1024            comments_before_ident,
1025            #[cfg(feature = "ast-comments")]
1026            comments_after_ident,
1027          });
1028
1029          if !self.cur_token_is(Token::COMMA) && !self.cur_token_is(Token::RANGLEBRACKET) {
1030            #[cfg(feature = "ast-span")]
1031            {
1032              self.parser_position.range = (begin_range + 1, self.peek_lexer_position.range.0);
1033              self.parser_position.line = self.lexer_position.line;
1034            }
1035
1036            self.errors.push(Error::PARSER {
1037              #[cfg(feature = "ast-span")]
1038              position: self.parser_position,
1039              msg: InvalidGenericSyntax.into(),
1040            });
1041
1042            return Err(Error::INCREMENTAL);
1043          }
1044        }
1045        Token::COMMA => self.next_token()?,
1046        Token::VALUE(_) => {
1047          #[cfg(feature = "ast-span")]
1048          {
1049            self.parser_position.range = (self.lexer_position.range.0, self.lexer_position.range.1);
1050            self.parser_position.line = self.lexer_position.line;
1051          }
1052
1053          self.errors.push(Error::PARSER {
1054            #[cfg(feature = "ast-span")]
1055            position: self.parser_position,
1056            msg: InvalidGenericIdentifier.into(),
1057          });
1058
1059          return Err(Error::INCREMENTAL);
1060        }
1061        _ => {
1062          #[cfg(feature = "ast-span")]
1063          {
1064            self.parser_position.range = (begin_range, self.lexer_position.range.0);
1065            self.parser_position.line = self.lexer_position.line;
1066          }
1067
1068          self.errors.push(Error::PARSER {
1069            #[cfg(feature = "ast-span")]
1070            position: self.parser_position,
1071            msg: InvalidGenericSyntax.into(),
1072          });
1073
1074          return Err(Error::INCREMENTAL);
1075        }
1076      }
1077    }
1078
1079    // Since generic params are only found after the identifier of a rule, don't
1080    // advance beyond the closing '>' to retain the expect_peek semantics for
1081    // '=', '/=' and '//='
1082
1083    #[cfg(feature = "ast-span")]
1084    {
1085      let end_range = self.lexer_position.range.1;
1086      generic_params.span = (begin_range, end_range, self.lexer_position.line);
1087    }
1088
1089    Ok(generic_params)
1090  }
1091
1092  #[allow(missing_docs)]
1093  pub fn parse_genericargs(&mut self) -> Result<GenericArgs<'a>> {
1094    if self.peek_token_is(&Token::LANGLEBRACKET) {
1095      self.next_token()?;
1096    }
1097
1098    #[cfg(feature = "ast-span")]
1099    let begin_generic_arg_range = self.lexer_position.range.0;
1100    #[cfg(feature = "ast-span")]
1101    let begin_generic_arg_line = self.lexer_position.line;
1102
1103    // Required for type2 mutual recursion
1104    if let Token::LANGLEBRACKET = &self.cur_token {
1105      self.next_token()?;
1106    }
1107
1108    let mut generic_args = GenericArgs::default();
1109
1110    while !self.cur_token_is(Token::RANGLEBRACKET) {
1111      #[cfg(feature = "ast-comments")]
1112      let leading_comments = self.collect_comments()?;
1113      #[cfg(not(feature = "ast-comments"))]
1114      self.advance_newline()?;
1115
1116      let t1 = self.parse_type1(None)?;
1117
1118      #[cfg(feature = "ast-comments")]
1119      let trailing_comments = self.collect_comments()?;
1120      #[cfg(not(feature = "ast-comments"))]
1121      self.advance_newline()?;
1122
1123      generic_args.args.push(GenericArg {
1124        #[cfg(feature = "ast-comments")]
1125        comments_before_type: leading_comments,
1126        arg: Box::from(t1),
1127        #[cfg(feature = "ast-comments")]
1128        comments_after_type: trailing_comments,
1129      });
1130
1131      if let Token::COMMA = self.cur_token {
1132        self.next_token()?;
1133      }
1134
1135      if let Token::EOF = &self.cur_token {
1136        self.errors.push(Error::PARSER {
1137          #[cfg(feature = "ast-span")]
1138          position: self.parser_position,
1139          msg: MissingGenericClosingDelimiter.into(),
1140        });
1141
1142        return Err(Error::INCREMENTAL);
1143      }
1144    }
1145
1146    if let Token::RANGLEBRACKET = &self.cur_token {
1147      #[cfg(feature = "ast-span")]
1148      {
1149        self.parser_position.range.1 = self.lexer_position.range.1;
1150      }
1151      self.next_token()?;
1152    }
1153
1154    #[cfg(feature = "ast-span")]
1155    {
1156      generic_args.span = (
1157        begin_generic_arg_range,
1158        self.parser_position.range.1,
1159        begin_generic_arg_line,
1160      );
1161    }
1162
1163    Ok(generic_args)
1164  }
1165
1166  // parenthesized_type can be provided as an argument to retrieve its span and
1167  // comments if it has been previously parsed
1168  #[allow(missing_docs)]
1169  pub fn parse_type(&mut self, parenthesized_type: Option<Type2<'a>>) -> Result<Type<'a>> {
1170    #[cfg(feature = "ast-span")]
1171    {
1172      self.parser_position.range = self.lexer_position.range;
1173      self.parser_position.line = self.lexer_position.line;
1174    }
1175
1176    #[cfg(feature = "ast-span")]
1177    let begin_type_range = if let Some(Type2::ParenthesizedType { span, .. }) = parenthesized_type {
1178      self.parser_position.line = span.2;
1179
1180      span.0
1181    } else {
1182      self.parser_position.range.0
1183    };
1184
1185    let mut t = Type {
1186      type_choices: Vec::new(),
1187      #[cfg(feature = "ast-span")]
1188      span: (begin_type_range, 0, self.parser_position.line),
1189    };
1190
1191    #[cfg(feature = "ast-comments")]
1192    let mut tc = TypeChoice {
1193      type1: self.parse_type1(parenthesized_type)?,
1194      comments_before_type: None,
1195      comments_after_type: None,
1196    };
1197
1198    #[cfg(not(feature = "ast-comments"))]
1199    let tc = TypeChoice {
1200      type1: self.parse_type1(parenthesized_type)?,
1201    };
1202
1203    #[cfg(feature = "ast-comments")]
1204    {
1205      tc.comments_after_type = self.collect_comments()?;
1206    }
1207    #[cfg(not(feature = "ast-comments"))]
1208    self.advance_newline()?;
1209
1210    t.type_choices.push(tc);
1211
1212    while let Token::TCHOICE = &self.cur_token {
1213      self.next_token()?;
1214
1215      #[cfg(feature = "ast-comments")]
1216      let comments_before_type = self.collect_comments()?;
1217      #[cfg(not(feature = "ast-comments"))]
1218      self.advance_newline()?;
1219
1220      #[cfg(feature = "ast-comments")]
1221      let mut tc = TypeChoice {
1222        comments_before_type,
1223        comments_after_type: None,
1224        type1: self.parse_type1(None)?,
1225      };
1226
1227      #[cfg(not(feature = "ast-comments"))]
1228      let tc = TypeChoice {
1229        type1: self.parse_type1(None)?,
1230      };
1231
1232      #[cfg(feature = "ast-comments")]
1233      {
1234        tc.comments_after_type = self.collect_comments()?;
1235      }
1236      #[cfg(not(feature = "ast-comments"))]
1237      self.advance_newline()?;
1238
1239      t.type_choices.push(tc);
1240    }
1241
1242    #[cfg(feature = "ast-span")]
1243    {
1244      t.span.1 = self.parser_position.range.1;
1245    }
1246
1247    Ok(t)
1248  }
1249
1250  // parenthesized_type can be provided as an argument to retrieve its span and
1251  // comments if it has been previously parsed
1252  #[allow(missing_docs)]
1253  pub fn parse_type1(&mut self, parenthesized_type: Option<Type2<'a>>) -> Result<Type1<'a>> {
1254    #[cfg(feature = "ast-span")]
1255    let mut begin_type1_line = self.lexer_position.line;
1256    #[cfg(feature = "ast-span")]
1257    let mut begin_type1_range = self.lexer_position.range.0;
1258
1259    let t2_1 = if let Some(Type2::ParenthesizedType {
1260      #[cfg(feature = "ast-comments")]
1261      comments_before_type,
1262      pt,
1263      #[cfg(feature = "ast-comments")]
1264      comments_after_type,
1265      #[cfg(feature = "ast-span")]
1266      span,
1267    }) = parenthesized_type
1268    {
1269      #[cfg(feature = "ast-span")]
1270      {
1271        begin_type1_line = span.2;
1272        begin_type1_range = span.0;
1273      }
1274
1275      Type2::ParenthesizedType {
1276        #[cfg(feature = "ast-comments")]
1277        comments_before_type,
1278        pt,
1279        #[cfg(feature = "ast-comments")]
1280        comments_after_type,
1281        #[cfg(feature = "ast-span")]
1282        span,
1283      }
1284    } else {
1285      self.parse_type2()?
1286    };
1287
1288    #[cfg(feature = "ast-span")]
1289    let mut span = (
1290      begin_type1_range,
1291      self.lexer_position.range.1,
1292      begin_type1_line,
1293    );
1294
1295    #[cfg(feature = "ast-comments")]
1296    let comments_after_type = self.collect_comments()?;
1297    #[cfg(not(feature = "ast-comments"))]
1298    self.advance_newline()?;
1299
1300    let op = match &self.cur_token {
1301      Token::RANGEOP(i) => {
1302        #[cfg(feature = "ast-span")]
1303        {
1304          span.0 = self.lexer_position.range.0;
1305        }
1306
1307        Some(RangeCtlOp::RangeOp {
1308          is_inclusive: *i,
1309          #[cfg(feature = "ast-span")]
1310          span,
1311        })
1312      }
1313      Token::ControlOperator(ctrl) => {
1314        #[cfg(feature = "ast-span")]
1315        {
1316          span.0 = self.lexer_position.range.0;
1317        }
1318
1319        Some(RangeCtlOp::CtlOp {
1320          ctrl: *ctrl,
1321          #[cfg(feature = "ast-span")]
1322          span,
1323        })
1324      }
1325      _ => None,
1326    };
1327
1328    #[cfg(feature = "ast-span")]
1329    {
1330      span = (
1331        begin_type1_range,
1332        self.parser_position.range.1,
1333        begin_type1_line,
1334      );
1335    }
1336
1337    match op {
1338      Some(operator) => {
1339        self.next_token()?;
1340
1341        #[cfg(feature = "ast-comments")]
1342        let comments_after_operator = self.collect_comments()?;
1343        #[cfg(not(feature = "ast-comments"))]
1344        self.advance_newline()?;
1345
1346        let t2 = self.parse_type2()?;
1347
1348        #[cfg(feature = "ast-span")]
1349        {
1350          span.1 = self.parser_position.range.1;
1351        }
1352
1353        Ok(Type1 {
1354          type2: t2_1,
1355          operator: Some(Operator {
1356            #[cfg(feature = "ast-comments")]
1357            comments_before_operator: comments_after_type,
1358            operator,
1359            #[cfg(feature = "ast-comments")]
1360            comments_after_operator,
1361            type2: t2,
1362          }),
1363          #[cfg(feature = "ast-comments")]
1364          comments_after_type: None,
1365          #[cfg(feature = "ast-span")]
1366          span,
1367        })
1368      }
1369      None => Ok(Type1 {
1370        type2: t2_1,
1371        operator: None,
1372        #[cfg(feature = "ast-comments")]
1373        comments_after_type,
1374        #[cfg(feature = "ast-span")]
1375        span,
1376      }),
1377    }
1378  }
1379
1380  #[allow(missing_docs)]
1381  pub fn parse_type2(&mut self) -> Result<Type2<'a>> {
1382    let t2 = match &self.cur_token {
1383      // value
1384      Token::VALUE(value) => {
1385        #[cfg(feature = "ast-span")]
1386        {
1387          self.parser_position.range = self.lexer_position.range;
1388          self.parser_position.line = self.lexer_position.line;
1389        }
1390
1391        #[cfg(feature = "ast-span")]
1392        let span = (
1393          self.parser_position.range.0,
1394          self.parser_position.range.1,
1395          self.parser_position.line,
1396        );
1397
1398        match value {
1399          token::Value::TEXT(t) => Ok(Type2::TextValue {
1400            value: t.clone(),
1401            #[cfg(feature = "ast-span")]
1402            span,
1403          }),
1404          token::Value::INT(i) => Ok(Type2::IntValue {
1405            value: *i,
1406            #[cfg(feature = "ast-span")]
1407            span,
1408          }),
1409          token::Value::UINT(ui) => Ok(Type2::UintValue {
1410            value: *ui,
1411            #[cfg(feature = "ast-span")]
1412            span,
1413          }),
1414          token::Value::FLOAT(f) => Ok(Type2::FloatValue {
1415            value: *f,
1416            #[cfg(feature = "ast-span")]
1417            span,
1418          }),
1419          token::Value::BYTE(token::ByteValue::UTF8(Cow::Borrowed(utf8))) => {
1420            Ok(Type2::UTF8ByteString {
1421              value: Cow::Borrowed(utf8),
1422              #[cfg(feature = "ast-span")]
1423              span,
1424            })
1425          }
1426          token::Value::BYTE(token::ByteValue::UTF8(Cow::Owned(utf8))) => {
1427            Ok(Type2::UTF8ByteString {
1428              value: Cow::Owned(utf8.to_owned()),
1429              #[cfg(feature = "ast-span")]
1430              span,
1431            })
1432          }
1433          token::Value::BYTE(token::ByteValue::B16(Cow::Borrowed(b16))) => {
1434            Ok(Type2::B16ByteString {
1435              value: Cow::Borrowed(b16),
1436              #[cfg(feature = "ast-span")]
1437              span,
1438            })
1439          }
1440          token::Value::BYTE(token::ByteValue::B16(Cow::Owned(b16))) => Ok(Type2::B16ByteString {
1441            value: Cow::Owned(b16.to_owned()),
1442            #[cfg(feature = "ast-span")]
1443            span,
1444          }),
1445          token::Value::BYTE(token::ByteValue::B64(Cow::Borrowed(b64))) => {
1446            Ok(Type2::B64ByteString {
1447              value: Cow::Borrowed(b64),
1448              #[cfg(feature = "ast-span")]
1449              span,
1450            })
1451          }
1452          token::Value::BYTE(token::ByteValue::B64(Cow::Owned(b64))) => Ok(Type2::B64ByteString {
1453            value: Cow::Owned(b64.to_owned()),
1454            #[cfg(feature = "ast-span")]
1455            span,
1456          }),
1457        }
1458      }
1459
1460      // typename [genericarg]
1461      Token::IDENT(ident, socket) => {
1462        #[cfg(feature = "ast-span")]
1463        let begin_type2_range = self.lexer_position.range.0;
1464        #[cfg(feature = "ast-span")]
1465        let begin_type2_line = self.lexer_position.line;
1466
1467        // optional genericarg detected
1468        if self.peek_token_is(&Token::LANGLEBRACKET) {
1469          let ident = self.identifier_from_ident_token(ident, *socket);
1470          let ga = self.parse_genericargs()?;
1471
1472          #[cfg(feature = "ast-span")]
1473          let end_type2_range = self.parser_position.range.1;
1474
1475          if ident.socket.is_none() {
1476            let mut is_generic_param = false;
1477            if let Some(idents) = &self.current_rule_generic_param_idents {
1478              is_generic_param = idents.contains(&ident.ident);
1479            }
1480
1481            #[cfg(feature = "ast-span")]
1482            if !is_generic_param && !self.typenames.contains(ident.ident) {
1483              self.unknown_rule_idents.push((ident.ident, ident.span));
1484            }
1485
1486            #[cfg(not(feature = "ast-span"))]
1487            if !is_generic_param && !self.typenames.contains(ident.ident) {
1488              self.unknown_rule_idents.push(ident.ident);
1489            }
1490          }
1491
1492          return Ok(Type2::Typename {
1493            ident,
1494            generic_args: Some(ga),
1495            #[cfg(feature = "ast-span")]
1496            span: (begin_type2_range, end_type2_range, begin_type2_line),
1497          });
1498        }
1499
1500        #[cfg(feature = "ast-span")]
1501        {
1502          self.parser_position.range = self.lexer_position.range;
1503          self.parser_position.line = self.lexer_position.line;
1504        }
1505
1506        let ident = self.identifier_from_ident_token(ident, *socket);
1507
1508        if ident.socket.is_none() {
1509          let mut is_generic_param = false;
1510          if let Some(idents) = &self.current_rule_generic_param_idents {
1511            is_generic_param = idents.contains(&ident.ident);
1512          }
1513
1514          #[cfg(feature = "ast-span")]
1515          if !is_generic_param && !self.typenames.contains(ident.ident) {
1516            self.unknown_rule_idents.push((ident.ident, ident.span));
1517          }
1518
1519          #[cfg(not(feature = "ast-span"))]
1520          if !is_generic_param && !self.typenames.contains(ident.ident) {
1521            self.unknown_rule_idents.push(ident.ident);
1522          }
1523        }
1524
1525        Ok(Type2::Typename {
1526          ident,
1527          generic_args: None,
1528          #[cfg(feature = "ast-span")]
1529          span: (
1530            self.parser_position.range.0,
1531            self.parser_position.range.1,
1532            self.parser_position.line,
1533          ),
1534        })
1535      }
1536
1537      // ( type )
1538      Token::LPAREN => {
1539        #[cfg(feature = "ast-span")]
1540        let begin_type2_range = self.lexer_position.range.0;
1541        #[cfg(feature = "ast-span")]
1542        let begin_type2_line = self.lexer_position.line;
1543
1544        self.next_token()?;
1545
1546        #[cfg(feature = "ast-comments")]
1547        let comments_before_type = self.collect_comments()?;
1548        #[cfg(not(feature = "ast-comments"))]
1549        self.advance_newline()?;
1550
1551        let pt = self.parse_type(None)?;
1552
1553        #[cfg(feature = "ast-span")]
1554        {
1555          self.parser_position.range.0 = begin_type2_range;
1556          self.parser_position.range.1 = self.lexer_position.range.1;
1557          self.parser_position.line = begin_type2_line;
1558        }
1559
1560        #[cfg(feature = "ast-comments")]
1561        let comments_after_type = self.collect_comments()?;
1562        #[cfg(not(feature = "ast-comments"))]
1563        self.advance_newline()?;
1564
1565        Ok(Type2::ParenthesizedType {
1566          #[cfg(feature = "ast-comments")]
1567          comments_before_type,
1568          #[cfg(feature = "ast-comments")]
1569          comments_after_type,
1570          pt,
1571          #[cfg(feature = "ast-span")]
1572          span: (
1573            self.parser_position.range.0,
1574            self.parser_position.range.1,
1575            self.parser_position.line,
1576          ),
1577        })
1578      }
1579
1580      // { group }
1581      Token::LBRACE => {
1582        #[cfg(feature = "ast-span")]
1583        let begin_type2_range = self.lexer_position.range.0;
1584        #[cfg(feature = "ast-span")]
1585        let begin_type2_line = self.lexer_position.line;
1586
1587        #[cfg(feature = "ast-comments")]
1588        let mut group = self.parse_group()?;
1589        #[cfg(not(feature = "ast-comments"))]
1590        let group = self.parse_group()?;
1591
1592        // if the group starts with a multi-line comment,
1593        // we take the first comment inside the 1st group to be comments_before_group
1594        #[cfg(feature = "ast-comments")]
1595        let comments_before_group = if let Some(GroupChoice {
1596          comments_before_grpchoice,
1597          ..
1598        }) = group.group_choices.first_mut()
1599        {
1600          comments_before_grpchoice
1601            .as_mut()
1602            .and_then(|comments| {
1603              if comments.0.len() > 1 {
1604                Some(comments.0.remove(0))
1605              } else {
1606                None
1607              }
1608            })
1609            .map(|comment| Comments(vec![comment]))
1610        } else {
1611          None
1612        };
1613
1614        #[cfg(feature = "ast-span")]
1615        let span = (
1616          begin_type2_range,
1617          self.lexer_position.range.1,
1618          begin_type2_line,
1619        );
1620
1621        #[cfg(feature = "ast-comments")]
1622        let comments_after_group = self.collect_comments()?;
1623        #[cfg(not(feature = "ast-comments"))]
1624        self.advance_newline()?;
1625
1626        Ok(Type2::Map {
1627          #[cfg(feature = "ast-comments")]
1628          comments_before_group,
1629          group,
1630          #[cfg(feature = "ast-span")]
1631          span,
1632          #[cfg(feature = "ast-comments")]
1633          comments_after_group,
1634        })
1635      }
1636
1637      // [ group ]
1638      Token::LBRACKET => {
1639        #[cfg(feature = "ast-span")]
1640        let begin_type2_range = self.lexer_position.range.0;
1641        #[cfg(feature = "ast-span")]
1642        let begin_type2_line = self.lexer_position.line;
1643
1644        #[cfg(feature = "ast-comments")]
1645        let mut group = self.parse_group()?;
1646        #[cfg(not(feature = "ast-comments"))]
1647        let group = self.parse_group()?;
1648
1649        // if the group starts with a multi-line comment,
1650        // we take the first comment inside the 1st group to be comments_before_group
1651        #[cfg(feature = "ast-comments")]
1652        let comments_before_group = if let Some(GroupChoice {
1653          comments_before_grpchoice,
1654          ..
1655        }) = group.group_choices.first_mut()
1656        {
1657          comments_before_grpchoice
1658            .as_mut()
1659            .and_then(|comments| {
1660              if comments.0.len() > 1 {
1661                Some(comments.0.remove(0))
1662              } else {
1663                None
1664              }
1665            })
1666            .map(|comment| Comments(vec![comment]))
1667        } else {
1668          None
1669        };
1670
1671        #[cfg(feature = "ast-span")]
1672        let span = (
1673          begin_type2_range,
1674          self.lexer_position.range.1,
1675          begin_type2_line,
1676        );
1677
1678        #[cfg(feature = "ast-comments")]
1679        let comments_after_group = self.collect_comments()?;
1680        #[cfg(not(feature = "ast-comments"))]
1681        self.advance_newline()?;
1682
1683        Ok(Type2::Array {
1684          #[cfg(feature = "ast-comments")]
1685          comments_before_group,
1686          group,
1687          #[cfg(feature = "ast-comments")]
1688          comments_after_group,
1689          #[cfg(feature = "ast-span")]
1690          span,
1691        })
1692      }
1693
1694      // ~ typename [genericarg]
1695      Token::UNWRAP => {
1696        self.next_token()?;
1697
1698        #[cfg(feature = "ast-comments")]
1699        let comments = self.collect_comments()?;
1700        #[cfg(not(feature = "ast-comments"))]
1701        self.advance_newline()?;
1702
1703        let ident = if let Some(ident) = self.cur_token.in_standard_prelude() {
1704          Some(self.identifier_from_ident_token(ident, None))
1705        } else if let Token::IDENT(ident, socket) = &self.cur_token {
1706          Some(self.identifier_from_ident_token(ident, *socket))
1707        } else {
1708          None
1709        };
1710
1711        if let Some(ident) = ident {
1712          if self.peek_token_is(&Token::LANGLEBRACKET) {
1713            self.next_token()?;
1714
1715            return Ok(Type2::Unwrap {
1716              #[cfg(feature = "ast-comments")]
1717              comments,
1718              ident,
1719              generic_args: Some(self.parse_genericargs()?),
1720              #[cfg(feature = "ast-span")]
1721              span: (0, 0, 0),
1722            });
1723          }
1724
1725          return Ok(Type2::Unwrap {
1726            #[cfg(feature = "ast-comments")]
1727            comments,
1728            ident,
1729            generic_args: None,
1730            #[cfg(feature = "ast-span")]
1731            span: (0, 0, 0),
1732          });
1733        }
1734
1735        self.errors.push(Error::PARSER {
1736          #[cfg(feature = "ast-span")]
1737          position: self.parser_position,
1738          msg: InvalidUnwrapSyntax.into(),
1739        });
1740
1741        Err(Error::INCREMENTAL)
1742      }
1743
1744      // & ( group )
1745      // & groupname [genericarg]
1746      Token::GTOCHOICE => {
1747        #[cfg(feature = "ast-span")]
1748        let begin_type2_range = self.lexer_position.range.0;
1749        #[cfg(feature = "ast-span")]
1750        let begin_type2_line = self.lexer_position.line;
1751
1752        self.next_token()?;
1753
1754        #[cfg(feature = "ast-comments")]
1755        let comments = self.collect_comments()?;
1756        #[cfg(not(feature = "ast-comments"))]
1757        self.advance_newline()?;
1758
1759        match &self.cur_token {
1760          Token::LPAREN => {
1761            self.next_token()?;
1762
1763            #[cfg(feature = "ast-comments")]
1764            let comments_before_group = self.collect_comments()?;
1765            #[cfg(not(feature = "ast-comments"))]
1766            self.advance_newline()?;
1767
1768            let group = self.parse_group()?;
1769
1770            #[cfg(feature = "ast-comments")]
1771            let comments_after_group = self.collect_comments()?;
1772            #[cfg(not(feature = "ast-comments"))]
1773            self.advance_newline()?;
1774
1775            Ok(Type2::ChoiceFromInlineGroup {
1776              #[cfg(feature = "ast-comments")]
1777              comments,
1778              #[cfg(feature = "ast-comments")]
1779              comments_before_group,
1780              group,
1781              #[cfg(feature = "ast-comments")]
1782              comments_after_group,
1783              #[cfg(feature = "ast-span")]
1784              span: (
1785                begin_type2_range,
1786                self.parser_position.range.1,
1787                begin_type2_line,
1788              ),
1789            })
1790          }
1791          Token::IDENT(ident, socket) => {
1792            let ident = self.identifier_from_ident_token(ident, *socket);
1793            if self.peek_token_is(&Token::LANGLEBRACKET) {
1794              self.next_token()?;
1795
1796              let generic_args = Some(self.parse_genericargs()?);
1797
1798              return Ok(Type2::ChoiceFromGroup {
1799                #[cfg(feature = "ast-comments")]
1800                comments,
1801                ident,
1802                generic_args,
1803                #[cfg(feature = "ast-span")]
1804                span: (
1805                  begin_type2_range,
1806                  self.parser_position.range.1,
1807                  begin_type2_line,
1808                ),
1809              });
1810            }
1811
1812            #[cfg(feature = "ast-span")]
1813            {
1814              self.parser_position.range.1 = self.lexer_position.range.1;
1815            }
1816
1817            Ok(Type2::ChoiceFromGroup {
1818              #[cfg(feature = "ast-comments")]
1819              comments,
1820              ident,
1821              generic_args: None,
1822              #[cfg(feature = "ast-span")]
1823              span: (
1824                begin_type2_range,
1825                self.parser_position.range.1,
1826                begin_type2_line,
1827              ),
1828            })
1829          }
1830          _ => {
1831            self.errors.push(Error::PARSER {
1832              #[cfg(feature = "ast-span")]
1833              position: self.parser_position,
1834              msg: InvalidGroupToChoiceEnumSyntax.into(),
1835            });
1836            Err(Error::INCREMENTAL)
1837          }
1838        }
1839      }
1840
1841      // # 6 ["." uint] ( type )
1842      // # DIGIT ["." uint]   ; major/ai
1843      // #                    ; any
1844      // Token::TAG(tag) => match tag {
1845      //   Tag::DATA(data) => Ok(Type2::TaggedData(data.clone())),
1846      //   Tag::MAJORTYPE(mt) => Ok(Type2::DataMajorType(*mt)),
1847      //   Tag::ANY => Ok(Type2::Any),
1848      // },
1849      Token::TAG(mt, constraint) => {
1850        #[cfg(feature = "ast-span")]
1851        let begin_type2_range = self.lexer_position.range.0;
1852        #[cfg(feature = "ast-span")]
1853        let begin_type2_line = self.lexer_position.line;
1854
1855        // Extract values to avoid borrow checker issues
1856        let mt_val = *mt;
1857        let constraint_val = *constraint;
1858
1859        match (mt_val, constraint_val) {
1860          // Tagged data item containing the given type as the tagged value
1861          (Some(6), tag) => {
1862            self.next_token()?;
1863            if !self.cur_token_is(Token::LPAREN) {
1864              self.errors.push(Error::PARSER {
1865                #[cfg(feature = "ast-span")]
1866                position: self.parser_position,
1867                msg: InvalidTagSyntax.into(),
1868              });
1869
1870              return Err(Error::INCREMENTAL);
1871            }
1872
1873            self.next_token()?;
1874
1875            #[cfg(feature = "ast-comments")]
1876            let comments_before_type = self.collect_comments()?;
1877            #[cfg(not(feature = "ast-comments"))]
1878            self.advance_newline()?;
1879
1880            let t = self.parse_type(None)?;
1881
1882            #[cfg(feature = "ast-comments")]
1883            let comments_after_type = self.collect_comments()?;
1884            #[cfg(not(feature = "ast-comments"))]
1885            self.advance_newline()?;
1886
1887            if !self.cur_token_is(Token::RPAREN) {
1888              self.errors.push(Error::PARSER {
1889                #[cfg(feature = "ast-span")]
1890                position: self.parser_position,
1891                msg: InvalidTagSyntax.into(),
1892              });
1893
1894              return Err(Error::INCREMENTAL);
1895            }
1896
1897            Ok(Type2::TaggedData {
1898              tag,
1899              #[cfg(feature = "ast-comments")]
1900              comments_before_type,
1901              t,
1902              #[cfg(feature = "ast-comments")]
1903              comments_after_type,
1904              #[cfg(feature = "ast-span")]
1905              span: (
1906                begin_type2_range,
1907                self.parser_position.range.1,
1908                begin_type2_line,
1909              ),
1910            })
1911          }
1912          // Tagged data of a major type
1913          (Some(mt), constraint) => Ok(Type2::DataMajorType {
1914            mt,
1915            constraint,
1916            #[cfg(feature = "ast-span")]
1917            span: (
1918              begin_type2_range,
1919              self.lexer_position.range.1,
1920              begin_type2_line,
1921            ),
1922          }),
1923          #[cfg(feature = "ast-span")]
1924          _ => Ok(Type2::Any {
1925            span: (
1926              begin_type2_range,
1927              self.lexer_position.range.1,
1928              begin_type2_line,
1929            ),
1930          }),
1931          #[cfg(not(feature = "ast-span"))]
1932          _ => Ok(Type2::Any {}),
1933        }
1934      }
1935      _ => {
1936        #[cfg(feature = "ast-comments")]
1937        self.collect_comments()?;
1938        #[cfg(not(feature = "ast-comments"))]
1939        self.advance_newline()?;
1940
1941        match self.cur_token.in_standard_prelude() {
1942          Some(s) => {
1943            let ident = self.identifier_from_ident_token(s, None);
1944            #[cfg(feature = "ast-span")]
1945            {
1946              self.parser_position.range = self.lexer_position.range;
1947              self.parser_position.line = self.lexer_position.line;
1948            }
1949
1950            Ok(Type2::Typename {
1951              ident,
1952              generic_args: None,
1953              #[cfg(feature = "ast-span")]
1954              span: (
1955                self.parser_position.range.0,
1956                self.parser_position.range.1,
1957                self.parser_position.line,
1958              ),
1959            })
1960          }
1961          None => {
1962            #[cfg(feature = "ast-span")]
1963            {
1964              self.parser_position.line = self.lexer_position.line;
1965              self.parser_position.range = self.lexer_position.range;
1966            }
1967
1968            if let Token::COLON | Token::ARROWMAP = &self.cur_token {
1969              self.errors.push(Error::PARSER {
1970                #[cfg(feature = "ast-span")]
1971                position: self.parser_position,
1972                msg: MissingGroupEntryMemberKey.into(),
1973              });
1974
1975              return Err(Error::INCREMENTAL);
1976            }
1977
1978            if let Token::RBRACE | Token::RBRACKET | Token::RPAREN = &self.cur_token {
1979              self.errors.push(Error::PARSER {
1980                #[cfg(feature = "ast-span")]
1981                position: self.parser_position,
1982                msg: MissingGroupEntry.into(),
1983              });
1984
1985              return Err(Error::INCREMENTAL);
1986            }
1987
1988            self.errors.push(Error::PARSER {
1989              #[cfg(feature = "ast-span")]
1990              position: self.parser_position,
1991              msg: InvalidGroupEntrySyntax.into(),
1992            });
1993
1994            Err(Error::INCREMENTAL)
1995          }
1996        }
1997      }
1998    };
1999
2000    #[cfg(feature = "ast-span")]
2001    {
2002      self.parser_position.range.1 = self.lexer_position.range.1;
2003    }
2004
2005    self.next_token()?;
2006
2007    t2
2008  }
2009
2010  #[allow(missing_docs)]
2011  pub fn parse_group(&mut self) -> Result<Group<'a>> {
2012    #[cfg(feature = "ast-span")]
2013    let begin_group_range =
2014      if let Token::LBRACE | Token::LPAREN | Token::LBRACKET | Token::GCHOICE = &self.cur_token {
2015        self.peek_lexer_position.range.0
2016      } else {
2017        self.lexer_position.range.0
2018      };
2019
2020    let closing_delimiter = token::closing_delimiter(&self.cur_token);
2021
2022    let mut group = Group {
2023      group_choices: Vec::new(),
2024      #[cfg(feature = "ast-span")]
2025      span: (begin_group_range, 0, self.lexer_position.line),
2026    };
2027
2028    group.group_choices.push(self.parse_grpchoice()?);
2029
2030    while let Token::GCHOICE = &self.cur_token {
2031      group.group_choices.push(self.parse_grpchoice()?);
2032    }
2033
2034    #[cfg(feature = "ast-span")]
2035    {
2036      group.span.1 = self.parser_position.range.1;
2037    }
2038
2039    if let Some(cd) = closing_delimiter.as_ref() {
2040      if cd != &self.cur_token {
2041        self.errors.push(Error::PARSER {
2042          #[cfg(feature = "ast-span")]
2043          position: self.lexer_position,
2044          msg: MissingClosingDelimiter.into(),
2045        });
2046
2047        return Err(Error::INCREMENTAL);
2048      }
2049    }
2050
2051    Ok(group)
2052  }
2053
2054  #[allow(missing_docs)]
2055  pub fn parse_grpchoice(&mut self) -> Result<GroupChoice<'a>> {
2056    let mut grpchoice = GroupChoice {
2057      group_entries: Vec::new(),
2058      #[cfg(feature = "ast-comments")]
2059      comments_before_grpchoice: None,
2060      #[cfg(feature = "ast-span")]
2061      span: (self.lexer_position.range.0, 0, self.lexer_position.line),
2062    };
2063
2064    // Track whether we're in an array context to pass to parse_grpent
2065    let mut in_array_context = false;
2066
2067    if let Token::GCHOICE = &self.cur_token {
2068      self.next_token()?;
2069
2070      #[cfg(feature = "ast-comments")]
2071      {
2072        grpchoice.comments_before_grpchoice = self.collect_comments()?;
2073      }
2074      #[cfg(not(feature = "ast-comments"))]
2075      self.advance_newline()?;
2076
2077      #[cfg(feature = "ast-span")]
2078      {
2079        grpchoice.span.0 = self.lexer_position.range.0;
2080      }
2081    } else if let Token::LBRACKET = &self.cur_token {
2082      // This is an array context
2083      in_array_context = true;
2084      self.next_token()?;
2085
2086      #[cfg(feature = "ast-span")]
2087      {
2088        grpchoice.span.0 = self.lexer_position.range.0;
2089      }
2090
2091      #[cfg(feature = "ast-comments")]
2092      {
2093        grpchoice.comments_before_grpchoice = self.collect_comments()?;
2094      }
2095      #[cfg(not(feature = "ast-comments"))]
2096      self.advance_newline()?;
2097    } else if let Token::LBRACE = &self.cur_token {
2098      // This is a map/object context, not an array
2099      self.next_token()?;
2100
2101      #[cfg(feature = "ast-span")]
2102      {
2103        grpchoice.span.0 = self.lexer_position.range.0;
2104      }
2105
2106      #[cfg(feature = "ast-comments")]
2107      {
2108        grpchoice.comments_before_grpchoice = self.collect_comments()?;
2109      }
2110      #[cfg(not(feature = "ast-comments"))]
2111      self.advance_newline()?;
2112    };
2113
2114    // TODO: The logic in this while loop is quite messy. Need to figure out a
2115    // better way to advance the token when parsing the entries in a group
2116    // choice
2117    while !self.cur_token_is(Token::RBRACE)
2118      && !self.cur_token_is(Token::RPAREN)
2119      && !self.cur_token_is(Token::RBRACKET)
2120      && !self.cur_token_is(Token::EOF)
2121    {
2122      let ge = if in_array_context {
2123        // In array context, use from_rule=false and prevent TypeGroupname conversion
2124        self.parse_grpent_array_context(false)?
2125      } else {
2126        // In other contexts (parentheses, braces), allow TypeGroupname conversion
2127        self.parse_grpent(false)?
2128      };
2129
2130      if let Token::GCHOICE = &self.cur_token {
2131        grpchoice.group_entries.push((
2132          ge,
2133          OptionalComma {
2134            optional_comma: false,
2135            #[cfg(feature = "ast-comments")]
2136            trailing_comments: None,
2137            _a: PhantomData,
2138          },
2139        ));
2140
2141        #[cfg(feature = "ast-span")]
2142        {
2143          grpchoice.span.1 = self.parser_position.range.1;
2144        }
2145
2146        return Ok(grpchoice);
2147      }
2148
2149      // Don't advance the token if it is part of a member key, comma or an
2150      // opening or closing map/group delimiter. Otherwise, advance
2151      if !self.cur_token_is(Token::RPAREN)
2152        && !self.cur_token_is(Token::RBRACE)
2153        && !self.cur_token_is(Token::RBRACKET)
2154        && !self.cur_token_is(Token::LPAREN)
2155        && !self.cur_token_is(Token::LBRACE)
2156        && !self.cur_token_is(Token::LBRACKET)
2157        && !self.cur_token_is(Token::COMMA)
2158        && !self.cur_token_is(Token::OPTIONAL)
2159        && !self.cur_token_is(Token::ONEORMORE)
2160        && !self.cur_token_is(Token::ASTERISK)
2161        && !self.peek_token_is(&Token::COLON)
2162        && !self.peek_token_is(&Token::ARROWMAP)
2163        && !self.cur_token_is(Token::EOF)
2164        && !matches!(self.cur_token, Token::IDENT(..))
2165      {
2166        #[cfg(feature = "ast-span")]
2167        {
2168          self.parser_position.range.1 = self.lexer_position.range.1;
2169        }
2170        self.next_token()?;
2171      }
2172
2173      let mut optional_comma = false;
2174
2175      if let Token::COMMA = &self.cur_token {
2176        optional_comma = true;
2177
2178        #[cfg(feature = "ast-span")]
2179        {
2180          self.parser_position.range.1 = self.lexer_position.range.1;
2181        }
2182        self.next_token()?;
2183      }
2184
2185      #[cfg(feature = "ast-comments")]
2186      let trailing_comments = self.collect_comments()?;
2187      #[cfg(not(feature = "ast-comments"))]
2188      self.advance_newline()?;
2189
2190      grpchoice.group_entries.push((
2191        ge,
2192        OptionalComma {
2193          optional_comma,
2194          #[cfg(feature = "ast-comments")]
2195          trailing_comments,
2196          _a: PhantomData,
2197        },
2198      ));
2199    }
2200
2201    #[cfg(feature = "ast-span")]
2202    {
2203      grpchoice.span.1 = self.parser_position.range.1;
2204    }
2205
2206    Ok(grpchoice)
2207  }
2208
2209  #[allow(missing_docs)]
2210  pub fn parse_grpent(&mut self, from_rule: bool) -> Result<GroupEntry<'a>> {
2211    self.parse_grpent_internal(from_rule, false)
2212  }
2213
2214  fn parse_grpent_array_context(&mut self, from_rule: bool) -> Result<GroupEntry<'a>> {
2215    self.parse_grpent_internal(from_rule, true)
2216  }
2217
2218  fn parse_grpent_internal(
2219    &mut self,
2220    from_rule: bool,
2221    in_array_context: bool,
2222  ) -> Result<GroupEntry<'a>> {
2223    #[cfg(feature = "ast-span")]
2224    let begin_grpent_range = self.lexer_position.range.0;
2225    #[cfg(feature = "ast-span")]
2226    let begin_grpent_line = self.lexer_position.line;
2227
2228    let occur = self.parse_occur(true)?;
2229
2230    // If parsing group entry from a rule, set member key to none
2231    let member_key = if from_rule {
2232      None
2233    } else {
2234      self.parse_memberkey(true)?
2235    };
2236
2237    if self.cur_token_is(Token::LPAREN) && member_key.is_none() {
2238      self.next_token()?;
2239
2240      #[cfg(feature = "ast-comments")]
2241      let comments_before_group = self.collect_comments()?;
2242      #[cfg(not(feature = "ast-comments"))]
2243      self.advance_newline()?;
2244
2245      let group = self.parse_group()?;
2246
2247      #[cfg(feature = "ast-span")]
2248      let mut span = (
2249        begin_grpent_range,
2250        self.parser_position.range.1,
2251        begin_grpent_line,
2252      );
2253
2254      #[cfg(feature = "ast-span")]
2255      {
2256        self.parser_position.range.1 = self.lexer_position.range.1;
2257      }
2258
2259      #[cfg(feature = "ast-comments")]
2260      let comments_after_group = self.collect_comments()?;
2261      #[cfg(not(feature = "ast-comments"))]
2262      self.advance_newline()?;
2263
2264      if !self.cur_token_is(Token::RPAREN) {
2265        self.errors.push(Error::PARSER {
2266          #[cfg(feature = "ast-span")]
2267          position: self.lexer_position,
2268          msg: MissingClosingParend.into(),
2269        });
2270        return Err(Error::INCREMENTAL);
2271      }
2272
2273      #[cfg(feature = "ast-span")]
2274      {
2275        span.1 = self.parser_position.range.1;
2276      }
2277
2278      self.next_token()?;
2279
2280      return Ok(GroupEntry::InlineGroup {
2281        occur,
2282        group,
2283        #[cfg(feature = "ast-comments")]
2284        comments_before_group,
2285        #[cfg(feature = "ast-comments")]
2286        comments_after_group,
2287        #[cfg(feature = "ast-span")]
2288        span,
2289      });
2290    }
2291
2292    #[cfg(feature = "ast-span")]
2293    let mut span = (
2294      begin_grpent_range,
2295      self.parser_position.range.1,
2296      begin_grpent_line,
2297    );
2298
2299    match member_key {
2300      Some(MemberKey::NonMemberKey {
2301        #[cfg(feature = "ast-comments")]
2302          non_member_key: NonMemberKey::Type(mut entry_type),
2303        #[cfg(not(feature = "ast-comments"))]
2304          non_member_key: NonMemberKey::Type(entry_type),
2305        #[cfg(feature = "ast-comments")]
2306        comments_before_type_or_group,
2307        #[cfg(feature = "ast-comments")]
2308        comments_after_type_or_group,
2309      }) => {
2310        #[cfg(feature = "ast-span")]
2311        if let Token::COMMA = &self.cur_token {
2312          span.1 = self.lexer_position.range.1;
2313        }
2314
2315        #[cfg(feature = "ast-comments")]
2316        let trailing_comments = entry_type.take_comments_after_type();
2317
2318        #[cfg(feature = "ast-span")]
2319        if let Some((name, generic_args, _)) = entry_type.groupname_entry() {
2320          if self.groupnames.contains(name.ident) || matches!(name.socket, Some(SocketPlug::GROUP))
2321          {
2322            if name.socket.is_none() {
2323              self.unknown_rule_idents = self
2324                .unknown_rule_idents
2325                .clone()
2326                .into_iter()
2327                .filter(|(ident, _)| ident != &name.ident)
2328                .collect();
2329            }
2330            return Ok(GroupEntry::TypeGroupname {
2331              ge: TypeGroupnameEntry {
2332                occur,
2333                name,
2334                generic_args,
2335              },
2336              #[cfg(feature = "ast-comments")]
2337              leading_comments: comments_before_type_or_group,
2338              #[cfg(feature = "ast-comments")]
2339              trailing_comments,
2340              span,
2341            });
2342          }
2343        }
2344
2345        #[cfg(not(feature = "ast-span"))]
2346        if let Some((name, generic_args)) = entry_type.groupname_entry() {
2347          if self.groupnames.contains(name.ident) || matches!(name.socket, Some(SocketPlug::GROUP))
2348          {
2349            if name.socket.is_none() {
2350              self.unknown_rule_idents = self
2351                .unknown_rule_idents
2352                .clone()
2353                .into_iter()
2354                .filter(|ident| ident != &name.ident)
2355                .collect();
2356            }
2357            return Ok(GroupEntry::TypeGroupname {
2358              ge: TypeGroupnameEntry {
2359                occur,
2360                name,
2361                generic_args,
2362              },
2363              #[cfg(feature = "ast-comments")]
2364              leading_comments: comments_before_type_or_group,
2365              #[cfg(feature = "ast-comments")]
2366              trailing_comments,
2367            });
2368          }
2369        }
2370
2371        // A parse tree that returns a type instead of a member key needs to
2372        // advance the token in the case of "(", "{" or "[". Otherwise, infinite
2373        // recursive loop occurs
2374        if let Token::LPAREN | Token::LBRACE | Token::LBRACKET = self.cur_token {
2375          self.next_token()?;
2376        }
2377
2378        #[cfg(feature = "ast-comments")]
2379        let trailing_comments = if let Some(comments) = entry_type.split_comments_after_type() {
2380          Some(comments)
2381        } else {
2382          comments_after_type_or_group
2383        };
2384
2385        Ok(GroupEntry::ValueMemberKey {
2386          ge: Box::from(ValueMemberKeyEntry {
2387            occur,
2388            member_key: None,
2389            entry_type,
2390          }),
2391          #[cfg(feature = "ast-comments")]
2392          leading_comments: comments_before_type_or_group,
2393          #[cfg(feature = "ast-comments")]
2394          trailing_comments,
2395          #[cfg(feature = "ast-span")]
2396          span,
2397        })
2398      }
2399      Some(MemberKey::NonMemberKey {
2400        non_member_key: NonMemberKey::Group(group),
2401        #[cfg(feature = "ast-comments")]
2402        comments_before_type_or_group,
2403        #[cfg(feature = "ast-comments")]
2404        comments_after_type_or_group,
2405      }) => {
2406        #[cfg(feature = "ast-span")]
2407        if let Token::COMMA = &self.cur_token {
2408          span.1 = self.lexer_position.range.1;
2409        }
2410
2411        Ok(GroupEntry::InlineGroup {
2412          occur,
2413          group,
2414          #[cfg(feature = "ast-span")]
2415          span,
2416          #[cfg(feature = "ast-comments")]
2417          comments_before_group: comments_before_type_or_group,
2418          #[cfg(feature = "ast-comments")]
2419          comments_after_group: comments_after_type_or_group,
2420        })
2421      }
2422      member_key @ Some(_) => {
2423        #[cfg(feature = "ast-comments")]
2424        let mut entry_type = self.parse_type(None)?;
2425        #[cfg(not(feature = "ast-comments"))]
2426        let entry_type = self.parse_type(None)?;
2427
2428        #[cfg(feature = "ast-comments")]
2429        let trailing_comments = entry_type.split_comments_after_type();
2430
2431        #[cfg(feature = "ast-span")]
2432        {
2433          span.1 = self.parser_position.range.1;
2434        }
2435
2436        #[cfg(feature = "ast-span")]
2437        if let Token::COMMA = &self.cur_token {
2438          span.1 = self.lexer_position.range.1;
2439        }
2440
2441        Ok(GroupEntry::ValueMemberKey {
2442          ge: Box::from(ValueMemberKeyEntry {
2443            occur,
2444            member_key,
2445            entry_type,
2446          }),
2447          #[cfg(feature = "ast-comments")]
2448          leading_comments: None,
2449          #[cfg(feature = "ast-comments")]
2450          trailing_comments,
2451          #[cfg(feature = "ast-span")]
2452          span,
2453        })
2454      }
2455      None => {
2456        #[cfg(feature = "ast-comments")]
2457        let mut entry_type = self.parse_type(None)?;
2458        #[cfg(not(feature = "ast-comments"))]
2459        let entry_type = self.parse_type(None)?;
2460
2461        #[cfg(feature = "ast-span")]
2462        {
2463          span.1 = self.parser_position.range.1;
2464        }
2465
2466        #[cfg(feature = "ast-comments")]
2467        let trailing_comments = if let Some(comments) = entry_type.take_comments_after_type() {
2468          Some(comments)
2469        } else {
2470          self.collect_comments()?
2471        };
2472        #[cfg(not(feature = "ast-comments"))]
2473        self.advance_newline()?;
2474
2475        #[cfg(feature = "ast-span")]
2476        if let Token::COMMA = &self.cur_token {
2477          span.1 = self.lexer_position.range.1;
2478        }
2479
2480        #[cfg(feature = "ast-span")]
2481        if let Some((name, generic_args, _)) = entry_type.groupname_entry() {
2482          // Check if it's a known groupname OR if it could be a forward reference to a group
2483          if self.groupnames.contains(name.ident) || matches!(name.socket, Some(SocketPlug::GROUP))
2484          {
2485            if generic_args.is_some() && self.peek_token_is(&Token::LANGLEBRACKET) {
2486              while !self.peek_token_is(&Token::RANGLEBRACKET) {
2487                self.next_token()?;
2488              }
2489
2490              self.next_token()?;
2491            }
2492
2493            if name.socket.is_none() {
2494              self.unknown_rule_idents = self
2495                .unknown_rule_idents
2496                .clone()
2497                .into_iter()
2498                .filter(|(ident, _)| ident != &name.ident)
2499                .collect();
2500            }
2501            return Ok(GroupEntry::TypeGroupname {
2502              ge: TypeGroupnameEntry {
2503                occur,
2504                name,
2505                generic_args,
2506              },
2507              #[cfg(feature = "ast-comments")]
2508              leading_comments: None,
2509              #[cfg(feature = "ast-comments")]
2510              trailing_comments,
2511              span,
2512            });
2513          }
2514        }
2515
2516        #[cfg(not(feature = "ast-span"))]
2517        if let Some((name, generic_args)) = entry_type.groupname_entry() {
2518          if self.groupnames.contains(name.ident) || matches!(name.socket, Some(SocketPlug::GROUP))
2519          {
2520            if generic_args.is_some() && self.peek_token_is(&Token::LANGLEBRACKET) {
2521              while !self.peek_token_is(&Token::RANGLEBRACKET) {
2522                self.next_token()?;
2523              }
2524
2525              self.next_token()?;
2526            }
2527
2528            if name.socket.is_none() {
2529              self.unknown_rule_idents = self
2530                .unknown_rule_idents
2531                .clone()
2532                .into_iter()
2533                .filter(|ident| ident != &name.ident)
2534                .collect();
2535            }
2536            return Ok(GroupEntry::TypeGroupname {
2537              ge: TypeGroupnameEntry {
2538                occur,
2539                name,
2540                generic_args,
2541              },
2542              #[cfg(feature = "ast-comments")]
2543              leading_comments: None,
2544              #[cfg(feature = "ast-comments")]
2545              trailing_comments,
2546            });
2547          }
2548        }
2549
2550        // If we have a simple identifier that could be a group reference (even if not yet defined),
2551        // create a TypeGroupname entry instead of a ValueMemberKey with no member_key.
2552        //
2553        // ISSUE #268 FIX: Only prevent TypeGroupname conversion when we're explicitly in an
2554        // array context. This maintains backwards compatibility for arrays while allowing
2555        // group references in parentheses.
2556        #[cfg(feature = "ast-span")]
2557        if !from_rule && !in_array_context && member_key.is_none() {
2558          if let Some((name, generic_args, _)) = entry_type.groupname_entry() {
2559            return Ok(GroupEntry::TypeGroupname {
2560              ge: TypeGroupnameEntry {
2561                occur,
2562                name,
2563                generic_args,
2564              },
2565              #[cfg(feature = "ast-comments")]
2566              leading_comments: None,
2567              #[cfg(feature = "ast-comments")]
2568              trailing_comments,
2569              span,
2570            });
2571          }
2572        }
2573
2574        #[cfg(not(feature = "ast-span"))]
2575        if !from_rule && !in_array_context && member_key.is_none() {
2576          if let Some((name, generic_args)) = entry_type.groupname_entry() {
2577            return Ok(GroupEntry::TypeGroupname {
2578              ge: TypeGroupnameEntry {
2579                occur,
2580                name,
2581                generic_args,
2582              },
2583              #[cfg(feature = "ast-comments")]
2584              leading_comments: None,
2585              #[cfg(feature = "ast-comments")]
2586              trailing_comments,
2587            });
2588          }
2589        }
2590
2591        Ok(GroupEntry::ValueMemberKey {
2592          ge: Box::from(ValueMemberKeyEntry {
2593            occur,
2594            member_key: None,
2595            entry_type,
2596          }),
2597          #[cfg(feature = "ast-comments")]
2598          leading_comments: None,
2599          #[cfg(feature = "ast-comments")]
2600          trailing_comments,
2601          #[cfg(feature = "ast-span")]
2602          span,
2603        })
2604      }
2605    }
2606  }
2607
2608  // An ident memberkey could one of the following:
2609  //    type1 S ["^" S] "=>"
2610  //  / bareword S ":
2611  fn parse_memberkey_from_ident(
2612    &mut self,
2613    is_optional: bool,
2614    ident: &'a str,
2615    socket: Option<token::SocketPlug>,
2616    #[cfg(feature = "ast-span")] begin_memberkey_range: usize,
2617    #[cfg(feature = "ast-span")] begin_memberkey_line: usize,
2618  ) -> Result<Option<MemberKey<'a>>> {
2619    if !self.peek_token_is(&Token::COLON)
2620      && !self.peek_token_is(&Token::ARROWMAP)
2621      && !self.peek_token_is(&Token::CUT)
2622      && is_optional
2623    {
2624      return Ok(None);
2625    }
2626
2627    #[cfg(feature = "ast-span")]
2628    {
2629      self.parser_position.range.1 = self.peek_lexer_position.range.1;
2630    }
2631
2632    #[cfg(feature = "ast-span")]
2633    let end_t1_range = self.lexer_position.range.1;
2634
2635    #[cfg(feature = "ast-span")]
2636    let mut ident = self.identifier_from_ident_token(ident, socket);
2637    #[cfg(not(feature = "ast-span"))]
2638    let ident = self.identifier_from_ident_token(ident, socket);
2639    #[cfg(feature = "ast-span")]
2640    {
2641      ident.span = (begin_memberkey_range, end_t1_range, begin_memberkey_line);
2642    }
2643
2644    self.next_token()?;
2645
2646    #[cfg(feature = "ast-comments")]
2647    let comments_before_cut = self.collect_comments()?;
2648    #[cfg(not(feature = "ast-comments"))]
2649    self.advance_newline()?;
2650
2651    let mk = if let Token::CUT = &self.cur_token {
2652      self.next_token()?;
2653
2654      #[cfg(feature = "ast-comments")]
2655      let comments_after_cut = self.collect_comments()?;
2656      #[cfg(not(feature = "ast-comments"))]
2657      self.advance_newline()?;
2658
2659      if !self.cur_token_is(Token::ARROWMAP) {
2660        self.errors.push(Error::PARSER {
2661          #[cfg(feature = "ast-span")]
2662          position: self.lexer_position,
2663          msg: InvalidMemberKeyArrowMapSyntax.into(),
2664        });
2665        return Err(Error::INCREMENTAL);
2666      }
2667
2668      #[cfg(feature = "ast-span")]
2669      let end_memberkey_range = self.lexer_position.range.1;
2670
2671      #[cfg(feature = "ast-comments")]
2672      let comments_after_arrowmap = if let Token::COMMENT(_) = self.peek_token {
2673        self.next_token()?;
2674
2675        self.collect_comments()?
2676      } else {
2677        None
2678      };
2679
2680      #[cfg(not(feature = "ast-comments"))]
2681      self.advance_newline()?;
2682
2683      let t1 = MemberKey::Type1 {
2684        t1: Box::from(Type1 {
2685          type2: Type2::Typename {
2686            ident,
2687            generic_args: None,
2688            #[cfg(feature = "ast-span")]
2689            span: (begin_memberkey_range, end_t1_range, begin_memberkey_line),
2690          },
2691          operator: None,
2692          #[cfg(feature = "ast-comments")]
2693          comments_after_type: None,
2694          #[cfg(feature = "ast-span")]
2695          span: (begin_memberkey_range, end_t1_range, begin_memberkey_line),
2696        }),
2697        #[cfg(feature = "ast-comments")]
2698        comments_before_cut,
2699        is_cut: true,
2700        #[cfg(feature = "ast-comments")]
2701        comments_after_cut,
2702        #[cfg(feature = "ast-comments")]
2703        comments_after_arrowmap,
2704        #[cfg(feature = "ast-span")]
2705        span: (
2706          begin_memberkey_range,
2707          end_memberkey_range,
2708          begin_memberkey_line,
2709        ),
2710      };
2711
2712      self.next_token()?;
2713
2714      Some(t1)
2715    } else if let Token::ARROWMAP = &self.cur_token {
2716      #[cfg(feature = "ast-span")]
2717      let end_memberkey_range = self.lexer_position.range.1;
2718
2719      #[cfg(feature = "ast-comments")]
2720      let comments_after_arrowmap = if let Token::COMMENT(_) = &self.peek_token {
2721        self.next_token()?;
2722
2723        self.collect_comments()?
2724      } else {
2725        None
2726      };
2727
2728      #[cfg(not(feature = "ast-comments"))]
2729      self.advance_newline()?;
2730
2731      let t1 = MemberKey::Type1 {
2732        t1: Box::from(Type1 {
2733          type2: Type2::Typename {
2734            ident,
2735            generic_args: None,
2736            #[cfg(feature = "ast-span")]
2737            span: (begin_memberkey_range, end_t1_range, begin_memberkey_line),
2738          },
2739          operator: None,
2740          #[cfg(feature = "ast-comments")]
2741          comments_after_type: None,
2742          #[cfg(feature = "ast-span")]
2743          span: (begin_memberkey_range, end_t1_range, begin_memberkey_line),
2744        }),
2745        #[cfg(feature = "ast-comments")]
2746        comments_before_cut,
2747        is_cut: false,
2748        #[cfg(feature = "ast-comments")]
2749        comments_after_cut: None,
2750        #[cfg(feature = "ast-comments")]
2751        comments_after_arrowmap,
2752        #[cfg(feature = "ast-span")]
2753        span: (
2754          begin_memberkey_range,
2755          end_memberkey_range,
2756          begin_memberkey_line,
2757        ),
2758      };
2759
2760      self.next_token()?;
2761
2762      #[cfg(feature = "ast-comments")]
2763      let _ = self.collect_comments()?;
2764      #[cfg(not(feature = "ast-comments"))]
2765      self.advance_newline()?;
2766
2767      Some(t1)
2768    } else {
2769      if let Token::COLON = &self.cur_token {
2770        self.next_token()?;
2771      }
2772
2773      #[cfg(feature = "ast-comments")]
2774      let comments_after_colon = self.collect_comments()?;
2775      #[cfg(not(feature = "ast-comments"))]
2776      self.advance_newline()?;
2777
2778      Some(MemberKey::Bareword {
2779        ident,
2780        #[cfg(feature = "ast-comments")]
2781        comments: comments_before_cut,
2782        #[cfg(feature = "ast-comments")]
2783        comments_after_colon,
2784        #[cfg(feature = "ast-span")]
2785        span: (
2786          begin_memberkey_range,
2787          self.parser_position.range.1,
2788          begin_memberkey_line,
2789        ),
2790      })
2791    };
2792
2793    Ok(mk)
2794  }
2795
2796  #[allow(missing_docs)]
2797  pub fn parse_memberkey(&mut self, is_optional: bool) -> Result<Option<MemberKey<'a>>> {
2798    #[cfg(feature = "ast-span")]
2799    let begin_memberkey_range = self.lexer_position.range.0;
2800    #[cfg(feature = "ast-span")]
2801    let begin_memberkey_line = self.lexer_position.line;
2802
2803    if let Some(t) = self.cur_token.in_standard_prelude() {
2804      return self.parse_memberkey_from_ident(
2805        is_optional,
2806        t,
2807        None,
2808        #[cfg(feature = "ast-span")]
2809        begin_memberkey_range,
2810        #[cfg(feature = "ast-span")]
2811        begin_memberkey_line,
2812      );
2813    }
2814
2815    match &self.cur_token {
2816      Token::IDENT(ident, socket) => {
2817        let ident = *ident;
2818        let socket = *socket;
2819
2820        self.parse_memberkey_from_ident(
2821          is_optional,
2822          ident,
2823          socket,
2824          #[cfg(feature = "ast-span")]
2825          begin_memberkey_range,
2826          #[cfg(feature = "ast-span")]
2827          begin_memberkey_line,
2828        )
2829      }
2830      Token::VALUE(value) => {
2831        if !self.peek_token_is(&Token::COLON)
2832          && !self.peek_token_is(&Token::ARROWMAP)
2833          && !self.peek_token_is(&Token::CUT)
2834          && is_optional
2835        {
2836          return Ok(None);
2837        }
2838
2839        #[cfg(feature = "ast-span")]
2840        {
2841          self.parser_position.range.1 = self.peek_lexer_position.range.1;
2842        }
2843
2844        let value = value.clone();
2845
2846        let t1 = self.parse_type1(None)?;
2847
2848        #[cfg(feature = "ast-comments")]
2849        let comments_before_cut = self.collect_comments()?;
2850        #[cfg(not(feature = "ast-comments"))]
2851        self.advance_newline()?;
2852
2853        let mk = if let Token::CUT = &self.cur_token {
2854          self.next_token()?;
2855
2856          #[cfg(feature = "ast-comments")]
2857          let comments_after_cut = self.collect_comments()?;
2858          #[cfg(not(feature = "ast-comments"))]
2859          self.advance_newline()?;
2860
2861          if !self.cur_token_is(Token::ARROWMAP) {
2862            self.errors.push(Error::PARSER {
2863              #[cfg(feature = "ast-span")]
2864              position: self.lexer_position,
2865              msg: InvalidMemberKeyArrowMapSyntax.into(),
2866            });
2867            return Err(Error::INCREMENTAL);
2868          }
2869
2870          #[cfg(feature = "ast-span")]
2871          let end_memberkey_range = self.lexer_position.range.1;
2872
2873          self.next_token()?;
2874
2875          #[cfg(feature = "ast-comments")]
2876          let memberkey_comments = self.collect_comments()?;
2877          #[cfg(not(feature = "ast-comments"))]
2878          self.advance_newline()?;
2879
2880          Some(MemberKey::Type1 {
2881            t1: Box::from(t1),
2882            #[cfg(feature = "ast-comments")]
2883            comments_before_cut,
2884            is_cut: true,
2885            #[cfg(feature = "ast-comments")]
2886            comments_after_cut,
2887            #[cfg(feature = "ast-comments")]
2888            comments_after_arrowmap: memberkey_comments,
2889            #[cfg(feature = "ast-span")]
2890            span: (
2891              begin_memberkey_range,
2892              end_memberkey_range,
2893              begin_memberkey_line,
2894            ),
2895          })
2896        } else {
2897          #[cfg(feature = "ast-comments")]
2898          let comments = self.collect_comments()?;
2899          #[cfg(not(feature = "ast-comments"))]
2900          self.advance_newline()?;
2901
2902          if !self.cur_token_is(Token::ARROWMAP) && !self.cur_token_is(Token::COLON) {
2903            self.errors.push(Error::PARSER {
2904              #[cfg(feature = "ast-span")]
2905              position: self.lexer_position,
2906              msg: InvalidMemberKeySyntax.into(),
2907            });
2908            return Err(Error::INCREMENTAL);
2909          }
2910
2911          #[cfg(feature = "ast-span")]
2912          {
2913            self.parser_position.range.1 = self.lexer_position.range.1;
2914          }
2915
2916          self.next_token()?;
2917
2918          #[cfg(feature = "ast-comments")]
2919          let memberkey_comments = self.collect_comments()?;
2920          #[cfg(not(feature = "ast-comments"))]
2921          self.advance_newline()?;
2922
2923          Some(MemberKey::Value {
2924            value,
2925            #[cfg(feature = "ast-comments")]
2926            comments,
2927            #[cfg(feature = "ast-comments")]
2928            comments_after_colon: memberkey_comments,
2929            #[cfg(feature = "ast-span")]
2930            span: (
2931              begin_memberkey_range,
2932              self.parser_position.range.1,
2933              begin_memberkey_line,
2934            ),
2935          })
2936        };
2937
2938        if let Token::COLON = &self.cur_token {
2939          self.next_token()?;
2940        }
2941
2942        Ok(mk)
2943      }
2944      // Indicates either an inline parenthesized type or an inline group. If
2945      // the latter, don't parse as memberkey
2946      Token::LPAREN => {
2947        #[cfg(feature = "ast-span")]
2948        let begin_memberkey_range = self.lexer_position.range.0;
2949        #[cfg(feature = "ast-span")]
2950        let begin_memberkey_line = self.lexer_position.line;
2951
2952        let mut nested_parend_count = 0;
2953
2954        self.next_token()?;
2955
2956        #[cfg(feature = "ast-comments")]
2957        let comments_before_type_or_group = self.collect_comments()?;
2958        #[cfg(not(feature = "ast-comments"))]
2959        self.advance_newline()?;
2960
2961        let mut tokens: Vec<lexer::Item> = Vec::new();
2962
2963        #[cfg(feature = "ast-comments")]
2964        let mut comments_after_type_or_group = None;
2965
2966        let mut has_group_entries = false;
2967        let mut closing_parend = false;
2968        #[cfg(feature = "ast-span")]
2969        let mut closing_parend_index = 0;
2970        while !closing_parend {
2971          if let Token::ARROWMAP
2972          | Token::COLON
2973          | Token::OPTIONAL
2974          | Token::ASTERISK
2975          | Token::GCHOICE = &self.cur_token
2976          {
2977            has_group_entries = true;
2978          }
2979
2980          // TODO: parse nested comments
2981          if let Token::LPAREN = &self.cur_token {
2982            nested_parend_count += 1;
2983          }
2984
2985          if let Token::RPAREN = &self.cur_token {
2986            match nested_parend_count.cmp(&0) {
2987              Ordering::Greater => nested_parend_count -= 1,
2988              Ordering::Equal | Ordering::Less => {
2989                closing_parend = true;
2990                #[cfg(feature = "ast-span")]
2991                {
2992                  closing_parend_index = self.lexer_position.range.1;
2993                }
2994              }
2995            }
2996          }
2997
2998          tokens.push(Ok((self.lexer_position, self.cur_token.clone())));
2999
3000          #[cfg(feature = "ast-span")]
3001          {
3002            self.parser_position.range.1 = self.lexer_position.range.1;
3003          }
3004
3005          self.next_token()?;
3006
3007          #[cfg(feature = "ast-comments")]
3008          {
3009            comments_after_type_or_group = self.collect_comments()?;
3010          }
3011          #[cfg(not(feature = "ast-comments"))]
3012          self.advance_newline()?;
3013
3014          if let Token::EOF = &self.cur_token {
3015            self.errors.push(Error::PARSER {
3016              #[cfg(feature = "ast-span")]
3017              position: self.lexer_position,
3018              msg: MissingClosingParend.into(),
3019            });
3020
3021            return Err(Error::INCREMENTAL);
3022          }
3023        }
3024
3025        // Create a new parser for the previously-lexed tokens.
3026        let mut parser = Parser::new(self.str_input, Box::new(tokens.into_iter()))?;
3027        parser.groupnames = self.groupnames.clone();
3028        parser.typenames = self.typenames.clone();
3029
3030        // Parse tokens vec as group
3031        if has_group_entries {
3032          let group = match parser.parse_group() {
3033            Ok(g) => g,
3034            Err(Error::INCREMENTAL) => {
3035              for e in parser.errors.into_iter() {
3036                self.errors.push(e);
3037              }
3038
3039              return Err(Error::INCREMENTAL);
3040            }
3041            Err(e) => return Err(e),
3042          };
3043          self
3044            .unknown_rule_idents
3045            .append(&mut parser.unknown_rule_idents);
3046
3047          return Ok(Some(MemberKey::NonMemberKey {
3048            non_member_key: NonMemberKey::Group(group),
3049            #[cfg(feature = "ast-comments")]
3050            comments_before_type_or_group,
3051            #[cfg(feature = "ast-comments")]
3052            comments_after_type_or_group,
3053          }));
3054        }
3055
3056        // Parse tokens vec as type
3057        let t = match parser.parse_type(None) {
3058          Ok(t) => t,
3059          Err(Error::INCREMENTAL) => {
3060            for e in parser.errors.into_iter() {
3061              self.errors.push(e);
3062            }
3063
3064            return Err(Error::INCREMENTAL);
3065          }
3066          Err(e) => return Err(e),
3067        };
3068        self
3069          .unknown_rule_idents
3070          .append(&mut parser.unknown_rule_idents);
3071
3072        #[cfg(feature = "ast-comments")]
3073        let comments_before_cut = self.collect_comments()?;
3074        #[cfg(not(feature = "ast-comments"))]
3075        self.advance_newline()?;
3076
3077        if let Token::CUT = &self.cur_token {
3078          self.next_token()?;
3079
3080          #[cfg(feature = "ast-comments")]
3081          let comments_after_cut = self.collect_comments()?;
3082          #[cfg(not(feature = "ast-comments"))]
3083          self.advance_newline()?;
3084
3085          if !self.cur_token_is(Token::ARROWMAP) {
3086            self.errors.push(Error::PARSER {
3087              #[cfg(feature = "ast-span")]
3088              position: self.lexer_position,
3089              msg: InvalidMemberKeyArrowMapSyntax.into(),
3090            });
3091            return Err(Error::INCREMENTAL);
3092          }
3093
3094          #[cfg(feature = "ast-span")]
3095          let end_memberkey_range = self.lexer_position.range.1;
3096
3097          let t1 = Some(MemberKey::Type1 {
3098            t1: Box::from(Type1 {
3099              type2: Type2::ParenthesizedType {
3100                pt: t,
3101                #[cfg(feature = "ast-comments")]
3102                comments_before_type: comments_before_type_or_group,
3103                #[cfg(feature = "ast-comments")]
3104                comments_after_type: comments_after_type_or_group,
3105                #[cfg(feature = "ast-span")]
3106                span: (
3107                  begin_memberkey_range,
3108                  closing_parend_index,
3109                  begin_memberkey_line,
3110                ),
3111              },
3112              #[cfg(feature = "ast-comments")]
3113              comments_after_type: comments_before_cut.clone(),
3114              operator: None,
3115              #[cfg(feature = "ast-span")]
3116              span: (
3117                begin_memberkey_range,
3118                closing_parend_index,
3119                begin_memberkey_line,
3120              ),
3121            }),
3122            #[cfg(feature = "ast-comments")]
3123            comments_before_cut,
3124            is_cut: true,
3125            #[cfg(feature = "ast-comments")]
3126            comments_after_cut,
3127            #[cfg(feature = "ast-comments")]
3128            comments_after_arrowmap: None,
3129            #[cfg(feature = "ast-span")]
3130            span: (
3131              begin_memberkey_range,
3132              end_memberkey_range,
3133              begin_memberkey_line,
3134            ),
3135          });
3136
3137          return Ok(t1);
3138        }
3139
3140        let t1 = if let Token::ARROWMAP = &self.cur_token {
3141          self.next_token()?;
3142
3143          #[cfg(feature = "ast-span")]
3144          {
3145            self.parser_position.range.1 = self.lexer_position.range.1;
3146          }
3147
3148          #[cfg(feature = "ast-comments")]
3149          let memberkey_comments = self.collect_comments()?;
3150          #[cfg(not(feature = "ast-comments"))]
3151          self.advance_newline()?;
3152
3153          Some(MemberKey::Type1 {
3154            t1: Box::from(Type1 {
3155              type2: Type2::ParenthesizedType {
3156                pt: t,
3157                #[cfg(feature = "ast-comments")]
3158                comments_before_type: comments_before_type_or_group,
3159                #[cfg(feature = "ast-comments")]
3160                comments_after_type: comments_after_type_or_group,
3161                #[cfg(feature = "ast-span")]
3162                span: (
3163                  begin_memberkey_range,
3164                  closing_parend_index,
3165                  begin_memberkey_line,
3166                ),
3167              },
3168              #[cfg(feature = "ast-comments")]
3169              comments_after_type: comments_before_cut.clone(),
3170              operator: None,
3171              #[cfg(feature = "ast-span")]
3172              span: (
3173                begin_memberkey_range,
3174                closing_parend_index,
3175                begin_memberkey_line,
3176              ),
3177            }),
3178            #[cfg(feature = "ast-comments")]
3179            comments_before_cut,
3180            is_cut: false,
3181            #[cfg(feature = "ast-comments")]
3182            comments_after_cut: None,
3183            #[cfg(feature = "ast-comments")]
3184            comments_after_arrowmap: memberkey_comments,
3185            #[cfg(feature = "ast-span")]
3186            span: (
3187              begin_memberkey_range,
3188              self.lexer_position.range.0,
3189              begin_memberkey_line,
3190            ),
3191          })
3192        } else {
3193          Some(MemberKey::NonMemberKey {
3194            non_member_key: NonMemberKey::Type(Type {
3195              type_choices: t.type_choices,
3196              #[cfg(feature = "ast-span")]
3197              span: (
3198                begin_memberkey_range,
3199                self.parser_position.range.1,
3200                begin_memberkey_line,
3201              ),
3202            }),
3203            #[cfg(feature = "ast-comments")]
3204            comments_before_type_or_group,
3205            #[cfg(feature = "ast-comments")]
3206            comments_after_type_or_group,
3207          })
3208        };
3209
3210        Ok(t1)
3211      }
3212      _ => {
3213        let t1 = self.parse_type1(None)?;
3214
3215        #[cfg(feature = "ast-comments")]
3216        let comments_before_cut = self.collect_comments()?;
3217        #[cfg(not(feature = "ast-comments"))]
3218        self.advance_newline()?;
3219
3220        if let Token::CUT = &self.cur_token {
3221          self.next_token()?;
3222
3223          #[cfg(feature = "ast-comments")]
3224          let comments_after_cut = self.collect_comments()?;
3225          #[cfg(not(feature = "ast-comments"))]
3226          self.advance_newline()?;
3227
3228          if !self.cur_token_is(Token::ARROWMAP) {
3229            self.errors.push(Error::PARSER {
3230              #[cfg(feature = "ast-span")]
3231              position: self.lexer_position,
3232              msg: InvalidMemberKeyArrowMapSyntax.into(),
3233            });
3234            return Err(Error::INCREMENTAL);
3235          }
3236
3237          #[cfg(feature = "ast-span")]
3238          let end_memberkey_range = self.lexer_position.range.1;
3239
3240          self.next_token()?;
3241
3242          #[cfg(feature = "ast-comments")]
3243          let memberkey_comments = self.collect_comments()?;
3244          #[cfg(not(feature = "ast-comments"))]
3245          self.advance_newline()?;
3246
3247          return Ok(Some(MemberKey::Type1 {
3248            t1: Box::from(t1),
3249            #[cfg(feature = "ast-comments")]
3250            comments_before_cut,
3251            is_cut: true,
3252            #[cfg(feature = "ast-comments")]
3253            comments_after_cut,
3254            #[cfg(feature = "ast-comments")]
3255            comments_after_arrowmap: memberkey_comments,
3256            #[cfg(feature = "ast-span")]
3257            span: (
3258              begin_memberkey_range,
3259              end_memberkey_range,
3260              begin_memberkey_line,
3261            ),
3262          }));
3263        }
3264
3265        let t1 = if let Token::ARROWMAP = &self.cur_token {
3266          self.next_token()?;
3267
3268          #[cfg(feature = "ast-span")]
3269          {
3270            self.parser_position.range.1 = self.lexer_position.range.1;
3271          }
3272
3273          #[cfg(feature = "ast-comments")]
3274          let memberkey_comments = self.collect_comments()?;
3275          #[cfg(not(feature = "ast-comments"))]
3276          self.advance_newline()?;
3277
3278          Some(MemberKey::Type1 {
3279            t1: Box::from(t1),
3280            #[cfg(feature = "ast-comments")]
3281            comments_before_cut,
3282            is_cut: false,
3283            #[cfg(feature = "ast-comments")]
3284            comments_after_cut: None,
3285            #[cfg(feature = "ast-comments")]
3286            comments_after_arrowmap: memberkey_comments,
3287            #[cfg(feature = "ast-span")]
3288            span: (
3289              begin_memberkey_range,
3290              self.parser_position.range.1,
3291              begin_memberkey_line,
3292            ),
3293          })
3294        } else {
3295          Some(MemberKey::NonMemberKey {
3296            non_member_key: NonMemberKey::Type(Type {
3297              type_choices: vec![TypeChoice {
3298                #[cfg(feature = "ast-comments")]
3299                comments_before_type: None,
3300                #[cfg(feature = "ast-comments")]
3301                comments_after_type: None,
3302                type1: t1,
3303              }],
3304              #[cfg(feature = "ast-span")]
3305              span: (
3306                begin_memberkey_range,
3307                self.parser_position.range.1,
3308                begin_memberkey_line,
3309              ),
3310            }),
3311            #[cfg(feature = "ast-comments")]
3312            comments_before_type_or_group: None,
3313            #[cfg(feature = "ast-comments")]
3314            comments_after_type_or_group: comments_before_cut,
3315          })
3316        };
3317
3318        Ok(t1)
3319      }
3320    }
3321  }
3322
3323  #[allow(missing_docs)]
3324  pub fn parse_occur(&mut self, is_optional: bool) -> Result<Option<Occurrence<'a>>> {
3325    #[cfg(feature = "ast-span")]
3326    let begin_occur_range = self.lexer_position.range.0;
3327    #[cfg(feature = "ast-span")]
3328    let begin_occur_line = self.lexer_position.line;
3329    #[cfg(feature = "ast-span")]
3330    {
3331      self.parser_position.line = self.lexer_position.line;
3332    }
3333
3334    match &self.cur_token {
3335      Token::OPTIONAL => {
3336        #[cfg(feature = "ast-span")]
3337        {
3338          self.parser_position.range = self.lexer_position.range;
3339        }
3340
3341        self.next_token()?;
3342
3343        #[cfg(feature = "ast-comments")]
3344        let comments = self.collect_comments()?;
3345        #[cfg(not(feature = "ast-comments"))]
3346        self.advance_newline()?;
3347
3348        Ok(Some(Occurrence {
3349          #[cfg(feature = "ast-span")]
3350          occur: Occur::Optional {
3351            span: (
3352              self.parser_position.range.0,
3353              self.parser_position.range.1,
3354              self.parser_position.line,
3355            ),
3356          },
3357          #[cfg(not(feature = "ast-span"))]
3358          occur: Occur::Optional {},
3359          #[cfg(feature = "ast-comments")]
3360          comments,
3361          _a: PhantomData,
3362        }))
3363      }
3364      Token::ONEORMORE => {
3365        #[cfg(feature = "ast-span")]
3366        {
3367          self.parser_position.range = self.lexer_position.range;
3368        }
3369
3370        self.next_token()?;
3371
3372        #[cfg(feature = "ast-comments")]
3373        let comments = self.collect_comments()?;
3374        #[cfg(not(feature = "ast-comments"))]
3375        self.advance_newline()?;
3376
3377        Ok(Some(Occurrence {
3378          #[cfg(feature = "ast-span")]
3379          occur: Occur::OneOrMore {
3380            span: (
3381              self.parser_position.range.0,
3382              self.parser_position.range.1,
3383              self.parser_position.line,
3384            ),
3385          },
3386          #[cfg(not(feature = "ast-span"))]
3387          occur: Occur::OneOrMore {},
3388          #[cfg(feature = "ast-comments")]
3389          comments,
3390          _a: PhantomData,
3391        }))
3392      }
3393      Token::ASTERISK => {
3394        let occur = if let Token::VALUE(token::Value::UINT(u)) = &self.peek_token {
3395          #[cfg(feature = "ast-span")]
3396          {
3397            self.parser_position.range.0 = self.lexer_position.range.0;
3398            self.parser_position.range.1 = self.peek_lexer_position.range.1;
3399          }
3400
3401          Occur::Exact {
3402            lower: None,
3403            upper: Some(*u),
3404            #[cfg(feature = "ast-span")]
3405            span: (
3406              self.parser_position.range.0,
3407              self.parser_position.range.1,
3408              self.parser_position.line,
3409            ),
3410          }
3411        } else {
3412          #[cfg(feature = "ast-span")]
3413          {
3414            self.parser_position.range = self.lexer_position.range;
3415            Occur::ZeroOrMore {
3416              span: (
3417                self.parser_position.range.0,
3418                self.parser_position.range.1,
3419                self.parser_position.line,
3420              ),
3421            }
3422          }
3423
3424          #[cfg(not(feature = "ast-span"))]
3425          Occur::ZeroOrMore {}
3426        };
3427
3428        self.next_token()?;
3429
3430        if let Token::VALUE(token::Value::UINT(_)) = &self.cur_token {
3431          self.next_token()?;
3432        }
3433
3434        #[cfg(feature = "ast-comments")]
3435        let comments = self.collect_comments()?;
3436        #[cfg(not(feature = "ast-comments"))]
3437        self.advance_newline()?;
3438
3439        Ok(Some(Occurrence {
3440          occur,
3441          #[cfg(feature = "ast-comments")]
3442          comments,
3443          _a: PhantomData,
3444        }))
3445      }
3446      Token::VALUE(_) => {
3447        let lower = if let Token::VALUE(token::Value::UINT(li)) = &self.cur_token {
3448          Some(*li)
3449        } else {
3450          None
3451        };
3452
3453        if !self.peek_token_is(&Token::ASTERISK) {
3454          if is_optional {
3455            return Ok(None);
3456          }
3457
3458          self.errors.push(Error::PARSER {
3459            #[cfg(feature = "ast-span")]
3460            position: self.lexer_position,
3461            msg: InvalidOccurrenceSyntax.into(),
3462          });
3463
3464          return Err(Error::INCREMENTAL);
3465        }
3466
3467        self.next_token()?;
3468
3469        #[cfg(feature = "ast-span")]
3470        {
3471          self.parser_position.range.1 = self.lexer_position.range.1;
3472        }
3473
3474        self.next_token()?;
3475
3476        let upper = if let Token::VALUE(token::Value::UINT(ui)) = &self.cur_token {
3477          let ui = *ui;
3478
3479          #[cfg(feature = "ast-span")]
3480          {
3481            self.parser_position.range.1 = self.lexer_position.range.1;
3482          }
3483
3484          self.next_token()?;
3485
3486          Some(ui)
3487        } else {
3488          None
3489        };
3490
3491        #[cfg(feature = "ast-comments")]
3492        let comments = self.collect_comments()?;
3493        #[cfg(not(feature = "ast-comments"))]
3494        self.advance_newline()?;
3495
3496        Ok(Some(Occurrence {
3497          occur: Occur::Exact {
3498            lower,
3499            upper,
3500            #[cfg(feature = "ast-span")]
3501            span: (
3502              begin_occur_range,
3503              self.parser_position.range.1,
3504              begin_occur_line,
3505            ),
3506          },
3507          #[cfg(feature = "ast-comments")]
3508          comments,
3509          _a: PhantomData,
3510        }))
3511      }
3512      _ => Ok(None),
3513    }
3514  }
3515
3516  fn cur_token_is(&self, t: Token) -> bool {
3517    mem::discriminant(&self.cur_token) == mem::discriminant(&t)
3518  }
3519
3520  fn peek_token_is(&self, t: &Token) -> bool {
3521    mem::discriminant(&self.peek_token) == mem::discriminant(t)
3522  }
3523
3524  fn expect_peek(&mut self, t: &Token) -> Result<bool> {
3525    if self.peek_token_is(t) {
3526      return self.next_token().map(|_| true);
3527    }
3528
3529    Ok(false)
3530  }
3531
3532  /// Create `ast::Identifier` from `Token::IDENT(ident)`
3533  fn identifier_from_ident_token(
3534    &self,
3535    ident: &'a str,
3536    socket: Option<token::SocketPlug>,
3537  ) -> Identifier<'a> {
3538    Identifier {
3539      ident,
3540      socket,
3541      #[cfg(feature = "ast-span")]
3542      span: (
3543        self.lexer_position.range.0,
3544        self.lexer_position.range.1,
3545        self.lexer_position.line,
3546      ),
3547    }
3548  }
3549}
3550
3551/// Returns a `ast::CDDL` from a `&str`
3552///
3553/// # Arguments
3554///
3555/// * `input` - A string slice with the CDDL text input
3556/// * `print_stderr` - When true, print any errors to stderr
3557///
3558/// # Example
3559///
3560/// ```
3561/// use cddl::parser::cddl_from_str;
3562///
3563/// let input = r#"myrule = int"#;
3564/// let _ = cddl_from_str(input, true);
3565#[cfg(not(target_arch = "wasm32"))]
3566#[cfg(feature = "std")]
3567pub fn cddl_from_str(input: &str, print_stderr: bool) -> std::result::Result<CDDL<'_>, String> {
3568  match Parser::new(input, Box::new(lexer::lexer_from_str(input).iter())).map_err(|e| e.to_string())
3569  {
3570    Ok(mut p) => match p.parse_cddl() {
3571      Ok(c) => Ok(c),
3572      Err(Error::INCREMENTAL) => {
3573        let e = if print_stderr {
3574          p.report_errors(true)
3575        } else {
3576          p.report_errors(false)
3577        };
3578
3579        if let Ok(Some(e)) = e {
3580          return Err(e);
3581        }
3582
3583        Err(Error::INCREMENTAL.to_string())
3584      }
3585      Err(e) => Err(e.to_string()),
3586    },
3587    Err(e) => Err(e),
3588  }
3589}
3590
3591/// Identify root type name from CDDL input string
3592#[cfg(feature = "std")]
3593#[cfg(not(target_arch = "wasm32"))]
3594pub fn root_type_name_from_cddl_str(input: &str) -> std::result::Result<String, String> {
3595  let cddl = cddl_from_str(input, false)?;
3596
3597  for r in cddl.rules.iter() {
3598    // First type rule is root
3599    if let Rule::Type { rule, .. } = r {
3600      if rule.generic_params.is_none() {
3601        return Ok(rule.name.to_string());
3602      }
3603    }
3604  }
3605
3606  Err("cddl spec contains no root type".to_string())
3607}
3608
3609impl CDDL<'_> {
3610  /// Parses CDDL from a byte slice
3611  #[cfg(not(target_arch = "wasm32"))]
3612  #[cfg(feature = "std")]
3613  pub fn from_slice(input: &[u8]) -> std::result::Result<CDDL<'_>, String> {
3614    let str_input = std::str::from_utf8(input).map_err(|e| e.to_string())?;
3615
3616    match Parser::new(str_input, Box::new(lexer::Lexer::from_slice(input).iter()))
3617      .map_err(|e| e.to_string())
3618    {
3619      Ok(mut p) => match p.parse_cddl() {
3620        Ok(c) => Ok(c),
3621        Err(Error::INCREMENTAL) => {
3622          if let Ok(Some(e)) = p.report_errors(false) {
3623            return Err(e);
3624          }
3625
3626          Err(Error::INCREMENTAL.to_string())
3627        }
3628        Err(e) => Err(e.to_string()),
3629      },
3630      Err(e) => Err(e),
3631    }
3632  }
3633
3634  /// Parses CDDL from a byte slice
3635  #[cfg(not(target_arch = "wasm32"))]
3636  #[cfg(not(feature = "std"))]
3637  pub fn from_slice(input: &[u8]) -> std::result::Result<CDDL<'_>, String> {
3638    let str_input = std::str::from_utf8(input).map_err(|e| e.to_string())?;
3639
3640    match Parser::new(str_input, Box::new(lexer::Lexer::from_slice(input).iter()))
3641      .map_err(|e| e.to_string())
3642    {
3643      Ok(mut p) => match p.parse_cddl() {
3644        Ok(c) => Ok(c),
3645        Err(Error::INCREMENTAL) => {
3646          if let Some(e) = p.report_errors() {
3647            return Err(e);
3648          }
3649
3650          Err(Error::INCREMENTAL.to_string())
3651        }
3652        Err(e) => Err(e.to_string()),
3653      },
3654      Err(e) => Err(e),
3655    }
3656  }
3657}
3658
3659/// Returns a `ast::CDDL` from a `&str`
3660///
3661/// # Arguments
3662///
3663/// * `lexer` - A mutable reference to a `lexer::Lexer`. Can be created from
3664///   `cddl::lexer_from_str()`
3665/// * `input` - A string slice with the CDDL text input
3666///
3667/// # Example
3668///
3669/// ```
3670/// use cddl::cddl_from_str;
3671///
3672/// let input = r#"myrule = int"#;
3673///
3674/// let _ = cddl_from_str(input);
3675/// ```
3676#[cfg(not(target_arch = "wasm32"))]
3677#[cfg(not(feature = "std"))]
3678pub fn cddl_from_str(input: &str) -> std::result::Result<CDDL<'_>, String> {
3679  match Parser::new(input, Box::new(lexer::lexer_from_str(input).iter())).map_err(|e| e.to_string())
3680  {
3681    Ok(mut p) => match p.parse_cddl() {
3682      Ok(c) => Ok(c),
3683      Err(Error::INCREMENTAL) => {
3684        if let Some(e) = p.report_errors() {
3685          return Err(e);
3686        }
3687
3688        Err(Error::INCREMENTAL.to_string())
3689      }
3690      Err(e) => Err(e.to_string()),
3691    },
3692    Err(e) => Err(e),
3693  }
3694}
3695
3696/// Returns a `ast::CDDL` wrapped in `JsValue` from a `&str`
3697///
3698/// # Arguments
3699///
3700/// * `input` - A string slice with the CDDL text input
3701///
3702/// # Example
3703///
3704/// ```typescript
3705/// import * as wasm from 'cddl';
3706///
3707/// let cddl: any;
3708/// try {
3709///   cddl = wasm.cddl_from_str(text);
3710/// } catch (e) {
3711///   console.error(e);
3712/// }
3713/// ```
3714#[cfg(target_arch = "wasm32")]
3715#[wasm_bindgen]
3716pub fn cddl_from_str(input: &str) -> result::Result<JsValue, JsValue> {
3717  #[derive(Serialize)]
3718  struct ParserError {
3719    position: Position,
3720    msg: ErrorMsg,
3721  }
3722
3723  match Parser::new(input, Box::new(lexer::Lexer::new(input).iter())) {
3724    Ok(mut p) => match p.parse_cddl() {
3725      Ok(c) => serde_wasm_bindgen::to_value(&c).map_err(|e| JsValue::from(e.to_string())),
3726      Err(Error::INCREMENTAL) => {
3727        if !p.errors.is_empty() {
3728          return Err(
3729            serde_wasm_bindgen::to_value(
3730              &p.errors
3731                .iter()
3732                .filter_map(|e| {
3733                  if let Error::PARSER { position, msg } = e {
3734                    Some(ParserError {
3735                      position: *position,
3736                      msg: msg.clone(),
3737                    })
3738                  } else {
3739                    None
3740                  }
3741                })
3742                .collect::<Vec<ParserError>>(),
3743            )
3744            .map_err(|e| JsValue::from(e.to_string()))?,
3745          );
3746        }
3747
3748        Err(JsValue::from(Error::INCREMENTAL.to_string()))
3749      }
3750      Err(e) => Err(JsValue::from(e.to_string())),
3751    },
3752    Err(e) => Err(JsValue::from(e.to_string())),
3753  }
3754}
3755
3756#[cfg(feature = "lsp")]
3757#[cfg(target_arch = "wasm32")]
3758#[wasm_bindgen]
3759/// Formats cddl from input string
3760pub fn format_cddl_from_str(input: &str) -> result::Result<String, JsValue> {
3761  #[derive(Serialize)]
3762  struct ParserError {
3763    position: Position,
3764    msg: ErrorMsg,
3765  }
3766
3767  match Parser::new(input, Box::new(lexer::Lexer::new(input).iter())) {
3768    Ok(mut p) => match p.parse_cddl() {
3769      Ok(c) => Ok(c.to_string()),
3770      Err(Error::INCREMENTAL) => {
3771        if !p.errors.is_empty() {
3772          return Err(
3773            serde_wasm_bindgen::to_value(
3774              &p.errors
3775                .iter()
3776                .filter_map(|e| {
3777                  if let Error::PARSER { position, msg } = e {
3778                    Some(ParserError {
3779                      position: *position,
3780                      msg: msg.clone(),
3781                    })
3782                  } else {
3783                    None
3784                  }
3785                })
3786                .collect::<Vec<ParserError>>(),
3787            )
3788            .map_err(|e| JsValue::from(e.to_string()))?,
3789          );
3790        }
3791
3792        Err(JsValue::from(Error::INCREMENTAL.to_string()))
3793      }
3794      Err(e) => Err(JsValue::from(e.to_string())),
3795    },
3796    Err(e) => Err(JsValue::from(e.to_string())),
3797  }
3798}
3799
3800#[cfg(test)]
3801mod tests {
3802  use super::*;
3803  use crate::lexer;
3804
3805  #[test]
3806  fn test_multiple_rules_with_reference_to_parenthesized_type() {
3807    let input = r#"basic = (d: #6.23(uint), e: bytes)
3808        outer = [a: uint, b: basic, c: "some text"]"#;
3809
3810    // Use the parser directly for better error diagnostics
3811    let mut parser = Parser::new(input, Box::new(lexer::lexer_from_str(input).iter())).unwrap();
3812    let result = parser.parse_cddl();
3813
3814    // Ensure there are no errors
3815    assert!(result.is_ok(), "Parser errors: {:?}", parser.errors);
3816
3817    // Check that the CDDL contains two rules
3818    let cddl = result.unwrap();
3819    assert_eq!(cddl.rules.len(), 2);
3820
3821    // Verify rule names
3822    let rule_names: Vec<_> = cddl.rules.iter().map(|r| r.name()).collect();
3823    assert!(rule_names.contains(&"basic".to_string()));
3824    assert!(rule_names.contains(&"outer".to_string()));
3825  }
3826}