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