Skip to main content

lisette_syntax/parse/
expressions.rs

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