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