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(); 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(); self.next(); 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 self.stream.peek_ahead(2).byte_offset + self.stream.peek_ahead(2).byte_length
589 - amp_token.byte_offset
590 } else {
591 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}