Skip to main content

lisette_syntax/parse/
patterns.rs

1use ecow::EcoString;
2
3use super::{MAX_TUPLE_ARITY, ParseError, Parser};
4use crate::ast::{Annotation, Binding, Literal, Pattern, RestPattern, Span, StructFieldPattern};
5use crate::lex::Token;
6use crate::lex::TokenKind::*;
7use crate::types::Type;
8
9impl<'source> Parser<'source> {
10    pub fn parse_pattern_allowing_or(&mut self) -> Pattern {
11        let start = self.current_token();
12        let first = self.parse_pattern();
13
14        if self.is_not(Pipe) {
15            return first;
16        }
17
18        let mut patterns = vec![first];
19        while self.advance_if(Pipe) {
20            patterns.push(self.parse_pattern());
21        }
22
23        Pattern::Or {
24            patterns,
25            span: self.span_from_tokens(start),
26        }
27    }
28
29    pub fn parse_pattern(&mut self) -> Pattern {
30        if !self.enter_recursion() {
31            let span = self.span_from_token(self.current_token());
32            self.resync_on_error();
33            return Pattern::WildCard { span };
34        }
35        let result = self.parse_pattern_inner();
36        self.leave_recursion();
37        result
38    }
39
40    fn parse_pattern_inner(&mut self) -> Pattern {
41        let start = self.current_token();
42
43        if self.current_token().kind.is_keyword() {
44            let keyword = self.current_token().text.to_string();
45            let span = self.span_from_token(start);
46            let error = ParseError::new("Reserved keyword", span, "reserved keyword")
47                .with_parse_code("keyword_as_binding")
48                .with_help(format!("Rename binding `{}`", keyword));
49            self.errors.push(error);
50            self.next();
51            return Pattern::Identifier {
52                identifier: keyword.into(),
53                span,
54            };
55        }
56
57        match self.current_token().kind {
58            Integer => self.parse_integer_pattern(),
59            Float => self.parse_float_pattern(),
60            Boolean => self.parse_boolean_pattern(),
61            String => self.parse_string_pattern(),
62            Char => self.parse_char_pattern(),
63
64            Imaginary => {
65                self.track_error(
66                    "not allowed",
67                    "Imaginary literals are not supported in patterns",
68                );
69                self.next();
70                Pattern::WildCard {
71                    span: self.span_from_tokens(start),
72                }
73            }
74
75            LeftParen => self.parse_tuple_or_unit_pattern(),
76
77            LeftSquareBracket => self.parse_slice_pattern(),
78
79            Identifier => self.parse_identifier_based_pattern(),
80
81            Minus => self.parse_negative_pattern(),
82
83            _ => {
84                self.unexpected_token("pattern");
85                Pattern::WildCard {
86                    span: self.span_from_tokens(start),
87                }
88            }
89        }
90    }
91
92    fn parse_negative_pattern(&mut self) -> Pattern {
93        let start = self.current_token();
94        self.next();
95
96        match self.current_token().kind {
97            Integer => {
98                let int_pattern = self.parse_integer_pattern();
99                if let Pattern::Literal {
100                    literal: Literal::Integer { value, text },
101                    ty: _,
102                    span: _,
103                } = int_pattern
104                {
105                    let neg_text = match text {
106                        Some(t) => format!("-{}", t),
107                        None => format!("-{}", value),
108                    };
109                    Pattern::Literal {
110                        literal: Literal::Integer {
111                            value,
112                            text: Some(neg_text),
113                        },
114                        ty: Type::uninferred(),
115                        span: self.span_from_tokens(start),
116                    }
117                } else {
118                    int_pattern
119                }
120            }
121            Float => {
122                let span = self.span_from_tokens(start);
123                self.track_error_at(
124                    span,
125                    "not allowed",
126                    "Float literals are not supported in patterns",
127                );
128                self.next();
129                Pattern::WildCard {
130                    span: self.span_from_tokens(start),
131                }
132            }
133            _ => {
134                self.track_error(
135                    "expected number after `-`",
136                    "Negative patterns require a number, e.g., `-5`",
137                );
138                Pattern::WildCard {
139                    span: self.span_from_tokens(start),
140                }
141            }
142        }
143    }
144
145    fn parse_nested_pattern(&mut self) -> Pattern {
146        let pattern = self.parse_pattern();
147
148        if self.is(Pipe) {
149            let token = self.current_token();
150            let span = Span::new(self.file_id, token.byte_offset, token.byte_length);
151            self.emit_nested_or_error(span);
152
153            while self.is(Pipe) {
154                self.next(); // consume `|`
155                self.parse_pattern();
156            }
157        }
158
159        pattern
160    }
161
162    fn emit_nested_or_error(&mut self, span: Span) {
163        let error = ParseError::new("Invalid or-pattern", span, "or-pattern not allowed here")
164            .with_parse_code("nested_or_pattern")
165            .with_help("Use `Ok(x) | Ok(y)` instead of `Ok(x | y)`");
166        self.errors.push(error);
167    }
168
169    fn check_nested_or_pattern(&mut self, pattern: &Pattern) {
170        if let Pattern::Or { span, .. } = pattern {
171            self.emit_nested_or_error(*span);
172        }
173    }
174
175    fn parse_integer_pattern(&mut self) -> Pattern {
176        let start = self.current_token();
177        let text = start.text;
178        let literal = self.parse_integer_text(text);
179        self.next();
180
181        Pattern::Literal {
182            literal,
183            ty: Type::uninferred(),
184            span: self.span_from_tokens(start),
185        }
186    }
187
188    fn parse_float_pattern(&mut self) -> Pattern {
189        let start = self.current_token();
190        let float_text = start.text.to_string();
191        self.next();
192
193        let span = self.span_from_tokens(start);
194        self.error_float_pattern_not_allowed(span, &float_text);
195
196        Pattern::WildCard { span }
197    }
198
199    fn parse_boolean_pattern(&mut self) -> Pattern {
200        let start = self.current_token();
201        let b = start.text == "true";
202        self.next();
203
204        Pattern::Literal {
205            literal: Literal::Boolean(b),
206            ty: Type::uninferred(),
207            span: self.span_from_tokens(start),
208        }
209    }
210
211    fn parse_string_pattern(&mut self) -> Pattern {
212        let start = self.current_token();
213        let s = start.text;
214        self.next();
215        let s_stripped = if s.len() >= 2 && s.starts_with('"') && s.ends_with('"') {
216            s[1..s.len() - 1].to_string()
217        } else {
218            s.to_string()
219        };
220
221        Pattern::Literal {
222            literal: Literal::String(s_stripped),
223            ty: Type::uninferred(),
224            span: self.span_from_tokens(start),
225        }
226    }
227
228    fn parse_char_pattern(&mut self) -> Pattern {
229        let start = self.current_token();
230        let s = start.text;
231        self.next();
232        let char_str = if s.len() >= 2 && s.starts_with('\'') && s.ends_with('\'') {
233            s[1..s.len() - 1].to_string()
234        } else {
235            s.to_string()
236        };
237
238        Pattern::Literal {
239            literal: Literal::Char(char_str),
240            ty: Type::uninferred(),
241            span: self.span_from_tokens(start),
242        }
243    }
244
245    fn parse_tuple_or_unit_pattern(&mut self) -> Pattern {
246        let start = self.current_token();
247        self.ensure(LeftParen);
248
249        if self.advance_if(RightParen) {
250            return Pattern::Unit {
251                ty: Type::uninferred(),
252                span: self.span_from_tokens(start),
253            };
254        }
255
256        let first = self.parse_pattern_allowing_or();
257
258        if self.advance_if(RightParen) {
259            if matches!(first, Pattern::Or { .. }) {
260                self.check_nested_or_pattern(&first);
261            }
262            return first;
263        }
264
265        if matches!(first, Pattern::Or { .. }) {
266            self.check_nested_or_pattern(&first);
267        }
268
269        let mut elements = vec![first];
270        self.expect_comma_or(RightParen);
271
272        while self.is_not(RightParen) {
273            elements.push(self.parse_nested_pattern());
274            self.expect_comma_or(RightParen);
275        }
276
277        self.ensure(RightParen);
278
279        let span = self.span_from_tokens(start);
280
281        if elements.len() > MAX_TUPLE_ARITY {
282            self.error_tuple_arity(elements.len(), span);
283        }
284
285        Pattern::Tuple { elements, span }
286    }
287
288    fn parse_slice_pattern(&mut self) -> Pattern {
289        let start = self.current_token();
290        self.ensure(LeftSquareBracket);
291
292        let mut elements = Vec::new();
293        let mut rest = RestPattern::Absent;
294
295        while self.is_not(RightSquareBracket) {
296            if let Some((binding, rest_start)) = self.try_parse_rest() {
297                if rest.is_present() {
298                    self.track_error(
299                        "multiple rest patterns in slice pattern",
300                        "Only one `..` or `..rest` is allowed.",
301                    );
302                } else {
303                    rest = match binding {
304                        Some(name) => RestPattern::Bind {
305                            name,
306                            span: self.span_from_tokens(rest_start),
307                        },
308                        None => RestPattern::Discard(self.span_from_tokens(rest_start)),
309                    };
310                }
311                self.expect_comma_or(RightSquareBracket);
312                continue;
313            }
314
315            if rest.is_present() {
316                let suffix_start = self.current_token();
317                self.parse_pattern();
318                let suffix_span = self.span_from_tokens(suffix_start);
319                let error = ParseError::new("Invalid pattern", suffix_span, "not supported")
320                    .with_parse_code("suffix_slice_pattern")
321                    .with_help("Use `[first, ..rest]` instead of `[..rest, last]`.")
322                    .with_note("Elements after rest pattern are not supported.");
323                self.errors.push(error);
324                self.expect_comma_or(RightSquareBracket);
325                continue;
326            }
327
328            elements.push(self.parse_nested_pattern());
329            self.expect_comma_or(RightSquareBracket);
330        }
331
332        let span = self.span_from_tokens(start);
333        self.ensure(RightSquareBracket);
334
335        Pattern::Slice {
336            prefix: elements,
337            rest,
338            element_ty: Type::uninferred(),
339            span,
340        }
341    }
342
343    fn parse_identifier_based_pattern(&mut self) -> Pattern {
344        let start = self.current_token();
345        let name = self.current_token().text.to_string();
346        self.next();
347
348        let full_name = if self.is(Dot) {
349            self.parse_qualified_pattern_name(name)
350        } else if self.is(Colon) && self.stream.peek_ahead(1).kind == Colon {
351            let colon_token = self.current_token();
352            let span = Span::new(self.file_id, colon_token.byte_offset, 2);
353            let after = self.stream.peek_ahead(2);
354            let example = if after.kind == Identifier {
355                format!("{}.{}", name, after.text)
356            } else {
357                format!("{}.<variant>", name)
358            };
359            self.track_error_at(
360                span,
361                "invalid syntax",
362                format!(
363                    "Use `.` instead of `::` for enum variant access, e.g. `{}`",
364                    example
365                ),
366            );
367            self.next(); // consume first `:`
368            self.next(); // consume second `:`
369            let mut full_name = name;
370            if self.is(Identifier) {
371                full_name.push('.');
372                full_name.push_str(self.current_token().text);
373                self.next();
374            }
375            self.parse_qualified_pattern_name(full_name)
376        } else {
377            name.clone()
378        };
379
380        match self.current_token().kind {
381            LeftCurlyBrace => self.parse_struct_pattern(full_name, start),
382            LeftParen => self.parse_enum_variant_pattern(full_name, start),
383            _ => {
384                let span = self.span_from_tokens(start);
385                if full_name == "_" {
386                    Pattern::WildCard { span }
387                } else if full_name.contains('.') || self.is_uppercase(&full_name) {
388                    Pattern::EnumVariant {
389                        identifier: full_name.into(),
390                        fields: vec![],
391                        rest: false,
392                        ty: Type::uninferred(),
393                        span,
394                    }
395                } else {
396                    Pattern::Identifier {
397                        identifier: full_name.into(),
398                        span,
399                    }
400                }
401            }
402        }
403    }
404
405    fn parse_qualified_pattern_name(
406        &mut self,
407        initial: std::string::String,
408    ) -> std::string::String {
409        let mut name = initial;
410
411        while self.advance_if(Dot) {
412            if self.is_not(Identifier) {
413                break;
414            }
415            name.push('.');
416            name.push_str(self.current_token().text);
417            self.next();
418        }
419
420        name
421    }
422
423    fn parse_struct_pattern(
424        &mut self,
425        name: std::string::String,
426        start: Token<'source>,
427    ) -> Pattern {
428        self.ensure(LeftCurlyBrace);
429
430        let mut fields = Vec::new();
431        let mut seen_fields: Vec<(EcoString, Span)> = Vec::new();
432        let mut rest = false;
433
434        while self.is_not(RightCurlyBrace) {
435            if self.advance_if(DotDot) {
436                rest = true;
437                if self.is(Identifier) {
438                    self.next();
439                }
440                if self.advance_if(Comma) && self.is_not(RightCurlyBrace) {
441                    self.track_error(
442                        "cannot be last",
443                        "Move the spread expression `..rest` to the last position in the struct",
444                    );
445                }
446                break;
447            }
448
449            let field_start = self.current_token();
450            let field_name = self.read_identifier();
451            let field_name_span = self.span_from_tokens(field_start);
452
453            if let Some((_, first_span)) = seen_fields.iter().find(|(n, _)| n == &field_name) {
454                self.error_duplicate_field_in_pattern(&field_name, *first_span, field_name_span);
455            }
456
457            let field_pattern = if self.advance_if(Colon) {
458                self.parse_nested_pattern()
459            } else {
460                let span = field_name_span;
461                if field_name == "_" {
462                    Pattern::WildCard { span }
463                } else {
464                    Pattern::Identifier {
465                        identifier: field_name.clone(),
466                        span,
467                    }
468                }
469            };
470
471            seen_fields.push((field_name.clone(), field_name_span));
472            fields.push(StructFieldPattern {
473                name: field_name,
474                value: field_pattern,
475            });
476
477            self.expect_comma_or(RightCurlyBrace);
478        }
479
480        self.ensure(RightCurlyBrace);
481
482        Pattern::Struct {
483            identifier: name.into(),
484            fields,
485            rest,
486            ty: Type::uninferred(),
487            span: self.span_from_tokens(start),
488        }
489    }
490
491    fn parse_enum_variant_pattern(
492        &mut self,
493        name: std::string::String,
494        start: Token<'source>,
495    ) -> Pattern {
496        self.ensure(LeftParen);
497
498        let mut fields = Vec::new();
499        let mut rest = false;
500
501        while self.is_not(RightParen) {
502            if self.advance_if(DotDot) {
503                rest = true;
504                self.advance_if(Comma);
505                break;
506            }
507            fields.push(self.parse_nested_pattern());
508            self.expect_comma_or(RightParen);
509        }
510
511        self.ensure(RightParen);
512
513        Pattern::EnumVariant {
514            identifier: name.into(),
515            fields,
516            rest,
517            ty: Type::uninferred(),
518            span: self.span_from_tokens(start),
519        }
520    }
521
522    pub fn parse_binding(&mut self) -> Binding {
523        Binding {
524            pattern: self.parse_pattern(),
525            annotation: self.parse_optional_type_annotation(),
526            typed_pattern: None,
527            ty: Type::uninferred(),
528            mutable: false,
529        }
530    }
531
532    pub fn parse_binding_allowing_or(&mut self) -> Binding {
533        Binding {
534            pattern: self.parse_pattern_allowing_or(),
535            annotation: self.parse_optional_type_annotation(),
536            typed_pattern: None,
537            ty: Type::uninferred(),
538            mutable: false,
539        }
540    }
541
542    fn parse_optional_type_annotation(&mut self) -> Option<Annotation> {
543        if self.advance_if(Colon) {
544            if self.can_start_annotation() {
545                Some(self.parse_annotation())
546            } else {
547                self.track_error(
548                    "expected type after `:`",
549                    "Annotate the type, e.g. `x: int`.",
550                );
551                None
552            }
553        } else {
554            None
555        }
556    }
557
558    pub fn parse_binding_with_type(&mut self) -> Binding {
559        if self.is_current_uppercase() && self.stream.peek_ahead(1).kind == Colon {
560            let start = self.current_token();
561            let name = start.text.to_string();
562            self.next();
563
564            let span = self.span_from_tokens(start);
565            self.error_uppercase_binding(span);
566
567            return Binding {
568                pattern: Pattern::Identifier {
569                    identifier: name.into(),
570                    span,
571                },
572                annotation: self.parse_optional_type_annotation(),
573                typed_pattern: None,
574                ty: Type::uninferred(),
575                mutable: false,
576            };
577        }
578
579        if self.is(Ampersand) {
580            let amp_token = self.current_token();
581            let next = self.stream.peek_ahead(1);
582            let is_mut_self = next.kind == Mut && self.stream.peek_ahead(2).text == "self";
583            let is_ref_self = next.kind == Identifier && next.text == "self";
584
585            if is_ref_self || is_mut_self {
586                let span_len = if is_mut_self {
587                    // &mut self
588                    self.stream.peek_ahead(2).byte_offset + self.stream.peek_ahead(2).byte_length
589                        - amp_token.byte_offset
590                } else {
591                    // &self
592                    next.byte_offset + next.byte_length - amp_token.byte_offset
593                };
594                let span = Span::new(self.file_id, amp_token.byte_offset, span_len);
595                self.track_error_at(
596                    span,
597                    "invalid syntax",
598                    "Lisette methods receive `self` by reference. Use `self` instead",
599                );
600                self.next();
601                if is_mut_self {
602                    self.next();
603                }
604            }
605        }
606
607        let is_mut = self.advance_if(Mut);
608
609        let pattern = self.parse_pattern();
610
611        if let Pattern::Identifier { identifier, .. } = &pattern
612            && identifier == "self"
613            && self.is_not(Colon)
614        {
615            return Binding {
616                pattern,
617                annotation: None,
618                typed_pattern: None,
619                ty: Type::uninferred(),
620                mutable: false,
621            };
622        }
623
624        self.ensure(Colon);
625        let annotation = self.parse_annotation();
626
627        Binding {
628            pattern,
629            annotation: Some(annotation),
630            typed_pattern: None,
631            ty: Type::uninferred(),
632            mutable: is_mut,
633        }
634    }
635
636    fn try_parse_rest(&mut self) -> Option<(Option<EcoString>, Token<'source>)> {
637        if self.is(DotDot) {
638            let rest_start = self.current_token();
639            self.ensure(DotDot);
640            if self.is(Identifier) {
641                let name: EcoString = self.current_token().text.into();
642                self.next();
643                return Some((Some(name), rest_start));
644            }
645            return Some((None, rest_start));
646        }
647
648        if self.is(Identifier) {
649            let text = self.current_token().text;
650            if let Some(binding) = text.strip_prefix("..") {
651                let rest_start = self.current_token();
652                self.next();
653                let name = if binding.is_empty() {
654                    None
655                } else {
656                    Some(EcoString::from(binding))
657                };
658                return Some((name, rest_start));
659            }
660        }
661
662        None
663    }
664
665    fn is_uppercase(&self, identifier: &str) -> bool {
666        identifier.chars().next().unwrap_or('a').is_uppercase()
667    }
668
669    fn is_current_uppercase(&self) -> bool {
670        self.is(Identifier) && self.is_uppercase(self.current_token().text)
671    }
672
673    pub fn can_start_pattern(&self) -> bool {
674        matches!(
675            self.current_token().kind,
676            Integer
677                | Float
678                | Boolean
679                | String
680                | Char
681                | LeftParen
682                | LeftSquareBracket
683                | Identifier
684                | Minus
685        )
686    }
687}