Skip to main content

microcad_syntax/parser/
mod.rs

1// Copyright © 2026 The µcad authors <info@microcad.xyz>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4mod error;
5mod helpers;
6mod simplify;
7
8use crate::Span;
9use crate::ast::*;
10use crate::parser::error::{ParseErrorKind, Rich};
11use crate::parser::helpers::*;
12use crate::parser::simplify::simplify_unary_op;
13use crate::tokens::*;
14
15use chumsky::input::{Input, MappedInput};
16use chumsky::prelude::*;
17use chumsky::{Parser, extra, select_ref};
18pub use error::ParseError;
19use helpers::ParserExt;
20use std::str::FromStr;
21
22type Error<'tokens> = Rich<'tokens, Token<'tokens>, Span, ParseErrorKind>;
23type Extra<'tokens> = extra::Err<Error<'tokens>>;
24
25pub fn map_token_input<'a, 'token>(
26    spanned: &'a SpannedToken<Token<'token>>,
27) -> (&'a Token<'token>, &'a Span) {
28    (&spanned.token, &spanned.span)
29}
30
31type InputMap<'input, 'token> =
32    fn(&'input SpannedToken<Token<'token>>) -> (&'input Token<'token>, &'input Span);
33
34type ParserInput<'input, 'token> = MappedInput<
35    'input,
36    Token<'token>,
37    Span,
38    &'input [SpannedToken<Token<'token>>],
39    InputMap<'input, 'token>,
40>;
41
42fn input<'input, 'tokens>(
43    input: &'input [SpannedToken<Token<'tokens>>],
44) -> ParserInput<'input, 'tokens> {
45    let end = input.last().map(|t| t.span.end).unwrap_or_default();
46    Input::map(input, end..end, map_token_input)
47}
48
49/// Build an abstract syntax tree from a list of tokens
50pub fn parse<'tokens>(
51    tokens: &'tokens [SpannedToken<Token<'tokens>>],
52) -> Result<SourceFile, Vec<ParseError>> {
53    parser()
54        .parse(input(tokens))
55        .into_result()
56        .map_err(|errors| errors.into_iter().map(ParseError::new).collect())
57}
58
59const STRUCTURAL_TOKENS: &[Token] = &[
60    Token::SigilOpenCurlyBracket,
61    Token::SigilCloseCurlyBracket,
62    Token::SigilOpenBracket,
63    Token::SigilCloseBracket,
64    Token::SigilOpenSquareBracket,
65    Token::SigilCloseSquareBracket,
66    Token::SigilSemiColon,
67];
68
69fn parser<'tokens>()
70-> impl Parser<'tokens, ParserInput<'tokens, 'tokens>, SourceFile, Extra<'tokens>> {
71    let mut statement_list_parser = Recursive::declare();
72    let mut statement_parser = Recursive::declare();
73    let mut expression_parser = Recursive::declare();
74    let mut type_parser = Recursive::declare();
75    let mut attribute_parser = Recursive::declare();
76    let mut if_inner = Recursive::declare();
77
78    let semi_recovery = none_of(Token::SigilSemiColon).repeated().ignored();
79
80    let reserved_keyword = select_ref! {
81        token @ (
82            Token::KeywordPlugin |
83            Token::KeywordAssembly |
84            Token::KeywordMaterial |
85            Token::KeywordUnit |
86            Token::KeywordEnum |
87            Token::KeywordStruct |
88            Token::KeywordMatch |
89            Token::KeywordType
90        ) => token.kind(),
91    }
92    .boxed();
93    let keyword = select_ref! {
94        token @ (
95            Token::KeywordMod |
96            Token::KeywordPart |
97            Token::KeywordSketch |
98            Token::KeywordOp |
99            Token::KeywordFn |
100            Token::KeywordIf |
101            Token::KeywordElse |
102            Token::KeywordUse |
103            Token::KeywordAs |
104            Token::KeywordReturn |
105            Token::KeywordPub |
106            Token::KeywordConst |
107            Token::KeywordProp |
108            Token::KeywordInit
109        ) => token.kind(),
110    }
111    .boxed();
112
113    let block_recovery =
114        ignore_till_matched_curly().map_with(|_, e| StatementList::dummy(e.span()));
115
116    let block = whitespace_parser()
117        .or_not()
118        .ignore_then(statement_list_parser.clone())
119        .then_maybe_whitespace()
120        .delimited_with_spanned_error(
121            just(Token::SigilOpenCurlyBracket),
122            just(Token::SigilCloseCurlyBracket),
123            |err: Error, open, end| {
124                Rich::custom(
125                    err.span().clone(),
126                    ParseErrorKind::UnclosedBracket {
127                        open,
128                        end,
129                        kind: "code block",
130                        close_token: Token::SigilCloseCurlyBracket,
131                    },
132                )
133            },
134        )
135        .recover_with(via_parser(block_recovery))
136        .boxed();
137
138    let identifier_parser = select_ref! { Token::Identifier(ident) = e => Identifier {
139        span: e.span(),
140        name: ident.as_ref().into(),
141    } }
142    .or(reserved_keyword
143        .clone()
144        .validate(|kind, e, emitter| {
145            emitter.emit(Rich::custom(
146                e.span(),
147                ParseErrorKind::ReservedKeywordAsIdentifier(kind),
148            ));
149            kind
150        })
151        .map_with(|kind, e| Identifier {
152            span: e.span(),
153            name: kind.into(),
154        }))
155    .or(keyword
156        .validate(|kind, e, emitter| {
157            emitter.emit(Rich::custom(
158                e.span(),
159                ParseErrorKind::KeywordAsIdentifier(kind),
160            ));
161            kind
162        })
163        .map_with(|kind, e| Identifier {
164            span: e.span(),
165            name: kind.into(),
166        }))
167    .labelled("identifier")
168    .boxed();
169
170    let qualified_name = identifier_parser
171        .clone()
172        .separated_by(just(Token::SigilDoubleColon))
173        .at_least(1)
174        .collect::<Vec<_>>()
175        .with_extras()
176        .map_with(|(parts, extras), e| QualifiedName {
177            span: e.span(),
178            parts,
179            extras,
180        })
181        .labelled("qualified name")
182        .boxed();
183
184    let single_type = select_ref! {
185        Token::Identifier(ident) = e => SingleType {
186            span: e.span(),
187            name: ident.as_ref().into()
188        },
189        Token::Unit(unit) = e => SingleType {
190            span: e.span(),
191            name: unit.as_ref().into()
192        },
193        Token::SigilQuote = e => SingleType {
194            span: e.span(),
195            name: r#"""#.into()
196        },
197    }
198    .labelled("quantity type")
199    .boxed();
200
201    type_parser.define({
202        let single = single_type.clone().map(Type::Single);
203        let array = whitespace_parser()
204            .or_not()
205            .ignore_then(type_parser.clone())
206            .then_maybe_whitespace()
207            .delimited_by(
208                just(Token::SigilOpenSquareBracket),
209                just(Token::SigilCloseSquareBracket),
210            )
211            .map_with(|inner, e| {
212                Type::Array(ArrayType {
213                    span: e.span(),
214                    inner: Box::new(inner),
215                })
216            })
217            .labelled("array type")
218            .boxed();
219
220        let tuple = whitespace_parser()
221            .or_not()
222            .ignore_then(identifier_parser.clone())
223            .then_ignore(just(Token::SigilColon))
224            .or_not()
225            .then_maybe_whitespace()
226            .then(type_parser.clone())
227            .then_maybe_whitespace()
228            .separated_by(just(Token::SigilComma))
229            .allow_trailing()
230            .collect::<Vec<_>>()
231            .then_maybe_whitespace()
232            .delimited_with_spanned_error(
233                just(Token::SigilOpenBracket),
234                just(Token::SigilCloseBracket),
235                |err: Error, open, end| {
236                    Rich::custom(
237                        err.span().clone(),
238                        ParseErrorKind::UnclosedBracket {
239                            open,
240                            end,
241                            kind: "tuple type",
242                            close_token: Token::SigilCloseBracket,
243                        },
244                    )
245                },
246            )
247            .map_with(|inner, e| {
248                Type::Tuple(TupleType {
249                    span: e.span(),
250                    inner,
251                })
252            })
253            .labelled("tuple type")
254            .boxed();
255
256        single.or(array).or(tuple).labelled("type").boxed()
257    });
258
259    let literal_parser = {
260        let single_value = select_ref! {
261            Token::LiteralFloat(x) = e => {
262                match f64::from_str(x) {
263                    Ok(value) => LiteralKind::Float(FloatLiteral {
264                        value,
265                        span: e.span(),
266                    }),
267                    Err(err) => LiteralKind::Error(LiteralError {
268                        span: e.span(),
269                        kind: err.into(),
270                    })
271                }
272            },
273            Token::LiteralInt(x) = e => {
274                match i64::from_str(x) {
275                    Ok(value) => LiteralKind::Integer(IntegerLiteral {
276                    value,
277                    span: e.span(),
278                }),
279                    Err(err) => LiteralKind::Error(LiteralError {
280                        span: e.span(),
281                        kind: err.into(),
282                    })
283                }
284            },
285            Token::LiteralString(content) = e => {
286                LiteralKind::String(StringLiteral {
287                    span: e.span(),
288                    content: content.as_ref().into(),
289                })
290            },
291            Token::LiteralBool(value) = e => {
292                LiteralKind::Bool(BoolLiteral {
293                    span: e.span(),
294                    value: *value,
295                })
296            },
297        }
298        .boxed();
299
300        single_value
301            .then(single_type.clone().or_not())
302            .with_extras()
303            .try_map_with(|((literal, ty), extras), e| {
304                let literal = match (literal, ty) {
305                    (LiteralKind::Float(float), Some(ty)) => {
306                        LiteralKind::Quantity(QuantityLiteral {
307                            span: e.span(),
308                            value: float.value,
309                            ty,
310                        })
311                    }
312                    (LiteralKind::Integer(int), Some(ty)) => {
313                        LiteralKind::Quantity(QuantityLiteral {
314                            span: e.span(),
315                            value: int.value as f64,
316                            ty,
317                        })
318                    }
319                    (_, Some(_)) => LiteralKind::Error(LiteralError {
320                        span: e.span(),
321                        kind: LiteralErrorKind::Untypable,
322                    }),
323                    (literal, None) => literal,
324                };
325                Ok(Literal {
326                    span: e.span(),
327                    literal,
328                    extras,
329                })
330            })
331            .labelled("literal")
332            .boxed()
333    };
334
335    let unary_operator_parser = select_ref! {
336        Token::OperatorSubtract = e => UnaryOperator { span: e.span(), operation: UnaryOperatorType::Minus },
337        Token::OperatorAdd = e => UnaryOperator { span: e.span(), operation: UnaryOperatorType::Plus },
338        Token::OperatorNot = e => UnaryOperator { span: e.span(), operation: UnaryOperatorType::Not },
339    }
340    .labelled("unary operator")
341    .boxed();
342
343    let doc_comment = select_ref! {
344        Token::DocComment(comment) => comment,
345    }
346    .then_whitespace()
347    .repeated()
348    .at_least(1)
349    .collect::<Vec<_>>()
350    .map_with(|lines, e| Comment {
351        span: e.span(),
352        lines: lines.into_iter().map(|s| s.as_ref().into()).collect(),
353    })
354    .labelled("doc-comment")
355    .or_not()
356    .boxed();
357
358    let tuple_recovery = nested_delimiters(
359        Token::SigilOpenBracket,
360        Token::SigilCloseBracket,
361        [
362            (
363                Token::SigilOpenSquareBracket,
364                Token::SigilCloseSquareBracket,
365            ),
366            (Token::SigilOpenCurlyBracket, Token::SigilCloseCurlyBracket),
367        ],
368        |_| (),
369    )
370    .map_with(|_, e| (vec![TupleItem::dummy(e.span())], ItemExtras::default()));
371
372    let tuple_body = identifier_parser
373        .clone()
374        .then_maybe_whitespace()
375        .then_ignore(just(Token::OperatorAssignment).then_maybe_whitespace())
376        .or_not()
377        .then(expression_parser.clone())
378        .with_extras()
379        .map_with(|((name, value), extras), e| TupleItem {
380            span: e.span(),
381            extras,
382            name,
383            value,
384        })
385        .then_maybe_whitespace()
386        .separated_by(just(Token::SigilComma).then_maybe_whitespace())
387        .allow_trailing()
388        .collect::<Vec<_>>()
389        .boxed();
390
391    let call_inner = qualified_name
392        .clone()
393        .then_maybe_whitespace()
394        .then(
395            whitespace_parser()
396                .or_not()
397                .ignore_then(tuple_body.clone())
398                .with_extras()
399                .then_maybe_whitespace()
400                .map_with(|(arguments, extras), e| ArgumentList {
401                    span: e.span(),
402                    extras,
403                    arguments: arguments
404                        .into_iter()
405                        .map(|item| match item.name {
406                            Some(name) => Argument::Named(NamedArgument {
407                                span: item.span,
408                                extras: item.extras,
409                                name,
410                                value: item.value,
411                            }),
412                            None => Argument::Unnamed(UnnamedArgument {
413                                span: item.span,
414                                extras: item.extras,
415                                value: item.value,
416                            }),
417                        })
418                        .collect::<Vec<_>>(),
419                })
420                .labelled("function arguments")
421                .delimited_with_spanned_error(
422                    just(Token::SigilOpenBracket),
423                    just(Token::SigilCloseBracket),
424                    |err: Error, open, end| {
425                        Rich::custom(
426                            err.span().clone(),
427                            ParseErrorKind::UnclosedBracket {
428                                open,
429                                end,
430                                kind: "function arguments",
431                                close_token: Token::SigilCloseBracket,
432                            },
433                        )
434                    },
435                )
436                .recover_with(via_parser(
437                    tuple_recovery
438                        .clone()
439                        .map_with(|_, e| ArgumentList::dummy(e.span())),
440                )),
441        )
442        .with_extras()
443        .map_with(|((name, arguments), extras), e| Call {
444            span: e.span(),
445            extras,
446            name,
447            arguments,
448        })
449        .boxed();
450
451    statement_parser.define({
452        let visibility = select_ref! {
453            Token::KeywordPub => Visibility::Public,
454        }
455        .labelled("visibility");
456
457        let expression = attribute_parser
458            .clone()
459            .then(expression_parser.clone())
460            .with_extras()
461            .map_with(
462                |((attributes, expression), extras), e| ExpressionStatement {
463                    span: e.span(),
464                    extras,
465                    attributes,
466                    expression,
467                },
468            )
469            .map(Statement::Expression)
470            .boxed();
471
472        let local_assignment_inner = attribute_parser
473            .clone()
474            .then(identifier_parser.clone())
475            .then_maybe_whitespace()
476            .then(
477                just(Token::SigilColon)
478                    .then_maybe_whitespace()
479                    .ignore_then(type_parser.clone())
480                    .then_maybe_whitespace()
481                    .or_not(),
482            )
483            .then_ignore(just(Token::OperatorAssignment))
484            .then_maybe_whitespace()
485            .then(
486                expression_parser.clone().recover_with(via_parser(
487                    semi_recovery
488                        .clone()
489                        .map_with(|_, e| Expression::Error(e.span())),
490                )),
491            )
492            .with_extras()
493            .map_with(
494                |((((attributes, name), ty), value), extras), e| LocalAssignment {
495                    span: e.span(),
496                    extras,
497                    attributes,
498                    name,
499                    value: Box::new(value),
500                    ty,
501                },
502            )
503            .boxed();
504
505        let local_assignment = local_assignment_inner
506            .clone()
507            .map(Statement::LocalAssignment)
508            .labelled("local assignment");
509
510        let const_assignment_inner = doc_comment
511            .clone()
512            .then(attribute_parser.clone())
513            .then(visibility.then_whitespace().or_not())
514            .then(just(Token::KeywordConst).map_with(|_, e| e.span()))
515            .then_maybe_whitespace()
516            .then(identifier_parser.clone())
517            .then_maybe_whitespace()
518            .then(
519                just(Token::SigilColon)
520                    .then_maybe_whitespace()
521                    .ignore_then(type_parser.clone())
522                    .then_maybe_whitespace()
523                    .or_not(),
524            )
525            .then_ignore(just(Token::OperatorAssignment))
526            .then_maybe_whitespace()
527            .then(
528                expression_parser.clone().recover_with(via_parser(
529                    semi_recovery
530                        .clone()
531                        .map_with(|_, e| Expression::Error(e.span())),
532                )),
533            )
534            .with_extras()
535            .map_with(
536                |(
537                    ((((((doc, attributes), visibility), keyword_span), name), ty), value),
538                    extras,
539                ),
540                 e| {
541                    ConstAssignment {
542                        span: e.span(),
543                        keyword_span,
544                        extras,
545                        doc,
546                        attributes,
547                        visibility,
548                        name,
549                        value: Box::new(value),
550                        ty,
551                    }
552                },
553            )
554            .boxed();
555
556        let const_assignment = const_assignment_inner
557            .clone()
558            .map(Statement::Const)
559            .labelled("const assignment");
560
561        // A pub assignment without the `const` keyword will eventually become a const assignment
562        let pub_assignment_inner = doc_comment
563            .clone()
564            .then(attribute_parser.clone())
565            .then(just(Token::KeywordPub).map_with(|_, e| e.span()))
566            .then_maybe_whitespace()
567            .then(identifier_parser.clone())
568            .then_maybe_whitespace()
569            .then(
570                just(Token::SigilColon)
571                    .then_maybe_whitespace()
572                    .ignore_then(type_parser.clone())
573                    .then_maybe_whitespace()
574                    .or_not(),
575            )
576            .then_ignore(just(Token::OperatorAssignment))
577            .then_maybe_whitespace()
578            .then(
579                expression_parser.clone().recover_with(via_parser(
580                    semi_recovery
581                        .clone()
582                        .map_with(|_, e| Expression::Error(e.span())),
583                )),
584            )
585            .with_extras()
586            .map_with(
587                |((((((doc, attributes), keyword_span), name), ty), value), extras), e| {
588                    ConstAssignment {
589                        span: e.span(),
590                        keyword_span,
591                        extras,
592                        doc,
593                        attributes,
594                        visibility: Some(Visibility::Public),
595                        name,
596                        value: Box::new(value),
597                        ty,
598                    }
599                },
600            )
601            .boxed();
602
603        let pub_assignment = pub_assignment_inner
604            .clone()
605            .map(Statement::Const)
606            .labelled("pub const assignment");
607
608        let property_assignment_inner = doc_comment
609            .clone()
610            .then(attribute_parser.clone())
611            .then(just(Token::KeywordProp).map_with(|_, e| e.span()))
612            .then_maybe_whitespace()
613            .then(identifier_parser.clone())
614            .then_maybe_whitespace()
615            .then(
616                just(Token::SigilColon)
617                    .then_maybe_whitespace()
618                    .ignore_then(type_parser.clone())
619                    .then_maybe_whitespace()
620                    .or_not(),
621            )
622            .then_ignore(just(Token::OperatorAssignment))
623            .then_maybe_whitespace()
624            .then(
625                expression_parser.clone().recover_with(via_parser(
626                    semi_recovery
627                        .clone()
628                        .map_with(|_, e| Expression::Error(e.span())),
629                )),
630            )
631            .with_extras()
632            .map_with(
633                |((((((doc, attributes), keyword_span), name), ty), value), extras), e| {
634                    PropertyAssignment {
635                        span: e.span(),
636                        keyword_span,
637                        extras,
638                        doc,
639                        attributes,
640                        name,
641                        value: Box::new(value),
642                        ty,
643                    }
644                },
645            )
646            .boxed();
647
648        let property_assignment = property_assignment_inner
649            .clone()
650            .map(Statement::Property)
651            .labelled("property assignment");
652
653        attribute_parser.define({
654            let attribute_command = local_assignment_inner
655                .clone()
656                .map(AttributeCommand::Assignment)
657                .or(call_inner.clone().map(AttributeCommand::Call))
658                .or(identifier_parser.clone().map(AttributeCommand::Ident));
659
660            just(Token::SigilHash)
661                .ignore_then(just(Token::OperatorNot).or_not().map(|opt| opt.is_some()))
662                .then(
663                    attribute_command
664                        .separated_by(just(Token::SigilComma).then_maybe_whitespace())
665                        .at_least(1)
666                        .collect::<Vec<_>>()
667                        .then_maybe_whitespace()
668                        .delimited_by(
669                            just(Token::SigilOpenSquareBracket),
670                            just(Token::SigilCloseSquareBracket),
671                        ),
672                )
673                .then_whitespace()
674                .with_extras()
675                .map_with(|((is_inner, commands), extras), e| Attribute {
676                    span: e.span(),
677                    is_inner,
678                    extras,
679                    commands,
680                })
681                .labelled("attribute")
682                .repeated()
683                .collect::<Vec<Attribute>>()
684                .boxed()
685        });
686
687        let comment = comment_parser()
688            .map(Statement::Comment)
689            .then_maybe_whitespace()
690            .labelled("comment")
691            .boxed();
692
693        let parameter_list_inner = whitespace_parser()
694            .or_not()
695            .ignore_then(identifier_parser.clone())
696            .then_maybe_whitespace()
697            .then(
698                just(Token::SigilColon)
699                    .then_maybe_whitespace()
700                    .ignore_then(
701                        type_parser.clone().recover_with(via_parser(
702                            recovery_expect_any_except(&[
703                                Token::SigilComma,
704                                Token::OperatorAssignment,
705                                Token::SigilCloseBracket,
706                            ])
707                            .map_with(|_, e| Type::dummy(e.span())),
708                        )),
709                    )
710                    .then_maybe_whitespace()
711                    .or_not(),
712            )
713            .then(
714                just(Token::OperatorAssignment)
715                    .then_maybe_whitespace()
716                    .ignore_then(
717                        expression_parser.clone().recover_with(via_parser(
718                            recovery_expect_any_except(&[
719                                Token::SigilComma,
720                                Token::SigilCloseBracket,
721                            ])
722                            .map_with(|_, e| Expression::Error(e.span())),
723                        )),
724                    )
725                    .then_maybe_whitespace()
726                    .or_not(),
727            )
728            .with_extras()
729            .map_with(|(((name, ty), default), extras), e| Parameter {
730                span: e.span(),
731                extras,
732                name,
733                ty,
734                default,
735            })
736            .then_maybe_whitespace()
737            .separated_by(just(Token::SigilComma))
738            .allow_trailing()
739            .collect::<Vec<_>>()
740            .boxed();
741
742        let parameter_list = parameter_list_inner
743            .then_maybe_whitespace()
744            .with_extras()
745            .map_with(|(parameters, extras), e| ParameterList {
746                span: e.span(),
747                extras,
748                parameters,
749            })
750            .delimited_with_spanned_error(
751                just(Token::SigilOpenBracket),
752                just(Token::SigilCloseBracket),
753                |err: Error, open, end| {
754                    Rich::custom(
755                        err.span().clone(),
756                        ParseErrorKind::UnclosedBracket {
757                            open,
758                            end,
759                            kind: "function parameters",
760                            close_token: Token::SigilCloseBracket,
761                        },
762                    )
763                },
764            )
765            .recover_with(via_parser(
766                ignore_till_matched_brackets()
767                    .or(none_of(STRUCTURAL_TOKENS).repeated())
768                    .map_with(|_, e| ParameterList::dummy(e.span())),
769            ))
770            .boxed();
771
772        let module = doc_comment
773            .clone()
774            .then(attribute_parser.clone())
775            .then(visibility.then_whitespace().or_not())
776            .then(just(Token::KeywordMod).map_with(|_, e| e.span()))
777            .then_whitespace()
778            .then(
779                identifier_parser.clone().recover_with(via_parser(
780                    recovery_expect_any_except(&[Token::SigilOpenCurlyBracket])
781                        .map_with(|_, e| Identifier::dummy(e.span())),
782                )),
783            )
784            .then_maybe_whitespace()
785            .then(
786                block
787                    .clone()
788                    .map(Some)
789                    .or(just(Token::SigilSemiColon).map(|_| None)),
790            )
791            .with_extras()
792            .map_with(
793                |((((((doc, attributes), visibility), keyword_span), name), body), extras), e| {
794                    Statement::Module(ModuleDefinition {
795                        span: e.span(),
796                        keyword_span,
797                        extras,
798                        doc,
799                        attributes,
800                        visibility,
801                        name,
802                        body,
803                    })
804                },
805            )
806            .boxed();
807
808        let use_part = identifier_parser
809            .clone()
810            .map(UseStatementPart::Identifier)
811            .or(just(Token::OperatorMultiply).map_with(|_, e| UseStatementPart::Glob(e.span())))
812            .recover_with(via_parser(
813                recovery_expect_any_except(&[Token::SigilDoubleColon])
814                    .map_with(|_, e| UseStatementPart::Error(e.span())),
815            ))
816            .boxed();
817
818        let use_parts = use_part
819            .separated_by(just(Token::SigilDoubleColon))
820            .at_least(1)
821            .collect::<Vec<_>>()
822            .with_extras()
823            .map_with(|(parts, extras), e| UseName {
824                span: e.span(),
825                extras,
826                parts,
827            })
828            .boxed();
829
830        let use_statement = attribute_parser
831            .clone()
832            .then(visibility.then_whitespace().or_not())
833            .then(just(Token::KeywordUse).map_with(|_, e| e.span()))
834            .then_whitespace()
835            .then(use_parts)
836            .then(
837                whitespace_parser()
838                    .then(just(Token::KeywordAs))
839                    .then_whitespace()
840                    .ignore_then(identifier_parser.clone().recover_with(via_parser(
841                        recovery_expect_any().map_with(|_, e| Identifier::dummy(e.span())),
842                    )))
843                    .or_not(),
844            )
845            .with_extras()
846            .map_with(
847                |(((((attributes, visibility), keyword_span), name), use_as), extras), e| {
848                    Statement::Use(UseStatement {
849                        span: e.span(),
850                        attributes,
851                        visibility,
852                        keyword_span,
853                        extras,
854                        name,
855                        use_as,
856                    })
857                },
858            )
859            .boxed();
860
861        let workbench_kind = select_ref! {
862            Token::KeywordSketch => WorkbenchKind::Sketch,
863            Token::KeywordPart => WorkbenchKind::Part,
864            Token::KeywordOp => WorkbenchKind::Op,
865        }
866        .boxed();
867
868        let init = doc_comment
869            .clone()
870            .then(attribute_parser.clone())
871            .then(just(Token::KeywordInit).map_with(|_, e| e.span()))
872            .then_maybe_whitespace()
873            .then(parameter_list.clone())
874            .then_maybe_whitespace()
875            .then(block.clone())
876            .with_extras()
877            .map_with(
878                |(((((doc, attributes), keyword_span), arguments), body), extras), e| {
879                    Statement::Init(InitDefinition {
880                        span: e.span(),
881                        keyword_span,
882                        extras,
883                        doc,
884                        attributes,
885                        parameters: arguments,
886                        body,
887                    })
888                },
889            )
890            .boxed();
891        let workbench = doc_comment
892            .clone()
893            .then(attribute_parser.clone())
894            .then(visibility.then_whitespace().or_not())
895            .then(workbench_kind.map_with(|kind, e| (kind, e.span())))
896            .then_whitespace()
897            .then(
898                identifier_parser.clone().recover_with(via_parser(
899                    recovery_expect_any_except(&[
900                        Token::SigilOpenCurlyBracket,
901                        Token::SigilOpenBracket,
902                    ])
903                    .map_with(|_, e| Identifier::dummy(e.span())),
904                )),
905            )
906            .then_maybe_whitespace()
907            .then(parameter_list.clone())
908            .then_maybe_whitespace()
909            .then(block.clone())
910            .with_extras()
911            .map_with(
912                |(
913                    (
914                        (
915                            ((((doc, attributes), visibility), (kind, keyword_span)), name),
916                            arguments,
917                        ),
918                        body,
919                    ),
920                    extras,
921                ),
922                 e| {
923                    Statement::Workbench(WorkbenchDefinition {
924                        span: e.span(),
925                        keyword_span,
926                        extras,
927                        kind,
928                        doc,
929                        attributes,
930                        visibility,
931                        name,
932                        plan: arguments,
933                        body,
934                    })
935                },
936            )
937            .boxed();
938
939        let return_statement = just(Token::KeywordReturn)
940            .map_with(|_, e| e.span())
941            .then_maybe_whitespace()
942            .then(expression_parser.clone().or_not())
943            .with_extras()
944            .map_with(|((keyword_span, value), extras), e| {
945                Statement::Return(Return {
946                    span: e.span(),
947                    keyword_span,
948                    extras,
949                    value,
950                })
951            })
952            .boxed();
953
954        let function = doc_comment
955            .clone()
956            .then(visibility.then_whitespace().or_not())
957            .then(just(Token::KeywordFn).map_with(|_, e| e.span()))
958            .then_whitespace()
959            .then(
960                identifier_parser.clone().recover_with(via_parser(
961                    recovery_expect_any_except(&[
962                        Token::SigilOpenCurlyBracket,
963                        Token::SigilOpenBracket,
964                    ])
965                    .map_with(|_, e| Identifier::dummy(e.span())),
966                )),
967            )
968            .then_maybe_whitespace()
969            .then(parameter_list.clone())
970            .then_maybe_whitespace()
971            .then(
972                just(Token::SigilSingleArrow)
973                    .then_maybe_whitespace()
974                    .ignore_then(type_parser.clone())
975                    .then_maybe_whitespace()
976                    .or_not(),
977            )
978            .then(block.clone())
979            .with_extras()
980            .map_with(
981                |(
982                    ((((((doc, visibility), keyword_span), name), arguments), return_type), body),
983                    extras,
984                ),
985                 e| {
986                    Statement::Function(FunctionDefinition {
987                        span: e.span(),
988                        keyword_span,
989                        extras,
990                        doc,
991                        visibility,
992                        name,
993                        arguments,
994                        return_type,
995                        body,
996                    })
997                },
998            )
999            .boxed();
1000
1001        let if_expression = attribute_parser
1002            .clone()
1003            .then(if_inner.clone().map(Expression::If))
1004            .with_extras()
1005            .map_with(|((attributes, expression), extras), e| {
1006                Statement::Expression(ExpressionStatement {
1007                    span: e.span(),
1008                    extras,
1009                    attributes,
1010                    expression,
1011                })
1012            })
1013            .labelled("if statement")
1014            .boxed();
1015
1016        let inner_doc_statement = select_ref! {
1017            Token::InnerDocComment(comment) => String::from(comment.as_ref()),
1018        }
1019        .then_maybe_whitespace()
1020        .repeated()
1021        .at_least(1)
1022        .collect::<Vec<_>>()
1023        .map_with(|lines, e| Comment {
1024            span: e.span(),
1025            lines,
1026        })
1027        .labelled("inner doc-comment")
1028        .map(Statement::InnerDocComment)
1029        .boxed();
1030
1031        let not_assigment = whitespace_parser()
1032            .or_not()
1033            .then(none_of([
1034                Token::OperatorAssignment,
1035                Token::SigilDoubleColon,
1036            ]))
1037            .ignored()
1038            .or(just(Token::SigilSemiColon).ignored())
1039            .rewind()
1040            .boxed();
1041
1042        let reserved_keyword_statement = reserved_keyword
1043            .clone()
1044            .then_ignore(not_assigment.clone())
1045            .try_map_with(|kind, e| {
1046                Err::<(), _>(Rich::custom(
1047                    e.span(),
1048                    ParseErrorKind::ReservedKeyword(kind),
1049                ))
1050            })
1051            .ignored()
1052            .recover_with(via_parser(
1053                reserved_keyword
1054                    .then_ignore(not_assigment)
1055                    .clone()
1056                    .ignore_then(
1057                        none_of(STRUCTURAL_TOKENS)
1058                            .repeated()
1059                            .then_ignore(ignore_till_matched_curly())
1060                            .or(ignore_till_semi().then_ignore(just(Token::SigilSemiColon))),
1061                    ),
1062            ))
1063            .map_with(|_, e| Statement::Error(e.span()))
1064            .boxed();
1065
1066        let with_semi = return_statement
1067            .or(use_statement)
1068            .or(const_assignment)
1069            .or(pub_assignment)
1070            .or(property_assignment)
1071            .or(local_assignment)
1072            .or(expression)
1073            .then_maybe_whitespace()
1074            .boxed();
1075
1076        let without_semi = function
1077            .or(inner_doc_statement)
1078            .or(init)
1079            .or(workbench)
1080            .or(module)
1081            .or(comment)
1082            .or(if_expression)
1083            .boxed();
1084
1085        without_semi
1086            .or(reserved_keyword_statement)
1087            .or(with_semi.then_ignore(
1088                just(Token::SigilSemiColon)
1089                    .labelled("semicolon")
1090                    .ignored()
1091                    .recover_with(via_parser(
1092                        none_of(STRUCTURAL_TOKENS)
1093                            .repeated()
1094                            .then_ignore(just(Token::SigilSemiColon)),
1095                    )),
1096            ))
1097            .boxed()
1098            .labelled("statement")
1099    });
1100
1101    statement_list_parser.define({
1102        let trailing_expr = attribute_parser
1103            .clone()
1104            .then(expression_parser.clone())
1105            .with_extras()
1106            .map_with(
1107                |((attributes, expression), extras), e| ExpressionStatement {
1108                    span: e.span(),
1109                    extras,
1110                    attributes,
1111                    expression,
1112                },
1113            )
1114            .map(Statement::Expression)
1115            .map(Box::new)
1116            .or_not()
1117            .then_maybe_whitespace()
1118            .boxed();
1119
1120        whitespace_parser()
1121            .or_not()
1122            .ignore_then(statement_parser)
1123            .then_maybe_whitespace()
1124            .repeated()
1125            .collect::<Vec<_>>()
1126            .then(trailing_expr)
1127            .with_extras()
1128            .map_with(|((statements, tail), extras), e| StatementList {
1129                span: e.span(),
1130                extras,
1131                statements,
1132                tail,
1133            })
1134            .then_maybe_whitespace()
1135            .boxed()
1136    });
1137
1138    expression_parser.define({
1139        let unclosed_string = select_ref! {
1140            Token::Error(LexerError::UnclosedString(_)) => (),
1141        }
1142        .ignore_then(
1143            semi_recovery
1144                .clone()
1145                .try_map_with(|_, e| {
1146                    let span: Span = e.span();
1147                    Err::<Expression, _>(Rich::custom(
1148                        (span.start - 1)..span.end,
1149                        ParseErrorKind::UnterminatedString,
1150                    ))
1151                })
1152                .recover_with(via_parser(
1153                    semi_recovery
1154                        .clone()
1155                        .map_with(|_, e| Expression::Error(e.span())),
1156                )),
1157        )
1158        .labelled("unclosed string")
1159        .boxed();
1160
1161        let literal = literal_parser
1162            .map(Expression::Literal)
1163            .labelled("literal")
1164            .boxed()
1165            .or(unclosed_string);
1166
1167        let marker = just(Token::SigilAt)
1168            .ignore_then(identifier_parser.clone())
1169            .map(Expression::Marker)
1170            .labelled("marker")
1171            .boxed();
1172
1173        let string_content_part = select_ref! {
1174            Token::StringContent(content) = e => StringPart::Content(StringLiteral {
1175                span: e.span(),
1176                content: content.as_ref().into(),
1177            }),
1178            Token::Character(char) = e => StringPart::Char(StringCharacter {
1179                span: e.span(),
1180                character: *char,
1181            }),
1182        }
1183        .labelled("string content")
1184        .boxed();
1185
1186        let format_precision = select_ref!(
1187            Token::StringFormatPrecision(precision) = e => {
1188                u32::from_str(&precision[1..]).map_err(|err| (err, e.span()))
1189            }
1190        );
1191        let format_width = select_ref!(
1192            Token::StringFormatWidth(width) = e => {
1193                u32::from_str(&width[1..]).map_err(|err| (err, e.span()))
1194            }
1195        );
1196        let format_spec = format_width
1197            .or_not()
1198            .then(format_precision.or_not())
1199            .map_with(|(width, precision), e| StringFormatSpecification {
1200                span: e.span(),
1201                width,
1202                precision,
1203            })
1204            .labelled("string format specification")
1205            .boxed();
1206
1207        let string_format_part = expression_parser
1208            .clone()
1209            .then(format_spec)
1210            .with_extras()
1211            .delimited_by(
1212                just(Token::StringFormatOpen),
1213                just(Token::StringFormatClose),
1214            )
1215            .map_with(
1216                |((expression, specification), extras), e| StringExpression {
1217                    span: e.span(),
1218                    extras,
1219                    expression: Box::new(expression),
1220                    specification: Box::new(specification),
1221                },
1222            )
1223            .map(StringPart::Expression)
1224            .labelled("string format expression")
1225            .boxed();
1226        let string_part = string_content_part
1227            .or(string_format_part)
1228            .labelled("format string content");
1229
1230        let string_format = string_part
1231            .repeated()
1232            .collect::<Vec<_>>()
1233            .delimited_by(just(Token::FormatStringStart), just(Token::FormatStringEnd))
1234            .with_extras()
1235            .map_with(|(parts, extras), e| FormatString {
1236                span: e.span(),
1237                extras,
1238                parts,
1239            })
1240            .map(Expression::String)
1241            .boxed();
1242
1243        let tuple = whitespace_parser()
1244            .or_not()
1245            .ignore_then(tuple_body.clone())
1246            .then_maybe_whitespace()
1247            .with_extras()
1248            .delimited_with_spanned_error(
1249                just(Token::SigilOpenBracket),
1250                just(Token::SigilCloseBracket),
1251                |err: Error, open, end| {
1252                    Rich::custom(
1253                        err.span().clone(),
1254                        ParseErrorKind::UnclosedBracket {
1255                            open,
1256                            end,
1257                            kind: "tuple",
1258                            close_token: Token::SigilCloseBracket,
1259                        },
1260                    )
1261                },
1262            )
1263            .map_with(|(values, extras), e| {
1264                Expression::Tuple(TupleExpression {
1265                    span: e.span(),
1266                    extras,
1267                    values,
1268                })
1269            })
1270            .labelled("tuple");
1271
1272        let bracketed = expression_parser
1273            .clone()
1274            .then_maybe_whitespace()
1275            .delimited_with_spanned_error(
1276                just(Token::SigilOpenBracket).then_maybe_whitespace(),
1277                just(Token::SigilCloseBracket),
1278                |err: Error, open, end| {
1279                    Rich::custom(
1280                        err.span().clone(),
1281                        ParseErrorKind::UnclosedBracket {
1282                            open,
1283                            end,
1284                            kind: "bracketed expression",
1285                            close_token: Token::SigilCloseBracket,
1286                        },
1287                    )
1288                },
1289            )
1290            .boxed();
1291
1292        let array_item = expression_parser
1293            .clone()
1294            .with_extras()
1295            .map_with(|(expression, extras), e| ArrayItem {
1296                span: e.span(),
1297                extras,
1298                expression,
1299            })
1300            .boxed();
1301
1302        let array_range = array_item
1303            .clone()
1304            .then_maybe_whitespace()
1305            .then_ignore(just(Token::SigilDoubleDot))
1306            .then_maybe_whitespace()
1307            .then(array_item.clone())
1308            .then_maybe_whitespace()
1309            .with_extras()
1310            .delimited_by(
1311                just(Token::SigilOpenSquareBracket).then_maybe_whitespace(),
1312                just(Token::SigilCloseSquareBracket),
1313            )
1314            .then(single_type.clone().or_not())
1315            .map_with(|(((start, end), extras), ty), e| {
1316                Expression::ArrayRange(ArrayRangeExpression {
1317                    span: e.span(),
1318                    extras,
1319                    start: Box::new(start),
1320                    end: Box::new(end),
1321                    ty,
1322                })
1323            })
1324            .labelled("array range")
1325            .boxed();
1326
1327        let array_list = array_item
1328            .clone()
1329            .then_maybe_whitespace()
1330            .separated_by(just(Token::SigilComma).then_maybe_whitespace())
1331            .allow_trailing()
1332            .collect::<Vec<_>>()
1333            .with_extras()
1334            .delimited_by(
1335                just(Token::SigilOpenSquareBracket).then_maybe_whitespace(),
1336                just(Token::SigilCloseSquareBracket),
1337            )
1338            .then(single_type.clone().or_not())
1339            .map_with(|((items, extras), ty), e| {
1340                Expression::ArrayList(ArrayListExpression {
1341                    span: e.span(),
1342                    extras,
1343                    items,
1344                    ty,
1345                })
1346            })
1347            .labelled("array")
1348            .boxed();
1349
1350        let block_expression = block
1351            .clone()
1352            .map(Expression::Block)
1353            .labelled("block expression")
1354            .boxed();
1355
1356        if_inner.define(
1357            just(Token::KeywordIf)
1358                .map_with(|_, e| e.span())
1359                .then_whitespace()
1360                .then(expression_parser.clone())
1361                .then_maybe_whitespace()
1362                .then(block.clone())
1363                .then_maybe_whitespace()
1364                .then(
1365                    just(Token::KeywordElse)
1366                        .map_with(|_, e| e.span())
1367                        .then_maybe_whitespace()
1368                        .then(if_inner.clone())
1369                        .map(|(span, inner)| (span, Box::new(inner)))
1370                        .or_not(),
1371                )
1372                .then(
1373                    just(Token::KeywordElse)
1374                        .map_with(|_, e| e.span())
1375                        .then_maybe_whitespace()
1376                        .then(block.clone())
1377                        .or_not(),
1378                )
1379                .with_extras()
1380                .map_with(
1381                    |(((((if_span, condition), body), next_if), else_body), extras), e| {
1382                        let (next_if_span, next_if) = next_if
1383                            .map(|(span, if_expr)| (Some(span), Some(if_expr)))
1384                            .unwrap_or((None, None));
1385                        let (else_span, else_body) = else_body
1386                            .map(|(span, body)| (Some(span), Some(body)))
1387                            .unwrap_or((None, None));
1388                        If {
1389                            span: e.span(),
1390                            if_span,
1391                            extras,
1392                            condition: Box::new(condition),
1393                            body,
1394                            next_if_span,
1395                            next_if,
1396                            else_span,
1397                            else_body,
1398                        }
1399                    },
1400                )
1401                .boxed(),
1402        );
1403        let if_expression = if_inner
1404            .map(Expression::If)
1405            .labelled("if expression")
1406            .boxed();
1407
1408        let qualified_name_expr = identifier_parser
1409            .clone()
1410            .map_with(|ident, e| QualifiedName {
1411                span: e.span(),
1412                parts: vec![ident],
1413                extras: ItemExtras::default(),
1414            })
1415            .foldl_with(
1416                just(Token::SigilDoubleColon)
1417                    .ignore_then(identifier_parser.clone())
1418                    .repeated(),
1419                |mut acc, part, _| {
1420                    acc.span.end = part.span.end;
1421                    acc.parts.push(part);
1422                    acc
1423                },
1424            )
1425            .with_extras()
1426            .map(|(mut name, extras)| {
1427                name.extras = extras;
1428                name
1429            })
1430            .map(Expression::QualifiedName)
1431            .boxed();
1432
1433        let call = call_inner
1434            .clone()
1435            .map(Expression::Call)
1436            .labelled("method call");
1437
1438        let bracket_based = bracketed.or(tuple).recover_with(via_parser(
1439            tuple_recovery
1440                .clone()
1441                .map_with(|_, e| Expression::Error(e.span())),
1442        ));
1443
1444        let base = literal
1445            .or(string_format)
1446            .or(call)
1447            .or(marker)
1448            .or(bracket_based)
1449            .or(array_range)
1450            .or(array_list)
1451            .or(block_expression)
1452            .or(if_expression)
1453            .or(qualified_name_expr)
1454            .boxed();
1455
1456        let access_attribute = just(Token::SigilHash)
1457            .ignore_then(identifier_parser.clone())
1458            .map(Element::Attribute)
1459            .labelled("attribute access")
1460            .boxed();
1461
1462        let access_tuple = just(Token::SigilDot)
1463            .ignore_then(identifier_parser.clone())
1464            .map(Element::Tuple)
1465            .labelled("tuple access")
1466            .boxed();
1467
1468        let access_method = just(Token::SigilDot)
1469            .ignore_then(call_inner)
1470            .map(Element::Method)
1471            .labelled("method call")
1472            .boxed();
1473
1474        let access_array = expression_parser
1475            .clone()
1476            .delimited_by(
1477                just(Token::SigilOpenSquareBracket),
1478                just(Token::SigilCloseSquareBracket),
1479            )
1480            .map(Box::new)
1481            .map(Element::ArrayElement)
1482            .labelled("array access")
1483            .boxed();
1484
1485        let access_item = access_attribute
1486            .or(access_method)
1487            .or(access_tuple)
1488            .or(access_array)
1489            .boxed();
1490
1491        let element_access = base
1492            .clone()
1493            .foldl_with(
1494                whitespace_parser()
1495                    .or_not()
1496                    .ignore_then(access_item)
1497                    .repeated(),
1498                |value, element, e| {
1499                    Expression::ElementAccess(ElementAccess {
1500                        span: e.span(),
1501                        value: value.into(),
1502                        element,
1503                    })
1504                },
1505            )
1506            .labelled("element access")
1507            .boxed();
1508
1509        let unary_expression = unary_operator_parser
1510            .then_maybe_whitespace()
1511            .then(element_access.clone())
1512            .with_extras()
1513            .map_with(|((op, rhs), extras), e| UnaryOperation {
1514                span: e.span(),
1515                extras,
1516                operation: op,
1517                rhs: rhs.into(),
1518            })
1519            .map(simplify_unary_op)
1520            .boxed();
1521
1522        let binary_param = element_access.or(unary_expression.clone());
1523
1524        let near = binop(binary_param, &[Token::OperatorNear]);
1525        let xor = binop(near, &[Token::OperatorPowerXor, Token::OperatorXor]);
1526        let union_intersect = binop(xor, &[Token::OperatorUnion, Token::OperatorIntersect]);
1527        let mul_div = binop(
1528            union_intersect,
1529            &[Token::OperatorMultiply, Token::OperatorDivide],
1530        );
1531        let add_sub = binop(mul_div, &[Token::OperatorAdd, Token::OperatorSubtract]);
1532        let less_greater_eq = binop(
1533            add_sub,
1534            &[Token::OperatorLessEqual, Token::OperatorGreaterEqual],
1535        );
1536        let less_greater = binop(
1537            less_greater_eq,
1538            &[Token::OperatorLessThan, Token::OperatorGreaterThan],
1539        );
1540        let eq_neq = binop(
1541            less_greater,
1542            &[Token::OperatorEqual, Token::OperatorNotEqual],
1543        );
1544        let or_and = binop(eq_neq, &[Token::OperatorOr, Token::OperatorAnd]);
1545
1546        or_and.labelled("expression").boxed()
1547    });
1548
1549    statement_list_parser
1550        .then_ignore(end())
1551        .map_with(move |statements, ex| SourceFile {
1552            span: ex.span(),
1553            statements,
1554        })
1555}