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