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_unexpected_backtick(),
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 unquoted: &str = if raw.len() >= 2 && raw.starts_with('"') && raw.ends_with('"') {
948            &raw[1..raw.len() - 1]
949        } else {
950            debug_assert!(
951                false,
952                "lexer produced String token without quotes: {:?}",
953                raw
954            );
955            raw
956        };
957
958        if alias.is_none() && self.is(As) && self.stream.peek_ahead(1).kind == Identifier {
959            let as_token = self.current_token();
960            let alias_identifier = self.stream.peek_ahead(1);
961            self.next();
962            self.next();
963            self.error_import_alias_after_path(
964                self.span_from_tokens(as_token),
965                alias_identifier.text,
966                unquoted,
967            );
968        }
969
970        let name: EcoString = unquoted.into();
971        let name_span = Span::new(self.file_id, name_token.byte_offset, name_token.byte_length);
972
973        Expression::ModuleImport {
974            name,
975            name_span,
976            alias,
977            span: self.span_from_tokens(start),
978        }
979    }
980
981    pub fn parse_assignment(&mut self) -> Expression {
982        let start = self.current_token();
983
984        let lhs = self.parse_expression();
985
986        let compound_operator = match self.current_token().kind {
987            PlusEqual => Some(BinaryOperator::Addition),
988            MinusEqual => Some(BinaryOperator::Subtraction),
989            StarEqual => Some(BinaryOperator::Multiplication),
990            SlashEqual => Some(BinaryOperator::Division),
991            PercentEqual => Some(BinaryOperator::Remainder),
992            _ => None,
993        };
994
995        if let Some(operator) = compound_operator {
996            if !self.is_valid_assignment_target(&lhs) {
997                self.track_error(
998                    "invalid assignment target",
999                    "Only variables, fields, and indices can be assigned to.",
1000                );
1001                self.next();
1002                let _rhs = self.parse_expression();
1003                return lhs;
1004            }
1005            self.next();
1006            let rhs = self.parse_expression();
1007            return Expression::Assignment {
1008                target: lhs.clone().into(),
1009                value: Expression::Binary {
1010                    left: lhs.into(),
1011                    operator,
1012                    right: rhs.into(),
1013                    ty: Type::uninferred(),
1014                    span: self.span_from_tokens(start),
1015                }
1016                .into(),
1017                compound_operator: Some(operator),
1018                span: self.span_from_tokens(start),
1019            };
1020        }
1021
1022        if self.current_token().kind == Colon && self.stream.peek_ahead(1).kind == Equal {
1023            let span = Span::new(self.file_id, self.current_token().byte_offset, 2);
1024            self.track_error_at(
1025                span,
1026                "Go-style short declaration",
1027                "Use `let x = ...` instead of `:=` for variable declarations",
1028            );
1029            self.next();
1030            self.next();
1031            let _ = self.parse_expression();
1032            return lhs;
1033        }
1034
1035        if !self.is(Equal) {
1036            return lhs;
1037        }
1038
1039        if !self.is_valid_assignment_target(&lhs) {
1040            self.track_error(
1041                "invalid assignment target",
1042                "Only variables, fields, and indices can be assigned to.",
1043            );
1044        }
1045
1046        self.ensure(Equal);
1047
1048        Expression::Assignment {
1049            target: lhs.into(),
1050            value: self.parse_expression().into(),
1051            compound_operator: None,
1052            span: self.span_from_tokens(start),
1053        }
1054    }
1055
1056    fn is_valid_assignment_target(&self, expression: &Expression) -> bool {
1057        use Expression::*;
1058
1059        matches!(
1060            expression,
1061            Identifier { .. }
1062                | DotAccess { .. }
1063                | IndexedAccess { .. }
1064                | Unary {
1065                    operator: UnaryOperator::Deref,
1066                    ..
1067                }
1068        )
1069    }
1070
1071    fn parse_format_string(&mut self) -> Expression {
1072        let start = self.current_token();
1073        self.ensure(FormatStringStart);
1074
1075        let mut parts = Vec::new();
1076
1077        loop {
1078            if self.at_eof() || self.at_item_boundary() {
1079                break;
1080            }
1081            match self.current_token().kind {
1082                FormatStringText => {
1083                    let text = self.current_token().text;
1084                    self.next();
1085                    parts.push(FormatStringPart::Text(cook_string_contents(text)));
1086                }
1087                FormatStringInterpolationStart => {
1088                    self.ensure(FormatStringInterpolationStart);
1089                    let expression = self.parse_expression();
1090                    parts.push(FormatStringPart::Expression(Box::new(expression)));
1091                    if self.is(Colon) {
1092                        let start_offset = self.current_token().byte_offset;
1093                        self.next();
1094                        while !self.at_eof()
1095                            && !self.is(FormatStringInterpolationEnd)
1096                            && !self.is(FormatStringEnd)
1097                            && !self.at_item_boundary()
1098                        {
1099                            self.next();
1100                        }
1101                        let span = self.span_from_offset(start_offset);
1102                        let error = ParseError::new(
1103                            "Format specifiers not supported",
1104                            span,
1105                            "not supported in format strings",
1106                        )
1107                        .with_parse_code("format_specifier")
1108                        .with_help(
1109                            "Use `fmt.Sprintf` for formatted output, e.g. `fmt.Sprintf(\"%02x\", n)`",
1110                        );
1111                        self.errors.push(error);
1112                    }
1113                    self.advance_if(FormatStringInterpolationEnd);
1114                }
1115                FormatStringEnd => {
1116                    self.ensure(FormatStringEnd);
1117                    break;
1118                }
1119                _ => break,
1120            }
1121        }
1122
1123        Expression::Literal {
1124            literal: Literal::FormatString(parts),
1125            ty: Type::uninferred(),
1126            span: self.span_from_tokens(start),
1127        }
1128    }
1129
1130    pub fn parse_task(&mut self) -> Expression {
1131        let start = self.current_token();
1132
1133        self.ensure(Task);
1134
1135        let expression = if self.is(LeftCurlyBrace) {
1136            self.parse_block_expression()
1137        } else {
1138            self.parse_expression()
1139        };
1140
1141        if !matches!(
1142            expression,
1143            Expression::Call { .. } | Expression::Block { .. }
1144        ) {
1145            let span = expression.get_span();
1146            let error = ParseError::new("Invalid `task`", span, "expected `()`")
1147                .with_parse_code("task_missing_parens")
1148                .with_help("Add parens to call the function");
1149
1150            self.errors.push(error);
1151        }
1152
1153        Expression::Task {
1154            expression: Box::new(expression),
1155            ty: Type::uninferred(),
1156            span: self.span_from_tokens(start),
1157        }
1158    }
1159
1160    pub fn parse_defer(&mut self) -> Expression {
1161        let start = self.current_token();
1162
1163        self.ensure(Defer);
1164
1165        let expression = if self.is(LeftCurlyBrace) {
1166            self.parse_block_expression()
1167        } else {
1168            self.parse_expression()
1169        };
1170
1171        if !matches!(
1172            expression,
1173            Expression::Call { .. } | Expression::Block { .. }
1174        ) {
1175            let span = expression.get_span();
1176            let error = ParseError::new("Invalid `defer`", span, "expected `()`")
1177                .with_parse_code("defer_missing_parens")
1178                .with_help("Add parens to call the function");
1179
1180            self.errors.push(error);
1181        }
1182
1183        Expression::Defer {
1184            expression: Box::new(expression),
1185            ty: Type::uninferred(),
1186            span: self.span_from_tokens(start),
1187        }
1188    }
1189
1190    pub fn parse_try_block(&mut self) -> Expression {
1191        let start = self.current_token();
1192        let try_keyword_span = Span::new(self.file_id, start.byte_offset, start.byte_length);
1193
1194        self.ensure(Try);
1195
1196        if !self.is(LeftCurlyBrace) {
1197            let span = self.span_from_tokens(start);
1198            let error = ParseError::new("Invalid `try`", span, "requires a block")
1199                .with_parse_code("syntax_error")
1200                .with_help("Use `try { expression }` instead of `try expression`");
1201            self.errors.push(error);
1202            let expression = self.parse_expression();
1203            return Expression::TryBlock {
1204                items: vec![expression],
1205                ty: Type::uninferred(),
1206                try_keyword_span,
1207                span: self.span_from_tokens(start),
1208            };
1209        }
1210
1211        let brace_token = self.current_token();
1212        self.ensure(LeftCurlyBrace);
1213
1214        if !self.enter_recursion() {
1215            let span = self.span_from_token(self.current_token());
1216            let mut brace_depth = 1u32;
1217            while brace_depth > 0 && !self.at_eof() {
1218                match self.current_token().kind {
1219                    LeftCurlyBrace => brace_depth += 1,
1220                    RightCurlyBrace => brace_depth -= 1,
1221                    _ => {}
1222                }
1223                if brace_depth > 0 {
1224                    self.next();
1225                }
1226            }
1227            self.advance_if(RightCurlyBrace);
1228            return Expression::TryBlock {
1229                items: vec![],
1230                ty: Type::uninferred(),
1231                try_keyword_span,
1232                span,
1233            };
1234        }
1235
1236        let mut items = vec![];
1237
1238        while self.is_not(RightCurlyBrace) && !self.too_many_errors() {
1239            let position = self.position();
1240            let item = self.parse_block_item();
1241
1242            self.advance_if(Semicolon);
1243
1244            items.push(item);
1245            if self.position() == position {
1246                self.next();
1247            }
1248        }
1249
1250        let span = self.close_brace_span(start, brace_token);
1251
1252        self.leave_recursion();
1253
1254        Expression::TryBlock {
1255            items,
1256            ty: Type::uninferred(),
1257            try_keyword_span,
1258            span,
1259        }
1260    }
1261
1262    pub fn parse_recover_block(&mut self) -> Expression {
1263        let start = self.current_token();
1264        let recover_keyword_span = Span::new(self.file_id, start.byte_offset, start.byte_length);
1265
1266        self.ensure(Recover);
1267
1268        if !self.is(LeftCurlyBrace) {
1269            let span = self.span_from_tokens(start);
1270            let error = ParseError::new("Invalid `recover`", span, "requires a block")
1271                .with_parse_code("syntax_error")
1272                .with_help("Use `recover { expression }` instead of `recover expression`");
1273            self.errors.push(error);
1274            let expression = self.parse_expression();
1275            return Expression::RecoverBlock {
1276                items: vec![expression],
1277                ty: Type::uninferred(),
1278                recover_keyword_span,
1279                span: self.span_from_tokens(start),
1280            };
1281        }
1282
1283        let brace_token = self.current_token();
1284        self.ensure(LeftCurlyBrace);
1285
1286        if !self.enter_recursion() {
1287            let span = self.span_from_token(self.current_token());
1288            let mut brace_depth = 1u32;
1289            while brace_depth > 0 && !self.at_eof() {
1290                match self.current_token().kind {
1291                    LeftCurlyBrace => brace_depth += 1,
1292                    RightCurlyBrace => brace_depth -= 1,
1293                    _ => {}
1294                }
1295                if brace_depth > 0 {
1296                    self.next();
1297                }
1298            }
1299            self.advance_if(RightCurlyBrace);
1300            return Expression::RecoverBlock {
1301                items: vec![],
1302                ty: Type::uninferred(),
1303                recover_keyword_span,
1304                span,
1305            };
1306        }
1307
1308        let mut items = vec![];
1309
1310        while self.is_not(RightCurlyBrace) && !self.too_many_errors() {
1311            let position = self.position();
1312            let item = self.parse_block_item();
1313
1314            self.advance_if(Semicolon);
1315
1316            items.push(item);
1317            if self.position() == position {
1318                self.next();
1319            }
1320        }
1321
1322        let span = self.close_brace_span(start, brace_token);
1323
1324        self.leave_recursion();
1325
1326        Expression::RecoverBlock {
1327            items,
1328            ty: Type::uninferred(),
1329            recover_keyword_span,
1330            span,
1331        }
1332    }
1333
1334    pub fn parse_select(&mut self) -> Expression {
1335        let start = self.current_token();
1336
1337        self.ensure(Select);
1338        self.ensure(LeftCurlyBrace);
1339
1340        let mut arms = Vec::new();
1341
1342        while self.is_not(RightCurlyBrace) {
1343            let arm = self.parse_select_arm();
1344            arms.push(arm);
1345
1346            if self.is(RightCurlyBrace) {
1347                break;
1348            }
1349
1350            self.ensure(Comma);
1351        }
1352
1353        self.ensure(RightCurlyBrace);
1354
1355        Expression::Select {
1356            arms,
1357            ty: Type::uninferred(),
1358            span: self.span_from_tokens(start),
1359        }
1360    }
1361
1362    fn parse_select_arm(&mut self) -> SelectArm {
1363        match self.current_token().kind {
1364            Let => {
1365                self.ensure(Let);
1366                let binding = self.parse_pattern();
1367                self.ensure(Equal);
1368                let receive_expression = Box::new(self.parse_expression());
1369                self.ensure(ArrowDouble);
1370                let body = Box::new(self.parse_expression());
1371                SelectArm {
1372                    pattern: SelectArmPattern::Receive {
1373                        binding: Box::new(binding),
1374                        typed_pattern: None,
1375                        receive_expression,
1376                        body,
1377                    },
1378                }
1379            }
1380            Match => {
1381                let match_expression = self.parse_match();
1382                if let Expression::Match { subject, arms, .. } = match_expression {
1383                    SelectArm {
1384                        pattern: SelectArmPattern::MatchReceive {
1385                            receive_expression: subject,
1386                            arms,
1387                        },
1388                    }
1389                } else {
1390                    self.ensure(ArrowDouble);
1391                    let body = Box::new(self.parse_expression());
1392                    SelectArm {
1393                        pattern: SelectArmPattern::Send {
1394                            send_expression: Box::new(match_expression),
1395                            body,
1396                        },
1397                    }
1398                }
1399            }
1400            Identifier if self.current_token().text == "_" => {
1401                self.next();
1402                self.ensure(ArrowDouble);
1403                let body = Box::new(self.parse_expression());
1404                SelectArm {
1405                    pattern: SelectArmPattern::WildCard { body },
1406                }
1407            }
1408            _ => {
1409                let send_expression = Box::new(self.parse_expression());
1410                self.ensure(ArrowDouble);
1411                let body = Box::new(self.parse_expression());
1412                SelectArm {
1413                    pattern: SelectArmPattern::Send {
1414                        send_expression,
1415                        body,
1416                    },
1417                }
1418            }
1419        }
1420    }
1421
1422    pub fn with_control_flow_header<F, R>(&mut self, f: F) -> R
1423    where
1424        F: FnOnce(&mut Self) -> R,
1425    {
1426        let old = self.in_control_flow_header;
1427        self.in_control_flow_header = true;
1428        let result = f(self);
1429        self.in_control_flow_header = old;
1430        result
1431    }
1432
1433    fn keyword_in_value_position(&self) -> bool {
1434        if !self.current_token().kind.is_keyword() {
1435            return false;
1436        }
1437
1438        match self.current_token().kind {
1439            Return | Break | Continue => false,
1440
1441            Match | If | Task | Defer | Try | Recover | Select | Loop | Function => matches!(
1442                self.stream.peek_ahead(1).kind,
1443                RightParen
1444                    | Comma
1445                    | Dot
1446                    | Semicolon
1447                    | RightCurlyBrace
1448                    | RightSquareBracket
1449                    | ArrowDouble
1450                    | QuestionMark
1451                    | EOF
1452                    | Plus
1453                    | Star
1454                    | Slash
1455                    | Percent
1456                    | EqualDouble
1457                    | NotEqual
1458                    | LeftAngleBracket
1459                    | RightAngleBracket
1460                    | LessThanOrEqual
1461                    | GreaterThanOrEqual
1462                    | AmpersandDouble
1463                    | Pipeline
1464                    | Equal
1465                    | PlusEqual
1466                    | MinusEqual
1467                    | StarEqual
1468                    | SlashEqual
1469                    | PercentEqual
1470                    | DotDot
1471                    | DotDotEqual
1472                    | As
1473            ),
1474
1475            _ => true,
1476        }
1477    }
1478
1479    fn recover_keyword_as_identifier(&mut self) -> Expression {
1480        let token = self.current_token();
1481        let keyword = token.text.to_string();
1482        let span = self.span_from_token(token);
1483        let error = ParseError::new("Reserved keyword", span, "reserved keyword")
1484            .with_parse_code("keyword_as_identifier")
1485            .with_help(format!("Rename `{}`", keyword));
1486        self.errors.push(error);
1487        self.next();
1488        Expression::Identifier {
1489            value: keyword.into(),
1490            ty: Type::uninferred(),
1491            span,
1492            binding_id: None,
1493            qualified: None,
1494        }
1495    }
1496
1497    fn recover_unexpected_backtick(&mut self) -> Expression {
1498        let token = self.current_token();
1499        let token_span = self.span_from_token(token);
1500        let opening_span = Span::new(self.file_id, token.byte_offset, 1);
1501        let error = ParseError::new("Unexpected backtick", opening_span, "not allowed here")
1502            .with_parse_code("unexpected_backtick")
1503            .with_help(
1504                "Use a regular string `\"...\"` or a raw string `r\"...\"` for single- or multi-line strings. Backticks in Lisette are reserved for struct-tag attributes.",
1505            );
1506        self.errors.push(error);
1507        self.next();
1508        Expression::Literal {
1509            literal: Literal::String {
1510                value: std::string::String::new(),
1511                raw: true,
1512            },
1513            ty: Type::uninferred(),
1514            span: token_span,
1515        }
1516    }
1517}