1use bock_ast::{Literal, Pattern, RecordPatternField};
9
10use crate::value::{BockString, OrdF64, Value};
11
12pub type Binding = (String, Value);
14
15#[must_use]
21pub fn match_pattern(value: &Value, pattern: &Pattern) -> Option<Vec<Binding>> {
22 match pattern {
23 Pattern::Wildcard { .. } | Pattern::Rest { .. } => Some(vec![]),
24
25 Pattern::Bind { name, .. } => Some(vec![(name.name.clone(), value.clone())]),
26
27 Pattern::MutBind { name, .. } => Some(vec![(name.name.clone(), value.clone())]),
28
29 Pattern::Literal { lit, .. } => {
30 if literal_matches(lit, value) {
31 Some(vec![])
32 } else {
33 None
34 }
35 }
36
37 Pattern::Constructor { path, fields, .. } => {
38 let variant_name = path.segments.last().map(|s| s.name.as_str()).unwrap_or("");
39 match_constructor(variant_name, fields, value)
40 }
41
42 Pattern::Record {
43 path, fields, rest, ..
44 } => match_record(path, fields, *rest, value),
45
46 Pattern::Tuple { elems, .. } => match_tuple(elems, value),
47
48 Pattern::List { elems, rest, .. } => match_list(elems, rest.as_deref(), value),
49
50 Pattern::Or { alternatives, .. } => {
51 for alt in alternatives {
52 if let Some(bindings) = match_pattern(value, alt) {
53 return Some(bindings);
54 }
55 }
56 None
57 }
58
59 Pattern::Range {
60 lo, hi, inclusive, ..
61 } => match_range(lo, hi, *inclusive, value),
62 }
63}
64
65fn literal_matches(lit: &Literal, value: &Value) -> bool {
69 match lit {
70 Literal::Int(s) => {
71 let (numeric, _) = bock_ast::strip_type_suffix(s);
72 let clean = numeric.replace('_', "");
73 let parsed = if clean.starts_with("0x") || clean.starts_with("0X") {
74 i64::from_str_radix(&clean[2..], 16)
75 } else if clean.starts_with("0o") || clean.starts_with("0O") {
76 i64::from_str_radix(&clean[2..], 8)
77 } else if clean.starts_with("0b") || clean.starts_with("0B") {
78 i64::from_str_radix(&clean[2..], 2)
79 } else {
80 clean.parse::<i64>()
81 };
82 matches!(parsed, Ok(n) if *value == Value::Int(n))
83 }
84 Literal::Float(s) => {
85 let (numeric, _) = bock_ast::strip_type_suffix(s);
86 let parsed = numeric.replace('_', "").parse::<f64>();
87 matches!(parsed, Ok(f) if *value == Value::Float(OrdF64(f)))
88 }
89 Literal::Bool(b) => *value == Value::Bool(*b),
90 Literal::Char(s) => *value == Value::Char(s.chars().next().unwrap_or('\0')),
91 Literal::String(s) => *value == Value::String(BockString::new(s.clone())),
92 Literal::Unit => *value == Value::Void,
93 }
94}
95
96fn match_constructor(
98 variant_name: &str,
99 fields: &[Pattern],
100 value: &Value,
101) -> Option<Vec<Binding>> {
102 match (variant_name, value) {
103 ("Some", Value::Optional(Some(inner))) => {
104 if fields.len() == 1 {
105 match_pattern(inner, &fields[0])
106 } else {
107 None
108 }
109 }
110 ("None", Value::Optional(None)) => {
111 if fields.is_empty() {
112 Some(vec![])
113 } else {
114 None
115 }
116 }
117 ("Ok", Value::Result(Ok(inner))) => {
118 if fields.is_empty() {
119 Some(vec![])
120 } else if fields.len() == 1 {
121 match_pattern(inner, &fields[0])
122 } else {
123 None
124 }
125 }
126 ("Err", Value::Result(Err(inner))) => {
127 if fields.is_empty() {
128 Some(vec![])
129 } else if fields.len() == 1 {
130 match_pattern(inner, &fields[0])
131 } else {
132 None
133 }
134 }
135 (name, Value::Enum(ev)) if ev.variant == name => match (&ev.payload, fields.len()) {
136 (None, 0) => Some(vec![]),
137 (Some(inner), 1) => match_pattern(inner, &fields[0]),
138 _ => None,
139 },
140 _ => None,
141 }
142}
143
144fn match_record(
146 path: &bock_ast::TypePath,
147 fields: &[RecordPatternField],
148 rest: bool,
149 value: &Value,
150) -> Option<Vec<Binding>> {
151 let rv = match value {
152 Value::Record(rv) => rv,
153 _ => return None,
154 };
155
156 let type_name = path.segments.last().map(|s| s.name.as_str()).unwrap_or("");
157 if rv.type_name != type_name {
158 return None;
159 }
160 if !rest && fields.len() != rv.fields.len() {
162 return None;
163 }
164
165 let mut bindings = Vec::new();
166 for field in fields {
167 let field_val = rv.fields.get(&field.name.name)?;
168 if let Some(pat) = &field.pattern {
169 bindings.extend(match_pattern(field_val, pat)?);
170 } else {
171 bindings.push((field.name.name.clone(), field_val.clone()));
173 }
174 }
175 Some(bindings)
176}
177
178fn match_tuple(elems: &[Pattern], value: &Value) -> Option<Vec<Binding>> {
180 let vals = match value {
181 Value::Tuple(vals) => vals,
182 _ => return None,
183 };
184 if elems.len() != vals.len() {
185 return None;
186 }
187 let mut bindings = Vec::new();
188 for (pat, val) in elems.iter().zip(vals.iter()) {
189 bindings.extend(match_pattern(val, pat)?);
190 }
191 Some(bindings)
192}
193
194fn match_list(elems: &[Pattern], rest: Option<&Pattern>, value: &Value) -> Option<Vec<Binding>> {
196 let vals = match value {
197 Value::List(vals) => vals,
198 _ => return None,
199 };
200 if elems.len() > vals.len() {
201 return None;
202 }
203 if rest.is_none() && elems.len() != vals.len() {
204 return None;
205 }
206 let mut bindings = Vec::new();
207 for (pat, val) in elems.iter().zip(vals.iter()) {
208 bindings.extend(match_pattern(val, pat)?);
209 }
210 if let Some(rest_pat) = rest {
211 let rest_val = Value::List(vals[elems.len()..].to_vec());
212 bindings.extend(match_pattern(&rest_val, rest_pat)?);
213 }
214 Some(bindings)
215}
216
217fn match_range(lo: &Pattern, hi: &Pattern, inclusive: bool, value: &Value) -> Option<Vec<Binding>> {
219 let lo_val = pattern_to_value(lo)?;
220 let hi_val = pattern_to_value(hi)?;
221 let in_range = if inclusive {
222 *value >= lo_val && *value <= hi_val
223 } else {
224 *value >= lo_val && *value < hi_val
225 };
226 if in_range {
227 Some(vec![])
228 } else {
229 None
230 }
231}
232
233fn pattern_to_value(pattern: &Pattern) -> Option<Value> {
235 match pattern {
236 Pattern::Literal { lit, .. } => literal_to_value(lit),
237 _ => None,
238 }
239}
240
241fn literal_to_value(lit: &Literal) -> Option<Value> {
243 match lit {
244 Literal::Int(s) => {
245 let (numeric, _) = bock_ast::strip_type_suffix(s);
246 let clean = numeric.replace('_', "");
247 let n = if clean.starts_with("0x") || clean.starts_with("0X") {
248 i64::from_str_radix(&clean[2..], 16)
249 } else if clean.starts_with("0o") || clean.starts_with("0O") {
250 i64::from_str_radix(&clean[2..], 8)
251 } else if clean.starts_with("0b") || clean.starts_with("0B") {
252 i64::from_str_radix(&clean[2..], 2)
253 } else {
254 clean.parse::<i64>()
255 };
256 n.ok().map(Value::Int)
257 }
258 Literal::Float(s) => {
259 let (numeric, _) = bock_ast::strip_type_suffix(s);
260 numeric
261 .replace('_', "")
262 .parse::<f64>()
263 .ok()
264 .map(|f| Value::Float(OrdF64(f)))
265 }
266 Literal::Bool(b) => Some(Value::Bool(*b)),
267 Literal::Char(s) => Some(Value::Char(s.chars().next().unwrap_or('\0'))),
268 Literal::String(s) => Some(Value::String(BockString::new(s.clone()))),
269 Literal::Unit => Some(Value::Void),
270 }
271}
272
273#[cfg(test)]
276mod tests {
277 use super::*;
278 use crate::value::{EnumValue, RecordValue};
279 use bock_air::NodeIdGen;
280 use bock_ast::{Ident, TypePath};
281 use bock_errors::Span;
282 use std::collections::BTreeMap;
283
284 fn span() -> Span {
285 Span::dummy()
286 }
287
288 fn ident(name: &str) -> Ident {
289 Ident {
290 name: name.to_string(),
291 span: span(),
292 }
293 }
294
295 fn gen() -> NodeIdGen {
296 NodeIdGen::new()
297 }
298
299 fn type_path(name: &str) -> TypePath {
300 TypePath {
301 segments: vec![ident(name)],
302 span: span(),
303 }
304 }
305
306 #[test]
309 fn wildcard_matches_anything() {
310 let g = gen();
311 let pat = Pattern::Wildcard {
312 id: g.next(),
313 span: span(),
314 };
315 assert_eq!(match_pattern(&Value::Int(42), &pat), Some(vec![]));
316 assert_eq!(match_pattern(&Value::Bool(true), &pat), Some(vec![]));
317 assert_eq!(match_pattern(&Value::Void, &pat), Some(vec![]));
318 }
319
320 #[test]
323 fn bind_captures_value() {
324 let g = gen();
325 let pat = Pattern::Bind {
326 id: g.next(),
327 span: span(),
328 name: ident("x"),
329 };
330 let result = match_pattern(&Value::Int(99), &pat);
331 assert_eq!(result, Some(vec![("x".into(), Value::Int(99))]));
332 }
333
334 #[test]
335 fn mut_bind_captures_value() {
336 let g = gen();
337 let pat = Pattern::MutBind {
338 id: g.next(),
339 span: span(),
340 name: ident("y"),
341 };
342 let result = match_pattern(&Value::Bool(true), &pat);
343 assert_eq!(result, Some(vec![("y".into(), Value::Bool(true))]));
344 }
345
346 #[test]
349 fn literal_int_match() {
350 let g = gen();
351 let pat = Pattern::Literal {
352 id: g.next(),
353 span: span(),
354 lit: Literal::Int("42".to_string()),
355 };
356 assert_eq!(match_pattern(&Value::Int(42), &pat), Some(vec![]));
357 assert_eq!(match_pattern(&Value::Int(99), &pat), None);
358 }
359
360 #[test]
361 fn literal_bool_match() {
362 let g = gen();
363 let pat = Pattern::Literal {
364 id: g.next(),
365 span: span(),
366 lit: Literal::Bool(true),
367 };
368 assert_eq!(match_pattern(&Value::Bool(true), &pat), Some(vec![]));
369 assert_eq!(match_pattern(&Value::Bool(false), &pat), None);
370 }
371
372 #[test]
373 fn literal_string_match() {
374 let g = gen();
375 let pat = Pattern::Literal {
376 id: g.next(),
377 span: span(),
378 lit: Literal::String("hello".to_string()),
379 };
380 assert_eq!(
381 match_pattern(&Value::String(BockString::new("hello")), &pat),
382 Some(vec![])
383 );
384 assert_eq!(
385 match_pattern(&Value::String(BockString::new("world")), &pat),
386 None
387 );
388 }
389
390 #[test]
391 fn literal_float_match() {
392 let g = gen();
393 let pat = Pattern::Literal {
394 id: g.next(),
395 span: span(),
396 lit: Literal::Float("3.14".to_string()),
397 };
398 assert_eq!(
399 match_pattern(&Value::Float(OrdF64(3.14)), &pat),
400 Some(vec![])
401 );
402 }
403
404 #[test]
405 fn literal_unit_match() {
406 let g = gen();
407 let pat = Pattern::Literal {
408 id: g.next(),
409 span: span(),
410 lit: Literal::Unit,
411 };
412 assert_eq!(match_pattern(&Value::Void, &pat), Some(vec![]));
413 }
414
415 #[test]
418 fn constructor_some_match() {
419 let g = gen();
420 let inner_pat = Pattern::Bind {
421 id: g.next(),
422 span: span(),
423 name: ident("x"),
424 };
425 let pat = Pattern::Constructor {
426 id: g.next(),
427 span: span(),
428 path: type_path("Some"),
429 fields: vec![inner_pat],
430 };
431 let value = Value::Optional(Some(Box::new(Value::Int(5))));
432 let result = match_pattern(&value, &pat);
433 assert_eq!(result, Some(vec![("x".into(), Value::Int(5))]));
434 }
435
436 #[test]
437 fn constructor_none_match() {
438 let g = gen();
439 let pat = Pattern::Constructor {
440 id: g.next(),
441 span: span(),
442 path: type_path("None"),
443 fields: vec![],
444 };
445 assert_eq!(match_pattern(&Value::Optional(None), &pat), Some(vec![]));
446 let some = Value::Optional(Some(Box::new(Value::Int(1))));
448 assert_eq!(match_pattern(&some, &pat), None);
449 }
450
451 #[test]
452 fn constructor_ok_match() {
453 let g = gen();
454 let inner = Pattern::Bind {
455 id: g.next(),
456 span: span(),
457 name: ident("v"),
458 };
459 let pat = Pattern::Constructor {
460 id: g.next(),
461 span: span(),
462 path: type_path("Ok"),
463 fields: vec![inner],
464 };
465 let value = Value::Result(Ok(Box::new(Value::Int(42))));
466 assert_eq!(
467 match_pattern(&value, &pat),
468 Some(vec![("v".into(), Value::Int(42))])
469 );
470 }
471
472 #[test]
473 fn constructor_err_match() {
474 let g = gen();
475 let inner = Pattern::Bind {
476 id: g.next(),
477 span: span(),
478 name: ident("e"),
479 };
480 let pat = Pattern::Constructor {
481 id: g.next(),
482 span: span(),
483 path: type_path("Err"),
484 fields: vec![inner],
485 };
486 let value = Value::Result(Err(Box::new(Value::String(BockString::new("fail")))));
487 assert_eq!(
488 match_pattern(&value, &pat),
489 Some(vec![("e".into(), Value::String(BockString::new("fail")))])
490 );
491 }
492
493 #[test]
494 fn constructor_enum_match() {
495 let g = gen();
496 let inner = Pattern::Bind {
497 id: g.next(),
498 span: span(),
499 name: ident("r"),
500 };
501 let pat = Pattern::Constructor {
502 id: g.next(),
503 span: span(),
504 path: type_path("Circle"),
505 fields: vec![inner],
506 };
507 let value = Value::Enum(EnumValue {
508 type_name: "Shape".into(),
509 variant: "Circle".into(),
510 payload: Some(Box::new(Value::Float(OrdF64(1.5)))),
511 });
512 assert_eq!(
513 match_pattern(&value, &pat),
514 Some(vec![("r".into(), Value::Float(OrdF64(1.5)))])
515 );
516 }
517
518 #[test]
519 fn constructor_enum_no_payload() {
520 let g = gen();
521 let pat = Pattern::Constructor {
522 id: g.next(),
523 span: span(),
524 path: type_path("Red"),
525 fields: vec![],
526 };
527 let value = Value::Enum(EnumValue {
528 type_name: "Color".into(),
529 variant: "Red".into(),
530 payload: None,
531 });
532 assert_eq!(match_pattern(&value, &pat), Some(vec![]));
533 }
534
535 #[test]
536 fn constructor_enum_wrong_variant() {
537 let g = gen();
538 let pat = Pattern::Constructor {
539 id: g.next(),
540 span: span(),
541 path: type_path("Red"),
542 fields: vec![],
543 };
544 let value = Value::Enum(EnumValue {
545 type_name: "Color".into(),
546 variant: "Blue".into(),
547 payload: None,
548 });
549 assert_eq!(match_pattern(&value, &pat), None);
550 }
551
552 #[test]
555 fn record_match_shorthand() {
556 let g = gen();
557 let pat = Pattern::Record {
558 id: g.next(),
559 span: span(),
560 path: type_path("Point"),
561 fields: vec![
562 RecordPatternField {
563 span: span(),
564 name: ident("x"),
565 pattern: None,
566 },
567 RecordPatternField {
568 span: span(),
569 name: ident("y"),
570 pattern: None,
571 },
572 ],
573 rest: false,
574 };
575 let mut fields = BTreeMap::new();
576 fields.insert("x".to_string(), Value::Int(1));
577 fields.insert("y".to_string(), Value::Int(2));
578 let value = Value::Record(RecordValue {
579 type_name: "Point".into(),
580 fields,
581 });
582 let result = match_pattern(&value, &pat).unwrap();
583 assert!(result.contains(&("x".into(), Value::Int(1))));
584 assert!(result.contains(&("y".into(), Value::Int(2))));
585 }
586
587 #[test]
588 fn record_match_with_sub_pattern() {
589 let g = gen();
590 let pat = Pattern::Record {
591 id: g.next(),
592 span: span(),
593 path: type_path("User"),
594 fields: vec![RecordPatternField {
595 span: span(),
596 name: ident("name"),
597 pattern: Some(Pattern::Bind {
598 id: g.next(),
599 span: span(),
600 name: ident("n"),
601 }),
602 }],
603 rest: true,
604 };
605 let mut fields = BTreeMap::new();
606 fields.insert("name".to_string(), Value::String(BockString::new("alice")));
607 fields.insert("age".to_string(), Value::Int(30));
608 let value = Value::Record(RecordValue {
609 type_name: "User".into(),
610 fields,
611 });
612 let result = match_pattern(&value, &pat);
613 assert_eq!(
614 result,
615 Some(vec![("n".into(), Value::String(BockString::new("alice")))])
616 );
617 }
618
619 #[test]
620 fn record_wrong_type_name() {
621 let g = gen();
622 let pat = Pattern::Record {
623 id: g.next(),
624 span: span(),
625 path: type_path("Point"),
626 fields: vec![],
627 rest: true,
628 };
629 let value = Value::Record(RecordValue {
630 type_name: "Rect".into(),
631 fields: BTreeMap::new(),
632 });
633 assert_eq!(match_pattern(&value, &pat), None);
634 }
635
636 #[test]
637 fn record_rest_ignores_extra_fields() {
638 let g = gen();
639 let pat = Pattern::Record {
640 id: g.next(),
641 span: span(),
642 path: type_path("Point"),
643 fields: vec![RecordPatternField {
644 span: span(),
645 name: ident("x"),
646 pattern: None,
647 }],
648 rest: true,
649 };
650 let mut fields = BTreeMap::new();
651 fields.insert("x".to_string(), Value::Int(1));
652 fields.insert("y".to_string(), Value::Int(2));
653 fields.insert("z".to_string(), Value::Int(3));
654 let value = Value::Record(RecordValue {
655 type_name: "Point".into(),
656 fields,
657 });
658 let result = match_pattern(&value, &pat);
659 assert_eq!(result, Some(vec![("x".into(), Value::Int(1))]));
660 }
661
662 #[test]
663 fn record_no_rest_field_count_mismatch() {
664 let g = gen();
665 let pat = Pattern::Record {
666 id: g.next(),
667 span: span(),
668 path: type_path("Point"),
669 fields: vec![RecordPatternField {
670 span: span(),
671 name: ident("x"),
672 pattern: None,
673 }],
674 rest: false,
675 };
676 let mut fields = BTreeMap::new();
677 fields.insert("x".to_string(), Value::Int(1));
678 fields.insert("y".to_string(), Value::Int(2));
679 let value = Value::Record(RecordValue {
680 type_name: "Point".into(),
681 fields,
682 });
683 assert_eq!(match_pattern(&value, &pat), None);
684 }
685
686 #[test]
689 fn tuple_match() {
690 let g = gen();
691 let pat = Pattern::Tuple {
692 id: g.next(),
693 span: span(),
694 elems: vec![
695 Pattern::Bind {
696 id: g.next(),
697 span: span(),
698 name: ident("a"),
699 },
700 Pattern::Bind {
701 id: g.next(),
702 span: span(),
703 name: ident("b"),
704 },
705 ],
706 };
707 let value = Value::Tuple(vec![Value::Int(1), Value::Int(2)]);
708 let result = match_pattern(&value, &pat);
709 assert_eq!(
710 result,
711 Some(vec![
712 ("a".into(), Value::Int(1)),
713 ("b".into(), Value::Int(2))
714 ])
715 );
716 }
717
718 #[test]
719 fn tuple_length_mismatch() {
720 let g = gen();
721 let pat = Pattern::Tuple {
722 id: g.next(),
723 span: span(),
724 elems: vec![Pattern::Bind {
725 id: g.next(),
726 span: span(),
727 name: ident("a"),
728 }],
729 };
730 let value = Value::Tuple(vec![Value::Int(1), Value::Int(2)]);
731 assert_eq!(match_pattern(&value, &pat), None);
732 }
733
734 #[test]
737 fn list_exact_match() {
738 let g = gen();
739 let pat = Pattern::List {
740 id: g.next(),
741 span: span(),
742 elems: vec![
743 Pattern::Bind {
744 id: g.next(),
745 span: span(),
746 name: ident("a"),
747 },
748 Pattern::Bind {
749 id: g.next(),
750 span: span(),
751 name: ident("b"),
752 },
753 ],
754 rest: None,
755 };
756 let value = Value::List(vec![Value::Int(10), Value::Int(20)]);
757 let result = match_pattern(&value, &pat);
758 assert_eq!(
759 result,
760 Some(vec![
761 ("a".into(), Value::Int(10)),
762 ("b".into(), Value::Int(20))
763 ])
764 );
765 }
766
767 #[test]
768 fn list_with_rest_bind() {
769 let g = gen();
770 let pat = Pattern::List {
771 id: g.next(),
772 span: span(),
773 elems: vec![Pattern::Bind {
774 id: g.next(),
775 span: span(),
776 name: ident("head"),
777 }],
778 rest: Some(Box::new(Pattern::Bind {
779 id: g.next(),
780 span: span(),
781 name: ident("tail"),
782 })),
783 };
784 let value = Value::List(vec![Value::Int(1), Value::Int(2), Value::Int(3)]);
785 let result = match_pattern(&value, &pat);
786 assert_eq!(
787 result,
788 Some(vec![
789 ("head".into(), Value::Int(1)),
790 (
791 "tail".into(),
792 Value::List(vec![Value::Int(2), Value::Int(3)])
793 ),
794 ])
795 );
796 }
797
798 #[test]
799 fn list_with_rest_wildcard() {
800 let g = gen();
801 let pat = Pattern::List {
802 id: g.next(),
803 span: span(),
804 elems: vec![Pattern::Bind {
805 id: g.next(),
806 span: span(),
807 name: ident("first"),
808 }],
809 rest: Some(Box::new(Pattern::Rest {
810 id: g.next(),
811 span: span(),
812 })),
813 };
814 let value = Value::List(vec![Value::Int(1), Value::Int(2), Value::Int(3)]);
815 let result = match_pattern(&value, &pat);
816 assert_eq!(result, Some(vec![("first".into(), Value::Int(1))]));
817 }
818
819 #[test]
820 fn list_too_short() {
821 let g = gen();
822 let pat = Pattern::List {
823 id: g.next(),
824 span: span(),
825 elems: vec![
826 Pattern::Bind {
827 id: g.next(),
828 span: span(),
829 name: ident("a"),
830 },
831 Pattern::Bind {
832 id: g.next(),
833 span: span(),
834 name: ident("b"),
835 },
836 ],
837 rest: None,
838 };
839 let value = Value::List(vec![Value::Int(1)]);
840 assert_eq!(match_pattern(&value, &pat), None);
841 }
842
843 #[test]
844 fn list_no_rest_length_mismatch() {
845 let g = gen();
846 let pat = Pattern::List {
847 id: g.next(),
848 span: span(),
849 elems: vec![Pattern::Bind {
850 id: g.next(),
851 span: span(),
852 name: ident("a"),
853 }],
854 rest: None,
855 };
856 let value = Value::List(vec![Value::Int(1), Value::Int(2)]);
857 assert_eq!(match_pattern(&value, &pat), None);
858 }
859
860 #[test]
863 fn or_pattern_first_alternative() {
864 let g = gen();
865 let pat = Pattern::Or {
866 id: g.next(),
867 span: span(),
868 alternatives: vec![
869 Pattern::Literal {
870 id: g.next(),
871 span: span(),
872 lit: Literal::Int("1".into()),
873 },
874 Pattern::Literal {
875 id: g.next(),
876 span: span(),
877 lit: Literal::Int("2".into()),
878 },
879 ],
880 };
881 assert_eq!(match_pattern(&Value::Int(1), &pat), Some(vec![]));
882 }
883
884 #[test]
885 fn or_pattern_second_alternative() {
886 let g = gen();
887 let pat = Pattern::Or {
888 id: g.next(),
889 span: span(),
890 alternatives: vec![
891 Pattern::Literal {
892 id: g.next(),
893 span: span(),
894 lit: Literal::Int("1".into()),
895 },
896 Pattern::Literal {
897 id: g.next(),
898 span: span(),
899 lit: Literal::Int("2".into()),
900 },
901 ],
902 };
903 assert_eq!(match_pattern(&Value::Int(2), &pat), Some(vec![]));
904 }
905
906 #[test]
907 fn or_pattern_no_match() {
908 let g = gen();
909 let pat = Pattern::Or {
910 id: g.next(),
911 span: span(),
912 alternatives: vec![
913 Pattern::Literal {
914 id: g.next(),
915 span: span(),
916 lit: Literal::Int("1".into()),
917 },
918 Pattern::Literal {
919 id: g.next(),
920 span: span(),
921 lit: Literal::Int("2".into()),
922 },
923 ],
924 };
925 assert_eq!(match_pattern(&Value::Int(3), &pat), None);
926 }
927
928 #[test]
931 fn range_exclusive() {
932 let g = gen();
933 let pat = Pattern::Range {
934 id: g.next(),
935 span: span(),
936 lo: Box::new(Pattern::Literal {
937 id: g.next(),
938 span: span(),
939 lit: Literal::Int("1".into()),
940 }),
941 hi: Box::new(Pattern::Literal {
942 id: g.next(),
943 span: span(),
944 lit: Literal::Int("10".into()),
945 }),
946 inclusive: false,
947 };
948 assert_eq!(match_pattern(&Value::Int(1), &pat), Some(vec![]));
949 assert_eq!(match_pattern(&Value::Int(5), &pat), Some(vec![]));
950 assert_eq!(match_pattern(&Value::Int(10), &pat), None); assert_eq!(match_pattern(&Value::Int(0), &pat), None);
952 }
953
954 #[test]
955 fn range_inclusive() {
956 let g = gen();
957 let pat = Pattern::Range {
958 id: g.next(),
959 span: span(),
960 lo: Box::new(Pattern::Literal {
961 id: g.next(),
962 span: span(),
963 lit: Literal::Int("1".into()),
964 }),
965 hi: Box::new(Pattern::Literal {
966 id: g.next(),
967 span: span(),
968 lit: Literal::Int("10".into()),
969 }),
970 inclusive: true,
971 };
972 assert_eq!(match_pattern(&Value::Int(10), &pat), Some(vec![]));
973 assert_eq!(match_pattern(&Value::Int(11), &pat), None);
974 }
975
976 #[test]
979 fn rest_pattern_matches_anything() {
980 let g = gen();
981 let pat = Pattern::Rest {
982 id: g.next(),
983 span: span(),
984 };
985 assert_eq!(match_pattern(&Value::Int(42), &pat), Some(vec![]));
986 }
987
988 #[test]
991 fn nested_some_ok_tuple() {
992 let g = gen();
994 let tuple_pat = Pattern::Tuple {
995 id: g.next(),
996 span: span(),
997 elems: vec![
998 Pattern::Bind {
999 id: g.next(),
1000 span: span(),
1001 name: ident("a"),
1002 },
1003 Pattern::Bind {
1004 id: g.next(),
1005 span: span(),
1006 name: ident("b"),
1007 },
1008 ],
1009 };
1010 let ok_pat = Pattern::Constructor {
1011 id: g.next(),
1012 span: span(),
1013 path: type_path("Ok"),
1014 fields: vec![tuple_pat],
1015 };
1016 let some_pat = Pattern::Constructor {
1017 id: g.next(),
1018 span: span(),
1019 path: type_path("Some"),
1020 fields: vec![ok_pat],
1021 };
1022 let value = Value::Optional(Some(Box::new(Value::Result(Ok(Box::new(Value::Tuple(
1023 vec![Value::Int(1), Value::Int(2)],
1024 )))))));
1025 let result = match_pattern(&value, &some_pat);
1026 assert_eq!(
1027 result,
1028 Some(vec![
1029 ("a".into(), Value::Int(1)),
1030 ("b".into(), Value::Int(2))
1031 ])
1032 );
1033 }
1034
1035 #[test]
1036 fn nested_some_ok_mismatch() {
1037 let g = gen();
1039 let tuple_pat = Pattern::Tuple {
1040 id: g.next(),
1041 span: span(),
1042 elems: vec![
1043 Pattern::Bind {
1044 id: g.next(),
1045 span: span(),
1046 name: ident("a"),
1047 },
1048 Pattern::Bind {
1049 id: g.next(),
1050 span: span(),
1051 name: ident("b"),
1052 },
1053 ],
1054 };
1055 let ok_pat = Pattern::Constructor {
1056 id: g.next(),
1057 span: span(),
1058 path: type_path("Ok"),
1059 fields: vec![tuple_pat],
1060 };
1061 let some_pat = Pattern::Constructor {
1062 id: g.next(),
1063 span: span(),
1064 path: type_path("Some"),
1065 fields: vec![ok_pat],
1066 };
1067 let value = Value::Optional(Some(Box::new(Value::Result(Err(Box::new(Value::Int(99)))))));
1068 assert_eq!(match_pattern(&value, &some_pat), None);
1069 }
1070
1071 #[test]
1072 fn nested_list_with_constructor() {
1073 let g = gen();
1075 let some_pat = Pattern::Constructor {
1076 id: g.next(),
1077 span: span(),
1078 path: type_path("Some"),
1079 fields: vec![Pattern::Bind {
1080 id: g.next(),
1081 span: span(),
1082 name: ident("x"),
1083 }],
1084 };
1085 let pat = Pattern::List {
1086 id: g.next(),
1087 span: span(),
1088 elems: vec![some_pat],
1089 rest: Some(Box::new(Pattern::Rest {
1090 id: g.next(),
1091 span: span(),
1092 })),
1093 };
1094 let value = Value::List(vec![
1095 Value::Optional(Some(Box::new(Value::Int(5)))),
1096 Value::Optional(None),
1097 ]);
1098 let result = match_pattern(&value, &pat);
1099 assert_eq!(result, Some(vec![("x".into(), Value::Int(5))]));
1100 }
1101
1102 #[test]
1103 fn record_with_nested_constructor() {
1104 let g = gen();
1106 let pat = Pattern::Record {
1107 id: g.next(),
1108 span: span(),
1109 path: type_path("Point"),
1110 fields: vec![
1111 RecordPatternField {
1112 span: span(),
1113 name: ident("x"),
1114 pattern: Some(Pattern::Literal {
1115 id: g.next(),
1116 span: span(),
1117 lit: Literal::Int("0".to_string()),
1118 }),
1119 },
1120 RecordPatternField {
1121 span: span(),
1122 name: ident("y"),
1123 pattern: None,
1124 },
1125 ],
1126 rest: false,
1127 };
1128 let mut fields = BTreeMap::new();
1129 fields.insert("x".to_string(), Value::Int(0));
1130 fields.insert("y".to_string(), Value::Int(5));
1131 let value = Value::Record(RecordValue {
1132 type_name: "Point".into(),
1133 fields,
1134 });
1135 let result = match_pattern(&value, &pat);
1136 assert_eq!(result, Some(vec![("y".into(), Value::Int(5))]));
1137 }
1138
1139 #[test]
1140 fn record_with_nested_constructor_mismatch() {
1141 let g = gen();
1142 let pat = Pattern::Record {
1143 id: g.next(),
1144 span: span(),
1145 path: type_path("Point"),
1146 fields: vec![
1147 RecordPatternField {
1148 span: span(),
1149 name: ident("x"),
1150 pattern: Some(Pattern::Literal {
1151 id: g.next(),
1152 span: span(),
1153 lit: Literal::Int("0".to_string()),
1154 }),
1155 },
1156 RecordPatternField {
1157 span: span(),
1158 name: ident("y"),
1159 pattern: None,
1160 },
1161 ],
1162 rest: false,
1163 };
1164 let mut fields = BTreeMap::new();
1165 fields.insert("x".to_string(), Value::Int(1)); fields.insert("y".to_string(), Value::Int(5));
1167 let value = Value::Record(RecordValue {
1168 type_name: "Point".into(),
1169 fields,
1170 });
1171 assert_eq!(match_pattern(&value, &pat), None);
1172 }
1173
1174 #[test]
1177 fn tuple_pattern_against_non_tuple() {
1178 let g = gen();
1179 let pat = Pattern::Tuple {
1180 id: g.next(),
1181 span: span(),
1182 elems: vec![Pattern::Wildcard {
1183 id: g.next(),
1184 span: span(),
1185 }],
1186 };
1187 assert_eq!(match_pattern(&Value::Int(1), &pat), None);
1188 }
1189
1190 #[test]
1191 fn list_pattern_against_non_list() {
1192 let g = gen();
1193 let pat = Pattern::List {
1194 id: g.next(),
1195 span: span(),
1196 elems: vec![],
1197 rest: None,
1198 };
1199 assert_eq!(match_pattern(&Value::Int(1), &pat), None);
1200 }
1201
1202 #[test]
1203 fn record_pattern_against_non_record() {
1204 let g = gen();
1205 let pat = Pattern::Record {
1206 id: g.next(),
1207 span: span(),
1208 path: type_path("Foo"),
1209 fields: vec![],
1210 rest: true,
1211 };
1212 assert_eq!(match_pattern(&Value::Int(1), &pat), None);
1213 }
1214}