Skip to main content

lisette_syntax/parse/
expressions.rs

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