spade_parser/
lib.rs

1pub mod error;
2mod expression;
3pub mod item_type;
4mod items;
5pub mod lexer;
6mod statements;
7
8use std::marker::PhantomData;
9
10use colored::*;
11use itertools::Itertools;
12use local_impl::local_impl;
13use logos::Lexer;
14use spade_diagnostics::diag_list::DiagList;
15use statements::{AssertParser, BindingParser, DeclParser, LabelParser, RegisterParser, SetParser};
16use tracing::{debug, event, Level};
17
18use spade_ast::{
19    ArgumentList, ArgumentPattern, Attribute, AttributeList, BitLiteral, Block, CallKind,
20    EnumVariant, Expression, IntLiteral, Item, ModuleBody, NamedArgument, NamedTurbofish,
21    ParameterList, Pattern, PipelineStageReference, Statement, TraitSpec, TurbofishInner,
22    TypeExpression, TypeParam, TypeSpec, Unit, UnitHead, UnitKind, WhereClause,
23};
24use spade_common::location_info::{lspan, AsLabel, FullSpan, HasCodespan, Loc, WithLocation};
25use spade_common::name::{Identifier, Path};
26use spade_common::num_ext::InfallibleToBigInt;
27use spade_diagnostics::{diag_bail, Diagnostic};
28use spade_macros::trace_parser;
29
30use crate::error::{
31    unexpected_token_message, CSErrorTransformations, CommaSeparatedResult, ExpectedArgumentList,
32    Result, SuggestBraceEnumVariant, TokenSeparatedError, UnexpectedToken,
33};
34use crate::item_type::UnitKindLocal;
35use crate::lexer::{LiteralKind, TokenKind};
36
37pub use logos;
38
39/// A token with location info
40#[derive(Clone, Debug, PartialEq)]
41pub struct Token {
42    pub kind: TokenKind,
43    pub span: logos::Span,
44    pub file_id: usize,
45}
46
47impl Token {
48    pub fn new(kind: TokenKind, lexer: &Lexer<TokenKind>, file_id: usize) -> Self {
49        Self {
50            kind,
51            span: lexer.span(),
52            file_id,
53        }
54    }
55
56    pub fn loc(&self) -> Loc<()> {
57        Loc::new((), self.span.codespan(), self.file_id)
58    }
59}
60
61impl HasCodespan for Token {
62    fn codespan(&self) -> spade_codespan::Span {
63        self.span().codespan()
64    }
65}
66
67impl AsLabel for Token {
68    fn file_id(&self) -> usize {
69        self.file_id
70    }
71
72    fn span(&self) -> std::ops::Range<usize> {
73        self.span.clone()
74    }
75}
76
77impl From<Token> for FullSpan {
78    fn from(token: Token) -> FullSpan {
79        (token.codespan(), token.file_id)
80    }
81}
82
83#[derive(Debug, Clone)]
84pub enum Comment {
85    Line(Token),
86    Block(Token, Token),
87}
88
89// Clone for when you want to call a parse function but maybe discard the new parser state
90// depending on some later condition.
91#[derive(Clone)]
92pub struct Parser<'a> {
93    lex: Lexer<'a, TokenKind>,
94    peeked: Option<Token>,
95    // The last token that was eaten. Used in eof diagnostics
96    last_token: Option<Token>,
97    pub parse_stack: Vec<ParseStackEntry>,
98    file_id: usize,
99    unit_context: Option<Loc<UnitKind>>,
100    pub diags: DiagList,
101    recovering_tokens: Vec<Vec<TokenKind>>,
102    comments: Vec<Comment>,
103}
104
105impl<'a> Parser<'a> {
106    pub fn new(lex: Lexer<'a, TokenKind>, file_id: usize) -> Self {
107        Self {
108            lex,
109            peeked: None,
110            last_token: None,
111            parse_stack: vec![],
112            file_id,
113            unit_context: None,
114            diags: DiagList::new(),
115            recovering_tokens: vec![vec![TokenKind::Eof]],
116            comments: vec![],
117        }
118    }
119
120    pub fn comments(&self) -> &[Comment] {
121        &self.comments
122    }
123}
124
125/// Peek the next token. If it matches the specified token, get that token
126/// otherwise return Ok(none)
127#[macro_export]
128macro_rules! peek_for {
129    ($self:expr, $token:expr) => {
130        if let Some(t) = $self.peek_and_eat($token)? {
131            t
132        } else {
133            return Ok(None);
134        }
135    };
136}
137
138// Actual parsing functions
139impl<'a> Parser<'a> {
140    #[trace_parser]
141    #[tracing::instrument(level = "trace", skip(self))]
142    pub fn identifier(&mut self) -> Result<Loc<Identifier>> {
143        let token = self.eat_cond(TokenKind::is_identifier, "Identifier")?;
144
145        if let TokenKind::Identifier(name) = token.kind {
146            Ok(Identifier(name).at(self.file_id, &token.span))
147        } else {
148            unreachable!("eat_cond should have checked this");
149        }
150    }
151
152    #[trace_parser]
153    pub fn path(&mut self) -> Result<Loc<Path>> {
154        let mut result = vec![];
155        loop {
156            result.push(self.identifier()?);
157
158            if self.peek_and_eat(&TokenKind::PathSeparator)?.is_none() {
159                break;
160            }
161        }
162        // NOTE: (safe unwrap) The vec will have at least one element because the first thing
163        // in the loop must push an identifier.
164        let start = result.first().unwrap().span;
165        let end = result.last().unwrap().span;
166        Ok(Path(result).between(self.file_id, &start, &end))
167    }
168
169    pub fn named_turbofish(&mut self) -> Result<Loc<NamedTurbofish>> {
170        // This is a named arg
171        let name = self.identifier()?;
172        if self.peek_and_eat(&TokenKind::Colon)?.is_some() {
173            let value = self.type_expression()?;
174
175            let span = name.span.merge(value.span);
176
177            Ok(NamedTurbofish::Full(name, value).at(self.file_id, &span))
178        } else {
179            Ok(NamedTurbofish::Short(name.clone()).at(self.file_id, &name))
180        }
181    }
182
183    #[trace_parser]
184    pub fn turbofish(&mut self) -> Result<Option<Loc<TurbofishInner>>> {
185        let start = peek_for!(self, &TokenKind::PathSeparator);
186
187        if self.peek_kind(&TokenKind::Lt)? {
188            // safe unwrap, only None for token kind != Lt
189            let params = self.generic_spec_list()?.unwrap();
190
191            Ok(Some(params.map(|p| TurbofishInner::Positional(p))))
192        } else if self.peek_kind(&TokenKind::Dollar)? {
193            self.eat_unconditional()?;
194            let (params, loc) = self.surrounded(
195                &TokenKind::Lt,
196                |s| {
197                    s.comma_separated(Self::named_turbofish, &TokenKind::Gt)
198                        .extra_expected(vec!["identifier", "type spec"])
199                },
200                &TokenKind::Gt,
201            )?;
202
203            Ok(Some(TurbofishInner::Named(params).at_loc(&loc)))
204        } else {
205            let next = self.peek()?;
206            return Err(Diagnostic::error(next, "Expected $ or <")
207                .primary_label("Expected $ or <")
208                .secondary_label(
209                    start,
210                    ":: after an method is used to specify type parameters",
211                ));
212        }
213    }
214
215    #[trace_parser]
216    pub fn path_with_turbofish(
217        &mut self,
218    ) -> Result<Option<(Loc<Path>, Option<Loc<TurbofishInner>>)>> {
219        let mut result = vec![];
220        if !self.peek_cond(TokenKind::is_identifier, "Identifier")? {
221            return Ok(None);
222        }
223
224        loop {
225            result.push(self.identifier()?);
226
227            // NOTE: (safe unwrap) The vec will have at least one element because the first thing
228            // in the loop must push an identifier.
229            let path_start = result.first().unwrap().span;
230            let path_end = result.last().unwrap().span;
231
232            if self.peek_and_eat(&TokenKind::PathSeparator)?.is_none() {
233                break Ok(Some((
234                    Path(result).between(self.file_id, &path_start, &path_end),
235                    None,
236                )));
237            } else if self.peek_kind(&TokenKind::Lt)? {
238                // safe unwrap, only None for token kind != Lt
239                let params = self.generic_spec_list()?.unwrap();
240
241                break Ok(Some((
242                    Path(result).between(self.file_id, &path_start, &path_end),
243                    Some(params.map(|p| TurbofishInner::Positional(p))),
244                )));
245            } else if self.peek_kind(&TokenKind::Dollar)? {
246                self.eat_unconditional()?;
247                let (params, loc) = self.surrounded(
248                    &TokenKind::Lt,
249                    |s| {
250                        s.comma_separated(Self::named_turbofish, &TokenKind::Gt)
251                            .extra_expected(vec!["identifier", "type spec"])
252                    },
253                    &TokenKind::Gt,
254                )?;
255
256                break Ok(Some((
257                    Path(result).between(self.file_id, &path_start, &path_end),
258                    Some(TurbofishInner::Named(params).at_loc(&loc)),
259                )));
260            }
261        }
262    }
263
264    #[trace_parser]
265    fn array_literal(&mut self) -> Result<Option<Loc<Expression>>> {
266        let start = peek_for!(self, &TokenKind::OpenBracket);
267
268        // empty array
269        if let Some(end) = self.peek_and_eat(&TokenKind::CloseBracket)? {
270            return Ok(Some(Expression::ArrayLiteral(vec![]).between(
271                self.file_id,
272                &start,
273                &end,
274            )));
275        }
276
277        // non-empty array => must be an expression
278        let first = self.expression()?;
279
280        let expr = if self.peek_and_eat(&TokenKind::Semi).unwrap().is_some() {
281            // array shorthand ([<expr>; N])
282            Expression::ArrayShorthandLiteral(Box::new(first), Box::new(self.expression()?))
283        } else {
284            // eat comma, if any
285            let _ = self.peek_and_eat(&TokenKind::Comma)?;
286            // now we can continue with the rest of the elements
287            let mut inner = self
288                .comma_separated(Self::expression, &TokenKind::CloseBracket)
289                .no_context()?;
290            inner.insert(0, first);
291            Expression::ArrayLiteral(inner)
292        };
293
294        let end = self.eat(&TokenKind::CloseBracket)?;
295
296        Ok(Some(expr.between(self.file_id, &start, &end)))
297    }
298
299    #[trace_parser]
300    fn tuple_literal(&mut self) -> Result<Option<Loc<Expression>>> {
301        let start = peek_for!(self, &TokenKind::OpenParen);
302        if self.peek_kind(&TokenKind::CloseParen)? {
303            return Ok(Some(Expression::TupleLiteral(vec![]).between(
304                self.file_id,
305                &start,
306                &self.eat_unconditional()?,
307            )));
308        }
309        if let Some(_) = self.peek_and_eat(&TokenKind::Comma)? {
310            let closer = self.eat(&TokenKind::CloseParen)?;
311            return Ok(Some(Expression::TupleLiteral(vec![]).between(
312                self.file_id,
313                &start,
314                &closer,
315            )));
316        }
317
318        let first = self.expression()?;
319        let first_sep = self.eat_unconditional()?;
320
321        match &first_sep.kind {
322            TokenKind::CloseParen => {
323                let inner = first.inner.between(self.file_id, &start, &first_sep);
324                Ok(Some(Expression::Parenthesized(Box::new(inner)).between(
325                    self.file_id,
326                    &start,
327                    &first_sep,
328                )))
329            }
330            TokenKind::Comma => {
331                let rest = self
332                    .comma_separated(Self::expression, &TokenKind::CloseParen)
333                    .no_context()?;
334
335                let end = self.eat(&TokenKind::CloseParen)?;
336
337                Ok(Some(
338                    Expression::TupleLiteral(vec![first].into_iter().chain(rest).collect())
339                        .between(self.file_id, &start, &end),
340                ))
341            }
342            _ => Err(UnexpectedToken {
343                got: first_sep,
344                expected: vec!["expression", ",", ")"],
345            }
346            .into()),
347        }
348    }
349
350    #[trace_parser]
351    #[tracing::instrument(skip(self))]
352    fn entity_instance(&mut self) -> Result<Option<Loc<Expression>>> {
353        let start = peek_for!(self, &TokenKind::Instance);
354        let start_loc = ().at(self.file_id, &start);
355
356        // inst is only allowed in entity/pipeline, so check that we are in one of those
357        self.unit_context
358            .allows_inst(().at(self.file_id, &start.span()))?;
359
360        // Check if this is a pipeline or not
361        let pipeline_depth = if self.peek_kind(&TokenKind::OpenParen)? {
362            Some(self.surrounded(
363                &TokenKind::OpenParen,
364                |s| s.type_expression(),
365                &TokenKind::CloseParen,
366            )?)
367        } else {
368            None
369        };
370
371        let peeked = self.peek()?;
372        let (name, turbofish) = self.path_with_turbofish()?.ok_or_else(|| {
373            Diagnostic::from(UnexpectedToken {
374                got: peeked,
375                expected: vec!["identifier", "pipeline depth"],
376            })
377        })?;
378        let next_token = self.peek()?;
379
380        let args = self.argument_list()?.ok_or_else(|| {
381            ExpectedArgumentList {
382                next_token,
383                base_expr: ().between(self.file_id, &start, &name),
384            }
385            .with_suggestions()
386        })?;
387
388        if let Some((depth, end_paren)) = pipeline_depth {
389            Ok(Some(
390                Expression::Call {
391                    kind: CallKind::Pipeline(
392                        ().between(self.file_id, &start_loc, &end_paren),
393                        depth,
394                    ),
395                    callee: name,
396                    args: args.clone(),
397                    turbofish,
398                }
399                .between(self.file_id, &start.span, &args),
400            ))
401        } else {
402            Ok(Some(
403                Expression::Call {
404                    kind: CallKind::Entity(start_loc),
405                    callee: name,
406                    args: args.clone(),
407                    turbofish,
408                }
409                .between(self.file_id, &start.span, &args),
410            ))
411        }
412    }
413
414    // FIXME: Before changing this, merge it with type_level_if
415    #[trace_parser]
416    #[tracing::instrument(skip(self))]
417    pub fn if_expression(&mut self, allow_stages: bool) -> Result<Option<Loc<Expression>>> {
418        let start = peek_for!(self, &TokenKind::If);
419
420        let cond = self.expression()?;
421
422        let on_true = if let Some(block) = self.block(allow_stages)? {
423            block.map(Box::new).map(Expression::Block)
424        } else {
425            let got = self.peek()?;
426            return Err(Diagnostic::error(
427                got.loc(),
428                format!("Unexpected `{}`, expected a block", got.kind.as_str()),
429            )
430            .primary_label("expected a block here"));
431        };
432
433        self.eat(&TokenKind::Else)?;
434        let on_false = if let Some(block) = self.block(allow_stages)? {
435            block.map(Box::new).map(Expression::Block)
436        } else if let Some(expr) = self.if_expression(allow_stages)? {
437            expr
438        } else {
439            let got = self.peek()?;
440            return Err(Diagnostic::error(
441                got.loc(),
442                format!(
443                    "Unexpected `{}`, expected `if` or a block",
444                    got.kind.as_str()
445                ),
446            )
447            .primary_label("expected a block here"));
448        };
449        let end_span = on_false.span;
450
451        Ok(Some(
452            Expression::If(Box::new(cond), Box::new(on_true), Box::new(on_false)).between(
453                self.file_id,
454                &start.span,
455                &end_span,
456            ),
457        ))
458    }
459
460    // FIXME: Before changing this, merge it with if_expression
461    pub fn type_level_if(&mut self) -> Result<Option<Loc<Expression>>> {
462        let start = peek_for!(self, &TokenKind::Gen);
463
464        let Some(inner) = self.if_expression(true)? else {
465            return Err(
466                Diagnostic::error(self.peek()?, "gen must be followed by if")
467                    .primary_label("Expected if")
468                    .secondary_label(start, "Because of this gen"),
469            );
470        };
471        let end_span = inner.loc();
472        let Expression::If(cond, on_true, on_false) = inner.inner else {
473            diag_bail!(inner, "if_expression did not return an if")
474        };
475
476        let on_false = match &on_false.inner {
477            Expression::If(cond, on_true, on_false) => Box::new(
478                Expression::TypeLevelIf(cond.clone(), on_true.clone(), on_false.clone())
479                    .at_loc(&on_false),
480            ),
481            _ => on_false,
482        };
483
484        Ok(Some(
485            Expression::TypeLevelIf(cond, on_true, on_false).between(
486                self.file_id,
487                &start.span,
488                &end_span,
489            ),
490        ))
491    }
492
493    #[trace_parser]
494    pub fn match_expression(&mut self) -> Result<Option<Loc<Expression>>> {
495        let start = peek_for!(self, &TokenKind::Match);
496
497        let expression = self.expression()?;
498
499        let (patterns, body_loc) = self.surrounded(
500            &TokenKind::OpenBrace,
501            |s| {
502                s.comma_separated(
503                    |s| {
504                        let pattern = s.pattern()?;
505                        s.eat(&TokenKind::FatArrow)?;
506                        let value = s.expression()?;
507
508                        Ok((pattern, value))
509                    },
510                    &TokenKind::CloseBrace,
511                )
512                .no_context()
513            },
514            &TokenKind::CloseBrace,
515        )?;
516        let patterns = patterns.at_loc(&body_loc);
517
518        Ok(Some(
519            Expression::Match(Box::new(expression), patterns).between(
520                self.file_id,
521                &start.span,
522                &body_loc,
523            ),
524        ))
525    }
526
527    #[trace_parser]
528    pub fn unsafe_block(&mut self) -> Result<Option<Loc<Expression>>> {
529        let start = peek_for!(self, &TokenKind::Unsafe);
530
531        let Some(block) = self.block(false)? else {
532            let got = self.peek()?;
533            return Err(Diagnostic::error(
534                got.loc(),
535                format!("Unexpected `{}`, expected a block", got.kind.as_str()),
536            )
537            .primary_label("expected a block here"));
538        };
539
540        let block_loc = block.loc();
541        Ok(Some(Expression::Unsafe(Box::new(block)).between(
542            self.file_id,
543            &start.span,
544            &block_loc,
545        )))
546    }
547
548    #[trace_parser]
549    #[tracing::instrument(skip(self))]
550    pub fn int_literal(&mut self) -> Result<Option<Loc<IntLiteral>>> {
551        let plusminus = match &self.peek()?.kind {
552            TokenKind::Plus | TokenKind::Minus => Some(self.eat_unconditional()?),
553            _ => None,
554        };
555        if self.peek_cond(TokenKind::is_integer, "integer")? {
556            let token = self.eat_unconditional()?;
557            match &token.kind {
558                TokenKind::Integer(val)
559                | TokenKind::HexInteger(val)
560                | TokenKind::BinInteger(val) => {
561                    let (val_int, val_signed) = val;
562
563                    let signed_val = || {
564                        if plusminus.as_ref().map(|tok| &tok.kind) == Some(&TokenKind::Minus) {
565                            -val_int.to_bigint()
566                        } else {
567                            val_int.to_bigint()
568                        }
569                    };
570
571                    let inner = match val_signed {
572                        LiteralKind::Signed(size) => IntLiteral::Signed {
573                            val: signed_val(),
574                            size: size.clone(),
575                        },
576                        LiteralKind::Unsized => IntLiteral::Unsized(signed_val()),
577                        LiteralKind::Unsigned(size) => IntLiteral::Unsigned {
578                            val: val_int.clone(),
579                            size: size.clone(),
580                        },
581                    };
582                    let loc = if let Some(pm) = plusminus {
583                        ().between(self.file_id, &pm, &token)
584                    } else {
585                        token.loc()
586                    };
587                    Ok(Some(inner.at_loc(&loc)))
588                }
589                _ => unreachable!(),
590            }
591        } else if let Some(pm) = plusminus {
592            Err(Diagnostic::error(
593                pm.loc(),
594                format!("expected a number after '{}'", pm.kind.as_str()),
595            ))
596        } else {
597            Ok(None)
598        }
599    }
600
601    #[trace_parser]
602    fn bool_literal(&mut self) -> Result<Option<Loc<bool>>> {
603        if let Some(tok) = self.peek_and_eat(&TokenKind::True)? {
604            Ok(Some(true.at(self.file_id, &tok.span)))
605        } else if let Some(tok) = self.peek_and_eat(&TokenKind::False)? {
606            Ok(Some(false.at(self.file_id, &tok.span)))
607        } else {
608            Ok(None)
609        }
610    }
611
612    #[trace_parser]
613    fn str_literal(&mut self) -> Result<Option<Loc<String>>> {
614        if self.peek_cond(TokenKind::is_string, "string")? {
615            let token = self.eat_unconditional()?;
616            match &token.kind {
617                TokenKind::String(val) => Ok(Some(val.clone().at_loc(&token.loc()))),
618                _ => unreachable!(),
619            }
620        } else {
621            Ok(None)
622        }
623    }
624
625    #[trace_parser]
626    fn tri_literal(&mut self) -> Result<Option<Loc<BitLiteral>>> {
627        if let Some(tok) = self.peek_and_eat(&TokenKind::Low)? {
628            Ok(Some(BitLiteral::Low.at(self.file_id, &tok.span)))
629        } else if let Some(tok) = self.peek_and_eat(&TokenKind::High)? {
630            Ok(Some(BitLiteral::High.at(self.file_id, &tok.span)))
631        } else if let Some(tok) = self.peek_and_eat(&TokenKind::HighImp)? {
632            Ok(Some(BitLiteral::HighImp.at(self.file_id, &tok.span)))
633        } else {
634            Ok(None)
635        }
636    }
637
638    #[trace_parser]
639    #[tracing::instrument(skip(self))]
640    pub fn block(&mut self, is_pipeline: bool) -> Result<Option<Loc<Block>>> {
641        let start = peek_for!(self, &TokenKind::OpenBrace);
642
643        let (statements, result) = self.statements(is_pipeline)?;
644
645        let end = self.eat(&TokenKind::CloseBrace)?;
646
647        Ok(Some(Block { statements, result }.between(
648            self.file_id,
649            &start.span,
650            &end.span,
651        )))
652    }
653
654    #[trace_parser]
655    pub fn pipeline_reference(&mut self) -> Result<Option<Loc<Expression>>> {
656        let start = peek_for!(self, &TokenKind::Stage);
657        // Peek here because we can't peek in the .ok_or_else below
658        let next = self.peek()?;
659
660        let parsed = self.first_successful(vec![
661            &|s: &mut Self| s.pipeline_stage_reference(&start),
662            &|s: &mut Self| s.pipeline_stage_status(&start),
663        ])?;
664        match parsed {
665            Some(e) => Ok(Some(e)),
666            None => Err(Diagnostic::from(UnexpectedToken {
667                got: next,
668                expected: vec![".", "("],
669            })),
670        }
671    }
672
673    #[trace_parser]
674    pub fn pipeline_stage_reference(
675        &mut self,
676        stage_keyword: &Token,
677    ) -> Result<Option<Loc<Expression>>> {
678        peek_for!(self, &TokenKind::OpenParen);
679
680        self.unit_context.allows_pipeline_ref(stage_keyword.loc())?;
681
682        let next = self.peek()?;
683        let reference = match next.kind {
684            TokenKind::Plus => {
685                let start = self.eat_unconditional()?;
686                let offset = self.expression()?;
687                let result = PipelineStageReference::Relative(
688                    TypeExpression::ConstGeneric(Box::new(offset.clone())).between(
689                        self.file_id,
690                        &start,
691                        &offset,
692                    ),
693                );
694                result
695            }
696            TokenKind::Minus => {
697                let start = self.eat_unconditional()?;
698                let offset = self.expression()?;
699                let texpr = TypeExpression::ConstGeneric(Box::new(
700                    Expression::UnaryOperator(
701                        spade_ast::UnaryOperator::Sub.at(self.file_id, &next.span),
702                        Box::new(offset.clone()),
703                    )
704                    .between(self.file_id, &start, &offset),
705                ))
706                .between(self.file_id, &start, &offset);
707                PipelineStageReference::Relative(texpr)
708            }
709            TokenKind::Identifier(_) => PipelineStageReference::Absolute(self.identifier()?),
710            _ => {
711                return Err(Diagnostic::from(UnexpectedToken {
712                    got: next,
713                    expected: vec!["+", "-", "identifier"],
714                }));
715            }
716        };
717
718        let close_paren = self.eat(&TokenKind::CloseParen)?;
719
720        self.eat(&TokenKind::Dot)?;
721
722        let ident = self.identifier()?;
723
724        Ok(Some(
725            Expression::PipelineReference {
726                stage_kw_and_reference_loc: ().between(
727                    self.file_id,
728                    &stage_keyword.span,
729                    &close_paren.span,
730                ),
731                stage: reference,
732                name: ident.clone(),
733            }
734            .between(self.file_id, &stage_keyword.span, &ident),
735        ))
736    }
737
738    #[trace_parser]
739    pub fn pipeline_stage_status(
740        &mut self,
741        stage_keyword: &Token,
742    ) -> Result<Option<Loc<Expression>>> {
743        peek_for!(self, &TokenKind::Dot);
744
745        let ident = self.identifier()?;
746
747        match ident.inner.0.as_str() {
748            "valid" => Ok(Some(Expression::StageValid.between(
749                self.file_id,
750                stage_keyword,
751                &ident,
752            ))),
753            "ready" => Ok(Some(Expression::StageReady.between(
754                self.file_id,
755                stage_keyword,
756                &ident,
757            ))),
758            other => Err(Diagnostic::error(
759                &ident,
760                format!("Expected `ready` or `valid`, got `{other}`"),
761            )
762            .primary_label("Expected `ready` or `valid`")),
763        }
764    }
765
766    #[trace_parser]
767    fn argument_list(&mut self) -> Result<Option<Loc<ArgumentList>>> {
768        let is_named = self.peek_and_eat(&TokenKind::Dollar)?.is_some();
769        let opener = peek_for!(self, &TokenKind::OpenParen);
770
771        let argument_list = if is_named {
772            let args = self
773                .comma_separated(Self::named_argument, &TokenKind::CloseParen)
774                .extra_expected(vec![":"])
775                .map_err(|e| {
776                    debug!("check named arguments =");
777                    let Ok(tok) = self.peek() else {
778                        return e;
779                    };
780                    debug!("{:?}", tok);
781                    if tok.kind == TokenKind::Assignment {
782                        e.span_suggest_replace(
783                            "named arguments are specified with `:`",
784                            // FIXME: expand into whitespace
785                            // lifeguard: spade#309
786                            tok.loc(),
787                            ":",
788                        )
789                    } else {
790                        e
791                    }
792                })?
793                .into_iter()
794                .map(Loc::strip)
795                .collect();
796            ArgumentList::Named(args)
797        } else {
798            let args = self
799                .comma_separated(Self::expression, &TokenKind::CloseParen)
800                .no_context()?;
801
802            ArgumentList::Positional(args)
803        };
804        let end = self.eat(&TokenKind::CloseParen)?;
805        let span = lspan(opener.span).merge(lspan(end.span));
806        Ok(Some(argument_list.at(self.file_id, &span)))
807    }
808    #[trace_parser]
809    fn named_argument(&mut self) -> Result<Loc<NamedArgument>> {
810        // This is a named arg
811        let name = self.identifier()?;
812        if self.peek_and_eat(&TokenKind::Colon)?.is_some() {
813            let value = self.expression()?;
814
815            let span = name.span.merge(value.span);
816
817            Ok(NamedArgument::Full(name, value).at(self.file_id, &span))
818        } else {
819            Ok(NamedArgument::Short(name.clone()).at(self.file_id, &name))
820        }
821    }
822
823    #[trace_parser]
824    pub fn type_expression(&mut self) -> Result<Loc<TypeExpression>> {
825        if let Some(val) = self.int_literal()? {
826            Ok(TypeExpression::Integer(val.inner.clone().as_signed()).at_loc(&val))
827        } else if let Some(val) = self.str_literal()? {
828            Ok(TypeExpression::String(val.inner.clone()).at_loc(&val))
829        } else if self.peek_kind(&TokenKind::OpenBrace)? {
830            let (expr, span) = self.surrounded(
831                &TokenKind::OpenBrace,
832                |s| s.expression(),
833                &TokenKind::CloseBrace,
834            )?;
835            Ok(TypeExpression::ConstGeneric(Box::new(expr)).at(self.file_id, &span))
836        } else {
837            let inner = self.type_spec()?;
838
839            Ok(TypeExpression::TypeSpec(Box::new(inner.clone())).at_loc(&inner))
840        }
841    }
842
843    // Types
844    #[trace_parser]
845    pub fn type_spec(&mut self) -> Result<Loc<TypeSpec>> {
846        if let Some(inv) = self.peek_and_eat(&TokenKind::Inv)? {
847            let rest = self.type_expression()?;
848            Ok(TypeSpec::Inverted(Box::new(rest.clone())).between(self.file_id, &inv, &rest))
849        } else if let Some(tilde) = self.peek_and_eat(&TokenKind::Tilde)? {
850            return Err(Diagnostic::error(
851                tilde.clone(),
852                "The syntax for inverted ports has changed from ~ to inv",
853            )
854            .primary_label("~ cannot be used in a type")
855            .span_suggest("Consider using inv", tilde, "inv "));
856        } else if let Some(wire_sign) = self.peek_and_eat(&TokenKind::Ampersand)? {
857            if let Some(mut_) = self.peek_and_eat(&TokenKind::Mut)? {
858                return Err(Diagnostic::error(
859                    &().at(self.file_id, &mut_),
860                    "The syntax of &mut has changed to inv &",
861                )
862                .primary_label("&mut is now written as inv &")
863                .span_suggest_replace(
864                    "Consider using inv &",
865                    ().between(self.file_id, &wire_sign, &mut_),
866                    "inv &",
867                ));
868            }
869
870            let rest = self.type_expression()?;
871            Ok(TypeSpec::Wire(Box::new(rest.clone())).between(self.file_id, &wire_sign, &rest))
872        } else if let Some(tuple) = self.tuple_spec()? {
873            Ok(tuple)
874        } else if let Some(array) = self.array_spec()? {
875            Ok(array)
876        } else {
877            if !self.peek_cond(TokenKind::is_identifier, "Identifier")? {
878                return Err(Diagnostic::from(UnexpectedToken {
879                    got: self.peek()?,
880                    expected: vec!["type"],
881                }));
882            }
883            // Single type, maybe with generics
884            let (path, span) = self.path()?.separate();
885
886            if path.as_strs() == ["_"] {
887                return Ok(TypeSpec::Wildcard.at(self.file_id, &span));
888            }
889
890            // Check if this type has generic params
891            let generics = if self.peek_kind(&TokenKind::Lt)? {
892                let generic_start = self.eat_unconditional()?;
893                let type_exprs = self
894                    .comma_separated(Self::type_expression, &TokenKind::Gt)
895                    .extra_expected(vec!["type expression"])?;
896                let generic_end = self.eat(&TokenKind::Gt)?;
897                Some(type_exprs.between(self.file_id, &generic_start.span, &generic_end.span))
898            } else {
899                None
900            };
901
902            let span_end = generics.as_ref().map(|g| g.span).unwrap_or(span);
903            Ok(TypeSpec::Named(path, generics).between(self.file_id, &span, &span_end))
904        }
905    }
906
907    #[trace_parser]
908    pub fn tuple_spec(&mut self) -> Result<Option<Loc<TypeSpec>>> {
909        let start = peek_for!(self, &TokenKind::OpenParen);
910        if let Some(_) = self.peek_and_eat(&TokenKind::Comma)? {
911            let closer = self.eat(&TokenKind::CloseParen)?;
912            return Ok(Some(TypeSpec::Tuple(vec![]).between(
913                self.file_id,
914                &start,
915                &closer,
916            )));
917        }
918
919        let inner = self
920            .comma_separated(Self::type_expression, &TokenKind::CloseParen)
921            .no_context()?;
922        let end = self.eat(&TokenKind::CloseParen)?;
923
924        let span = lspan(start.span).merge(lspan(end.span));
925
926        Ok(Some(TypeSpec::Tuple(inner).at(self.file_id, &span)))
927    }
928
929    #[trace_parser]
930    pub fn array_spec(&mut self) -> Result<Option<Loc<TypeSpec>>> {
931        let start = peek_for!(self, &TokenKind::OpenBracket);
932
933        let inner = self.type_expression()?;
934
935        if let Some(end) = self.peek_and_eat(&TokenKind::CloseBracket)? {
936            return Err(Diagnostic::error(
937                ().between_locs(&start.loc(), &end.loc()),
938                "missing array size",
939            )
940            .primary_label("missing size for this array type")
941            .note("array types need a specified size")
942            .span_suggest_insert_before("insert a size here", end, "; /* N */"));
943        }
944
945        self.eat(&TokenKind::Semi)?;
946
947        let size = self.type_expression()?;
948
949        let end = self.eat(&TokenKind::CloseBracket)?;
950
951        Ok(Some(
952            TypeSpec::Array {
953                inner: Box::new(inner),
954                size: Box::new(size),
955            }
956            .between(self.file_id, &start, &end),
957        ))
958    }
959
960    /// A name with an associated type, as used in argument definitions as well
961    /// as struct definitions
962    ///
963    /// name: Type
964    #[trace_parser]
965    pub fn name_and_type(&mut self) -> Result<(Loc<Identifier>, Loc<TypeSpec>)> {
966        let name = self.identifier()?;
967        self.eat(&TokenKind::Colon)?;
968        let t = self.type_spec()?;
969        Ok((name, t))
970    }
971
972    #[trace_parser]
973    pub fn pattern(&mut self) -> Result<Loc<Pattern>> {
974        let result = self.first_successful(vec![
975            &|s| {
976                let start = peek_for!(s, &TokenKind::OpenParen);
977                let inner = s
978                    .comma_separated(Self::pattern, &TokenKind::CloseParen)
979                    .no_context()?;
980                let end = s.eat(&TokenKind::CloseParen)?;
981
982                Ok(Some(Pattern::Tuple(inner).between(
983                    s.file_id,
984                    &start.span,
985                    &end.span,
986                )))
987            },
988            &|s| {
989                let start = peek_for!(s, &TokenKind::OpenBracket);
990                let inner = s
991                    .comma_separated(Self::pattern, &TokenKind::CloseBracket)
992                    .no_context()?;
993                let end = s.eat(&TokenKind::CloseBracket)?;
994                Ok(Some(Pattern::Array(inner).between(
995                    s.file_id,
996                    &start.span,
997                    &end.span,
998                )))
999            },
1000            &|s| {
1001                Ok(s.int_literal()?
1002                    // Map option, then map Loc
1003                    .map(|val| val.map(Pattern::Integer)))
1004            },
1005            &|s| {
1006                Ok(s.bool_literal()?
1007                    // Map option, then map Loc
1008                    .map(|val| val.map(Pattern::Bool)))
1009            },
1010            &|s| {
1011                let path = s.path()?;
1012                let path_span = path.span;
1013
1014                if let Some(start_paren) = s.peek_and_eat(&TokenKind::OpenParen)? {
1015                    let inner = s
1016                        .comma_separated(Self::pattern, &TokenKind::CloseParen)
1017                        .no_context()?;
1018                    let end_paren = s.eat(&TokenKind::CloseParen)?;
1019
1020                    Ok(Some(
1021                        Pattern::Type(
1022                            path,
1023                            ArgumentPattern::Positional(inner).between(
1024                                s.file_id,
1025                                &start_paren.span,
1026                                &end_paren.span,
1027                            ),
1028                        )
1029                        .between(s.file_id, &path_span, &end_paren.span),
1030                    ))
1031                } else if let Some(start_brace) = s.peek_and_eat(&TokenKind::Dollar)? {
1032                    s.eat(&TokenKind::OpenParen)?;
1033                    let inner_parser = |s: &mut Self| {
1034                        let lhs = s.identifier()?;
1035                        let rhs = if s.peek_and_eat(&TokenKind::Colon)?.is_some() {
1036                            Some(s.pattern()?)
1037                        } else {
1038                            None
1039                        };
1040
1041                        Ok((lhs, rhs))
1042                    };
1043                    let inner = s
1044                        .comma_separated(inner_parser, &TokenKind::CloseParen)
1045                        .extra_expected(vec![":"])?;
1046                    let end_brace = s.eat(&TokenKind::CloseParen)?;
1047
1048                    Ok(Some(
1049                        Pattern::Type(
1050                            path,
1051                            ArgumentPattern::Named(inner).between(
1052                                s.file_id,
1053                                &start_brace.span,
1054                                &end_brace.span,
1055                            ),
1056                        )
1057                        .between(s.file_id, &path_span, &end_brace.span),
1058                    ))
1059                } else {
1060                    Ok(Some(Pattern::Path(path.clone()).at(s.file_id, &path)))
1061                }
1062            },
1063        ])?;
1064
1065        if let Some(result) = result {
1066            Ok(result)
1067        } else {
1068            Err(Diagnostic::from(UnexpectedToken {
1069                got: self.eat_unconditional()?,
1070                expected: vec!["Pattern"],
1071            }))
1072        }
1073    }
1074
1075    #[trace_parser]
1076    pub fn statements(
1077        &mut self,
1078        allow_stages: bool,
1079    ) -> Result<(Vec<Loc<Statement>>, Option<Loc<Expression>>)> {
1080        fn semi_validator(next: Token) -> Result<TokenKind> {
1081            match next.kind {
1082                TokenKind::GreekQuestionMark => Err(Diagnostic::error(
1083                    next.clone(),
1084                    format!("Expected `;`, got a greek question mark (;)"),
1085                )
1086                .help("The greek question mark (;) looks similar to the normal `;` character")),
1087                other => Ok(other),
1088            }
1089        }
1090        let semi_continuation = |inner: Loc<Statement>, parser: &mut Parser| {
1091            let next = parser.peek()?;
1092            let span = next.loc();
1093            match semi_validator(next) {
1094                Ok(TokenKind::Semi) => {
1095                    parser.eat_unconditional()?;
1096                    Ok(inner)
1097                }
1098                Ok(other) => Err(Diagnostic::error(
1099                    span,
1100                    format!("Expected `;`, got `{}`", other.as_str()),
1101                )
1102                .primary_label("Expected `;`")
1103                .span_suggest_insert_after(
1104                    "You probably forgot to end this statement with a `;`",
1105                    inner,
1106                    ";",
1107                )),
1108                Err(err) => Err(err),
1109            }
1110        };
1111
1112        let mut final_expr = None;
1113        let members = self.keyword_peeking_parser_or_else_seq(
1114            vec![
1115                Box::new(BindingParser {}.then(semi_continuation)),
1116                Box::new(RegisterParser {}.then(semi_continuation).then(
1117                    move |statement, _parser| {
1118                        if let Statement::PipelineRegMarker(_, _) = statement.inner {
1119                            if !allow_stages {
1120                                return Err(Diagnostic::error(
1121                                    statement.loc(),
1122                                    "stage outside pipeline",
1123                                )
1124                                .primary_label("stage is not allowed here")
1125                                .note("stages are only allowed in the root block of a pipeline"));
1126                            }
1127                        }
1128                        Ok(statement)
1129                    },
1130                )),
1131                Box::new(DeclParser {}.then(semi_continuation)),
1132                Box::new(LabelParser {}),
1133                Box::new(AssertParser {}.then(semi_continuation)),
1134                Box::new(SetParser {}.then(semi_continuation)),
1135            ],
1136            true,
1137            vec![TokenKind::CloseBrace],
1138            |parser| {
1139                if parser.peek_kind(&TokenKind::CloseBrace)? {
1140                    return Ok(None);
1141                }
1142                let (expr, loc) = parser.non_comptime_expression()?.separate_loc();
1143                if matches!(semi_validator(parser.peek()?)?, TokenKind::Semi) {
1144                    parser.eat_unconditional()?;
1145                    Ok(Some(Statement::Expression(expr).at_loc(&loc)))
1146                } else {
1147                    // no semicolon afterwards - set as return value and break out of loop
1148                    final_expr = Some(expr);
1149                    Ok(None)
1150                }
1151            },
1152        )?;
1153
1154        Ok((members, final_expr))
1155    }
1156
1157    #[trace_parser]
1158    pub fn self_arg(&mut self) -> Result<Option<Loc<()>>> {
1159        if self.peek_cond(
1160            |t| t == &TokenKind::Identifier("self".to_string()),
1161            "looking for self",
1162        )? {
1163            let tok = self.eat_unconditional()?;
1164            Ok(Some(().at(self.file_id, &tok.span)))
1165        } else {
1166            Ok(None)
1167        }
1168    }
1169
1170    #[trace_parser]
1171    pub fn parameter(&mut self) -> Result<(AttributeList, Loc<Identifier>, Loc<TypeSpec>)> {
1172        let attrs = self.attributes()?;
1173        let (name, ty) = self.name_and_type()?;
1174        Ok((attrs, name, ty))
1175    }
1176
1177    #[trace_parser]
1178    pub fn parameter_list(&mut self) -> Result<ParameterList> {
1179        let self_ = if self.peek_cond(
1180            |tok| tok == &TokenKind::Identifier(String::from("self")),
1181            "Expected argument",
1182        )? {
1183            let self_tok = self.eat_unconditional()?;
1184            self.peek_and_eat(&TokenKind::Comma)?;
1185            Some(().at(self.file_id, &self_tok))
1186        } else {
1187            None
1188        };
1189
1190        Ok(ParameterList {
1191            self_,
1192            args: self
1193                .comma_separated(Self::parameter, &TokenKind::CloseParen)
1194                .no_context()?,
1195        })
1196    }
1197
1198    #[tracing::instrument(skip(self))]
1199    pub fn type_parameter_list(&mut self) -> Result<ParameterList> {
1200        Ok(ParameterList::without_self(
1201            self.comma_separated(Self::parameter, &TokenKind::CloseBrace)
1202                .no_context()?,
1203        ))
1204    }
1205
1206    #[trace_parser]
1207    pub fn type_param(&mut self) -> Result<Loc<TypeParam>> {
1208        // If this is a type level integer
1209        if let Some(_hash) = self.peek_and_eat(&TokenKind::Hash)? {
1210            let meta_type = self.identifier()?;
1211            let name = self.identifier()?;
1212
1213            let loc = ().between_locs(&meta_type, &name);
1214            Ok(TypeParam::TypeWithMeta {
1215                meta: meta_type,
1216                name,
1217            }
1218            .at_loc(&loc))
1219        } else {
1220            let (id, loc) = self.identifier()?.separate();
1221            let traits = if self.peek_and_eat(&TokenKind::Colon)?.is_some() {
1222                self.token_separated(
1223                    Self::path_with_generic_spec,
1224                    &TokenKind::Plus,
1225                    vec![TokenKind::Comma, TokenKind::Gt],
1226                )
1227                .no_context()?
1228                .into_iter()
1229                .map(|(path, type_params)| {
1230                    let loc = ().at_loc(&path);
1231                    TraitSpec { path, type_params }.at_loc(&loc)
1232                })
1233                .collect()
1234            } else {
1235                vec![]
1236            };
1237            Ok(TypeParam::TypeName { name: id, traits }.at(self.file_id, &loc))
1238        }
1239    }
1240
1241    #[trace_parser]
1242    pub fn generics_list(&mut self) -> Result<Option<Loc<Vec<Loc<TypeParam>>>>> {
1243        if self.peek_kind(&TokenKind::Lt)? {
1244            let (params, loc) = self.surrounded(
1245                &TokenKind::Lt,
1246                |s| {
1247                    s.comma_separated(Self::type_param, &TokenKind::Gt)
1248                        .extra_expected(vec!["type parameter"])
1249                },
1250                &TokenKind::Gt,
1251            )?;
1252            Ok(Some(params.at_loc(&loc)))
1253        } else {
1254            Ok(None)
1255        }
1256    }
1257
1258    #[trace_parser]
1259    pub fn generic_spec_list(&mut self) -> Result<Option<Loc<Vec<Loc<TypeExpression>>>>> {
1260        if self.peek_kind(&TokenKind::Lt)? {
1261            let (params, loc) = self.surrounded(
1262                &TokenKind::Lt,
1263                |s| {
1264                    s.comma_separated(Self::type_expression, &TokenKind::Gt)
1265                        .extra_expected(vec!["type spec"])
1266                },
1267                &TokenKind::Gt,
1268            )?;
1269            Ok(Some(params.at_loc(&loc)))
1270        } else {
1271            Ok(None)
1272        }
1273    }
1274
1275    #[trace_parser]
1276    pub fn path_with_generic_spec(
1277        &mut self,
1278    ) -> Result<(Loc<Path>, Option<Loc<Vec<Loc<TypeExpression>>>>)> {
1279        Ok((self.path()?, self.generic_spec_list()?))
1280    }
1281
1282    fn disallow_attributes(&self, attributes: &AttributeList, item_start: &Token) -> Result<()> {
1283        if attributes.0.is_empty() {
1284            Ok(())
1285        } else {
1286            let mut diagnostic = Diagnostic::error(
1287                ().between_locs(attributes.0.first().unwrap(), attributes.0.last().unwrap()),
1288                "invalid attribute location",
1289            )
1290            .primary_label("attributes are not allowed here")
1291            .secondary_label(
1292                item_start.loc(),
1293                format!("...because this is a {}", item_start.kind.as_str()),
1294            )
1295            .note("attributes are only allowed on structs, enums, their variants, functions and pipelines");
1296            if matches!(item_start.kind, TokenKind::Mod) {
1297                diagnostic.add_help(
1298                    "If you want to document this module, use inside comments (//!) instead.",
1299                );
1300            }
1301            Err(diagnostic)
1302        }
1303    }
1304
1305    pub fn unit_kind(&mut self, start_token: &Token) -> Result<Option<Loc<UnitKind>>> {
1306        match start_token.kind {
1307            TokenKind::Pipeline => {
1308                self.eat_unconditional()?;
1309                let (depth, depth_span) = self.surrounded(
1310                    &TokenKind::OpenParen,
1311                    |s| match s.type_expression() {
1312                        Ok(t) => Ok(t),
1313                        Err(diag) => Err(diag.secondary_label(
1314                            ().at(s.file_id, start_token),
1315                            "Pipelines require a pipeline depth",
1316                        )),
1317                    },
1318                    &TokenKind::CloseParen,
1319                )?;
1320
1321                Ok(Some(UnitKind::Pipeline(depth).between(
1322                    self.file_id,
1323                    start_token,
1324                    &depth_span,
1325                )))
1326            }
1327            TokenKind::Function => {
1328                self.eat_unconditional()?;
1329                Ok(Some(UnitKind::Function.at(self.file_id, start_token)))
1330            }
1331            TokenKind::Entity => {
1332                self.eat_unconditional()?;
1333                Ok(Some(UnitKind::Entity.at(self.file_id, start_token)))
1334            }
1335            _ => Ok(None),
1336        }
1337    }
1338
1339    #[trace_parser]
1340    #[tracing::instrument(skip(self))]
1341    pub fn unit_head(&mut self, attributes: &AttributeList) -> Result<Option<Loc<UnitHead>>> {
1342        let unsafe_token = self.peek_and_eat(&TokenKind::Unsafe)?;
1343        let extern_token = self.peek_and_eat(&TokenKind::Extern)?;
1344        let start_token = self.peek()?;
1345        let Some(unit_kind) = self.unit_kind(&start_token)? else {
1346            if let Some(prev) = unsafe_token.or(extern_token) {
1347                return Err(Diagnostic::error(
1348                    start_token,
1349                    "Expected `fn`, `entity` or `pipeline`",
1350                )
1351                .primary_label("Expected `fn`, `entity` or `pipeline`")
1352                .secondary_label(prev, "Because of this keyword"));
1353            } else {
1354                return Ok(None);
1355            }
1356        };
1357
1358        let name = self.identifier()?;
1359
1360        let type_params = self.generics_list()?;
1361
1362        // Input types
1363        let (inputs, inputs_loc) = self.surrounded(
1364            &TokenKind::OpenParen,
1365            Self::parameter_list,
1366            &TokenKind::CloseParen,
1367        )?;
1368        let inputs = inputs.at_loc(&inputs_loc);
1369
1370        // Return type
1371        let output_type = if let Some(arrow) = self.peek_and_eat(&TokenKind::SlimArrow)? {
1372            Some((arrow.loc(), self.type_spec()?))
1373        } else {
1374            None
1375        };
1376
1377        let where_clauses = self.where_clauses()?;
1378
1379        let end = output_type
1380            .as_ref()
1381            .map(|o| o.1.loc())
1382            .unwrap_or(inputs.loc());
1383
1384        Ok(Some(
1385            UnitHead {
1386                unsafe_token: unsafe_token.map(|token| token.loc()),
1387                extern_token: extern_token.map(|token| token.loc()),
1388                attributes: attributes.clone(),
1389                unit_kind,
1390                name,
1391                inputs,
1392                output_type,
1393                type_params,
1394                where_clauses,
1395            }
1396            .between(self.file_id, &start_token, &end),
1397        ))
1398    }
1399
1400    fn where_clauses(&mut self) -> Result<Vec<WhereClause>> {
1401        if let Some(where_kw) = self.peek_and_eat(&TokenKind::Where)? {
1402            let clauses = self
1403                .token_separated(
1404                    |s| {
1405                        if s.peek_cond(|t| matches!(t, &TokenKind::Identifier(_)), "identifier")? {
1406                            let name = s.path()?;
1407                            let _colon = s.eat(&TokenKind::Colon)?;
1408
1409                            if s.peek_cond(
1410                                |tok| tok == &TokenKind::OpenBrace || tok == &TokenKind::Semi,
1411                                "{",
1412                            )? {
1413                                let expression = s
1414                                    .surrounded(
1415                                        &TokenKind::OpenBrace,
1416                                        Self::expression,
1417                                        &TokenKind::CloseBrace,
1418                                    )?
1419                                    .0;
1420
1421                                Ok(WhereClause::GenericInt {
1422                                    target: name,
1423                                    expression,
1424                                })
1425                            } else {
1426                                let traits = s
1427                                    .token_separated(
1428                                        Self::path_with_generic_spec,
1429                                        &TokenKind::Plus,
1430                                        vec![
1431                                            TokenKind::Comma,
1432                                            TokenKind::OpenBrace,
1433                                            TokenKind::Semi,
1434                                        ],
1435                                    )
1436                                    .extra_expected(vec!["identifier"])?
1437                                    .into_iter()
1438                                    .map(|(path, type_params)| {
1439                                        let loc = ().at_loc(&path);
1440                                        TraitSpec { path, type_params }.at_loc(&loc)
1441                                    })
1442                                    .collect();
1443
1444                                Ok(WhereClause::TraitBounds {
1445                                    target: name,
1446                                    traits,
1447                                })
1448                            }
1449                        } else {
1450                            Err(Diagnostic::bug(
1451                                ().at(s.file_id, &where_kw),
1452                                "Comma separated should not show this error",
1453                            ))
1454                        }
1455                    },
1456                    &TokenKind::Comma,
1457                    vec![TokenKind::OpenBrace, TokenKind::Semi],
1458                )
1459                .extra_expected(vec!["identifier"])?;
1460
1461            Ok(clauses)
1462        } else {
1463            Ok(vec![])
1464        }
1465    }
1466
1467    #[trace_parser]
1468    pub fn impl_body(&mut self) -> Result<Vec<Loc<Unit>>> {
1469        let result = self.keyword_peeking_parser_seq(
1470            vec![Box::new(items::UnitParser {}.map(|u| Ok(u)))],
1471            true,
1472            vec![TokenKind::CloseBrace],
1473        )?;
1474
1475        Ok(result)
1476    }
1477
1478    #[trace_parser]
1479    #[tracing::instrument(level = "debug", skip(self))]
1480    pub fn enum_variant(&mut self) -> Result<EnumVariant> {
1481        let attributes = self.attributes()?;
1482
1483        let name = self.identifier()?;
1484
1485        let args = if let Some(start) = self.peek_and_eat(&TokenKind::OpenBrace)? {
1486            let result = self.type_parameter_list()?;
1487            let end = self.eat(&TokenKind::CloseBrace)?;
1488            Some(result.between(self.file_id, &start, &end))
1489        } else if self.peek_kind(&TokenKind::Comma)? || self.peek_kind(&TokenKind::CloseBrace)? {
1490            None
1491        } else {
1492            let token = self.peek()?;
1493            let message = unexpected_token_message(&token.kind, "`{`, `,` or `}`");
1494            // FIXME: Error::Eof => Diagnostic
1495            let mut err = Diagnostic::error(token, message);
1496            self.maybe_suggest_brace_enum_variant(&mut err)?;
1497            return Err(err);
1498        };
1499
1500        Ok(EnumVariant {
1501            attributes,
1502            name,
1503            args,
1504        })
1505    }
1506
1507    fn maybe_suggest_brace_enum_variant(&mut self, err: &mut Diagnostic) -> Result<bool> {
1508        let open_paren = match self.peek_and_eat(&TokenKind::OpenParen)? {
1509            Some(open_paren) => open_paren.loc(),
1510            _ => return Ok(false),
1511        };
1512        let mut try_parameter_list = self.clone();
1513        if try_parameter_list.parameter_list().is_err() {
1514            return Ok(false);
1515        }
1516        let close_paren = match try_parameter_list.peek_and_eat(&TokenKind::CloseParen)? {
1517            Some(close_paren) => close_paren.loc(),
1518            _ => return Ok(false),
1519        };
1520        err.push_subdiagnostic(
1521            SuggestBraceEnumVariant {
1522                open_paren,
1523                close_paren,
1524            }
1525            .into(),
1526        );
1527        Ok(true)
1528    }
1529
1530    // Parses `<identifier>=<subtree>` if `identifier` matches the specified identifier
1531    #[trace_parser]
1532    #[tracing::instrument(skip(self, value))]
1533    pub fn attribute_key_value<T>(
1534        &mut self,
1535        key: &str,
1536        value: impl Fn(&mut Self) -> Result<T>,
1537    ) -> Result<Option<(Loc<String>, T)>> {
1538        let next = self.peek()?;
1539        if next.kind == TokenKind::Identifier(key.to_string()) {
1540            self.eat_unconditional()?;
1541
1542            self.eat(&TokenKind::Assignment)?;
1543
1544            Ok(Some((
1545                key.to_string().at(self.file_id, &next),
1546                value(self)?,
1547            )))
1548        } else {
1549            Ok(None)
1550        }
1551    }
1552
1553    #[trace_parser]
1554    #[tracing::instrument(skip(self))]
1555    pub fn attribute_inner(&mut self) -> Result<Attribute> {
1556        let start = self.identifier()?;
1557
1558        macro_rules! bool_or_payload {
1559            ($name:ident bool) => {
1560                let mut $name = false;
1561            };
1562            ($name:ident $rest:tt) => {
1563                let mut $name = None;
1564            };
1565        }
1566        macro_rules! rhs_or_present {
1567            ($name:ident, $tok:expr, $s:ident, bool) => {
1568                $name = true
1569            };
1570            ($name:ident, $tok:expr, $s:ident, $subparser:tt) => {{
1571                if let Some(prev) = &$name {
1572                    return Err(Diagnostic::error(
1573                        $tok,
1574                        format!("{} specified more than once", stringify!($name)),
1575                    )
1576                    .primary_label("Specified multiple times")
1577                    .secondary_label(prev, "Previously specified here")
1578                    .into());
1579                }
1580
1581                $s.peek_and_eat(&TokenKind::Assignment)?;
1582                $name = Some($subparser?)
1583            }};
1584        }
1585
1586        macro_rules! check_required {
1587            ($attr_token:expr, $name:ident) => {};
1588            ($attr_token:expr, $name:ident $required:ident) => {
1589                let $name = if let Some(inner) = $name {
1590                    inner
1591                } else {
1592                    return Err(Diagnostic::error(
1593                        $attr_token,
1594                        format!("Missing argument '{}'", stringify!($name)),
1595                    )
1596                    .primary_label(format!("Missing argument '{}'", stringify!($name)))
1597                    .into());
1598                };
1599            };
1600        }
1601
1602        macro_rules! attribute_arg_parser {
1603            ($attr:expr, $self:expr, $s:ident, $result_struct:path{ $($name:ident $([$required:ident])?:  $assignment:tt),* }) => {
1604                {
1605                $( bool_or_payload!($name $assignment) );*;
1606
1607                let params = vec![$(stringify!($name)),*];
1608
1609                $self.surrounded(
1610                    &TokenKind::OpenParen, |$s| {
1611                        loop {
1612                            let next = $s.peek()?;
1613                            match &next.kind {
1614                                $(
1615                                    TokenKind::Identifier(ident) if ident == stringify!($name) => {
1616                                        $s.eat_unconditional()?;
1617                                        rhs_or_present!($name, next, $s, $assignment);
1618                                    }
1619                                ),*
1620                                TokenKind::Identifier(_) => {
1621                                    return Err(Diagnostic::error(next, format!("Invalid parameter for {}", $attr))
1622                                        .primary_label("Invalid parameter")
1623                                        .note(if params.is_empty() {
1624                                            format!(
1625                                                "{} does not take any parameters",
1626                                                $attr
1627                                            )
1628                                        } else if params.len() == 1 {
1629                                            format!(
1630                                                "{} only takes the parameter {}",
1631                                                $attr,
1632                                                params[0]
1633                                            )
1634                                        } else {
1635                                            format!(
1636                                                "{} only takes the parameters {} or {}",
1637                                                $attr,
1638                                                params.iter().take(params.len()-1).join(", "),
1639                                                params[params.len() - 1]
1640                                            )
1641                                        })
1642                                        .into()
1643                                    )
1644                                }
1645                                TokenKind::Comma => {
1646                                    $s.eat_unconditional()?;
1647                                }
1648                                TokenKind::CloseParen => {
1649                                    break
1650                                },
1651                                _ => {
1652                                    return Err(Diagnostic::from(UnexpectedToken {
1653                                        got: next,
1654                                        expected: vec!["identifier", ",", ")"],
1655                                    }).into())
1656                                }
1657                            }
1658                        }
1659
1660                        Ok(())
1661                    },
1662                    &TokenKind::CloseParen
1663                )?;
1664
1665                $(check_required!($attr, $name $($required)?);)*
1666
1667                $result_struct {
1668                    $($name),*
1669                }
1670            }
1671            }
1672        }
1673
1674        match start.inner.0.as_str() {
1675            "no_mangle" => {
1676                if self.peek_kind(&TokenKind::OpenParen)? {
1677                    let (all, _) = self.surrounded(
1678                        &TokenKind::OpenParen,
1679                        Self::identifier,
1680                        &TokenKind::CloseParen,
1681                    )?;
1682                    if all.inner.0.as_str() != "all" {
1683                        Err(Diagnostic::error(&all, "Invalid attribute syntax")
1684                            .primary_label("Unexpected parameter to `#[no_mangle])")
1685                            .span_suggest_replace("Did you mean `#[no_mangle(all)]`?", all, "all"))
1686                    } else {
1687                        Ok(Attribute::NoMangle { all: true })
1688                    }
1689                } else {
1690                    Ok(Attribute::NoMangle { all: false })
1691                }
1692            }
1693            "fsm" => {
1694                if self.peek_kind(&TokenKind::OpenParen)? {
1695                    let (state, _) = self.surrounded(
1696                        &TokenKind::OpenParen,
1697                        Self::identifier,
1698                        &TokenKind::CloseParen,
1699                    )?;
1700                    Ok(Attribute::Fsm { state: Some(state) })
1701                } else {
1702                    Ok(Attribute::Fsm { state: None })
1703                }
1704            }
1705            "optimize" => {
1706                let (passes, _) = self.surrounded(
1707                    &TokenKind::OpenParen,
1708                    |s| {
1709                        s.comma_separated(|s| s.identifier(), &TokenKind::CloseParen)
1710                            .no_context()
1711                    },
1712                    &TokenKind::CloseParen,
1713                )?;
1714
1715                Ok(Attribute::Optimize {
1716                    passes: passes
1717                        .into_iter()
1718                        .map(|loc| loc.map(|ident| ident.0))
1719                        .collect(),
1720                })
1721            }
1722            "surfer_translator" => {
1723                let (result, _) = self.surrounded(
1724                    &TokenKind::OpenParen,
1725                    |s| {
1726                        let tok = s.peek()?;
1727                        if let TokenKind::String(name) = tok.kind {
1728                            s.eat_unconditional()?;
1729                            Ok(Attribute::SurferTranslator(name))
1730                        } else {
1731                            Err(UnexpectedToken {
1732                                got: tok,
1733                                expected: vec!["string"],
1734                            }
1735                            .into())
1736                        }
1737                    },
1738                    &TokenKind::CloseParen,
1739                )?;
1740                Ok(result)
1741            }
1742            "wal_trace" => {
1743                if self.peek_kind(&TokenKind::OpenParen)? {
1744                    Ok(attribute_arg_parser!(
1745                        start,
1746                        self,
1747                        s,
1748                        Attribute::WalTrace {
1749                            clk: { s.expression() },
1750                            rst: { s.expression() }
1751                        }
1752                    ))
1753                } else {
1754                    Ok(Attribute::WalTrace {
1755                        clk: None,
1756                        rst: None,
1757                    })
1758                }
1759            }
1760            "wal_traceable" => Ok(attribute_arg_parser!(
1761                start,
1762                self,
1763                s,
1764                Attribute::WalTraceable {
1765                    suffix: { s.identifier() },
1766                    uses_clk: bool,
1767                    uses_rst: bool
1768                }
1769            )),
1770            "wal_suffix" => Ok(attribute_arg_parser!(start, self, s, Attribute::WalSuffix {
1771                suffix [required]: {s.identifier()}
1772            })),
1773            other => Err(
1774                Diagnostic::error(&start, format!("Unknown attribute '{other}'"))
1775                    .primary_label("Unrecognised attribute"),
1776            ),
1777        }
1778    }
1779
1780    #[trace_parser]
1781    pub fn attributes(&mut self) -> Result<AttributeList> {
1782        // peek_for!(self, &TokenKind::Hash)
1783        let mut result = AttributeList(vec![]);
1784        loop {
1785            if let Some(start) = self.peek_and_eat(&TokenKind::Hash)? {
1786                let (inner, loc) = self.surrounded(
1787                    &TokenKind::OpenBracket,
1788                    Self::attribute_inner,
1789                    &TokenKind::CloseBracket,
1790                )?;
1791
1792                result.0.push(inner.between(self.file_id, &start, &loc));
1793            } else if self.peek_cond(
1794                |tk| matches!(tk, TokenKind::OutsideDocumentation(_)),
1795                "Outside doc-comment",
1796            )? {
1797                let token = self.eat_unconditional()?;
1798                let TokenKind::OutsideDocumentation(doc) = token.kind else {
1799                    unreachable!("eat_cond should have checked this");
1800                };
1801                result
1802                    .0
1803                    .push(Attribute::Documentation { content: doc }.at(token.file_id, &token.span));
1804            } else {
1805                break;
1806            }
1807        }
1808        Ok(result)
1809    }
1810
1811    #[trace_parser]
1812    #[tracing::instrument(skip(self))]
1813    pub fn module_body(&mut self) -> Result<ModuleBody> {
1814        let mut documentation = vec![];
1815        while self.peek_cond(
1816            |tk| matches!(tk, TokenKind::InsideDocumentation(_)),
1817            "Inside doc-comment",
1818        )? {
1819            let token = self.eat_unconditional()?;
1820            let TokenKind::InsideDocumentation(doc) = token.kind else {
1821                unreachable!("eat_cond should have checked this");
1822            };
1823            documentation.push(doc);
1824        }
1825
1826        let members = self.keyword_peeking_parser_seq(
1827            vec![
1828                Box::new(items::UnitParser {}.map(|inner| Ok(Item::Unit(inner)))),
1829                Box::new(items::TraitDefParser {}.map(|inner| Ok(Item::TraitDef(inner)))),
1830                Box::new(items::ImplBlockParser {}.map(|inner| Ok(Item::ImplBlock(inner)))),
1831                Box::new(items::StructParser {}.map(|inner| Ok(Item::Type(inner)))),
1832                Box::new(items::EnumParser {}.map(|inner| Ok(Item::Type(inner)))),
1833                Box::new(items::ModuleParser {}),
1834                Box::new(items::UseParser {}.map(|inner| Ok(Item::Use(inner)))),
1835            ],
1836            true,
1837            vec![],
1838        )?;
1839        Ok(ModuleBody {
1840            members,
1841            documentation,
1842        })
1843    }
1844
1845    /// A module body which is not part of a `mod`. Errors if there is anything
1846    /// but an item found after the last item
1847    #[trace_parser]
1848    #[tracing::instrument(skip(self))]
1849    pub fn top_level_module_body(&mut self) -> Result<Loc<ModuleBody>> {
1850        let start_token = self.peek()?;
1851        let result = self.module_body()?;
1852        let end_token = self.peek()?;
1853
1854        if self.peek_kind(&TokenKind::Eof)? {
1855            Ok(result.between(self.file_id, &start_token, &end_token))
1856        } else {
1857            let got = self.peek()?;
1858            Err(Diagnostic::error(
1859                got.loc(),
1860                format!("expected item, got `{}`", got.kind.as_str()),
1861            )
1862            .primary_label("expected item"))
1863        }
1864    }
1865}
1866
1867// Helper functions for combining parsers
1868impl<'a> Parser<'a> {
1869    #[tracing::instrument(skip_all, fields(parsers = parsers.len()))]
1870    fn first_successful<T>(
1871        &mut self,
1872        parsers: Vec<&dyn Fn(&mut Self) -> Result<Option<T>>>,
1873    ) -> Result<Option<T>> {
1874        for parser in parsers {
1875            match parser(self) {
1876                Ok(Some(val)) => {
1877                    event!(Level::INFO, "Parser matched");
1878                    return Ok(Some(val));
1879                }
1880                Ok(None) => continue,
1881                Err(e) => return Err(e),
1882            }
1883        }
1884        event!(Level::INFO, "No parser matched");
1885        Ok(None)
1886    }
1887
1888    /// Attempts to parse an inner structure surrounded by two tokens, like `( x )`.
1889    ///
1890    /// If the `start` token is not found, an error is produced.
1891    ///
1892    /// If the end token is not found, return a mismatch error
1893    #[tracing::instrument(level = "debug", skip(self, inner))]
1894    fn surrounded<T>(
1895        &mut self,
1896        start: &TokenKind,
1897        mut inner: impl FnMut(&mut Self) -> Result<T>,
1898        end_kind: &TokenKind,
1899    ) -> Result<(T, Loc<()>)> {
1900        let opener = self.eat(start)?;
1901        let result = inner(self)?;
1902        // FIXME: Better error handling here. We are throwing away potential EOFs
1903        let end = if let Some(end) = self.peek_and_eat(end_kind)? {
1904            end
1905        } else {
1906            let got = self.eat_unconditional()?;
1907            return Err(Diagnostic::error(
1908                got.loc(),
1909                format!(
1910                    "expected closing `{}`, got `{}`",
1911                    end_kind.as_str(),
1912                    got.kind.as_str()
1913                ),
1914            )
1915            .primary_label(format!("expected `{}`", end_kind.as_str()))
1916            .secondary_label(
1917                opener.loc(),
1918                format!("...to close this `{}`", start.as_str()),
1919            ));
1920        };
1921
1922        Ok((
1923            result,
1924            Loc::new((), lspan(opener.span).merge(lspan(end.span)), self.file_id),
1925        ))
1926    }
1927
1928    pub fn comma_separated<T>(
1929        &mut self,
1930        inner: impl Fn(&mut Self) -> Result<T>,
1931        // This end marker is used for allowing trailing commas. It should
1932        // be whatever ends the collection that is searched. I.e. (a,b,c,) should have
1933        // `)`, and {} should have `}`
1934        end_marker: &TokenKind,
1935    ) -> CommaSeparatedResult<Vec<T>> {
1936        self.token_separated(inner, &TokenKind::Comma, vec![end_marker.clone()])
1937    }
1938
1939    // NOTE: This cannot currently use #[trace_parser] as it returns an error which is not
1940    // convertible into `Error`. If we end up with more functions like this, that
1941    // macro should probably be made smarter
1942    #[tracing::instrument(level = "debug", skip(self, inner))]
1943    pub fn token_separated<T>(
1944        &mut self,
1945        inner: impl Fn(&mut Self) -> Result<T>,
1946        separator: &TokenKind,
1947        // This end marker is used for allowing trailing commas. It should
1948        // be whatever ends the collection that is searched. I.e. (a,b,c,) should have
1949        // `)`, and {} should have `}`
1950        end_markers: Vec<TokenKind>,
1951    ) -> CommaSeparatedResult<Vec<T>> {
1952        self.parse_stack
1953            .push(ParseStackEntry::Enter("comma_separated".to_string()));
1954        let ret = || -> CommaSeparatedResult<Vec<T>> {
1955            let mut result = vec![];
1956            loop {
1957                // The list might be empty
1958                if end_markers
1959                    .iter()
1960                    .map(|m| self.peek_kind(m))
1961                    .collect::<Result<Vec<_>>>()?
1962                    .into_iter()
1963                    .any(|x| x)
1964                {
1965                    break;
1966                }
1967                result.push(inner(self)?);
1968
1969                // Now we expect to either find a comma, in which case we resume the
1970                // search, or an end marker, in which case we abort
1971                if end_markers
1972                    .iter()
1973                    .map(|m| self.peek_kind(m))
1974                    .collect::<Result<Vec<_>>>()?
1975                    .into_iter()
1976                    .any(|x| x)
1977                {
1978                    break;
1979                } else if self.peek_kind(separator)? {
1980                    self.eat_unconditional()?;
1981                } else {
1982                    return Err(TokenSeparatedError::UnexpectedToken {
1983                        got: self.peek()?,
1984                        separator: separator.clone(),
1985                        end_tokens: end_markers,
1986                    });
1987                }
1988            }
1989            Ok(result)
1990        }();
1991        if let Err(e) = &ret {
1992            self.parse_stack
1993                .push(ParseStackEntry::ExitWithDiagnostic(e.clone().no_context()));
1994        } else {
1995            self.parse_stack.push(ParseStackEntry::Exit);
1996        }
1997
1998        ret
1999    }
2000
2001    fn keyword_peeking_parser_seq<T>(
2002        &mut self,
2003        parsers: Vec<Box<dyn KeywordPeekingParser<T>>>,
2004        support_attributes: bool,
2005        additional_continuations: Vec<TokenKind>,
2006    ) -> Result<Vec<T>> {
2007        let mut result = vec![];
2008        let continuations = parsers
2009            .iter()
2010            .map(|p| p.leading_tokens())
2011            .flatten()
2012            .chain(additional_continuations.iter().cloned())
2013            .collect::<Vec<_>>();
2014        loop {
2015            let inner = self._keyword_peeking_parser_inner(
2016                parsers.as_slice(),
2017                support_attributes,
2018                continuations.as_slice(),
2019            );
2020
2021            match inner {
2022                RecoveryResult::Ok(Some(stmt)) => result.push(stmt),
2023                RecoveryResult::Ok(None) => break,
2024                RecoveryResult::Recovered => continue,
2025            }
2026        }
2027        Ok(result)
2028    }
2029
2030    /// Works like `keyword_peeking_parser_seq` but runs the `other` function if none of the keyword parsers matched.
2031    ///
2032    /// If the `other` function returns a value, it is added to the result and the loop continues.
2033    /// If the `other` function returns `None`, the loop ends.
2034    fn keyword_peeking_parser_or_else_seq<T, F>(
2035        &mut self,
2036        parsers: Vec<Box<dyn KeywordPeekingParser<T>>>,
2037        support_attributes: bool,
2038        additional_continuations: Vec<TokenKind>,
2039        mut other: F,
2040    ) -> Result<Vec<T>>
2041    where
2042        F: FnMut(&mut Self) -> Result<Option<T>>,
2043    {
2044        let mut result = vec![];
2045        let continuations = parsers
2046            .iter()
2047            .map(|p| p.leading_tokens())
2048            .flatten()
2049            .chain(additional_continuations.iter().cloned())
2050            .collect::<Vec<_>>();
2051        loop {
2052            let inner = self._keyword_peeking_parser_inner(
2053                parsers.as_slice(),
2054                support_attributes,
2055                continuations.as_slice(),
2056            );
2057
2058            match inner {
2059                RecoveryResult::Ok(Some(stmt)) => result.push(stmt),
2060                RecoveryResult::Ok(None) => {
2061                    if let Some(other_res) = (other)(self)? {
2062                        result.push(other_res);
2063                    } else {
2064                        break;
2065                    }
2066                }
2067                RecoveryResult::Recovered => continue,
2068            }
2069        }
2070        Ok(result)
2071    }
2072
2073    fn _keyword_peeking_parser_inner<T>(
2074        &mut self,
2075        parsers: &[Box<dyn KeywordPeekingParser<T>>],
2076        support_attributes: bool,
2077        continuations: &[TokenKind],
2078    ) -> RecoveryResult<Option<T>> {
2079        self.with_recovery(
2080            |s| {
2081                let attributes = if support_attributes {
2082                    s.attributes()?
2083                } else {
2084                    AttributeList::empty()
2085                };
2086
2087                let next = s.peek()?;
2088                let mut result = None;
2089                for parser in parsers {
2090                    if parser.leading_tokens().contains(&next.kind) {
2091                        result = Some(parser.parse(s, &attributes)?)
2092                    }
2093                }
2094                Ok(result)
2095            },
2096            Vec::from(continuations),
2097        )
2098    }
2099
2100    pub fn with_recovery<T>(
2101        &mut self,
2102        inner: impl Fn(&mut Self) -> Result<T>,
2103        continuations: Vec<TokenKind>,
2104    ) -> RecoveryResult<T> {
2105        self.recovering_tokens.push(continuations);
2106        let result = match inner(self) {
2107            Ok(result) => RecoveryResult::Ok(result),
2108            Err(e) => {
2109                self.diags.errors.push(e);
2110
2111                // Once we error, consume tokens until we find a token in the
2112                // current continuation set.
2113                while let Ok(tok) = self.peek() {
2114                    if self
2115                        .recovering_tokens
2116                        .iter()
2117                        .rev()
2118                        .any(|list| list.iter().any(|t| t == &tok.kind))
2119                    {
2120                        break;
2121                    }
2122                    // Safe unwrap, we already peeked
2123                    self.eat_unconditional().unwrap();
2124                }
2125
2126                RecoveryResult::Recovered
2127            }
2128        };
2129        self.recovering_tokens.pop();
2130        result
2131    }
2132}
2133
2134// Helper functions for advancing the token stream
2135impl<'a> Parser<'a> {
2136    fn eat(&mut self, expected: &TokenKind) -> Result<Token> {
2137        self.parse_stack
2138            .push(ParseStackEntry::EatingExpected(expected.clone()));
2139        // Calling keep and eat in order to correctly handle >> as > > if desired
2140        let next = self.eat_unconditional()?;
2141        if &next.kind == expected {
2142            Ok(next)
2143        } else if expected == &TokenKind::Gt && next.kind == TokenKind::RightShift {
2144            self.peeked = Some(Token {
2145                kind: TokenKind::Gt,
2146                span: next.span.end..next.span.end,
2147                file_id: next.file_id,
2148            });
2149            Ok(Token {
2150                kind: TokenKind::Gt,
2151                span: next.span.start..next.span.start,
2152                file_id: next.file_id,
2153            })
2154        } else if expected == &TokenKind::Gt && next.kind == TokenKind::ArithmeticRightShift {
2155            self.peeked = Some(Token {
2156                kind: TokenKind::RightShift,
2157                span: next.span.start + 1..next.span.end,
2158                file_id: next.file_id,
2159            });
2160            Ok(Token {
2161                kind: TokenKind::Gt,
2162                span: next.span.start..next.span.start,
2163                file_id: next.file_id,
2164            })
2165        } else {
2166            Err(Diagnostic::from(UnexpectedToken {
2167                got: next,
2168                expected: vec![expected.as_str()],
2169            }))
2170        }
2171    }
2172
2173    fn eat_cond(
2174        &mut self,
2175        condition: impl Fn(&TokenKind) -> bool,
2176        expected_description: &'static str,
2177    ) -> Result<Token> {
2178        // Check if we already have a peeked token
2179        let next = self.eat_unconditional()?;
2180
2181        // Make sure we ate the correct token
2182        if !condition(&next.kind) {
2183            Err(Diagnostic::from(UnexpectedToken {
2184                got: next,
2185                expected: vec![expected_description],
2186            }))
2187        } else {
2188            Ok(next)
2189        }
2190    }
2191
2192    fn eat_unconditional(&mut self) -> Result<Token> {
2193        let food = self
2194            .peeked
2195            .take()
2196            .map(Ok)
2197            .unwrap_or_else(|| self.next_token())?;
2198
2199        self.parse_stack.push(ParseStackEntry::Ate(food.clone()));
2200        self.last_token = Some(food.clone());
2201        Ok(food)
2202    }
2203
2204    /// Peeks the next token. If it is the specified kind, returns that token, otherwise
2205    /// returns None.
2206    ///
2207    /// If kind is > and the peeking is also done for >>, which if found, is split
2208    /// into > which is returned, and > which populates the peek buffer
2209    fn peek_and_eat(&mut self, kind: &TokenKind) -> Result<Option<Token>> {
2210        // peek_cond_no_tracing because peek_kind handles >> -> > > transformation
2211        // which we don't want here
2212        if self.peek_kind(kind)? {
2213            Ok(Some(self.eat(kind)?))
2214        } else {
2215            Ok(None)
2216        }
2217    }
2218
2219    pub fn peek(&mut self) -> Result<Token> {
2220        if let Some(peeked) = self.peeked.clone() {
2221            Ok(peeked)
2222        } else {
2223            let result = match self.next_token() {
2224                Ok(token) => token,
2225                Err(e) => return Err(e),
2226            };
2227            self.peeked = Some(result.clone());
2228
2229            Ok(result)
2230        }
2231    }
2232
2233    fn peek_kind(&mut self, expected: &TokenKind) -> Result<bool> {
2234        let mut result = self.peek_cond_no_tracing(|kind| kind == expected)?;
2235        if expected == &TokenKind::Gt {
2236            result |= self.peek_cond_no_tracing(|kind| kind == &TokenKind::RightShift)?
2237                | self.peek_cond_no_tracing(|kind| kind == &TokenKind::ArithmeticRightShift)?
2238        }
2239        self.parse_stack
2240            .push(ParseStackEntry::PeekingFor(expected.clone(), result));
2241        Ok(result)
2242    }
2243
2244    /// Peek the next token, returning true if the result satisfies the condition.
2245    ///
2246    /// If we reached EOF and the peek returns None, returns false
2247    fn peek_cond(&mut self, cond: impl Fn(&TokenKind) -> bool, msg: &str) -> Result<bool> {
2248        let result = self.peek_cond_no_tracing(cond)?;
2249        self.parse_stack.push(ParseStackEntry::PeekingWithCondition(
2250            msg.to_string(),
2251            result,
2252        ));
2253        Ok(result)
2254    }
2255
2256    fn peek_cond_no_tracing(&mut self, cond: impl Fn(&TokenKind) -> bool) -> Result<bool> {
2257        self.peek().map(|token| cond(&token.kind))
2258    }
2259
2260    fn next_token(&mut self) -> Result<Token> {
2261        self.next_token_helper(0)
2262    }
2263
2264    fn next_token_helper(&mut self, block_comment_depth: usize) -> Result<Token> {
2265        let lex_dot_next = {
2266            let mut break_value: Option<std::result::Result<_, _>> = None;
2267            while let Some(next) = self.lex.next() {
2268                if matches!(next, Ok(TokenKind::Comment)) {
2269                    self.comments.push(Comment::Line(Token {
2270                        kind: TokenKind::Comment,
2271                        span: self.lex.span(),
2272                        file_id: self.file_id,
2273                    }));
2274                } else {
2275                    break_value = Some(next);
2276                    break;
2277                }
2278            }
2279            break_value
2280        };
2281
2282        let out = match lex_dot_next {
2283            Some(Ok(k)) => Ok(Token::new(k, &self.lex, self.file_id)),
2284            Some(Err(_)) => Err(Diagnostic::error(
2285                Loc::new((), lspan(self.lex.span()), self.file_id),
2286                "Lexer error, unexpected symbol",
2287            )),
2288            None => Ok(match &self.last_token {
2289                Some(last) => Token {
2290                    kind: TokenKind::Eof,
2291                    span: last.span.end..last.span.end,
2292                    file_id: last.file_id,
2293                },
2294                None => Token {
2295                    kind: TokenKind::Eof,
2296                    span: logos::Span { start: 0, end: 0 },
2297                    file_id: self.file_id,
2298                },
2299            }),
2300        }?;
2301
2302        match out.kind {
2303            TokenKind::BlockCommentStart => loop {
2304                let next = self.next_token_helper(block_comment_depth + 1)?;
2305                match next.kind {
2306                    TokenKind::BlockCommentEnd => {
2307                        if block_comment_depth == 0 {
2308                            self.comments.push(Comment::Block(out, next));
2309                        }
2310                        break self.next_token_helper(block_comment_depth);
2311                    }
2312                    TokenKind::Eof => {
2313                        break Err(Diagnostic::error(next, "Unterminated block comment")
2314                            .primary_label("Expected */")
2315                            .secondary_label(out, "to close this block comment"))
2316                    }
2317                    _ => {}
2318                }
2319            },
2320            _ => Ok(out),
2321        }
2322    }
2323}
2324
2325impl<'a> Parser<'a> {
2326    fn set_item_context(&mut self, context: Loc<UnitKind>) -> Result<()> {
2327        if let Some(prev) = &self.unit_context {
2328            Err(Diagnostic::bug(
2329                context.loc(),
2330                "overwriting previously uncleared item context",
2331            )
2332            .primary_label("new context set because of this")
2333            .secondary_label(prev.loc(), "previous context set here"))
2334        } else {
2335            self.unit_context = Some(context);
2336            Ok(())
2337        }
2338    }
2339
2340    fn clear_item_context(&mut self) {
2341        self.unit_context = None
2342    }
2343
2344    #[cfg(test)]
2345    fn set_parsing_entity(&mut self) {
2346        self.set_item_context(UnitKind::Entity.nowhere()).unwrap()
2347    }
2348}
2349
2350trait KeywordPeekingParser<T> {
2351    fn leading_tokens(&self) -> Vec<TokenKind>;
2352    fn parse(&self, parser: &mut Parser, attributes: &AttributeList) -> Result<T>;
2353}
2354
2355trait SizedKeywordPeekingParser<T>: Sized + KeywordPeekingParser<T> {
2356    fn map<F, O>(self, mapper: F) -> MappingParser<Self, F, T, O>
2357    where
2358        F: Fn(T) -> Result<O>,
2359    {
2360        MappingParser {
2361            inner: Box::new(self),
2362            mapper: Box::new(mapper),
2363            _phantoms: Default::default(),
2364        }
2365    }
2366
2367    fn then<F>(self, then: F) -> ThenParser<Self, F, T>
2368    where
2369        F: Fn(T, &mut Parser) -> Result<T>,
2370    {
2371        ThenParser {
2372            inner: Box::new(self),
2373            then: Box::new(then),
2374            _phantoms: Default::default(),
2375        }
2376    }
2377}
2378impl<TOuter, TInner> SizedKeywordPeekingParser<TInner> for TOuter where
2379    TOuter: KeywordPeekingParser<TInner> + Sized
2380{
2381}
2382
2383struct MappingParser<Inner, Mapper, I, T>
2384where
2385    Inner: SizedKeywordPeekingParser<I> + ?Sized,
2386    Mapper: Fn(I) -> Result<T>,
2387{
2388    inner: Box<Inner>,
2389    mapper: Box<Mapper>,
2390    _phantoms: (PhantomData<I>, PhantomData<T>),
2391}
2392
2393impl<Inner, Mapper, I, T> KeywordPeekingParser<T> for MappingParser<Inner, Mapper, I, T>
2394where
2395    Inner: SizedKeywordPeekingParser<I> + ?Sized,
2396    Mapper: Fn(I) -> Result<T>,
2397{
2398    fn leading_tokens(&self) -> Vec<TokenKind> {
2399        self.inner.leading_tokens()
2400    }
2401
2402    fn parse(&self, parser: &mut Parser, attributes: &AttributeList) -> Result<T> {
2403        (self.mapper)(self.inner.parse(parser, attributes)?)
2404    }
2405}
2406
2407/// Allows running parsing tasks after successfully running an inner parser. Used
2408/// for example to require `;` after some statements with a helpful error message to
2409/// point out where the `;` is missing.
2410/// This cannot be used to change the type of `T`, which is intentional as that could
2411/// easily change the grammar from LL(1)
2412struct ThenParser<Inner, After, T>
2413where
2414    Inner: SizedKeywordPeekingParser<T> + ?Sized,
2415    After: Fn(T, &mut Parser) -> Result<T>,
2416{
2417    inner: Box<Inner>,
2418    then: Box<After>,
2419    _phantoms: PhantomData<T>,
2420}
2421
2422impl<Inner, After, T> KeywordPeekingParser<T> for ThenParser<Inner, After, T>
2423where
2424    Inner: SizedKeywordPeekingParser<T> + ?Sized,
2425    After: Fn(T, &mut Parser) -> Result<T>,
2426{
2427    fn leading_tokens(&self) -> Vec<TokenKind> {
2428        self.inner.leading_tokens()
2429    }
2430
2431    fn parse(&self, parser: &mut Parser, attributes: &AttributeList) -> Result<T> {
2432        let inner = self.inner.parse(parser, attributes)?;
2433        (self.then)(inner, parser)
2434    }
2435}
2436
2437#[derive(Debug)]
2438pub enum RecoveryResult<T> {
2439    Ok(T),
2440    Recovered,
2441}
2442
2443#[local_impl]
2444impl<T> OptionExt for Option<T> {
2445    fn or_error(
2446        self,
2447        parser: &mut Parser,
2448        err: impl Fn(&mut Parser) -> Result<Diagnostic>,
2449    ) -> Result<T> {
2450        match self {
2451            Some(val) => Ok(val),
2452            None => Err(err(parser)?),
2453        }
2454    }
2455}
2456
2457#[derive(Clone)]
2458pub enum ParseStackEntry {
2459    Enter(String),
2460    Ate(Token),
2461    PeekingWithCondition(String, bool),
2462    PeekingFor(TokenKind, bool),
2463    EatingExpected(TokenKind),
2464    Exit,
2465    ExitWithDiagnostic(Diagnostic),
2466}
2467pub fn format_parse_stack(stack: &[ParseStackEntry]) -> String {
2468    let mut result = String::new();
2469    let mut indent_amount = 0;
2470
2471    for entry in stack {
2472        let mut next_indent_amount = indent_amount;
2473        let message = match entry {
2474            ParseStackEntry::Enter(function) => {
2475                next_indent_amount += 1;
2476                format!("{} `{}`", "trying".white(), function.blue())
2477            }
2478            ParseStackEntry::Ate(token) => format!(
2479                "{} '{}'",
2480                "Eating".bright_yellow(),
2481                token.kind.as_str().bright_purple()
2482            ),
2483            ParseStackEntry::PeekingFor(kind, success) => format!(
2484                "{} {} {}",
2485                "peeking for".white(),
2486                kind.as_str().bright_blue(),
2487                if *success {
2488                    "✓".green()
2489                } else {
2490                    "𐄂".red()
2491                }
2492            ),
2493            ParseStackEntry::PeekingWithCondition(needle, success) => format!(
2494                "{} {} {}",
2495                "peeking conditionally for ".white(),
2496                needle.bright_blue(),
2497                if *success {
2498                    "✓".green()
2499                } else {
2500                    "𐄂".red()
2501                }
2502            ),
2503            ParseStackEntry::EatingExpected(kind) => {
2504                format!(
2505                    "{} {}",
2506                    "eating expected".purple(),
2507                    kind.as_str().bright_purple()
2508                )
2509            }
2510            ParseStackEntry::Exit => {
2511                next_indent_amount -= 1;
2512                String::new()
2513            }
2514            ParseStackEntry::ExitWithDiagnostic(_diag) => {
2515                next_indent_amount -= 1;
2516                "Giving up".bright_red().to_string()
2517            }
2518        };
2519        if let ParseStackEntry::Exit = entry {
2520        } else {
2521            for _ in 0..indent_amount {
2522                result += "| ";
2523            }
2524            result += &message;
2525            result += "\n"
2526        }
2527        indent_amount = next_indent_amount;
2528    }
2529    result
2530}
2531
2532#[cfg(test)]
2533mod tests {
2534    use spade_ast as ast;
2535    use spade_ast::testutil::{ast_ident, ast_path};
2536    use spade_ast::*;
2537    use spade_common::num_ext::InfallibleToBigInt;
2538
2539    use crate::lexer::TokenKind;
2540    use crate::*;
2541
2542    use logos::Logos;
2543
2544    use spade_common::location_info::WithLocation;
2545
2546    #[macro_export]
2547    macro_rules! check_parse {
2548        ($string:expr , $method:ident$(($($arg:expr),*))?, $expected:expr$(, $run_on_parser:expr)?) => {
2549            let mut parser = Parser::new(TokenKind::lexer($string), 0);
2550
2551            $($run_on_parser(&mut parser);)?
2552
2553            let result = parser.$method($($($arg),*)?);
2554            // This is needed because type inference fails for some unexpected reason
2555            let expected: Result<_> = $expected;
2556
2557            if result != expected {
2558                println!("Parser state:\n{}", format_parse_stack(&parser.parse_stack));
2559                panic!(
2560                    "\n\n     {}: {:?}\n{}: {:?}",
2561                    "Got".red(),
2562                    result,
2563                    "Expected".green(),
2564                    expected
2565                );
2566            };
2567        };
2568    }
2569
2570    #[test]
2571    fn parsing_identifier_works() {
2572        check_parse!("abc123_", identifier, Ok(ast_ident("abc123_")));
2573    }
2574
2575    #[test]
2576    fn parsing_paths_works() {
2577        let expected = Path(vec![ast_ident("path"), ast_ident("to"), ast_ident("thing")]).nowhere();
2578        check_parse!("path::to::thing", path, Ok(expected));
2579    }
2580
2581    #[test]
2582    fn literals_are_expressions() {
2583        check_parse!(
2584            "123",
2585            expression,
2586            Ok(Expression::int_literal_signed(123).nowhere())
2587        );
2588    }
2589
2590    #[test]
2591    fn size_types_work() {
2592        let expected = TypeSpec::Named(
2593            ast_path("uint"),
2594            Some(vec![TypeExpression::Integer(10u32.to_bigint()).nowhere()].nowhere()),
2595        )
2596        .nowhere();
2597        check_parse!("uint<10>", type_spec, Ok(expected));
2598    }
2599
2600    #[test]
2601    fn nested_generics_work() {
2602        let code = "Option<int<5>>";
2603
2604        let expected = TypeSpec::Named(
2605            ast_path("Option"),
2606            Some(
2607                vec![TypeExpression::TypeSpec(Box::new(
2608                    TypeSpec::Named(
2609                        ast_path("int"),
2610                        Some(vec![TypeExpression::Integer(5u32.to_bigint()).nowhere()].nowhere()),
2611                    )
2612                    .nowhere(),
2613                ))
2614                .nowhere()]
2615                .nowhere(),
2616            ),
2617        )
2618        .nowhere();
2619
2620        check_parse!(code, type_spec, Ok(expected));
2621    }
2622
2623    #[test]
2624    fn module_body_parsing_works() {
2625        let code = include_str!("../parser_test_code/multiple_entities.sp");
2626
2627        let e1 = Unit {
2628            head: UnitHead {
2629                unsafe_token: None,
2630                extern_token: None,
2631                attributes: AttributeList::empty(),
2632                unit_kind: UnitKind::Entity.nowhere(),
2633                name: Identifier("e1".to_string()).nowhere(),
2634                inputs: aparams![],
2635                output_type: None,
2636                type_params: None,
2637                where_clauses: vec![],
2638            },
2639            body: Some(
2640                Expression::Block(Box::new(Block {
2641                    statements: vec![],
2642                    result: Some(Expression::int_literal_signed(0).nowhere()),
2643                }))
2644                .nowhere(),
2645            ),
2646        }
2647        .nowhere();
2648
2649        let e2 = Unit {
2650            head: UnitHead {
2651                unsafe_token: None,
2652                extern_token: None,
2653                attributes: AttributeList::empty(),
2654                unit_kind: UnitKind::Entity.nowhere(),
2655                name: Identifier("e2".to_string()).nowhere(),
2656                inputs: aparams![],
2657                output_type: None,
2658                type_params: None,
2659                where_clauses: vec![],
2660            },
2661            body: Some(
2662                Expression::Block(Box::new(Block {
2663                    statements: vec![],
2664                    result: Some(Expression::int_literal_signed(1).nowhere()),
2665                }))
2666                .nowhere(),
2667            ),
2668        }
2669        .nowhere();
2670
2671        let expected = ModuleBody {
2672            members: vec![Item::Unit(e1), Item::Unit(e2)],
2673            documentation: vec![],
2674        };
2675
2676        check_parse!(code, module_body, Ok(expected));
2677    }
2678
2679    #[test]
2680    fn dec_int_literals_work() {
2681        let code = "1";
2682        let expected = IntLiteral::unsized_(1).nowhere();
2683
2684        check_parse!(code, int_literal, Ok(Some(expected)));
2685    }
2686    #[test]
2687    fn dec_negative_int_literals_work() {
2688        let code = "-1";
2689        let expected = IntLiteral::unsized_(-1).nowhere();
2690
2691        check_parse!(code, int_literal, Ok(Some(expected)));
2692    }
2693    #[test]
2694    fn hex_int_literals_work() {
2695        let code = "0xff";
2696        let expected = IntLiteral::unsized_(255).nowhere();
2697
2698        check_parse!(code, int_literal, Ok(Some(expected)));
2699    }
2700    #[test]
2701    fn bin_int_literals_work() {
2702        let code = "0b101";
2703        let expected = IntLiteral::unsized_(5).nowhere();
2704
2705        check_parse!(code, int_literal, Ok(Some(expected)));
2706    }
2707
2708    #[test]
2709    fn type_spec_with_multiple_generics_works() {
2710        let code = "A<X, Y>";
2711
2712        let expected = TypeSpec::Named(
2713            ast_path("A"),
2714            Some(
2715                vec![
2716                    TypeExpression::TypeSpec(Box::new(
2717                        TypeSpec::Named(ast_path("X"), None).nowhere(),
2718                    ))
2719                    .nowhere(),
2720                    TypeExpression::TypeSpec(Box::new(
2721                        TypeSpec::Named(ast_path("Y"), None).nowhere(),
2722                    ))
2723                    .nowhere(),
2724                ]
2725                .nowhere(),
2726            ),
2727        )
2728        .nowhere();
2729
2730        check_parse!(code, type_spec, Ok(expected));
2731    }
2732
2733    #[test]
2734    fn entity_instantiation() {
2735        let code = "inst some_entity(x, y, z)";
2736
2737        let expected = Expression::Call {
2738            kind: CallKind::Entity(().nowhere()),
2739            callee: ast_path("some_entity"),
2740            args: ArgumentList::Positional(vec![
2741                Expression::Identifier(ast_path("x")).nowhere(),
2742                Expression::Identifier(ast_path("y")).nowhere(),
2743                Expression::Identifier(ast_path("z")).nowhere(),
2744            ])
2745            .nowhere(),
2746            turbofish: None,
2747        }
2748        .nowhere();
2749
2750        check_parse!(code, expression, Ok(expected), Parser::set_parsing_entity);
2751    }
2752
2753    #[test]
2754    fn named_args_work() {
2755        let code = "x: a";
2756
2757        let expected = NamedArgument::Full(
2758            ast_ident("x"),
2759            Expression::Identifier(ast_path("a")).nowhere(),
2760        )
2761        .nowhere();
2762
2763        check_parse!(code, named_argument, Ok(expected));
2764    }
2765
2766    #[test]
2767    fn named_capture_shorthand_works() {
2768        let code = "x";
2769
2770        let expected = NamedArgument::Short(ast_ident("x")).nowhere();
2771
2772        check_parse!(code, named_argument, Ok(expected));
2773    }
2774
2775    #[test]
2776    fn tuple_patterns_work() {
2777        let code = "(x, y)";
2778
2779        let expected = Pattern::Tuple(vec![Pattern::name("x"), Pattern::name("y")]).nowhere();
2780
2781        check_parse!(code, pattern, Ok(expected));
2782    }
2783
2784    #[test]
2785    fn integer_patterns_work() {
2786        let code = "1";
2787
2788        let expected = Pattern::integer(1).nowhere();
2789
2790        check_parse!(code, pattern, Ok(expected));
2791    }
2792
2793    #[test]
2794    fn hex_integer_patterns_work() {
2795        let code = "0xff";
2796
2797        let expected = Pattern::integer(255).nowhere();
2798
2799        check_parse!(code, pattern, Ok(expected));
2800    }
2801
2802    #[test]
2803    fn bin_integer_patterns_work() {
2804        let code = "0b101";
2805
2806        let expected = Pattern::integer(5).nowhere();
2807
2808        check_parse!(code, pattern, Ok(expected));
2809    }
2810
2811    #[test]
2812    fn bool_patterns_work() {
2813        let code = "true";
2814
2815        let expected = Pattern::Bool(true).nowhere();
2816
2817        check_parse!(code, pattern, Ok(expected));
2818    }
2819
2820    #[test]
2821    fn positional_type_patterns_work() {
2822        let code = "SomeType(x, y)";
2823
2824        let expected = Pattern::Type(
2825            ast_path("SomeType"),
2826            ArgumentPattern::Positional(vec![Pattern::name("x"), Pattern::name("y")]).nowhere(),
2827        )
2828        .nowhere();
2829
2830        check_parse!(code, pattern, Ok(expected));
2831    }
2832
2833    #[test]
2834    fn named_type_patterns_work() {
2835        let code = "SomeType$(x: a, y)";
2836
2837        let expected = Pattern::Type(
2838            ast_path("SomeType"),
2839            ArgumentPattern::Named(vec![
2840                (ast_ident("x"), Some(Pattern::name("a"))),
2841                (ast_ident("y"), None),
2842            ])
2843            .nowhere(),
2844        )
2845        .nowhere();
2846
2847        check_parse!(code, pattern, Ok(expected));
2848    }
2849
2850    #[test]
2851    fn modules_can_be_empty() {
2852        let code = r#"mod X {}"#;
2853
2854        let expected = ModuleBody {
2855            members: vec![Item::Module(
2856                Module {
2857                    name: ast_ident("X"),
2858                    body: ModuleBody {
2859                        members: vec![],
2860                        documentation: vec![],
2861                    }
2862                    .nowhere(),
2863                }
2864                .nowhere(),
2865            )],
2866            documentation: vec![],
2867        };
2868
2869        check_parse!(code, module_body, Ok(expected));
2870    }
2871
2872    #[test]
2873    fn modules_containing_items_work() {
2874        let code = r#"mod X {mod Y {}}"#;
2875
2876        let expected = ModuleBody {
2877            members: vec![Item::Module(
2878                Module {
2879                    name: ast_ident("X"),
2880                    body: ModuleBody {
2881                        members: vec![Item::Module(
2882                            Module {
2883                                name: ast_ident("Y"),
2884                                body: ModuleBody {
2885                                    members: vec![],
2886                                    documentation: vec![],
2887                                }
2888                                .nowhere(),
2889                            }
2890                            .nowhere(),
2891                        )],
2892                        documentation: vec![],
2893                    }
2894                    .nowhere(),
2895                }
2896                .nowhere(),
2897            )],
2898            documentation: vec![],
2899        };
2900
2901        check_parse!(code, module_body, Ok(expected));
2902    }
2903}