Skip to main content

microcad_syntax/parser/
mod.rs

1// Copyright © 2026 The µcad authors <info@ucad.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::helpers::{binop, comment_parser};
11use crate::parser::simplify::simplify_unary_op;
12use crate::tokens::*;
13use chumsky::error::Rich;
14use chumsky::input::{Input, MappedInput};
15use chumsky::prelude::*;
16use chumsky::{Parser, extra, select_ref};
17pub use error::ParseError;
18use helpers::ParserExt;
19use std::str::FromStr;
20
21type Extra<'tokens> = extra::Err<Rich<'tokens, Token<'tokens>, Span>>;
22
23pub fn map_token_input<'a, 'token>(
24    spanned: &'a SpannedToken<Token<'token>>,
25) -> (&'a Token<'token>, &'a Span) {
26    (&spanned.token, &spanned.span)
27}
28
29type InputMap<'input, 'token> =
30    fn(&'input SpannedToken<Token<'token>>) -> (&'input Token<'token>, &'input Span);
31
32type ParserInput<'input, 'token> = MappedInput<
33    'input,
34    Token<'token>,
35    Span,
36    &'input [SpannedToken<Token<'token>>],
37    InputMap<'input, 'token>,
38>;
39
40fn input<'input, 'tokens>(
41    input: &'input [SpannedToken<Token<'tokens>>],
42) -> ParserInput<'input, 'tokens> {
43    let end = input.last().map(|t| t.span.end).unwrap_or_default();
44    Input::map(input, end..end, map_token_input)
45}
46
47/// Build an abstract syntax tree from a list of tokens
48pub fn parse<'tokens>(
49    tokens: &'tokens [SpannedToken<Token<'tokens>>],
50) -> Result<SourceFile, Vec<ParseError>> {
51    parser()
52        .parse(input(tokens))
53        .into_result()
54        .map_err(|errors| errors.into_iter().map(ParseError::new).collect())
55}
56
57fn parser<'tokens>()
58-> impl Parser<'tokens, ParserInput<'tokens, 'tokens>, SourceFile, Extra<'tokens>> {
59    let mut statement_list_parser = Recursive::declare();
60    let mut statement_parser = Recursive::declare();
61    let mut expression_parser = Recursive::declare();
62    let mut type_parser = Recursive::declare();
63    let mut attribute_parser = Recursive::declare();
64    let mut if_inner = Recursive::declare();
65
66    let semi_recovery = none_of(Token::SigilSemiColon).repeated().ignored();
67
68    let block_recovery = just(Token::SigilOpenCurlyBracket)
69        .then(
70            none_of(Token::SigilCloseCurlyBracket)
71                .repeated()
72                .then(just(Token::SigilCloseCurlyBracket)),
73        )
74        .map_with(|_, e| StatementList {
75            span: e.span(),
76            statements: vec![Statement::Error(e.span())],
77            tail: None,
78            extras: ItemExtras::default(),
79        });
80
81    let block = statement_list_parser
82        .clone()
83        .delimited_by(
84            just(Token::SigilOpenCurlyBracket),
85            just(Token::SigilCloseCurlyBracket),
86        )
87        .recover_with(via_parser(block_recovery))
88        .boxed();
89
90    let identifier_parser = select_ref! { Token::Identifier(ident) = e => Identifier {
91        span: e.span(),
92        name: ident.as_ref().into(),
93    } }
94    .labelled("identifier")
95    .boxed();
96
97    let qualified_name = identifier_parser
98        .clone()
99        .separated_by(just(Token::SigilDoubleColon))
100        .at_least(1)
101        .collect::<Vec<_>>()
102        .with_extras()
103        .map_with(|(parts, extras), e| QualifiedName {
104            span: e.span(),
105            parts,
106            extras,
107        })
108        .labelled("qualified name")
109        .boxed();
110
111    let single_type = select_ref! {
112        Token::Identifier(ident) = e => SingleType {
113            span: e.span(),
114            name: ident.as_ref().into()
115        },
116        Token::Unit(unit) = e => SingleType {
117            span: e.span(),
118            name: unit.as_ref().into()
119        },
120        Token::SigilQuote = e => SingleType {
121            span: e.span(),
122            name: r#"""#.into()
123        },
124    }
125    .labelled("quantity type")
126    .boxed();
127
128    type_parser.define({
129        let single = single_type.clone().map(Type::Single);
130        let array = type_parser
131            .clone()
132            .delimited_by(
133                just(Token::SigilOpenSquareBracket),
134                just(Token::SigilCloseSquareBracket),
135            )
136            .map_with(|inner, e| {
137                Type::Array(ArrayType {
138                    span: e.span(),
139                    inner: Box::new(inner),
140                })
141            })
142            .labelled("array type")
143            .boxed();
144
145        let tuple = identifier_parser
146            .clone()
147            .then_ignore(just(Token::SigilColon))
148            .or_not()
149            .then(type_parser.clone())
150            .separated_by(just(Token::SigilComma))
151            .allow_trailing()
152            .collect::<Vec<_>>()
153            .delimited_by(
154                just(Token::SigilOpenBracket),
155                just(Token::SigilCloseBracket),
156            )
157            .map_with(|inner, e| {
158                Type::Tuple(TupleType {
159                    span: e.span(),
160                    inner,
161                })
162            })
163            .labelled("tuple type")
164            .boxed();
165
166        single.or(array).or(tuple).labelled("type").boxed()
167    });
168
169    let literal_parser = {
170        let single_value = select_ref! {
171            Token::LiteralFloat(x) = e => {
172                match f64::from_str(x) {
173                    Ok(value) => LiteralKind::Float(FloatLiteral {
174                        value,
175                        span: e.span(),
176                    }),
177                    Err(err) => LiteralKind::Error(LiteralError {
178                        span: e.span(),
179                        kind: err.into(),
180                    })
181                }
182            },
183            Token::LiteralInt(x) = e => {
184                match i64::from_str(x) {
185                    Ok(value) => LiteralKind::Integer(IntegerLiteral {
186                    value,
187                    span: e.span(),
188                }),
189                    Err(err) => LiteralKind::Error(LiteralError {
190                        span: e.span(),
191                        kind: err.into(),
192                    })
193                }
194            },
195            Token::LiteralString(content) = e => {
196                LiteralKind::String(StringLiteral {
197                    span: e.span(),
198                    content: content.as_ref().into(),
199                })
200            },
201            Token::LiteralBool(value) = e => {
202                LiteralKind::Bool(BoolLiteral {
203                    span: e.span(),
204                    value: *value,
205                })
206            },
207        }
208        .boxed();
209
210        single_value
211            .then(single_type.clone().or_not())
212            .with_extras()
213            .map_with(|((literal, ty), extras), e| {
214                let literal = match (literal, ty) {
215                    (LiteralKind::Float(float), Some(ty)) => {
216                        LiteralKind::Quantity(QuantityLiteral {
217                            span: e.span(),
218                            value: float.value,
219                            ty,
220                        })
221                    }
222                    (LiteralKind::Integer(int), Some(ty)) => {
223                        LiteralKind::Quantity(QuantityLiteral {
224                            span: e.span(),
225                            value: int.value as f64,
226                            ty,
227                        })
228                    }
229                    (_, Some(_)) => LiteralKind::Error(LiteralError {
230                        span: e.span(),
231                        kind: LiteralErrorKind::Untypable,
232                    }),
233                    (literal, None) => literal,
234                };
235                Literal {
236                    span: e.span(),
237                    literal,
238                    extras,
239                }
240            })
241            .labelled("literal")
242            .boxed()
243    };
244
245    let unary_operator_parser = select_ref! {
246        Token::OperatorSubtract => UnaryOperator::Minus,
247        Token::OperatorAdd => UnaryOperator::Plus,
248        Token::OperatorNot => UnaryOperator::Not,
249    }
250    .labelled("unary operator")
251    .boxed();
252
253    let doc_comment = select_ref! {
254        Token::DocComment(comment) => comment,
255    }
256    .repeated()
257    .at_least(1)
258    .collect::<Vec<_>>()
259    .map_with(|lines, e| Comment {
260        span: e.span(),
261        lines: lines.into_iter().map(|s| s.as_ref().into()).collect(),
262    })
263    .labelled("doc-comment")
264    .or_not()
265    .boxed();
266
267    let tuple_recovery = just(Token::SigilOpenBracket)
268        .then(
269            none_of(Token::SigilCloseBracket)
270                .repeated()
271                .then(just(Token::SigilCloseBracket)),
272        )
273        .map_with(|_, e| {
274            (
275                vec![TupleItem {
276                    span: e.span(),
277                    name: None,
278                    value: Expression::Error(e.span()),
279                    extras: ItemExtras::default(),
280                }],
281                ItemExtras::default(),
282            )
283        });
284
285    let tuple_body = identifier_parser
286        .clone()
287        .then_ignore(just(Token::OperatorAssignment))
288        .or_not()
289        .then(expression_parser.clone())
290        .with_extras()
291        .map_with(|((name, value), extras), e| TupleItem {
292            span: e.span(),
293            extras,
294            name,
295            value,
296        })
297        .separated_by(just(Token::SigilComma))
298        .allow_trailing()
299        .collect::<Vec<_>>()
300        .boxed();
301
302    let call_inner = qualified_name
303        .clone()
304        .then(
305            tuple_body
306                .clone()
307                .with_extras()
308                .map_with(|(arguments, extras), e| ArgumentList {
309                    span: e.span(),
310                    extras,
311                    arguments: arguments
312                        .into_iter()
313                        .map(|item| match item.name {
314                            Some(name) => Argument::Named(NamedArgument {
315                                span: item.span,
316                                extras: item.extras,
317                                name,
318                                value: item.value,
319                            }),
320                            None => Argument::Unnamed(UnnamedArgument {
321                                span: item.span,
322                                extras: item.extras,
323                                value: item.value,
324                            }),
325                        })
326                        .collect::<Vec<_>>(),
327                })
328                .labelled("function arguments")
329                .delimited_by(
330                    just(Token::SigilOpenBracket),
331                    just(Token::SigilCloseBracket),
332                )
333                .recover_with(via_parser(tuple_recovery.clone().map_with(|_, e| {
334                    ArgumentList {
335                        span: e.span(),
336                        extras: ItemExtras::default(),
337                        arguments: Vec::new(),
338                    }
339                }))),
340        )
341        .with_extras()
342        .map_with(|((name, arguments), extras), e| Call {
343            span: e.span(),
344            extras,
345            name,
346            arguments,
347        })
348        .boxed();
349
350    statement_parser.define({
351        let visibility = select_ref! {
352            Token::KeywordPub => Visibility::Public,
353        }
354        .labelled("visibility");
355
356        let expression = attribute_parser
357            .clone()
358            .then(expression_parser.clone())
359            .with_extras()
360            .map_with(
361                |((attributes, expression), extras), e| ExpressionStatement {
362                    span: e.span(),
363                    extras,
364                    attributes,
365                    expression,
366                },
367            )
368            .map(Statement::Expression)
369            .boxed();
370
371        let assignment_qualifier = select_ref! {
372            Token::KeywordConst => AssignmentQualifier::Const,
373            Token::KeywordProp => AssignmentQualifier::Prop,
374        }
375        .or_not()
376        .boxed();
377
378        let assignment_inner = doc_comment
379            .clone()
380            .then(attribute_parser.clone())
381            .then(visibility.or_not())
382            .then(assignment_qualifier)
383            .then(identifier_parser.clone())
384            .then(
385                just(Token::SigilColon)
386                    .ignore_then(type_parser.clone())
387                    .or_not(),
388            )
389            .then_ignore(just(Token::OperatorAssignment))
390            .then(
391                expression_parser.clone().recover_with(via_parser(
392                    semi_recovery
393                        .clone()
394                        .map_with(|_, e| Expression::Error(e.span())),
395                )),
396            )
397            .with_extras()
398            .map_with(
399                |(((((((doc, attributes), visibility), qualifier), name), ty), value), extras),
400                 e| {
401                    Assignment {
402                        span: e.span(),
403                        extras,
404                        doc,
405                        attributes,
406                        visibility,
407                        qualifier,
408                        name,
409                        value: Box::new(value),
410                        ty,
411                    }
412                },
413            )
414            .boxed();
415
416        let assignment = assignment_inner
417            .clone()
418            .map(Statement::Assignment)
419            .labelled("assignment");
420
421        attribute_parser.define({
422            let attribute_command = assignment_inner
423                .clone()
424                .map(AttributeCommand::Assignment)
425                .or(call_inner.clone().map(AttributeCommand::Call))
426                .or(identifier_parser.clone().map(AttributeCommand::Ident));
427
428            just(Token::SigilHash)
429                .ignore_then(just(Token::OperatorNot).or_not().map(|opt| opt.is_some()))
430                .then(
431                    attribute_command
432                        .separated_by(just(Token::SigilComma))
433                        .at_least(1)
434                        .collect::<Vec<_>>()
435                        .delimited_by(
436                            just(Token::SigilOpenSquareBracket),
437                            just(Token::SigilCloseSquareBracket),
438                        ),
439                )
440                .with_extras()
441                .map_with(|((is_inner, commands), extras), e| Attribute {
442                    span: e.span(),
443                    is_inner,
444                    extras,
445                    commands,
446                })
447                .labelled("attribute")
448                .repeated()
449                .collect::<Vec<Attribute>>()
450                .boxed()
451        });
452
453        let comment = comment_parser()
454            .map(Statement::Comment)
455            .labelled("comment")
456            .boxed();
457
458        let arguments_inner = identifier_parser
459            .clone()
460            .then(
461                just(Token::SigilColon)
462                    .ignore_then(type_parser.clone())
463                    .or_not(),
464            )
465            .then(
466                just(Token::OperatorAssignment)
467                    .ignore_then(expression_parser.clone())
468                    .or_not(),
469            )
470            .with_extras()
471            .map_with(|(((name, ty), default), extras), e| ArgumentDefinition {
472                span: e.span(),
473                extras,
474                name,
475                ty,
476                default,
477            })
478            .separated_by(just(Token::SigilComma))
479            .allow_trailing()
480            .collect::<Vec<_>>()
481            .boxed();
482
483        let arguments = arguments_inner
484            .with_extras()
485            .map_with(|(arguments, extras), e| ArgumentsDefinition {
486                span: e.span(),
487                extras,
488                arguments,
489            })
490            .delimited_by(
491                just(Token::SigilOpenBracket),
492                just(Token::SigilCloseBracket),
493            )
494            .boxed();
495
496        let module = doc_comment
497            .clone()
498            .then(attribute_parser.clone())
499            .then(visibility.or_not())
500            .then_ignore(just(Token::KeywordMod))
501            .then(identifier_parser.clone())
502            .then(
503                block
504                    .clone()
505                    .map(Some)
506                    .or(just(Token::SigilSemiColon).map(|_| None)),
507            )
508            .with_extras()
509            .map_with(
510                |(((((doc, attributes), visibility), name), body), extras), e| {
511                    Statement::Module(ModuleDefinition {
512                        span: e.span(),
513                        extras,
514                        doc,
515                        attributes,
516                        visibility,
517                        name,
518                        body,
519                    })
520                },
521            )
522            .boxed();
523
524        let use_parts = identifier_parser
525            .clone()
526            .map(UseStatementPart::Identifier)
527            .or(just(Token::OperatorMultiply).map_with(|_, e| UseStatementPart::Glob(e.span())))
528            .separated_by(just(Token::SigilDoubleColon))
529            .at_least(1)
530            .collect::<Vec<_>>()
531            .with_extras()
532            .map_with(|(parts, extras), e| UseName {
533                span: e.span(),
534                extras,
535                parts,
536            })
537            .boxed();
538
539        let use_statement = visibility
540            .or_not()
541            .then_ignore(just(Token::KeywordUse))
542            .then(use_parts)
543            .then(
544                just(Token::KeywordAs)
545                    .ignore_then(identifier_parser.clone())
546                    .or_not(),
547            )
548            .with_extras()
549            .map_with(|(((visibility, name), use_as), extras), e| {
550                Statement::Use(UseStatement {
551                    span: e.span(),
552                    extras,
553                    visibility,
554                    name,
555                    use_as,
556                })
557            })
558            .boxed();
559
560        let workspace_kind = select_ref! {
561            Token::KeywordSketch => WorkbenchKind::Sketch,
562            Token::KeywordPart => WorkbenchKind::Part,
563            Token::KeywordOp => WorkbenchKind::Op,
564        }
565        .boxed();
566
567
568        let init = doc_comment
569            .clone()
570            .then(just(Token::KeywordInit).map_with(|_, e| e.span()))
571            .then(arguments.clone())
572            .then(block.clone())
573            .with_extras()
574            .map_with(|((((doc, keyword_span), arguments), body), extras), e| {
575                Statement::Init(InitDefinition {
576                    span: e.span(),
577                    keyword_span,
578                    extras,
579                    doc,
580                    arguments,
581                    body,
582                })
583            })
584            .boxed();
585        let workspace = doc_comment
586            .clone()
587            .then(attribute_parser.clone())
588            .then(visibility.or_not())
589            .then(workspace_kind)
590            .then(identifier_parser.clone())
591            .then(arguments.clone())
592            .then(block.clone())
593            .with_extras()
594            .map_with(
595                |(((((((doc, attributes), visibility), kind), name), arguments), body), extras),
596                 e| {
597                    Statement::Workbench(WorkbenchDefinition {
598                        span: e.span(),
599                        extras,
600                        kind,
601                        doc,
602                        attributes,
603                        visibility,
604                        name,
605                        arguments,
606                        body,
607                    })
608                },
609            )
610            .boxed();
611
612        let return_statement = just(Token::KeywordReturn)
613            .ignore_then(expression_parser.clone().or_not())
614            .with_extras()
615            .map_with(|(value, extras), e| {
616                Statement::Return(Return {
617                    span: e.span(),
618                    extras,
619                    value,
620                })
621            })
622            .boxed();
623
624        let function = doc_comment
625            .clone()
626            .then(visibility.or_not())
627            .then_ignore(just(Token::KeywordFn))
628            .then(identifier_parser.clone())
629            .then(arguments.clone())
630            .then(
631                just(Token::SigilSingleArrow)
632                    .ignore_then(type_parser.clone())
633                    .or_not(),
634            )
635            .then(block.clone())
636            .with_extras()
637            .map_with(
638                |((((((doc, visibility), name), arguments), return_type), body), extras), e| {
639                    Statement::Function(FunctionDefinition {
640                        span: e.span(),
641                        extras,
642                        doc,
643                        visibility,
644                        name,
645                        arguments,
646                        return_type,
647                        body,
648                    })
649                },
650            )
651            .boxed();
652
653        let if_expression = attribute_parser
654            .clone()
655            .then(if_inner.clone().map(Expression::If))
656            .with_extras()
657            .map_with(|((attributes, expression), extras), e| {
658                Statement::Expression(ExpressionStatement {
659                    span: e.span(),
660                    extras,
661                    attributes,
662                    expression,
663                })
664            })
665            .labelled("if statement")
666            .boxed();
667
668        let doc_statement = select_ref! {
669            Token::DocComment(comment) => comment,
670        }
671        .repeated()
672        .at_least(1)
673        .collect::<Vec<_>>()
674        .map_with(|lines, e| Comment {
675            span: e.span(),
676            lines: lines.into_iter().map(|s| s.as_ref().into()).collect(),
677        })
678        .labelled("doc-comment")
679        .map(Statement::Comment)
680        .boxed();
681
682        let with_semi = assignment
683            .or(return_statement)
684            .or(use_statement)
685            .or(expression)
686            .boxed();
687
688        let without_semi = function
689            .or(doc_statement)
690            .or(init)
691            .or(workspace)
692            .or(module)
693            .or(comment)
694            .or(if_expression)
695            .boxed();
696
697        without_semi
698            .or(with_semi.then_ignore(just(Token::SigilSemiColon).labelled("semicolon")))
699            .labelled("statement")
700    });
701
702    statement_list_parser.define({
703        let trailing_expr = attribute_parser
704            .clone()
705            .then(expression_parser.clone())
706            .with_extras()
707            .map_with(
708                |((attributes, expression), extras), e| ExpressionStatement {
709                    span: e.span(),
710                    extras,
711                    attributes,
712                    expression,
713                },
714            )
715            .map(Statement::Expression)
716            .map(Box::new)
717            .or_not();
718        statement_parser
719            .repeated()
720            .collect::<Vec<_>>()
721            .then(trailing_expr)
722            .with_extras()
723            .map_with(|((statements, tail), extras), e| StatementList {
724                span: e.span(),
725                extras,
726                statements,
727                tail,
728            })
729            .boxed()
730    });
731
732    expression_parser.define({
733        let unclosed_string = select_ref! {
734            Token::Error(LexerError::UnclosedString(_)) => (),
735        }
736        .ignore_then(
737            semi_recovery
738                .clone()
739                .try_map_with(|_, e| {
740                    let span: Span = e.span();
741                    Err::<Expression, _>(Rich::custom(
742                        (span.start - 1)..span.end,
743                        "unclosed string",
744                    ))
745                })
746                .recover_with(via_parser(
747                    semi_recovery
748                        .clone()
749                        .map_with(|_, e| Expression::Error(e.span())),
750                )),
751        )
752        .labelled("unclosed string")
753        .boxed();
754
755        let literal = literal_parser
756            .map(Expression::Literal)
757            .labelled("literal")
758            .boxed()
759            .or(unclosed_string);
760
761        let marker = just(Token::SigilAt)
762            .ignore_then(identifier_parser.clone())
763            .map(Expression::Marker)
764            .labelled("marker")
765            .boxed();
766
767        let string_content_part = select_ref! {
768            Token::StringContent(content) = e => StringPart::Content(StringLiteral {
769                span: e.span(),
770                content: content.as_ref().into(),
771            }),
772            Token::Character(char) = e => StringPart::Char(StringCharacter {
773                span: e.span(),
774                character: *char,
775            }),
776        }
777        .labelled("string content")
778        .boxed();
779
780        let format_precision = select_ref!(
781            Token::StringFormatPrecision(precision) = e => {
782                u32::from_str(&precision[1..]).map_err(|err| (err, e.span()))
783            }
784        );
785        let format_width = select_ref!(
786            Token::StringFormatWidth(width) = e => {
787                u32::from_str(&width[1..]).map_err(|err| (err, e.span()))
788            }
789        );
790        let format_spec = format_width
791            .or_not()
792            .then(format_precision.or_not())
793            .map_with(|(width, precision), e| StringFormatSpecification {
794                span: e.span(),
795                width,
796                precision,
797            })
798            .labelled("string format specification")
799            .boxed();
800
801        let string_format_part = expression_parser
802            .clone()
803            .then(format_spec)
804            .with_extras()
805            .delimited_by(
806                just(Token::StringFormatOpen),
807                just(Token::StringFormatClose),
808            )
809            .map_with(
810                |((expression, specification), extras), e| StringExpression {
811                    span: e.span(),
812                    extras,
813                    expression: Box::new(expression),
814                    specification: Box::new(specification),
815                },
816            )
817            .map(StringPart::Expression)
818            .labelled("string format expression")
819            .boxed();
820        let string_part = string_content_part
821            .or(string_format_part)
822            .labelled("format string content");
823
824        let string_format = string_part
825            .repeated()
826            .collect::<Vec<_>>()
827            .delimited_by(just(Token::FormatStringStart), just(Token::FormatStringEnd))
828            .with_extras()
829            .map_with(|(parts, extras), e| FormatString {
830                span: e.span(),
831                extras,
832                parts,
833            })
834            .map(Expression::String)
835            .boxed();
836
837        let tuple = tuple_body
838            .clone()
839            .with_extras()
840            .delimited_by(
841                just(Token::SigilOpenBracket),
842                just(Token::SigilCloseBracket),
843            )
844            .recover_with(via_parser(tuple_recovery))
845            .map_with(|(values, extras), e| {
846                Expression::Tuple(TupleExpression {
847                    span: e.span(),
848                    extras,
849                    values,
850                })
851            })
852            .labelled("tuple");
853
854        let bracketed = expression_parser.clone().delimited_by(
855            just(Token::SigilOpenBracket),
856            just(Token::SigilCloseBracket),
857        );
858
859        let array_item = expression_parser
860            .clone()
861            .with_extras()
862            .map_with(|(expression, extras), e| ArrayItem {
863                span: e.span(),
864                extras,
865                expression,
866            })
867            .boxed();
868
869        let array_range = array_item
870            .clone()
871            .then_ignore(just(Token::SigilDoubleDot))
872            .then(array_item.clone())
873            .with_extras()
874            .delimited_by(
875                just(Token::SigilOpenSquareBracket),
876                just(Token::SigilCloseSquareBracket),
877            )
878            .then(single_type.clone().or_not())
879            .map_with(|(((start, end), extras), ty), e| {
880                Expression::ArrayRange(ArrayRangeExpression {
881                    span: e.span(),
882                    extras,
883                    start: Box::new(start),
884                    end: Box::new(end),
885                    ty,
886                })
887            })
888            .labelled("array range")
889            .boxed();
890
891        let array_list = array_item
892            .clone()
893            .separated_by(just(Token::SigilComma))
894            .allow_trailing()
895            .collect::<Vec<_>>()
896            .with_extras()
897            .delimited_by(
898                just(Token::SigilOpenSquareBracket),
899                just(Token::SigilCloseSquareBracket),
900            )
901            .then(single_type.clone().or_not())
902            .map_with(|((items, extras), ty), e| {
903                Expression::ArrayList(ArrayListExpression {
904                    span: e.span(),
905                    extras,
906                    items,
907                    ty,
908                })
909            })
910            .labelled("array")
911            .boxed();
912
913        let block_expression = block
914            .clone()
915            .map(Expression::Block)
916            .labelled("block expression")
917            .boxed();
918
919        if_inner.define(
920            just(Token::KeywordIf)
921                .ignore_then(expression_parser.clone())
922                .then(block.clone())
923                .then(
924                    just(Token::KeywordElse)
925                        .ignore_then(if_inner.clone())
926                        .map(Box::new)
927                        .or_not(),
928                )
929                .then(just(Token::KeywordElse).ignore_then(block.clone()).or_not())
930                .with_extras()
931                .map_with(
932                    |((((condition, body), next_if), else_body), extras), e| If {
933                        span: e.span(),
934                        extras,
935                        condition: Box::new(condition),
936                        body,
937                        next_if,
938                        else_body,
939                    },
940                )
941                .boxed(),
942        );
943        let if_expression = if_inner
944            .map(Expression::If)
945            .labelled("if expression")
946            .boxed();
947
948        let qualified_name_expr = identifier_parser
949            .clone()
950            .map_with(|ident, e| QualifiedName {
951                span: e.span(),
952                parts: vec![ident],
953                extras: ItemExtras::default(),
954            })
955            .foldl_with(
956                just(Token::SigilDoubleColon)
957                    .ignore_then(identifier_parser.clone())
958                    .repeated(),
959                |mut acc, part, _| {
960                    acc.span.end = part.span.end;
961                    acc.parts.push(part);
962                    acc
963                },
964            )
965            .with_extras()
966            .map(|(mut name, extras)| {
967                name.extras = extras;
968                name
969            })
970            .map(Expression::QualifiedName)
971            .boxed();
972
973        let call = call_inner
974            .clone()
975            .map(Expression::Call)
976            .labelled("method call");
977
978        let base = literal
979            .or(string_format)
980            .or(call)
981            .or(qualified_name_expr)
982            .or(marker)
983            .or(bracketed)
984            .or(tuple)
985            .or(array_range)
986            .or(array_list)
987            .or(block_expression)
988            .or(if_expression)
989            .boxed();
990
991        let access_attribute = just(Token::SigilHash)
992            .ignore_then(identifier_parser.clone())
993            .map(Element::Attribute)
994            .labelled("attribute access")
995            .boxed();
996
997        let access_tuple = just(Token::SigilDot)
998            .ignore_then(identifier_parser.clone())
999            .map(Element::Tuple)
1000            .labelled("tuple access")
1001            .boxed();
1002
1003        let access_method = just(Token::SigilDot)
1004            .ignore_then(call_inner)
1005            .map(Element::Method)
1006            .labelled("method call")
1007            .boxed();
1008
1009        let access_array = expression_parser
1010            .clone()
1011            .delimited_by(
1012                just(Token::SigilOpenSquareBracket),
1013                just(Token::SigilCloseSquareBracket),
1014            )
1015            .map(Box::new)
1016            .map(Element::ArrayElement)
1017            .labelled("array access")
1018            .boxed();
1019
1020        let access_item = access_attribute
1021            .or(access_method)
1022            .or(access_tuple)
1023            .or(access_array)
1024            .boxed();
1025
1026        let element_access = base
1027            .clone()
1028            .foldl_with(access_item.repeated(), |value, element, e| {
1029                Expression::ElementAccess(ElementAccess {
1030                    span: e.span(),
1031                    value: value.into(),
1032                    element,
1033                })
1034            })
1035            .labelled("element access")
1036            .boxed();
1037
1038        let unary_expression = unary_operator_parser
1039            .then(element_access.clone())
1040            .with_extras()
1041            .map_with(|((op, rhs), extras), e| UnaryOperation {
1042                span: e.span(),
1043                extras,
1044                operation: op,
1045                rhs: rhs.into(),
1046            })
1047            .map(simplify_unary_op)
1048            .boxed();
1049
1050        let binary_param = element_access.or(unary_expression.clone());
1051
1052        let near = binop(binary_param, &[Token::OperatorNear]);
1053        let xor = binop(near, &[Token::OperatorPowerXor, Token::OperatorXor]);
1054        let union_intersect = binop(xor, &[Token::OperatorUnion, Token::OperatorIntersect]);
1055        let mul_div = binop(
1056            union_intersect,
1057            &[Token::OperatorMultiply, Token::OperatorDivide],
1058        );
1059        let add_sub = binop(mul_div, &[Token::OperatorAdd, Token::OperatorSubtract]);
1060        let less_greater_eq = binop(
1061            add_sub,
1062            &[Token::OperatorLessEqual, Token::OperatorGreaterEqual],
1063        );
1064        let less_greater = binop(
1065            less_greater_eq,
1066            &[Token::OperatorLessThan, Token::OperatorGreaterThan],
1067        );
1068        let eq_neq = binop(
1069            less_greater,
1070            &[Token::OperatorEqual, Token::OperatorNotEqual],
1071        );
1072        let or_and = binop(eq_neq, &[Token::OperatorOr, Token::OperatorAnd]);
1073
1074        or_and.labelled("expression").boxed()
1075    });
1076
1077    statement_list_parser.map_with(move |statements, ex| SourceFile {
1078        span: ex.span(),
1079        statements,
1080    })
1081}