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