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