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 => BinaryOperator::Pipeline,
550
551            _ => {
552                self.track_error(format!(
553                    "expected binary operator, found {}",
554                    self.current_token().kind
555                ), "Binary operators: `+`, `-`, `*`, `/`, `%`, `&`, `|`, `^`, `&^`, `<<`, `>>`, `==`, `!=`, `<`, `>`, `<=`, `>=`, `&&`, `||`.");
556                BinaryOperator::Addition // meaningless fallback
557            }
558        };
559
560        self.next();
561
562        operator
563    }
564
565    fn parse_parenthesized_expression(&mut self) -> Expression {
566        let start = self.current_token();
567
568        let (expressions, has_trailing_comma) =
569            self.collect_delimited_expressions(LeftParen, RightParen);
570        let span = self.span_from_tokens(start);
571
572        match expressions.len() {
573            0 => Expression::Unit {
574                ty: Type::uninferred(),
575                span,
576            },
577            1 => {
578                if has_trailing_comma {
579                    self.error_tuple_arity(1, span);
580                }
581                let expression = expressions.into_iter().next().expect("len is 1");
582                Expression::Paren {
583                    ty: Type::uninferred(),
584                    expression: expression.into(),
585                    span,
586                }
587            }
588            n => {
589                if n > MAX_TUPLE_ARITY {
590                    self.error_tuple_arity(n, span);
591                }
592                Expression::Tuple {
593                    ty: Type::uninferred(),
594                    elements: expressions,
595                    span,
596                }
597            }
598        }
599    }
600
601    pub fn parse_try(&mut self, expression: Expression) -> Expression {
602        let start_offset = expression.get_span().byte_offset;
603
604        self.ensure(QuestionMark);
605
606        Expression::Propagate {
607            ty: Type::uninferred(),
608            expression: expression.into(),
609            span: self.span_from_offset(start_offset),
610        }
611    }
612
613    fn parse_lambda(&mut self) -> Expression {
614        let start = self.current_token();
615
616        let params = if self.is(Pipe) {
617            self.parse_lambda_params()
618        } else {
619            self.next();
620            vec![]
621        };
622
623        let has_return_type = self.is(Arrow);
624        let return_annotation = if has_return_type {
625            self.next();
626            self.parse_annotation()
627        } else {
628            Annotation::Unknown
629        };
630
631        if has_return_type && self.current_token().kind != LeftCurlyBrace {
632            self.track_error(
633                "not allowed",
634                "A lambda with a return type requires a block body",
635            );
636        }
637
638        let body = self.parse_expression();
639
640        Expression::Lambda {
641            params,
642            return_annotation,
643            body: body.into(),
644            ty: Type::uninferred(),
645            span: self.span_from_tokens(start),
646        }
647    }
648
649    pub fn parse_block_expression(&mut self) -> Expression {
650        let start = self.current_token();
651
652        self.ensure(LeftCurlyBrace);
653
654        if self.looks_like_map_literal() {
655            let key_span = self.span_from_token(self.current_token());
656            self.consume_to_matching_close_brace();
657            self.error_map_literal_not_supported(key_span);
658            return Expression::Block {
659                ty: Type::uninferred(),
660                items: vec![],
661                span: self.span_from_tokens(start),
662            };
663        }
664
665        if !self.enter_recursion() {
666            let span = self.span_from_token(self.current_token());
667            let mut brace_depth = 1u32;
668            while brace_depth > 0 && !self.at_eof() {
669                match self.current_token().kind {
670                    LeftCurlyBrace => brace_depth += 1,
671                    RightCurlyBrace => brace_depth -= 1,
672                    _ => {}
673                }
674                if brace_depth > 0 {
675                    self.next();
676                }
677            }
678            self.advance_if(RightCurlyBrace);
679            return Expression::Block {
680                ty: Type::uninferred(),
681                items: vec![],
682                span,
683            };
684        }
685
686        let mut items = vec![];
687
688        while self.is_not(RightCurlyBrace) && !self.too_many_errors() {
689            let position = self.position();
690            let item = self.parse_block_item();
691
692            self.advance_if(Semicolon);
693
694            items.push(item);
695            if self.position() == position {
696                self.next();
697            }
698        }
699
700        let span = self.close_brace_span(start, start);
701
702        self.leave_recursion();
703
704        Expression::Block {
705            ty: Type::uninferred(),
706            items,
707            span,
708        }
709    }
710
711    pub fn parse_function_params(&mut self) -> Vec<Binding> {
712        self.ensure(LeftParen);
713
714        let mut params = vec![];
715
716        while self.is_not(RightParen) {
717            params.push(self.parse_binding_with_type());
718            self.expect_comma_or(RightParen);
719        }
720
721        self.ensure(RightParen);
722
723        params
724    }
725
726    pub fn parse_lambda_params(&mut self) -> Vec<Binding> {
727        self.ensure(Pipe);
728
729        let mut params = vec![];
730
731        while self.is_not(Pipe) {
732            params.push(self.parse_binding());
733            self.expect_comma_or(Pipe);
734        }
735
736        self.ensure(Pipe);
737
738        params
739    }
740
741    pub fn parse_function(
742        &mut self,
743        doc: Option<std::string::String>,
744        attributes: Vec<Attribute>,
745    ) -> Expression {
746        let start = self.current_token();
747
748        self.ensure(Function);
749
750        let name_token = self.current_token();
751        let name_span = Span::new(self.file_id, name_token.byte_offset, name_token.byte_length);
752
753        let name = self.read_identifier_sequence();
754
755        let name_span = Span::new(name_span.file_id, name_span.byte_offset, name.len() as u32);
756
757        let generics = self.parse_generics();
758        let params = self.parse_function_params();
759        let return_annotation = self.parse_function_return_annotation();
760
761        let body = if self.is(LeftCurlyBrace) {
762            self.parse_block_expression()
763        } else {
764            Expression::NoOp
765        };
766
767        Expression::Function {
768            doc,
769            attributes,
770            name,
771            name_span,
772            generics,
773            params,
774            return_annotation,
775            return_type: Type::uninferred(),
776            visibility: Visibility::Private,
777            body: body.into(),
778            ty: Type::uninferred(),
779            span: self.span_from_tokens(start),
780        }
781    }
782
783    pub fn parse_field_access(&mut self, expression: Expression) -> Expression {
784        self.ensure(Dot);
785
786        let expression_start = expression.get_span().byte_offset;
787        let start = self.current_token();
788
789        if self.advance_if(Star) {
790            return Expression::Unary {
791                ty: Type::uninferred(),
792                operator: UnaryOperator::Deref,
793                expression: expression.into(),
794                span: self.span_from_tokens(start),
795            };
796        }
797
798        if self.is(Integer) {
799            let text = self.current_token().text;
800            let index: u32 = text.parse().unwrap_or_else(|_| {
801                self.track_error(
802                    format!("tuple index '{}' is too large", text),
803                    "Maximum index is `4294967295`.",
804                );
805                0
806            });
807
808            self.ensure(Integer);
809
810            return Expression::DotAccess {
811                ty: Type::uninferred(),
812                expression: expression.into(),
813                member: index.to_string().into(),
814                span: self.span_from_offset(expression_start),
815                dot_access_kind: None,
816                receiver_coercion: None,
817            };
818        }
819
820        let field = self.current_token().text;
821
822        self.ensure(Identifier);
823
824        Expression::DotAccess {
825            ty: Type::uninferred(),
826            expression: expression.into(),
827            member: field.into(),
828            span: self.span_from_offset(expression_start),
829            dot_access_kind: None,
830            receiver_coercion: None,
831        }
832    }
833
834    pub fn collect_delimited_expressions(
835        &mut self,
836        open: TokenKind,
837        close: TokenKind,
838    ) -> (Vec<Expression>, bool) {
839        self.ensure(open);
840
841        let mut expressions = vec![];
842        let mut has_trailing_comma = false;
843        loop {
844            if self.at_eof() || self.is(close) {
845                break;
846            }
847
848            if self.is(Function) && self.stream.peek_ahead(1).kind == LeftParen {
849                let span = self.track_fn_as_lambda_error();
850                self.resync_on_error();
851                expressions.push(Expression::Unit {
852                    ty: Type::uninferred(),
853                    span,
854                });
855                continue;
856            }
857
858            if self.at_item_boundary() {
859                let next = self.stream.peek_ahead(1).kind;
860                if next != close && next != Comma {
861                    break;
862                }
863            }
864            expressions.push(self.parse_expression());
865            has_trailing_comma = self.is(Comma);
866            self.expect_comma_or(close);
867        }
868
869        self.advance_if(close);
870
871        (expressions, has_trailing_comma)
872    }
873
874    fn make_expression_name(&mut self, expression: &Expression) -> EcoString {
875        let mut parts = Vec::new();
876        let mut current = expression;
877
878        loop {
879            match current {
880                Expression::Identifier { value, .. } => {
881                    parts.push(value.clone());
882                    break;
883                }
884                Expression::DotAccess {
885                    expression, member, ..
886                } => {
887                    parts.push(member.clone());
888                    current = expression;
889                }
890                _ => {
891                    self.track_error(
892                        "unexpected expression",
893                        "Expected an identifier or dotted path.",
894                    );
895                    return "_".into();
896                }
897            }
898        }
899
900        parts.reverse();
901        parts.join(".").into()
902    }
903
904    pub fn parse_let(&mut self) -> Expression {
905        let start = self.current_token();
906
907        self.ensure(Let);
908
909        let (mutable, mut_span) = if self.is(Mut) {
910            let mut_token = self.current_token();
911            let span = Span::new(self.file_id, mut_token.byte_offset, mut_token.byte_length);
912            self.next(); // consume `mut`
913            (true, Some(span))
914        } else {
915            (false, None)
916        };
917
918        let binding = self.parse_binding_allowing_or();
919
920        if !self.is(Equal)
921            && let Some(Annotation::Constructor { span, .. }) = binding.annotation.as_ref()
922        {
923            self.error_missing_initializer(*span);
924            let stub_span = self.span_from_tokens(start);
925            return Expression::Let {
926                binding: Box::new(binding),
927                value: Box::new(Expression::Block {
928                    ty: Type::uninferred(),
929                    items: vec![],
930                    span: stub_span,
931                }),
932                mutable,
933                mut_span,
934                else_block: None,
935                else_span: None,
936                typed_pattern: None,
937                ty: Type::uninferred(),
938                span: stub_span,
939            };
940        }
941
942        self.ensure(Equal);
943
944        let expression = self.parse_expression();
945
946        let (else_block, else_span) = if self.is(Else) {
947            let else_token = self.current_token();
948            let span = Span::new(self.file_id, else_token.byte_offset, else_token.byte_length);
949            self.next(); // consume `else`
950            (Some(Box::new(self.parse_block_expression())), Some(span))
951        } else {
952            (None, None)
953        };
954
955        Expression::Let {
956            binding: Box::new(binding),
957            value: expression.into(),
958            mutable,
959            mut_span,
960            else_block,
961            else_span,
962            typed_pattern: None,
963            ty: Type::uninferred(),
964            span: self.span_from_tokens(start),
965        }
966    }
967
968    pub fn parse_import(&mut self) -> Expression {
969        let start = self.current_token();
970
971        self.ensure(Import);
972
973        let alias = if self.current_token().kind == Identifier {
974            let alias_token = self.current_token();
975            let alias_text = alias_token.text;
976            let alias_span = Span::new(
977                self.file_id,
978                alias_token.byte_offset,
979                alias_token.byte_length,
980            );
981
982            if alias_text == "_" {
983                self.next();
984                Some(ImportAlias::Blank(alias_span))
985            } else if self.stream.peek_ahead(1).kind == String {
986                self.next();
987                Some(ImportAlias::Named(alias_text.into(), alias_span))
988            } else {
989                None
990            }
991        } else {
992            None
993        };
994
995        let name_token = self.current_token();
996
997        if name_token.kind != String {
998            let (label, help) = if name_token.kind == Identifier
999                && self.stream.peek_ahead(1).kind == Colon
1000            {
1001                let module_name = name_token.text;
1002                (
1003                    "expected double quotes".to_string(),
1004                    format!(
1005                        "Wrap the import path in double quotes: `import \"{0}:...\"`",
1006                        module_name
1007                    ),
1008                )
1009            } else if name_token.kind == Identifier {
1010                let module_name = name_token.text;
1011                (
1012                    "expected double quotes".to_string(),
1013                    format!(
1014                        "Wrap the import path in double quotes: `import \"{}\"`",
1015                        module_name
1016                    ),
1017                )
1018            } else {
1019                (
1020                    "expected module path".to_string(),
1021                    "Wrap the import path in double quotes, e.g. `import \"go:os\"`".to_string(),
1022                )
1023            };
1024
1025            self.track_error(label, help);
1026            self.resync_on_error();
1027            return Expression::Unit {
1028                ty: Type::uninferred(),
1029                span: self.span_from_tokens(start),
1030            };
1031        }
1032
1033        self.next();
1034
1035        let raw = name_token.text;
1036        let unquoted: &str = if raw.len() >= 2 && raw.starts_with('"') && raw.ends_with('"') {
1037            &raw[1..raw.len() - 1]
1038        } else {
1039            debug_assert!(
1040                false,
1041                "lexer produced String token without quotes: {:?}",
1042                raw
1043            );
1044            raw
1045        };
1046
1047        if alias.is_none() && self.is(As) && self.stream.peek_ahead(1).kind == Identifier {
1048            let as_token = self.current_token();
1049            let alias_identifier = self.stream.peek_ahead(1);
1050            self.next();
1051            self.next();
1052            self.error_import_alias_after_path(
1053                self.span_from_tokens(as_token),
1054                alias_identifier.text,
1055                unquoted,
1056            );
1057        }
1058
1059        let name: EcoString = unquoted.into();
1060        let name_span = Span::new(self.file_id, name_token.byte_offset, name_token.byte_length);
1061
1062        Expression::ModuleImport {
1063            name,
1064            name_span,
1065            alias,
1066            span: self.span_from_tokens(start),
1067        }
1068    }
1069
1070    pub fn parse_assignment(&mut self) -> Expression {
1071        let start = self.current_token();
1072
1073        let lhs = self.parse_expression();
1074
1075        let compound_operator = match self.current_token().kind {
1076            PlusEqual => Some(BinaryOperator::Addition),
1077            MinusEqual => Some(BinaryOperator::Subtraction),
1078            StarEqual => Some(BinaryOperator::Multiplication),
1079            SlashEqual => Some(BinaryOperator::Division),
1080            PercentEqual => Some(BinaryOperator::Remainder),
1081            AmpersandEqual => Some(BinaryOperator::BitwiseAnd),
1082            PipeEqual => Some(BinaryOperator::BitwiseOr),
1083            CaretEqual => Some(BinaryOperator::BitwiseXor),
1084            AndNotEqual => Some(BinaryOperator::BitwiseAndNot),
1085            ShiftLeftEqual => Some(BinaryOperator::ShiftLeft),
1086            ShiftRightEqual => Some(BinaryOperator::ShiftRight),
1087            _ => None,
1088        };
1089
1090        if let Some(operator) = compound_operator {
1091            if !self.is_valid_assignment_target(&lhs) {
1092                self.track_error(
1093                    "invalid assignment target",
1094                    "Only variables, fields, and indices can be assigned to.",
1095                );
1096                self.next();
1097                let _rhs = self.parse_expression();
1098                return lhs;
1099            }
1100            self.next();
1101            let rhs = self.parse_expression();
1102            return Expression::Assignment {
1103                target: lhs.clone().into(),
1104                value: Expression::Binary {
1105                    left: lhs.into(),
1106                    operator,
1107                    right: rhs.into(),
1108                    ty: Type::uninferred(),
1109                    span: self.span_from_tokens(start),
1110                }
1111                .into(),
1112                compound_operator: Some(operator),
1113                span: self.span_from_tokens(start),
1114            };
1115        }
1116
1117        if self.current_token().kind == Colon && self.stream.peek_ahead(1).kind == Equal {
1118            let span = Span::new(self.file_id, self.current_token().byte_offset, 2);
1119            self.track_error_at(
1120                span,
1121                "Go-style short declaration",
1122                "Use `let x = ...` instead of `:=` for variable declarations",
1123            );
1124            self.next();
1125            self.next();
1126            let _ = self.parse_expression();
1127            return lhs;
1128        }
1129
1130        if !self.is(Equal) {
1131            return lhs;
1132        }
1133
1134        if !self.is_valid_assignment_target(&lhs) {
1135            self.track_error(
1136                "invalid assignment target",
1137                "Only variables, fields, and indices can be assigned to.",
1138            );
1139        }
1140
1141        self.ensure(Equal);
1142
1143        Expression::Assignment {
1144            target: lhs.into(),
1145            value: self.parse_expression().into(),
1146            compound_operator: None,
1147            span: self.span_from_tokens(start),
1148        }
1149    }
1150
1151    fn is_valid_assignment_target(&self, expression: &Expression) -> bool {
1152        use Expression::*;
1153
1154        matches!(
1155            expression,
1156            Identifier { .. }
1157                | DotAccess { .. }
1158                | IndexedAccess { .. }
1159                | Unary {
1160                    operator: UnaryOperator::Deref,
1161                    ..
1162                }
1163        )
1164    }
1165
1166    fn parse_format_string(&mut self) -> Expression {
1167        let start = self.current_token();
1168        self.ensure(FormatStringStart);
1169
1170        let mut parts = Vec::new();
1171
1172        loop {
1173            if self.at_eof() || self.at_item_boundary() {
1174                break;
1175            }
1176            match self.current_token().kind {
1177                FormatStringText => {
1178                    let text = self.current_token().text;
1179                    self.next();
1180                    parts.push(FormatStringPart::Text(cook_string_contents(text)));
1181                }
1182                FormatStringInterpolationStart => {
1183                    self.ensure(FormatStringInterpolationStart);
1184                    let expression = self.parse_expression();
1185                    parts.push(FormatStringPart::Expression(Box::new(expression)));
1186                    if self.is(Colon) {
1187                        let start_offset = self.current_token().byte_offset;
1188                        self.next();
1189                        while !self.at_eof()
1190                            && !self.is(FormatStringInterpolationEnd)
1191                            && !self.is(FormatStringEnd)
1192                            && !self.at_item_boundary()
1193                        {
1194                            self.next();
1195                        }
1196                        let span = self.span_from_offset(start_offset);
1197                        let error = ParseError::new(
1198                            "Format specifiers not supported",
1199                            span,
1200                            "not supported in format strings",
1201                        )
1202                        .with_parse_code("format_specifier")
1203                        .with_help(
1204                            "Use `fmt.Sprintf` for formatted output, e.g. `fmt.Sprintf(\"%02x\", n)`",
1205                        );
1206                        self.errors.push(error);
1207                    }
1208                    self.advance_if(FormatStringInterpolationEnd);
1209                }
1210                FormatStringEnd => {
1211                    self.ensure(FormatStringEnd);
1212                    break;
1213                }
1214                _ => break,
1215            }
1216        }
1217
1218        Expression::Literal {
1219            literal: Literal::FormatString(parts),
1220            ty: Type::uninferred(),
1221            span: self.span_from_tokens(start),
1222        }
1223    }
1224
1225    pub fn parse_task(&mut self) -> Expression {
1226        let start = self.current_token();
1227
1228        self.ensure(Task);
1229
1230        let expression = if self.is(LeftCurlyBrace) {
1231            self.parse_block_expression()
1232        } else {
1233            self.parse_expression()
1234        };
1235
1236        if !matches!(
1237            expression,
1238            Expression::Call { .. } | Expression::Block { .. }
1239        ) {
1240            let span = expression.get_span();
1241            let error = ParseError::new("Invalid `task`", span, "expected `()`")
1242                .with_parse_code("task_missing_parens")
1243                .with_help("Add parens to call the function");
1244
1245            self.errors.push(error);
1246        }
1247
1248        Expression::Task {
1249            expression: Box::new(expression),
1250            ty: Type::uninferred(),
1251            span: self.span_from_tokens(start),
1252        }
1253    }
1254
1255    pub fn parse_defer(&mut self) -> Expression {
1256        let start = self.current_token();
1257
1258        self.ensure(Defer);
1259
1260        let expression = if self.is(LeftCurlyBrace) {
1261            self.parse_block_expression()
1262        } else {
1263            self.parse_expression()
1264        };
1265
1266        if !matches!(
1267            expression,
1268            Expression::Call { .. } | Expression::Block { .. }
1269        ) {
1270            let span = expression.get_span();
1271            let error = ParseError::new("Invalid `defer`", span, "expected `()`")
1272                .with_parse_code("defer_missing_parens")
1273                .with_help("Add parens to call the function");
1274
1275            self.errors.push(error);
1276        }
1277
1278        Expression::Defer {
1279            expression: Box::new(expression),
1280            ty: Type::uninferred(),
1281            span: self.span_from_tokens(start),
1282        }
1283    }
1284
1285    pub fn parse_try_block(&mut self) -> Expression {
1286        let start = self.current_token();
1287        let try_keyword_span = Span::new(self.file_id, start.byte_offset, start.byte_length);
1288
1289        self.ensure(Try);
1290
1291        if !self.is(LeftCurlyBrace) {
1292            let span = self.span_from_tokens(start);
1293            let error = ParseError::new("Invalid `try`", span, "requires a block")
1294                .with_parse_code("syntax_error")
1295                .with_help("Use `try { expression }` instead of `try expression`");
1296            self.errors.push(error);
1297            let expression = self.parse_expression();
1298            return Expression::TryBlock {
1299                items: vec![expression],
1300                ty: Type::uninferred(),
1301                try_keyword_span,
1302                span: self.span_from_tokens(start),
1303            };
1304        }
1305
1306        let brace_token = self.current_token();
1307        self.ensure(LeftCurlyBrace);
1308
1309        if !self.enter_recursion() {
1310            let span = self.span_from_token(self.current_token());
1311            let mut brace_depth = 1u32;
1312            while brace_depth > 0 && !self.at_eof() {
1313                match self.current_token().kind {
1314                    LeftCurlyBrace => brace_depth += 1,
1315                    RightCurlyBrace => brace_depth -= 1,
1316                    _ => {}
1317                }
1318                if brace_depth > 0 {
1319                    self.next();
1320                }
1321            }
1322            self.advance_if(RightCurlyBrace);
1323            return Expression::TryBlock {
1324                items: vec![],
1325                ty: Type::uninferred(),
1326                try_keyword_span,
1327                span,
1328            };
1329        }
1330
1331        let mut items = vec![];
1332
1333        while self.is_not(RightCurlyBrace) && !self.too_many_errors() {
1334            let position = self.position();
1335            let item = self.parse_block_item();
1336
1337            self.advance_if(Semicolon);
1338
1339            items.push(item);
1340            if self.position() == position {
1341                self.next();
1342            }
1343        }
1344
1345        let span = self.close_brace_span(start, brace_token);
1346
1347        self.leave_recursion();
1348
1349        Expression::TryBlock {
1350            items,
1351            ty: Type::uninferred(),
1352            try_keyword_span,
1353            span,
1354        }
1355    }
1356
1357    pub fn parse_recover_block(&mut self) -> Expression {
1358        let start = self.current_token();
1359        let recover_keyword_span = Span::new(self.file_id, start.byte_offset, start.byte_length);
1360
1361        self.ensure(Recover);
1362
1363        if !self.is(LeftCurlyBrace) {
1364            let span = self.span_from_tokens(start);
1365            let error = ParseError::new("Invalid `recover`", span, "requires a block")
1366                .with_parse_code("syntax_error")
1367                .with_help("Use `recover { expression }` instead of `recover expression`");
1368            self.errors.push(error);
1369            let expression = self.parse_expression();
1370            return Expression::RecoverBlock {
1371                items: vec![expression],
1372                ty: Type::uninferred(),
1373                recover_keyword_span,
1374                span: self.span_from_tokens(start),
1375            };
1376        }
1377
1378        let brace_token = self.current_token();
1379        self.ensure(LeftCurlyBrace);
1380
1381        if !self.enter_recursion() {
1382            let span = self.span_from_token(self.current_token());
1383            let mut brace_depth = 1u32;
1384            while brace_depth > 0 && !self.at_eof() {
1385                match self.current_token().kind {
1386                    LeftCurlyBrace => brace_depth += 1,
1387                    RightCurlyBrace => brace_depth -= 1,
1388                    _ => {}
1389                }
1390                if brace_depth > 0 {
1391                    self.next();
1392                }
1393            }
1394            self.advance_if(RightCurlyBrace);
1395            return Expression::RecoverBlock {
1396                items: vec![],
1397                ty: Type::uninferred(),
1398                recover_keyword_span,
1399                span,
1400            };
1401        }
1402
1403        let mut items = vec![];
1404
1405        while self.is_not(RightCurlyBrace) && !self.too_many_errors() {
1406            let position = self.position();
1407            let item = self.parse_block_item();
1408
1409            self.advance_if(Semicolon);
1410
1411            items.push(item);
1412            if self.position() == position {
1413                self.next();
1414            }
1415        }
1416
1417        let span = self.close_brace_span(start, brace_token);
1418
1419        self.leave_recursion();
1420
1421        Expression::RecoverBlock {
1422            items,
1423            ty: Type::uninferred(),
1424            recover_keyword_span,
1425            span,
1426        }
1427    }
1428
1429    pub fn parse_select(&mut self) -> Expression {
1430        let start = self.current_token();
1431
1432        self.ensure(Select);
1433        self.ensure(LeftCurlyBrace);
1434
1435        let mut arms = Vec::new();
1436
1437        while self.is_not(RightCurlyBrace) {
1438            let arm = self.parse_select_arm();
1439            arms.push(arm);
1440
1441            if self.is(RightCurlyBrace) {
1442                break;
1443            }
1444
1445            self.ensure(Comma);
1446        }
1447
1448        self.ensure(RightCurlyBrace);
1449
1450        Expression::Select {
1451            arms,
1452            ty: Type::uninferred(),
1453            span: self.span_from_tokens(start),
1454        }
1455    }
1456
1457    fn parse_select_arm(&mut self) -> SelectArm {
1458        match self.current_token().kind {
1459            Let => {
1460                self.ensure(Let);
1461                let binding = self.parse_pattern();
1462                self.ensure(Equal);
1463                let receive_expression = Box::new(self.parse_expression());
1464                self.ensure(ArrowDouble);
1465                let body = Box::new(self.parse_expression());
1466                SelectArm {
1467                    pattern: SelectArmPattern::Receive {
1468                        binding: Box::new(binding),
1469                        typed_pattern: None,
1470                        receive_expression,
1471                        body,
1472                    },
1473                }
1474            }
1475            Match => {
1476                let match_expression = self.parse_match();
1477                if let Expression::Match { subject, arms, .. } = match_expression {
1478                    SelectArm {
1479                        pattern: SelectArmPattern::MatchReceive {
1480                            receive_expression: subject,
1481                            arms,
1482                        },
1483                    }
1484                } else {
1485                    self.ensure(ArrowDouble);
1486                    let body = Box::new(self.parse_expression());
1487                    SelectArm {
1488                        pattern: SelectArmPattern::Send {
1489                            send_expression: Box::new(match_expression),
1490                            body,
1491                        },
1492                    }
1493                }
1494            }
1495            Identifier if self.current_token().text == "_" => {
1496                self.next();
1497                self.ensure(ArrowDouble);
1498                let body = Box::new(self.parse_expression());
1499                SelectArm {
1500                    pattern: SelectArmPattern::WildCard { body },
1501                }
1502            }
1503            _ => {
1504                let send_expression = Box::new(self.parse_expression());
1505                self.ensure(ArrowDouble);
1506                let body = Box::new(self.parse_expression());
1507                SelectArm {
1508                    pattern: SelectArmPattern::Send {
1509                        send_expression,
1510                        body,
1511                    },
1512                }
1513            }
1514        }
1515    }
1516
1517    pub fn with_control_flow_header<F, R>(&mut self, f: F) -> R
1518    where
1519        F: FnOnce(&mut Self) -> R,
1520    {
1521        let old = self.in_control_flow_header;
1522        self.in_control_flow_header = true;
1523        let result = f(self);
1524        self.in_control_flow_header = old;
1525        result
1526    }
1527
1528    fn keyword_in_value_position(&self) -> bool {
1529        if !self.current_token().kind.is_keyword() {
1530            return false;
1531        }
1532
1533        match self.current_token().kind {
1534            Return | Break | Continue => false,
1535
1536            Match | If | Task | Defer | Try | Recover | Select | Loop | Function => matches!(
1537                self.stream.peek_ahead(1).kind,
1538                RightParen
1539                    | Comma
1540                    | Dot
1541                    | Semicolon
1542                    | RightCurlyBrace
1543                    | RightSquareBracket
1544                    | ArrowDouble
1545                    | QuestionMark
1546                    | EOF
1547                    | Plus
1548                    | Star
1549                    | Slash
1550                    | Percent
1551                    | EqualDouble
1552                    | NotEqual
1553                    | LeftAngleBracket
1554                    | RightAngleBracket
1555                    | LessThanOrEqual
1556                    | GreaterThanOrEqual
1557                    | AmpersandDouble
1558                    | Ampersand
1559                    | Pipe
1560                    | Caret
1561                    | AndNot
1562                    | ShiftLeft
1563                    | ShiftRight
1564                    | Pipeline
1565                    | Equal
1566                    | PlusEqual
1567                    | MinusEqual
1568                    | StarEqual
1569                    | SlashEqual
1570                    | PercentEqual
1571                    | AmpersandEqual
1572                    | PipeEqual
1573                    | CaretEqual
1574                    | AndNotEqual
1575                    | ShiftLeftEqual
1576                    | ShiftRightEqual
1577                    | DotDot
1578                    | DotDotEqual
1579                    | As
1580            ),
1581
1582            _ => true,
1583        }
1584    }
1585
1586    fn recover_keyword_as_identifier(&mut self) -> Expression {
1587        let token = self.current_token();
1588        let keyword = token.text.to_string();
1589        let span = self.span_from_token(token);
1590        let error = ParseError::new("Reserved keyword", span, "reserved keyword")
1591            .with_parse_code("keyword_as_identifier")
1592            .with_help(format!("Rename `{}`", keyword));
1593        self.errors.push(error);
1594        self.next();
1595        Expression::Identifier {
1596            value: keyword.into(),
1597            ty: Type::uninferred(),
1598            span,
1599            binding_id: None,
1600            qualified: None,
1601        }
1602    }
1603
1604    fn looks_like_map_literal(&self) -> bool {
1605        let first = self.current_token().kind;
1606        let second = self.stream.peek_ahead(1).kind;
1607        matches!(first, String | RawString | Integer | Float) && second == Colon
1608    }
1609
1610    fn consume_to_matching_close_brace(&mut self) {
1611        let mut brace_depth = 1u32;
1612        while brace_depth > 0 && !self.at_eof() {
1613            match self.current_token().kind {
1614                LeftCurlyBrace => brace_depth += 1,
1615                RightCurlyBrace => brace_depth -= 1,
1616                _ => {}
1617            }
1618            if brace_depth > 0 {
1619                self.next();
1620            }
1621        }
1622        self.advance_if(RightCurlyBrace);
1623    }
1624
1625    fn recover_unexpected_backtick(&mut self) -> Expression {
1626        let token = self.current_token();
1627        let token_span = self.span_from_token(token);
1628        let opening_span = Span::new(self.file_id, token.byte_offset, 1);
1629        let error = ParseError::new("Unexpected backtick", opening_span, "not allowed here")
1630            .with_parse_code("unexpected_backtick")
1631            .with_help(
1632                "Use a regular string `\"...\"` or a raw string `r\"...\"` for single- or multi-line strings. Backticks in Lisette are reserved for struct-tag attributes.",
1633            );
1634        self.errors.push(error);
1635        self.next();
1636        Expression::Literal {
1637            literal: Literal::String {
1638                value: std::string::String::new(),
1639                raw: true,
1640            },
1641            ty: Type::uninferred(),
1642            span: token_span,
1643        }
1644    }
1645}