Skip to main content

lisette_syntax/parse/
expressions.rs

1use ecow::EcoString;
2
3use super::{MAX_TUPLE_ARITY, ParseError, Parser};
4use crate::ast::{
5    Annotation, Attribute, BinaryOperator, Binding, Expression, FormatStringPart, ImportAlias,
6    Literal, SelectArm, SelectArmPattern, Span, StructFieldAssignment, UnaryOperator, Visibility,
7};
8use crate::lex::TokenKind::{self, *};
9use crate::types::Type;
10
11impl<'source> Parser<'source> {
12    pub fn parse_expression(&mut self) -> Expression {
13        if !self.enter_recursion() {
14            let span = self.span_from_token(self.current_token());
15            self.resync_on_error();
16            return Expression::Unit {
17                ty: Type::uninferred(),
18                span,
19            };
20        }
21        let result = self.pratt_parse(0);
22        self.leave_recursion();
23        result
24    }
25
26    pub fn parse_atomic_expression(&mut self) -> Expression {
27        if self.keyword_in_value_position() {
28            return self.recover_keyword_as_identifier();
29        }
30
31        match self.current_token().kind {
32            Integer | Imaginary | Boolean | Char | String | Float => self.parse_literal(),
33            FormatStringStart => self.parse_format_string(),
34            LeftParen => self.parse_parenthesized_expression(),
35            LeftCurlyBrace => self.parse_block_expression(),
36            LeftSquareBracket => self.parse_slice_literal(),
37            Identifier => self.parse_identifier(),
38            Function => self.parse_function(None, vec![]),
39            Match => self.parse_match(),
40            If => self.parse_if(),
41            Pipe | PipeDouble => self.parse_lambda(),
42            Task => self.parse_task(),
43            Defer => self.parse_defer(),
44            Try => self.parse_try_block(),
45            Recover => self.parse_recover_block(),
46            Select => self.parse_select(),
47            Loop => self.parse_loop(),
48            Return => self.parse_return(),
49            Break => self.parse_break(),
50            Continue => self.parse_continue(),
51            DotDot | DotDotEqual => self.parse_range(None, self.current_token()),
52
53            LeftAngleBracket
54                if self.stream.peek_ahead(1).kind == Minus
55                    && self.current_token().byte_offset + self.current_token().byte_length
56                        == self.stream.peek_ahead(1).byte_offset =>
57            {
58                let start = self.current_token();
59                let span = Span::new(self.file_id, start.byte_offset, start.byte_length + 1);
60                self.track_error_at(
61                    span,
62                    "invalid syntax for channel receive",
63                    "Use `select { let v = ch.receive() => ... }` to receive from a channel",
64                );
65                self.resync_on_error();
66                Expression::Unit {
67                    ty: Type::uninferred(),
68                    span,
69                }
70            }
71
72            _ => self.unexpected_token("expr"),
73        }
74    }
75
76    pub fn parse_range(
77        &mut self,
78        start: Option<Box<Expression>>,
79        span_start: crate::lex::Token<'source>,
80    ) -> Expression {
81        if matches!(start.as_deref(), Some(Expression::Range { .. })) {
82            self.track_error("not allowed", "Chained range operators are not supported");
83        }
84
85        let inclusive = self.is(DotDotEqual);
86
87        self.next();
88
89        let has_end = !matches!(
90            self.current_token().kind,
91            RightCurlyBrace
92                | RightSquareBracket
93                | RightParen
94                | LeftCurlyBrace
95                | Semicolon
96                | Comma
97                | EOF
98        );
99
100        if inclusive && !has_end {
101            self.track_error(
102                "expected end value",
103                "Inclusive ranges require an end value.",
104            );
105        }
106
107        let end = if has_end {
108            Some(Box::new(self.parse_range_end()))
109        } else {
110            None
111        };
112
113        Expression::Range {
114            start,
115            end,
116            inclusive,
117            ty: Type::uninferred(),
118            span: self.span_from_tokens(span_start),
119        }
120    }
121
122    fn parse_literal(&mut self) -> Expression {
123        let start = self.current_token();
124
125        let literal = match self.current_token().kind {
126            Integer => {
127                let text = self.current_token().text;
128                let literal = self.parse_integer_text(text);
129                self.next();
130                literal
131            }
132            Float => {
133                let raw = self.current_token().text;
134                let cleaned = raw.replace('_', "");
135                let f: f64 = cleaned.parse().unwrap_or_else(|_| {
136                    self.track_error(
137                        format!("float literal '{}' is out of range", raw),
138                        "Value must be a valid 64-bit floating point number.",
139                    );
140                    0.0
141                });
142                let text = if raw.contains('e') || raw.contains('E') || raw.contains('_') {
143                    Some(raw.to_string())
144                } else {
145                    None
146                };
147                self.next();
148                Literal::Float { value: f, text }
149            }
150            Imaginary => {
151                let text = self.current_token().text;
152                let coef: f64 = text[..text.len() - 1]
153                    .replace('_', "")
154                    .parse()
155                    .unwrap_or_else(|_| {
156                        self.track_error(
157                            format!("imaginary literal '{}' is out of range", text),
158                            "Value must be a valid 64-bit floating point number.",
159                        );
160                        0.0
161                    });
162                self.next();
163                Literal::Imaginary(coef)
164            }
165            Boolean => {
166                let b = self.current_token().text == "true";
167                self.next();
168                Literal::Boolean(b)
169            }
170            String => {
171                let s = self.current_token().text;
172                self.next();
173                let s_stripped = if s.len() >= 2 && s.starts_with('"') && s.ends_with('"') {
174                    &s[1..s.len() - 1]
175                } else {
176                    debug_assert!(false, "lexer produced String token without quotes: {:?}", s);
177                    s
178                };
179                Literal::String(s_stripped.to_string())
180            }
181            Char => {
182                let c = self.current_token().text;
183                self.next();
184                let c_stripped = if c.len() >= 2 && c.starts_with('\'') && c.ends_with('\'') {
185                    &c[1..c.len() - 1]
186                } else {
187                    debug_assert!(false, "lexer produced Char token without quotes: {:?}", c);
188                    c
189                };
190                Literal::Char(c_stripped.to_string())
191            }
192            _ => return self.unexpected_token("literal"),
193        };
194
195        Expression::Literal {
196            literal,
197            ty: Type::uninferred(),
198            span: self.span_from_tokens(start),
199        }
200    }
201
202    fn parse_slice_literal(&mut self) -> Expression {
203        let start = self.current_token();
204        let (expressions, _) =
205            self.collect_delimited_expressions(LeftSquareBracket, RightSquareBracket);
206
207        Expression::Literal {
208            literal: Literal::Slice(expressions),
209            ty: Type::uninferred(),
210            span: self.span_from_tokens(start),
211        }
212    }
213
214    fn parse_identifier(&mut self) -> Expression {
215        let start = self.current_token();
216        let text = self.current_token().text;
217
218        if text == "go" {
219            let next = self.stream.peek_ahead(1).kind;
220            if next == LeftCurlyBrace || next == Identifier {
221                self.track_error(
222                    "invalid syntax",
223                    "Use `task { ... }` or `task my_function()` to spawn a concurrent task.",
224                );
225            }
226        }
227
228        self.ensure(Identifier);
229
230        Expression::Identifier {
231            value: text.into(),
232            ty: Type::uninferred(),
233            span: self.span_from_tokens(start),
234            binding_id: None,
235            qualified: None,
236        }
237    }
238
239    pub fn parse_struct_call(&mut self, expression: Expression) -> Expression {
240        let name = self.make_expression_name(&expression);
241        let name_span = expression.get_span();
242        let start_offset = name_span.byte_offset; // Start from the name, not the brace
243
244        self.ensure(LeftCurlyBrace);
245
246        let mut field_assignments = vec![];
247        let mut spread = None;
248        let mut seen_fields: Vec<(EcoString, Span)> = vec![];
249
250        while self.is_not(RightCurlyBrace) {
251            if self.is(DotDot) {
252                if spread.is_some() {
253                    self.track_error(
254                        "spread must be last",
255                        "Move the `..spread` to the end of the struct.",
256                    );
257                    break;
258                }
259
260                self.ensure(DotDot);
261
262                if self.is(RightCurlyBrace) || self.is(Comma) {
263                    self.track_error(
264                        "not allowed",
265                        "Use `..struct` to spread the fields of one struct into another",
266                    );
267                } else {
268                    spread = Some(self.parse_expression());
269                }
270
271                self.expect_comma_or(RightCurlyBrace);
272                continue;
273            }
274
275            if spread.is_some() {
276                self.track_error(
277                    "field after spread",
278                    "The `..spread` must be the last element in a struct expression. Move explicit fields before the spread.",
279                );
280                break;
281            }
282
283            let field_name_token = self.current_token();
284            let field_name_span = self.span_from_token(field_name_token);
285            let field_name = self.read_identifier();
286
287            if let Some((_, first_span)) = seen_fields.iter().find(|(n, _)| n == &field_name) {
288                self.error_duplicate_struct_field(&field_name, *first_span, field_name_span);
289            } else {
290                seen_fields.push((field_name.clone(), field_name_span));
291            }
292
293            let field_value = if self.advance_if(Colon) {
294                self.parse_expression()
295            } else {
296                Expression::Identifier {
297                    value: field_name.clone(),
298                    ty: Type::uninferred(),
299                    span: self.span_from_tokens(field_name_token),
300                    binding_id: None,
301                    qualified: None,
302                }
303            };
304
305            field_assignments.push(StructFieldAssignment {
306                name: field_name,
307                name_span: field_name_span,
308                value: Box::new(field_value),
309            });
310
311            self.expect_comma_or(RightCurlyBrace);
312        }
313
314        self.ensure(RightCurlyBrace);
315
316        Expression::StructCall {
317            ty: Type::uninferred(),
318            name,
319            field_assignments,
320            spread: spread.into(),
321            span: self.span_from_offset(start_offset),
322        }
323    }
324
325    pub fn parse_index_expression(&mut self, expression: Expression) -> Expression {
326        let start = self.current_token();
327
328        self.ensure(LeftSquareBracket);
329
330        let index = self.parse_expression();
331
332        self.ensure(RightSquareBracket);
333
334        Expression::IndexedAccess {
335            ty: Type::uninferred(),
336            expression: expression.into(),
337            index: index.into(),
338            span: self.span_from_tokens(start),
339        }
340    }
341
342    pub fn parse_function_call(
343        &mut self,
344        expression: Expression,
345        type_args: Vec<Annotation>,
346    ) -> Expression {
347        let start_offset = expression.get_span().byte_offset;
348
349        Expression::Call {
350            ty: Type::uninferred(),
351            expression: expression.into(),
352            args: self.collect_delimited_expressions(LeftParen, RightParen).0,
353            type_args,
354            span: self.span_from_offset(start_offset),
355        }
356    }
357
358    pub fn parse_type_args(&mut self) -> Vec<Annotation> {
359        self.ensure(LeftAngleBracket);
360
361        let mut type_args = vec![];
362
363        loop {
364            if self.at_eof() {
365                break;
366            }
367
368            type_args.push(self.parse_annotation());
369
370            if self.is(RightAngleBracket) {
371                break;
372            }
373
374            self.ensure(Comma);
375        }
376
377        self.ensure(RightAngleBracket);
378
379        type_args
380    }
381
382    pub fn parse_binary_operator(&mut self) -> BinaryOperator {
383        let operator = match self.current_token().kind {
384            Plus => BinaryOperator::Addition,
385            Minus => BinaryOperator::Subtraction,
386            Star => BinaryOperator::Multiplication,
387            Slash => BinaryOperator::Division,
388            LeftAngleBracket => BinaryOperator::LessThan,
389            LessThanOrEqual => BinaryOperator::LessThanOrEqual,
390            RightAngleBracket => BinaryOperator::GreaterThan,
391            GreaterThanOrEqual => BinaryOperator::GreaterThanOrEqual,
392            Percent => BinaryOperator::Remainder,
393            EqualDouble => BinaryOperator::Equal,
394            NotEqual => BinaryOperator::NotEqual,
395            AmpersandDouble => BinaryOperator::And,
396            PipeDouble => BinaryOperator::Or,
397            Pipeline => BinaryOperator::Pipeline,
398
399            _ => {
400                self.track_error(format!(
401                    "expected binary operator, found {}",
402                    self.current_token().kind
403                ), "Binary operators: `+`, `-`, `*`, `/`, `%`, `==`, `!=`, `<`, `>`, `<=`, `>=`, `&&`, `||`.");
404                BinaryOperator::Addition // meaningless fallback
405            }
406        };
407
408        self.next();
409
410        operator
411    }
412
413    fn parse_parenthesized_expression(&mut self) -> Expression {
414        let start = self.current_token();
415
416        let (expressions, has_trailing_comma) =
417            self.collect_delimited_expressions(LeftParen, RightParen);
418        let span = self.span_from_tokens(start);
419
420        match expressions.len() {
421            0 => Expression::Unit {
422                ty: Type::uninferred(),
423                span,
424            },
425            1 => {
426                if has_trailing_comma {
427                    self.error_tuple_arity(1, span);
428                }
429                let expression = expressions.into_iter().next().expect("len is 1");
430                Expression::Paren {
431                    ty: Type::uninferred(),
432                    expression: expression.into(),
433                    span,
434                }
435            }
436            n => {
437                if n > MAX_TUPLE_ARITY {
438                    self.error_tuple_arity(n, span);
439                }
440                Expression::Tuple {
441                    ty: Type::uninferred(),
442                    elements: expressions,
443                    span,
444                }
445            }
446        }
447    }
448
449    pub fn parse_try(&mut self, expression: Expression) -> Expression {
450        let start_offset = expression.get_span().byte_offset;
451
452        self.ensure(QuestionMark);
453
454        Expression::Propagate {
455            ty: Type::uninferred(),
456            expression: expression.into(),
457            span: self.span_from_offset(start_offset),
458        }
459    }
460
461    fn parse_lambda(&mut self) -> Expression {
462        let start = self.current_token();
463
464        let params = if self.is(Pipe) {
465            self.parse_lambda_params()
466        } else {
467            self.next();
468            vec![]
469        };
470
471        let has_return_type = self.is(Arrow);
472        let return_annotation = if has_return_type {
473            self.next();
474            self.parse_annotation()
475        } else {
476            Annotation::Unknown
477        };
478
479        if has_return_type && self.current_token().kind != LeftCurlyBrace {
480            self.track_error(
481                "not allowed",
482                "A lambda with a return type requires a block body",
483            );
484        }
485
486        let body = self.parse_expression();
487
488        Expression::Lambda {
489            params,
490            return_annotation,
491            body: body.into(),
492            ty: Type::uninferred(),
493            span: self.span_from_tokens(start),
494        }
495    }
496
497    pub fn parse_block_expression(&mut self) -> Expression {
498        let start = self.current_token();
499
500        self.ensure(LeftCurlyBrace);
501
502        if !self.enter_recursion() {
503            let span = self.span_from_token(self.current_token());
504            let mut brace_depth = 1u32;
505            while brace_depth > 0 && !self.at_eof() {
506                match self.current_token().kind {
507                    LeftCurlyBrace => brace_depth += 1,
508                    RightCurlyBrace => brace_depth -= 1,
509                    _ => {}
510                }
511                if brace_depth > 0 {
512                    self.next();
513                }
514            }
515            self.advance_if(RightCurlyBrace);
516            return Expression::Block {
517                ty: Type::uninferred(),
518                items: vec![],
519                span,
520            };
521        }
522
523        let mut items = vec![];
524
525        while self.is_not(RightCurlyBrace) && !self.too_many_errors() {
526            let position = self.position();
527            let item = self.parse_block_item();
528
529            self.advance_if(Semicolon);
530
531            items.push(item);
532            if self.position() == position {
533                self.next();
534            }
535        }
536
537        if self.is(RightCurlyBrace) {
538            self.next();
539        } else {
540            self.error_unclosed_block(&start);
541        }
542
543        self.leave_recursion();
544
545        Expression::Block {
546            ty: Type::uninferred(),
547            items,
548            span: self.span_from_tokens(start),
549        }
550    }
551
552    pub fn parse_function_params(&mut self) -> Vec<Binding> {
553        self.ensure(LeftParen);
554
555        let mut params = vec![];
556
557        while self.is_not(RightParen) {
558            params.push(self.parse_binding_with_type());
559            self.expect_comma_or(RightParen);
560        }
561
562        self.ensure(RightParen);
563
564        params
565    }
566
567    pub fn parse_lambda_params(&mut self) -> Vec<Binding> {
568        self.ensure(Pipe);
569
570        let mut params = vec![];
571
572        while self.is_not(Pipe) {
573            params.push(self.parse_binding());
574            self.expect_comma_or(Pipe);
575        }
576
577        self.ensure(Pipe);
578
579        params
580    }
581
582    pub fn parse_function(
583        &mut self,
584        doc: Option<std::string::String>,
585        attributes: Vec<Attribute>,
586    ) -> Expression {
587        let start = self.current_token();
588
589        self.ensure(Function);
590
591        let name_token = self.current_token();
592        let name_span = Span::new(self.file_id, name_token.byte_offset, name_token.byte_length);
593
594        let name = self.read_identifier_sequence();
595
596        let name_span = Span::new(name_span.file_id, name_span.byte_offset, name.len() as u32);
597
598        let generics = self.parse_generics();
599        let params = self.parse_function_params();
600        let return_annotation = self.parse_function_return_annotation();
601
602        let body = if self.is(LeftCurlyBrace) {
603            self.parse_block_expression()
604        } else {
605            Expression::NoOp
606        };
607
608        Expression::Function {
609            doc,
610            attributes,
611            name,
612            name_span,
613            generics,
614            params,
615            return_annotation,
616            return_type: Type::uninferred(),
617            visibility: Visibility::Private,
618            body: body.into(),
619            ty: Type::uninferred(),
620            span: self.span_from_tokens(start),
621        }
622    }
623
624    pub fn parse_field_access(&mut self, expression: Expression) -> Expression {
625        self.ensure(Dot);
626
627        let expression_start = expression.get_span().byte_offset;
628        let start = self.current_token();
629
630        if self.advance_if(Star) {
631            return Expression::Unary {
632                ty: Type::uninferred(),
633                operator: UnaryOperator::Deref,
634                expression: expression.into(),
635                span: self.span_from_tokens(start),
636            };
637        }
638
639        if self.is(Integer) {
640            let text = self.current_token().text;
641            let index: u32 = text.parse().unwrap_or_else(|_| {
642                self.track_error(
643                    format!("tuple index '{}' is too large", text),
644                    "Maximum index is `4294967295`.",
645                );
646                0
647            });
648
649            self.ensure(Integer);
650
651            return Expression::DotAccess {
652                ty: Type::uninferred(),
653                expression: expression.into(),
654                member: index.to_string().into(),
655                span: self.span_from_offset(expression_start),
656            };
657        }
658
659        let field = self.current_token().text;
660
661        self.ensure(Identifier);
662
663        Expression::DotAccess {
664            ty: Type::uninferred(),
665            expression: expression.into(),
666            member: field.into(),
667            span: self.span_from_offset(expression_start),
668        }
669    }
670
671    pub fn collect_delimited_expressions(
672        &mut self,
673        open: TokenKind,
674        close: TokenKind,
675    ) -> (Vec<Expression>, bool) {
676        self.ensure(open);
677
678        let mut expressions = vec![];
679        let mut has_trailing_comma = false;
680        loop {
681            if self.at_eof() || self.is(close) {
682                break;
683            }
684
685            if self.is(Function) && self.stream.peek_ahead(1).kind == LeftParen {
686                let start = self.current_token();
687                let span = Span::new(self.file_id, start.byte_offset, start.byte_length + 1);
688                let error = ParseError::new("Syntax error", span, "expected a lambda")
689                    .with_parse_code("fn_as_lambda")
690                    .with_help("Use a lambda instead: `|x| x * 2`");
691                self.errors.push(error);
692                self.resync_on_error();
693                expressions.push(Expression::Unit {
694                    ty: Type::uninferred(),
695                    span,
696                });
697                continue;
698            }
699
700            if self.at_item_boundary() {
701                let next = self.stream.peek_ahead(1).kind;
702                if next != close && next != Comma {
703                    break;
704                }
705            }
706            expressions.push(self.parse_expression());
707            has_trailing_comma = self.is(Comma);
708            self.expect_comma_or(close);
709        }
710
711        self.advance_if(close);
712
713        (expressions, has_trailing_comma)
714    }
715
716    fn make_expression_name(&mut self, expression: &Expression) -> EcoString {
717        let mut parts = Vec::new();
718        let mut current = expression;
719
720        loop {
721            match current {
722                Expression::Identifier { value, .. } => {
723                    parts.push(value.clone());
724                    break;
725                }
726                Expression::DotAccess {
727                    expression, member, ..
728                } => {
729                    parts.push(member.clone());
730                    current = expression;
731                }
732                _ => {
733                    self.track_error(
734                        "unexpected expression",
735                        "Expected an identifier or dotted path.",
736                    );
737                    return "_".into();
738                }
739            }
740        }
741
742        parts.reverse();
743        parts.join(".").into()
744    }
745
746    pub fn parse_let(&mut self) -> Expression {
747        let start = self.current_token();
748
749        self.ensure(Let);
750
751        let (mutable, mut_span) = if self.is(Mut) {
752            let mut_token = self.current_token();
753            let span = Span::new(self.file_id, mut_token.byte_offset, mut_token.byte_length);
754            self.next(); // consume `mut`
755            (true, Some(span))
756        } else {
757            (false, None)
758        };
759
760        let binding = self.parse_binding_allowing_or();
761
762        self.ensure(Equal);
763
764        let expression = self.parse_expression();
765
766        let (else_block, else_span) = if self.is(Else) {
767            let else_token = self.current_token();
768            let span = Span::new(self.file_id, else_token.byte_offset, else_token.byte_length);
769            self.next(); // consume `else`
770            (Some(Box::new(self.parse_block_expression())), Some(span))
771        } else {
772            (None, None)
773        };
774
775        Expression::Let {
776            binding: Box::new(binding),
777            value: expression.into(),
778            mutable,
779            mut_span,
780            else_block,
781            else_span,
782            typed_pattern: None,
783            ty: Type::uninferred(),
784            span: self.span_from_tokens(start),
785        }
786    }
787
788    pub fn parse_import(&mut self) -> Expression {
789        let start = self.current_token();
790
791        self.ensure(Import);
792
793        let alias = if self.current_token().kind == Identifier {
794            let alias_token = self.current_token();
795            let alias_text = alias_token.text;
796            let alias_span = Span::new(
797                self.file_id,
798                alias_token.byte_offset,
799                alias_token.byte_length,
800            );
801
802            if alias_text == "_" {
803                self.next();
804                Some(ImportAlias::Blank(alias_span))
805            } else if self.stream.peek_ahead(1).kind == String {
806                self.next();
807                Some(ImportAlias::Named(alias_text.into(), alias_span))
808            } else {
809                None
810            }
811        } else {
812            None
813        };
814
815        let name_token = self.current_token();
816
817        if name_token.kind != String {
818            let (label, help) = if name_token.kind == Identifier
819                && self.stream.peek_ahead(1).kind == Colon
820            {
821                let module_name = name_token.text;
822                (
823                    "expected double quotes".to_string(),
824                    format!(
825                        "Wrap the import path in double quotes: `import \"{0}:...\"`",
826                        module_name
827                    ),
828                )
829            } else if name_token.kind == Identifier {
830                let module_name = name_token.text;
831                (
832                    "expected double quotes".to_string(),
833                    format!(
834                        "Wrap the import path in double quotes: `import \"{}\"`",
835                        module_name
836                    ),
837                )
838            } else {
839                (
840                    "expected module path".to_string(),
841                    "Wrap the import path in double quotes, e.g. `import \"go:os\"`".to_string(),
842                )
843            };
844
845            self.track_error(label, help);
846            self.resync_on_error();
847            return Expression::Unit {
848                ty: Type::uninferred(),
849                span: self.span_from_tokens(start),
850            };
851        }
852
853        self.next();
854
855        let raw = name_token.text;
856        let name: EcoString = if raw.len() >= 2 && raw.starts_with('"') && raw.ends_with('"') {
857            raw[1..raw.len() - 1].into()
858        } else {
859            debug_assert!(
860                false,
861                "lexer produced String token without quotes: {:?}",
862                raw
863            );
864            raw.into()
865        };
866        let name_span = Span::new(self.file_id, name_token.byte_offset, name_token.byte_length);
867
868        Expression::ModuleImport {
869            name,
870            name_span,
871            alias,
872            span: self.span_from_tokens(start),
873        }
874    }
875
876    pub fn parse_assignment(&mut self) -> Expression {
877        let start = self.current_token();
878
879        let lhs = self.parse_expression();
880
881        let compound_operator = match self.current_token().kind {
882            PlusEqual => Some(BinaryOperator::Addition),
883            MinusEqual => Some(BinaryOperator::Subtraction),
884            StarEqual => Some(BinaryOperator::Multiplication),
885            SlashEqual => Some(BinaryOperator::Division),
886            PercentEqual => Some(BinaryOperator::Remainder),
887            _ => None,
888        };
889
890        if let Some(operator) = compound_operator {
891            if !self.is_valid_assignment_target(&lhs) {
892                self.track_error(
893                    "invalid assignment target",
894                    "Only variables, fields, and indices can be assigned to.",
895                );
896            }
897            self.next();
898            let rhs = self.parse_expression();
899            return Expression::Assignment {
900                target: lhs.clone().into(),
901                value: Expression::Binary {
902                    left: lhs.into(),
903                    operator,
904                    right: rhs.into(),
905                    ty: Type::uninferred(),
906                    span: self.span_from_tokens(start),
907                }
908                .into(),
909                compound_operator: Some(operator),
910                span: self.span_from_tokens(start),
911            };
912        }
913
914        if self.current_token().kind == Colon && self.stream.peek_ahead(1).kind == Equal {
915            let span = Span::new(self.file_id, self.current_token().byte_offset, 2);
916            self.track_error_at(
917                span,
918                "Go-style short declaration",
919                "Use `let x = ...` instead of `:=` for variable declarations",
920            );
921            self.next();
922            self.next();
923            let _ = self.parse_expression();
924            return lhs;
925        }
926
927        if !self.is(Equal) {
928            return lhs;
929        }
930
931        if !self.is_valid_assignment_target(&lhs) {
932            self.track_error(
933                "invalid assignment target",
934                "Only variables, fields, and indices can be assigned to.",
935            );
936        }
937
938        self.ensure(Equal);
939
940        Expression::Assignment {
941            target: lhs.into(),
942            value: self.parse_expression().into(),
943            compound_operator: None,
944            span: self.span_from_tokens(start),
945        }
946    }
947
948    fn is_valid_assignment_target(&self, expression: &Expression) -> bool {
949        use Expression::*;
950
951        matches!(
952            expression,
953            Identifier { .. }
954                | DotAccess { .. }
955                | IndexedAccess { .. }
956                | Unary {
957                    operator: UnaryOperator::Deref,
958                    ..
959                }
960        )
961    }
962
963    fn parse_format_string(&mut self) -> Expression {
964        let start = self.current_token();
965        self.ensure(FormatStringStart);
966
967        let mut parts = Vec::new();
968
969        loop {
970            if self.at_eof() || self.at_item_boundary() {
971                break;
972            }
973            match self.current_token().kind {
974                FormatStringText => {
975                    let text = self.current_token().text;
976                    self.next();
977                    parts.push(FormatStringPart::Text(text.to_string()));
978                }
979                FormatStringInterpolationStart => {
980                    self.ensure(FormatStringInterpolationStart);
981                    let expression = self.parse_expression();
982                    parts.push(FormatStringPart::Expression(Box::new(expression)));
983                    if self.is(Colon) {
984                        let start_offset = self.current_token().byte_offset;
985                        self.next();
986                        while !self.at_eof()
987                            && !self.is(FormatStringInterpolationEnd)
988                            && !self.is(FormatStringEnd)
989                            && !self.at_item_boundary()
990                        {
991                            self.next();
992                        }
993                        let span = self.span_from_offset(start_offset);
994                        let error = ParseError::new(
995                            "Format specifiers not supported",
996                            span,
997                            "not supported in format strings",
998                        )
999                        .with_parse_code("format_specifier")
1000                        .with_help(
1001                            "Use `fmt.Sprintf` for formatted output, e.g. `fmt.Sprintf(\"%02x\", n)`",
1002                        );
1003                        self.errors.push(error);
1004                    }
1005                    self.advance_if(FormatStringInterpolationEnd);
1006                }
1007                FormatStringEnd => {
1008                    self.ensure(FormatStringEnd);
1009                    break;
1010                }
1011                _ => break,
1012            }
1013        }
1014
1015        Expression::Literal {
1016            literal: Literal::FormatString(parts),
1017            ty: Type::uninferred(),
1018            span: self.span_from_tokens(start),
1019        }
1020    }
1021
1022    pub fn parse_task(&mut self) -> Expression {
1023        let start = self.current_token();
1024
1025        self.ensure(Task);
1026
1027        let expression = if self.is(LeftCurlyBrace) {
1028            self.parse_block_expression()
1029        } else {
1030            self.parse_expression()
1031        };
1032
1033        if !matches!(
1034            expression,
1035            Expression::Call { .. } | Expression::Block { .. }
1036        ) {
1037            let span = expression.get_span();
1038            let error = ParseError::new("Invalid `task`", span, "expected `()`")
1039                .with_parse_code("task_missing_parens")
1040                .with_help("Add parens to call the function");
1041
1042            self.errors.push(error);
1043        }
1044
1045        Expression::Task {
1046            expression: Box::new(expression),
1047            ty: Type::uninferred(),
1048            span: self.span_from_tokens(start),
1049        }
1050    }
1051
1052    pub fn parse_defer(&mut self) -> Expression {
1053        let start = self.current_token();
1054
1055        self.ensure(Defer);
1056
1057        let expression = if self.is(LeftCurlyBrace) {
1058            self.parse_block_expression()
1059        } else {
1060            self.parse_expression()
1061        };
1062
1063        if !matches!(
1064            expression,
1065            Expression::Call { .. } | Expression::Block { .. }
1066        ) {
1067            let span = expression.get_span();
1068            let error = ParseError::new("Invalid `defer`", span, "expected `()`")
1069                .with_parse_code("defer_missing_parens")
1070                .with_help("Add parens to call the function");
1071
1072            self.errors.push(error);
1073        }
1074
1075        Expression::Defer {
1076            expression: Box::new(expression),
1077            ty: Type::uninferred(),
1078            span: self.span_from_tokens(start),
1079        }
1080    }
1081
1082    pub fn parse_try_block(&mut self) -> Expression {
1083        let start = self.current_token();
1084        let try_keyword_span = Span::new(self.file_id, start.byte_offset, start.byte_length);
1085
1086        self.ensure(Try);
1087
1088        if !self.is(LeftCurlyBrace) {
1089            let span = self.span_from_tokens(start);
1090            let error = ParseError::new("Invalid `try`", span, "requires a block")
1091                .with_parse_code("syntax_error")
1092                .with_help("Use `try { expression }` instead of `try expression`");
1093            self.errors.push(error);
1094            let expression = self.parse_expression();
1095            return Expression::TryBlock {
1096                items: vec![expression],
1097                ty: Type::uninferred(),
1098                try_keyword_span,
1099                span: self.span_from_tokens(start),
1100            };
1101        }
1102
1103        let brace_token = self.current_token();
1104        self.ensure(LeftCurlyBrace);
1105
1106        if !self.enter_recursion() {
1107            let span = self.span_from_token(self.current_token());
1108            let mut brace_depth = 1u32;
1109            while brace_depth > 0 && !self.at_eof() {
1110                match self.current_token().kind {
1111                    LeftCurlyBrace => brace_depth += 1,
1112                    RightCurlyBrace => brace_depth -= 1,
1113                    _ => {}
1114                }
1115                if brace_depth > 0 {
1116                    self.next();
1117                }
1118            }
1119            self.advance_if(RightCurlyBrace);
1120            return Expression::TryBlock {
1121                items: vec![],
1122                ty: Type::uninferred(),
1123                try_keyword_span,
1124                span,
1125            };
1126        }
1127
1128        let mut items = vec![];
1129
1130        while self.is_not(RightCurlyBrace) && !self.too_many_errors() {
1131            let position = self.position();
1132            let item = self.parse_block_item();
1133
1134            self.advance_if(Semicolon);
1135
1136            items.push(item);
1137            if self.position() == position {
1138                self.next();
1139            }
1140        }
1141
1142        if self.is(RightCurlyBrace) {
1143            self.next();
1144        } else {
1145            self.error_unclosed_block(&brace_token);
1146        }
1147
1148        self.leave_recursion();
1149
1150        Expression::TryBlock {
1151            items,
1152            ty: Type::uninferred(),
1153            try_keyword_span,
1154            span: self.span_from_tokens(start),
1155        }
1156    }
1157
1158    pub fn parse_recover_block(&mut self) -> Expression {
1159        let start = self.current_token();
1160        let recover_keyword_span = Span::new(self.file_id, start.byte_offset, start.byte_length);
1161
1162        self.ensure(Recover);
1163
1164        if !self.is(LeftCurlyBrace) {
1165            let span = self.span_from_tokens(start);
1166            let error = ParseError::new("Invalid `recover`", span, "requires a block")
1167                .with_parse_code("syntax_error")
1168                .with_help("Use `recover { expression }` instead of `recover expression`");
1169            self.errors.push(error);
1170            let expression = self.parse_expression();
1171            return Expression::RecoverBlock {
1172                items: vec![expression],
1173                ty: Type::uninferred(),
1174                recover_keyword_span,
1175                span: self.span_from_tokens(start),
1176            };
1177        }
1178
1179        let brace_token = self.current_token();
1180        self.ensure(LeftCurlyBrace);
1181
1182        if !self.enter_recursion() {
1183            let span = self.span_from_token(self.current_token());
1184            let mut brace_depth = 1u32;
1185            while brace_depth > 0 && !self.at_eof() {
1186                match self.current_token().kind {
1187                    LeftCurlyBrace => brace_depth += 1,
1188                    RightCurlyBrace => brace_depth -= 1,
1189                    _ => {}
1190                }
1191                if brace_depth > 0 {
1192                    self.next();
1193                }
1194            }
1195            self.advance_if(RightCurlyBrace);
1196            return Expression::RecoverBlock {
1197                items: vec![],
1198                ty: Type::uninferred(),
1199                recover_keyword_span,
1200                span,
1201            };
1202        }
1203
1204        let mut items = vec![];
1205
1206        while self.is_not(RightCurlyBrace) && !self.too_many_errors() {
1207            let position = self.position();
1208            let item = self.parse_block_item();
1209
1210            self.advance_if(Semicolon);
1211
1212            items.push(item);
1213            if self.position() == position {
1214                self.next();
1215            }
1216        }
1217
1218        if self.is(RightCurlyBrace) {
1219            self.next();
1220        } else {
1221            self.error_unclosed_block(&brace_token);
1222        }
1223
1224        self.leave_recursion();
1225
1226        Expression::RecoverBlock {
1227            items,
1228            ty: Type::uninferred(),
1229            recover_keyword_span,
1230            span: self.span_from_tokens(start),
1231        }
1232    }
1233
1234    pub fn parse_select(&mut self) -> Expression {
1235        let start = self.current_token();
1236
1237        self.ensure(Select);
1238        self.ensure(LeftCurlyBrace);
1239
1240        let mut arms = Vec::new();
1241
1242        while self.is_not(RightCurlyBrace) {
1243            let arm = self.parse_select_arm();
1244            arms.push(arm);
1245
1246            if self.is(RightCurlyBrace) {
1247                break;
1248            }
1249
1250            self.ensure(Comma);
1251        }
1252
1253        self.ensure(RightCurlyBrace);
1254
1255        Expression::Select {
1256            arms,
1257            ty: Type::uninferred(),
1258            span: self.span_from_tokens(start),
1259        }
1260    }
1261
1262    fn parse_select_arm(&mut self) -> SelectArm {
1263        match self.current_token().kind {
1264            Let => {
1265                self.ensure(Let);
1266                let binding = self.parse_pattern();
1267                self.ensure(Equal);
1268                let receive_expression = Box::new(self.parse_expression());
1269                self.ensure(ArrowDouble);
1270                let body = Box::new(self.parse_expression());
1271                SelectArm {
1272                    pattern: SelectArmPattern::Receive {
1273                        binding: Box::new(binding),
1274                        typed_pattern: None,
1275                        receive_expression,
1276                        body,
1277                    },
1278                }
1279            }
1280            Match => {
1281                let match_expression = self.parse_match();
1282                if let Expression::Match { subject, arms, .. } = match_expression {
1283                    SelectArm {
1284                        pattern: SelectArmPattern::MatchReceive {
1285                            receive_expression: subject,
1286                            arms,
1287                        },
1288                    }
1289                } else {
1290                    self.ensure(ArrowDouble);
1291                    let body = Box::new(self.parse_expression());
1292                    SelectArm {
1293                        pattern: SelectArmPattern::Send {
1294                            send_expression: Box::new(match_expression),
1295                            body,
1296                        },
1297                    }
1298                }
1299            }
1300            Identifier if self.current_token().text == "_" => {
1301                self.next();
1302                self.ensure(ArrowDouble);
1303                let body = Box::new(self.parse_expression());
1304                SelectArm {
1305                    pattern: SelectArmPattern::WildCard { body },
1306                }
1307            }
1308            _ => {
1309                let send_expression = Box::new(self.parse_expression());
1310                self.ensure(ArrowDouble);
1311                let body = Box::new(self.parse_expression());
1312                SelectArm {
1313                    pattern: SelectArmPattern::Send {
1314                        send_expression,
1315                        body,
1316                    },
1317                }
1318            }
1319        }
1320    }
1321
1322    pub fn with_control_flow_header<F, R>(&mut self, f: F) -> R
1323    where
1324        F: FnOnce(&mut Self) -> R,
1325    {
1326        let old = self.in_control_flow_header;
1327        self.in_control_flow_header = true;
1328        let result = f(self);
1329        self.in_control_flow_header = old;
1330        result
1331    }
1332
1333    fn keyword_in_value_position(&self) -> bool {
1334        if !self.current_token().kind.is_keyword() {
1335            return false;
1336        }
1337
1338        match self.current_token().kind {
1339            Return | Break | Continue => false,
1340
1341            Match | If | Task | Defer | Try | Recover | Select | Loop | Function => matches!(
1342                self.stream.peek_ahead(1).kind,
1343                RightParen
1344                    | Comma
1345                    | Dot
1346                    | Semicolon
1347                    | RightCurlyBrace
1348                    | RightSquareBracket
1349                    | ArrowDouble
1350                    | QuestionMark
1351                    | EOF
1352                    | Plus
1353                    | Star
1354                    | Slash
1355                    | Percent
1356                    | EqualDouble
1357                    | NotEqual
1358                    | LeftAngleBracket
1359                    | RightAngleBracket
1360                    | LessThanOrEqual
1361                    | GreaterThanOrEqual
1362                    | AmpersandDouble
1363                    | Pipeline
1364                    | Equal
1365                    | PlusEqual
1366                    | MinusEqual
1367                    | StarEqual
1368                    | SlashEqual
1369                    | PercentEqual
1370                    | DotDot
1371                    | DotDotEqual
1372                    | As
1373            ),
1374
1375            _ => true,
1376        }
1377    }
1378
1379    fn recover_keyword_as_identifier(&mut self) -> Expression {
1380        let token = self.current_token();
1381        let keyword = token.text.to_string();
1382        let span = self.span_from_token(token);
1383        let error = ParseError::new("Reserved keyword", span, "reserved keyword")
1384            .with_parse_code("keyword_as_identifier")
1385            .with_help(format!("Rename `{}`", keyword));
1386        self.errors.push(error);
1387        self.next();
1388        Expression::Identifier {
1389            value: keyword.into(),
1390            ty: Type::uninferred(),
1391            span,
1392            binding_id: None,
1393            qualified: None,
1394        }
1395    }
1396}