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        let span = self.close_brace_span(start, start);
538
539        self.leave_recursion();
540
541        Expression::Block {
542            ty: Type::uninferred(),
543            items,
544            span,
545        }
546    }
547
548    pub fn parse_function_params(&mut self) -> Vec<Binding> {
549        self.ensure(LeftParen);
550
551        let mut params = vec![];
552
553        while self.is_not(RightParen) {
554            params.push(self.parse_binding_with_type());
555            self.expect_comma_or(RightParen);
556        }
557
558        self.ensure(RightParen);
559
560        params
561    }
562
563    pub fn parse_lambda_params(&mut self) -> Vec<Binding> {
564        self.ensure(Pipe);
565
566        let mut params = vec![];
567
568        while self.is_not(Pipe) {
569            params.push(self.parse_binding());
570            self.expect_comma_or(Pipe);
571        }
572
573        self.ensure(Pipe);
574
575        params
576    }
577
578    pub fn parse_function(
579        &mut self,
580        doc: Option<std::string::String>,
581        attributes: Vec<Attribute>,
582    ) -> Expression {
583        let start = self.current_token();
584
585        self.ensure(Function);
586
587        let name_token = self.current_token();
588        let name_span = Span::new(self.file_id, name_token.byte_offset, name_token.byte_length);
589
590        let name = self.read_identifier_sequence();
591
592        let name_span = Span::new(name_span.file_id, name_span.byte_offset, name.len() as u32);
593
594        let generics = self.parse_generics();
595        let params = self.parse_function_params();
596        let return_annotation = self.parse_function_return_annotation();
597
598        let body = if self.is(LeftCurlyBrace) {
599            self.parse_block_expression()
600        } else {
601            Expression::NoOp
602        };
603
604        Expression::Function {
605            doc,
606            attributes,
607            name,
608            name_span,
609            generics,
610            params,
611            return_annotation,
612            return_type: Type::uninferred(),
613            visibility: Visibility::Private,
614            body: body.into(),
615            ty: Type::uninferred(),
616            span: self.span_from_tokens(start),
617        }
618    }
619
620    pub fn parse_field_access(&mut self, expression: Expression) -> Expression {
621        self.ensure(Dot);
622
623        let expression_start = expression.get_span().byte_offset;
624        let start = self.current_token();
625
626        if self.advance_if(Star) {
627            return Expression::Unary {
628                ty: Type::uninferred(),
629                operator: UnaryOperator::Deref,
630                expression: expression.into(),
631                span: self.span_from_tokens(start),
632            };
633        }
634
635        if self.is(Integer) {
636            let text = self.current_token().text;
637            let index: u32 = text.parse().unwrap_or_else(|_| {
638                self.track_error(
639                    format!("tuple index '{}' is too large", text),
640                    "Maximum index is `4294967295`.",
641                );
642                0
643            });
644
645            self.ensure(Integer);
646
647            return Expression::DotAccess {
648                ty: Type::uninferred(),
649                expression: expression.into(),
650                member: index.to_string().into(),
651                span: self.span_from_offset(expression_start),
652            };
653        }
654
655        let field = self.current_token().text;
656
657        self.ensure(Identifier);
658
659        Expression::DotAccess {
660            ty: Type::uninferred(),
661            expression: expression.into(),
662            member: field.into(),
663            span: self.span_from_offset(expression_start),
664        }
665    }
666
667    pub fn collect_delimited_expressions(
668        &mut self,
669        open: TokenKind,
670        close: TokenKind,
671    ) -> (Vec<Expression>, bool) {
672        self.ensure(open);
673
674        let mut expressions = vec![];
675        let mut has_trailing_comma = false;
676        loop {
677            if self.at_eof() || self.is(close) {
678                break;
679            }
680
681            if self.is(Function) && self.stream.peek_ahead(1).kind == LeftParen {
682                let start = self.current_token();
683                let span = Span::new(self.file_id, start.byte_offset, start.byte_length + 1);
684                let error = ParseError::new("Syntax error", span, "expected a lambda")
685                    .with_parse_code("fn_as_lambda")
686                    .with_help("Use a lambda instead: `|x| x * 2`");
687                self.errors.push(error);
688                self.resync_on_error();
689                expressions.push(Expression::Unit {
690                    ty: Type::uninferred(),
691                    span,
692                });
693                continue;
694            }
695
696            if self.at_item_boundary() {
697                let next = self.stream.peek_ahead(1).kind;
698                if next != close && next != Comma {
699                    break;
700                }
701            }
702            expressions.push(self.parse_expression());
703            has_trailing_comma = self.is(Comma);
704            self.expect_comma_or(close);
705        }
706
707        self.advance_if(close);
708
709        (expressions, has_trailing_comma)
710    }
711
712    fn make_expression_name(&mut self, expression: &Expression) -> EcoString {
713        let mut parts = Vec::new();
714        let mut current = expression;
715
716        loop {
717            match current {
718                Expression::Identifier { value, .. } => {
719                    parts.push(value.clone());
720                    break;
721                }
722                Expression::DotAccess {
723                    expression, member, ..
724                } => {
725                    parts.push(member.clone());
726                    current = expression;
727                }
728                _ => {
729                    self.track_error(
730                        "unexpected expression",
731                        "Expected an identifier or dotted path.",
732                    );
733                    return "_".into();
734                }
735            }
736        }
737
738        parts.reverse();
739        parts.join(".").into()
740    }
741
742    pub fn parse_let(&mut self) -> Expression {
743        let start = self.current_token();
744
745        self.ensure(Let);
746
747        let (mutable, mut_span) = if self.is(Mut) {
748            let mut_token = self.current_token();
749            let span = Span::new(self.file_id, mut_token.byte_offset, mut_token.byte_length);
750            self.next(); // consume `mut`
751            (true, Some(span))
752        } else {
753            (false, None)
754        };
755
756        let binding = self.parse_binding_allowing_or();
757
758        self.ensure(Equal);
759
760        let expression = self.parse_expression();
761
762        let (else_block, else_span) = if self.is(Else) {
763            let else_token = self.current_token();
764            let span = Span::new(self.file_id, else_token.byte_offset, else_token.byte_length);
765            self.next(); // consume `else`
766            (Some(Box::new(self.parse_block_expression())), Some(span))
767        } else {
768            (None, None)
769        };
770
771        Expression::Let {
772            binding: Box::new(binding),
773            value: expression.into(),
774            mutable,
775            mut_span,
776            else_block,
777            else_span,
778            typed_pattern: None,
779            ty: Type::uninferred(),
780            span: self.span_from_tokens(start),
781        }
782    }
783
784    pub fn parse_import(&mut self) -> Expression {
785        let start = self.current_token();
786
787        self.ensure(Import);
788
789        let alias = if self.current_token().kind == Identifier {
790            let alias_token = self.current_token();
791            let alias_text = alias_token.text;
792            let alias_span = Span::new(
793                self.file_id,
794                alias_token.byte_offset,
795                alias_token.byte_length,
796            );
797
798            if alias_text == "_" {
799                self.next();
800                Some(ImportAlias::Blank(alias_span))
801            } else if self.stream.peek_ahead(1).kind == String {
802                self.next();
803                Some(ImportAlias::Named(alias_text.into(), alias_span))
804            } else {
805                None
806            }
807        } else {
808            None
809        };
810
811        let name_token = self.current_token();
812
813        if name_token.kind != String {
814            let (label, help) = if name_token.kind == Identifier
815                && self.stream.peek_ahead(1).kind == Colon
816            {
817                let module_name = name_token.text;
818                (
819                    "expected double quotes".to_string(),
820                    format!(
821                        "Wrap the import path in double quotes: `import \"{0}:...\"`",
822                        module_name
823                    ),
824                )
825            } else if name_token.kind == Identifier {
826                let module_name = name_token.text;
827                (
828                    "expected double quotes".to_string(),
829                    format!(
830                        "Wrap the import path in double quotes: `import \"{}\"`",
831                        module_name
832                    ),
833                )
834            } else {
835                (
836                    "expected module path".to_string(),
837                    "Wrap the import path in double quotes, e.g. `import \"go:os\"`".to_string(),
838                )
839            };
840
841            self.track_error(label, help);
842            self.resync_on_error();
843            return Expression::Unit {
844                ty: Type::uninferred(),
845                span: self.span_from_tokens(start),
846            };
847        }
848
849        self.next();
850
851        let raw = name_token.text;
852        let name: EcoString = if raw.len() >= 2 && raw.starts_with('"') && raw.ends_with('"') {
853            raw[1..raw.len() - 1].into()
854        } else {
855            debug_assert!(
856                false,
857                "lexer produced String token without quotes: {:?}",
858                raw
859            );
860            raw.into()
861        };
862        let name_span = Span::new(self.file_id, name_token.byte_offset, name_token.byte_length);
863
864        Expression::ModuleImport {
865            name,
866            name_span,
867            alias,
868            span: self.span_from_tokens(start),
869        }
870    }
871
872    pub fn parse_assignment(&mut self) -> Expression {
873        let start = self.current_token();
874
875        let lhs = self.parse_expression();
876
877        let compound_operator = match self.current_token().kind {
878            PlusEqual => Some(BinaryOperator::Addition),
879            MinusEqual => Some(BinaryOperator::Subtraction),
880            StarEqual => Some(BinaryOperator::Multiplication),
881            SlashEqual => Some(BinaryOperator::Division),
882            PercentEqual => Some(BinaryOperator::Remainder),
883            _ => None,
884        };
885
886        if let Some(operator) = compound_operator {
887            if !self.is_valid_assignment_target(&lhs) {
888                self.track_error(
889                    "invalid assignment target",
890                    "Only variables, fields, and indices can be assigned to.",
891                );
892            }
893            self.next();
894            let rhs = self.parse_expression();
895            return Expression::Assignment {
896                target: lhs.clone().into(),
897                value: Expression::Binary {
898                    left: lhs.into(),
899                    operator,
900                    right: rhs.into(),
901                    ty: Type::uninferred(),
902                    span: self.span_from_tokens(start),
903                }
904                .into(),
905                compound_operator: Some(operator),
906                span: self.span_from_tokens(start),
907            };
908        }
909
910        if self.current_token().kind == Colon && self.stream.peek_ahead(1).kind == Equal {
911            let span = Span::new(self.file_id, self.current_token().byte_offset, 2);
912            self.track_error_at(
913                span,
914                "Go-style short declaration",
915                "Use `let x = ...` instead of `:=` for variable declarations",
916            );
917            self.next();
918            self.next();
919            let _ = self.parse_expression();
920            return lhs;
921        }
922
923        if !self.is(Equal) {
924            return lhs;
925        }
926
927        if !self.is_valid_assignment_target(&lhs) {
928            self.track_error(
929                "invalid assignment target",
930                "Only variables, fields, and indices can be assigned to.",
931            );
932        }
933
934        self.ensure(Equal);
935
936        Expression::Assignment {
937            target: lhs.into(),
938            value: self.parse_expression().into(),
939            compound_operator: None,
940            span: self.span_from_tokens(start),
941        }
942    }
943
944    fn is_valid_assignment_target(&self, expression: &Expression) -> bool {
945        use Expression::*;
946
947        matches!(
948            expression,
949            Identifier { .. }
950                | DotAccess { .. }
951                | IndexedAccess { .. }
952                | Unary {
953                    operator: UnaryOperator::Deref,
954                    ..
955                }
956        )
957    }
958
959    fn parse_format_string(&mut self) -> Expression {
960        let start = self.current_token();
961        self.ensure(FormatStringStart);
962
963        let mut parts = Vec::new();
964
965        loop {
966            if self.at_eof() || self.at_item_boundary() {
967                break;
968            }
969            match self.current_token().kind {
970                FormatStringText => {
971                    let text = self.current_token().text;
972                    self.next();
973                    parts.push(FormatStringPart::Text(text.to_string()));
974                }
975                FormatStringInterpolationStart => {
976                    self.ensure(FormatStringInterpolationStart);
977                    let expression = self.parse_expression();
978                    parts.push(FormatStringPart::Expression(Box::new(expression)));
979                    if self.is(Colon) {
980                        let start_offset = self.current_token().byte_offset;
981                        self.next();
982                        while !self.at_eof()
983                            && !self.is(FormatStringInterpolationEnd)
984                            && !self.is(FormatStringEnd)
985                            && !self.at_item_boundary()
986                        {
987                            self.next();
988                        }
989                        let span = self.span_from_offset(start_offset);
990                        let error = ParseError::new(
991                            "Format specifiers not supported",
992                            span,
993                            "not supported in format strings",
994                        )
995                        .with_parse_code("format_specifier")
996                        .with_help(
997                            "Use `fmt.Sprintf` for formatted output, e.g. `fmt.Sprintf(\"%02x\", n)`",
998                        );
999                        self.errors.push(error);
1000                    }
1001                    self.advance_if(FormatStringInterpolationEnd);
1002                }
1003                FormatStringEnd => {
1004                    self.ensure(FormatStringEnd);
1005                    break;
1006                }
1007                _ => break,
1008            }
1009        }
1010
1011        Expression::Literal {
1012            literal: Literal::FormatString(parts),
1013            ty: Type::uninferred(),
1014            span: self.span_from_tokens(start),
1015        }
1016    }
1017
1018    pub fn parse_task(&mut self) -> Expression {
1019        let start = self.current_token();
1020
1021        self.ensure(Task);
1022
1023        let expression = if self.is(LeftCurlyBrace) {
1024            self.parse_block_expression()
1025        } else {
1026            self.parse_expression()
1027        };
1028
1029        if !matches!(
1030            expression,
1031            Expression::Call { .. } | Expression::Block { .. }
1032        ) {
1033            let span = expression.get_span();
1034            let error = ParseError::new("Invalid `task`", span, "expected `()`")
1035                .with_parse_code("task_missing_parens")
1036                .with_help("Add parens to call the function");
1037
1038            self.errors.push(error);
1039        }
1040
1041        Expression::Task {
1042            expression: Box::new(expression),
1043            ty: Type::uninferred(),
1044            span: self.span_from_tokens(start),
1045        }
1046    }
1047
1048    pub fn parse_defer(&mut self) -> Expression {
1049        let start = self.current_token();
1050
1051        self.ensure(Defer);
1052
1053        let expression = if self.is(LeftCurlyBrace) {
1054            self.parse_block_expression()
1055        } else {
1056            self.parse_expression()
1057        };
1058
1059        if !matches!(
1060            expression,
1061            Expression::Call { .. } | Expression::Block { .. }
1062        ) {
1063            let span = expression.get_span();
1064            let error = ParseError::new("Invalid `defer`", span, "expected `()`")
1065                .with_parse_code("defer_missing_parens")
1066                .with_help("Add parens to call the function");
1067
1068            self.errors.push(error);
1069        }
1070
1071        Expression::Defer {
1072            expression: Box::new(expression),
1073            ty: Type::uninferred(),
1074            span: self.span_from_tokens(start),
1075        }
1076    }
1077
1078    pub fn parse_try_block(&mut self) -> Expression {
1079        let start = self.current_token();
1080        let try_keyword_span = Span::new(self.file_id, start.byte_offset, start.byte_length);
1081
1082        self.ensure(Try);
1083
1084        if !self.is(LeftCurlyBrace) {
1085            let span = self.span_from_tokens(start);
1086            let error = ParseError::new("Invalid `try`", span, "requires a block")
1087                .with_parse_code("syntax_error")
1088                .with_help("Use `try { expression }` instead of `try expression`");
1089            self.errors.push(error);
1090            let expression = self.parse_expression();
1091            return Expression::TryBlock {
1092                items: vec![expression],
1093                ty: Type::uninferred(),
1094                try_keyword_span,
1095                span: self.span_from_tokens(start),
1096            };
1097        }
1098
1099        let brace_token = self.current_token();
1100        self.ensure(LeftCurlyBrace);
1101
1102        if !self.enter_recursion() {
1103            let span = self.span_from_token(self.current_token());
1104            let mut brace_depth = 1u32;
1105            while brace_depth > 0 && !self.at_eof() {
1106                match self.current_token().kind {
1107                    LeftCurlyBrace => brace_depth += 1,
1108                    RightCurlyBrace => brace_depth -= 1,
1109                    _ => {}
1110                }
1111                if brace_depth > 0 {
1112                    self.next();
1113                }
1114            }
1115            self.advance_if(RightCurlyBrace);
1116            return Expression::TryBlock {
1117                items: vec![],
1118                ty: Type::uninferred(),
1119                try_keyword_span,
1120                span,
1121            };
1122        }
1123
1124        let mut items = vec![];
1125
1126        while self.is_not(RightCurlyBrace) && !self.too_many_errors() {
1127            let position = self.position();
1128            let item = self.parse_block_item();
1129
1130            self.advance_if(Semicolon);
1131
1132            items.push(item);
1133            if self.position() == position {
1134                self.next();
1135            }
1136        }
1137
1138        let span = self.close_brace_span(start, brace_token);
1139
1140        self.leave_recursion();
1141
1142        Expression::TryBlock {
1143            items,
1144            ty: Type::uninferred(),
1145            try_keyword_span,
1146            span,
1147        }
1148    }
1149
1150    pub fn parse_recover_block(&mut self) -> Expression {
1151        let start = self.current_token();
1152        let recover_keyword_span = Span::new(self.file_id, start.byte_offset, start.byte_length);
1153
1154        self.ensure(Recover);
1155
1156        if !self.is(LeftCurlyBrace) {
1157            let span = self.span_from_tokens(start);
1158            let error = ParseError::new("Invalid `recover`", span, "requires a block")
1159                .with_parse_code("syntax_error")
1160                .with_help("Use `recover { expression }` instead of `recover expression`");
1161            self.errors.push(error);
1162            let expression = self.parse_expression();
1163            return Expression::RecoverBlock {
1164                items: vec![expression],
1165                ty: Type::uninferred(),
1166                recover_keyword_span,
1167                span: self.span_from_tokens(start),
1168            };
1169        }
1170
1171        let brace_token = self.current_token();
1172        self.ensure(LeftCurlyBrace);
1173
1174        if !self.enter_recursion() {
1175            let span = self.span_from_token(self.current_token());
1176            let mut brace_depth = 1u32;
1177            while brace_depth > 0 && !self.at_eof() {
1178                match self.current_token().kind {
1179                    LeftCurlyBrace => brace_depth += 1,
1180                    RightCurlyBrace => brace_depth -= 1,
1181                    _ => {}
1182                }
1183                if brace_depth > 0 {
1184                    self.next();
1185                }
1186            }
1187            self.advance_if(RightCurlyBrace);
1188            return Expression::RecoverBlock {
1189                items: vec![],
1190                ty: Type::uninferred(),
1191                recover_keyword_span,
1192                span,
1193            };
1194        }
1195
1196        let mut items = vec![];
1197
1198        while self.is_not(RightCurlyBrace) && !self.too_many_errors() {
1199            let position = self.position();
1200            let item = self.parse_block_item();
1201
1202            self.advance_if(Semicolon);
1203
1204            items.push(item);
1205            if self.position() == position {
1206                self.next();
1207            }
1208        }
1209
1210        let span = self.close_brace_span(start, brace_token);
1211
1212        self.leave_recursion();
1213
1214        Expression::RecoverBlock {
1215            items,
1216            ty: Type::uninferred(),
1217            recover_keyword_span,
1218            span,
1219        }
1220    }
1221
1222    pub fn parse_select(&mut self) -> Expression {
1223        let start = self.current_token();
1224
1225        self.ensure(Select);
1226        self.ensure(LeftCurlyBrace);
1227
1228        let mut arms = Vec::new();
1229
1230        while self.is_not(RightCurlyBrace) {
1231            let arm = self.parse_select_arm();
1232            arms.push(arm);
1233
1234            if self.is(RightCurlyBrace) {
1235                break;
1236            }
1237
1238            self.ensure(Comma);
1239        }
1240
1241        self.ensure(RightCurlyBrace);
1242
1243        Expression::Select {
1244            arms,
1245            ty: Type::uninferred(),
1246            span: self.span_from_tokens(start),
1247        }
1248    }
1249
1250    fn parse_select_arm(&mut self) -> SelectArm {
1251        match self.current_token().kind {
1252            Let => {
1253                self.ensure(Let);
1254                let binding = self.parse_pattern();
1255                self.ensure(Equal);
1256                let receive_expression = Box::new(self.parse_expression());
1257                self.ensure(ArrowDouble);
1258                let body = Box::new(self.parse_expression());
1259                SelectArm {
1260                    pattern: SelectArmPattern::Receive {
1261                        binding: Box::new(binding),
1262                        typed_pattern: None,
1263                        receive_expression,
1264                        body,
1265                    },
1266                }
1267            }
1268            Match => {
1269                let match_expression = self.parse_match();
1270                if let Expression::Match { subject, arms, .. } = match_expression {
1271                    SelectArm {
1272                        pattern: SelectArmPattern::MatchReceive {
1273                            receive_expression: subject,
1274                            arms,
1275                        },
1276                    }
1277                } else {
1278                    self.ensure(ArrowDouble);
1279                    let body = Box::new(self.parse_expression());
1280                    SelectArm {
1281                        pattern: SelectArmPattern::Send {
1282                            send_expression: Box::new(match_expression),
1283                            body,
1284                        },
1285                    }
1286                }
1287            }
1288            Identifier if self.current_token().text == "_" => {
1289                self.next();
1290                self.ensure(ArrowDouble);
1291                let body = Box::new(self.parse_expression());
1292                SelectArm {
1293                    pattern: SelectArmPattern::WildCard { body },
1294                }
1295            }
1296            _ => {
1297                let send_expression = Box::new(self.parse_expression());
1298                self.ensure(ArrowDouble);
1299                let body = Box::new(self.parse_expression());
1300                SelectArm {
1301                    pattern: SelectArmPattern::Send {
1302                        send_expression,
1303                        body,
1304                    },
1305                }
1306            }
1307        }
1308    }
1309
1310    pub fn with_control_flow_header<F, R>(&mut self, f: F) -> R
1311    where
1312        F: FnOnce(&mut Self) -> R,
1313    {
1314        let old = self.in_control_flow_header;
1315        self.in_control_flow_header = true;
1316        let result = f(self);
1317        self.in_control_flow_header = old;
1318        result
1319    }
1320
1321    fn keyword_in_value_position(&self) -> bool {
1322        if !self.current_token().kind.is_keyword() {
1323            return false;
1324        }
1325
1326        match self.current_token().kind {
1327            Return | Break | Continue => false,
1328
1329            Match | If | Task | Defer | Try | Recover | Select | Loop | Function => matches!(
1330                self.stream.peek_ahead(1).kind,
1331                RightParen
1332                    | Comma
1333                    | Dot
1334                    | Semicolon
1335                    | RightCurlyBrace
1336                    | RightSquareBracket
1337                    | ArrowDouble
1338                    | QuestionMark
1339                    | EOF
1340                    | Plus
1341                    | Star
1342                    | Slash
1343                    | Percent
1344                    | EqualDouble
1345                    | NotEqual
1346                    | LeftAngleBracket
1347                    | RightAngleBracket
1348                    | LessThanOrEqual
1349                    | GreaterThanOrEqual
1350                    | AmpersandDouble
1351                    | Pipeline
1352                    | Equal
1353                    | PlusEqual
1354                    | MinusEqual
1355                    | StarEqual
1356                    | SlashEqual
1357                    | PercentEqual
1358                    | DotDot
1359                    | DotDotEqual
1360                    | As
1361            ),
1362
1363            _ => true,
1364        }
1365    }
1366
1367    fn recover_keyword_as_identifier(&mut self) -> Expression {
1368        let token = self.current_token();
1369        let keyword = token.text.to_string();
1370        let span = self.span_from_token(token);
1371        let error = ParseError::new("Reserved keyword", span, "reserved keyword")
1372            .with_parse_code("keyword_as_identifier")
1373            .with_help(format!("Rename `{}`", keyword));
1374        self.errors.push(error);
1375        self.next();
1376        Expression::Identifier {
1377            value: keyword.into(),
1378            ty: Type::uninferred(),
1379            span,
1380            binding_id: None,
1381            qualified: None,
1382        }
1383    }
1384}