1use super::types::PatternTagExt;
6
7#[cfg(test)]
8mod tests {
9 use super::*;
10 use crate::ast_impl::{Pattern, SurfaceExpr};
11 use crate::pattern::*;
12 use crate::tokens::Span;
13 use crate::{Literal, Located};
14 fn mk_span() -> Span {
15 Span::new(0, 1, 1, 1)
16 }
17 fn mk_located<T>(value: T) -> Located<T> {
18 Located::new(value, mk_span())
19 }
20 #[test]
21 fn test_fresh_var() {
22 let mut compiler = PatternCompiler::new();
23 let var1 = compiler.fresh_var();
24 let var2 = compiler.fresh_var();
25 assert_ne!(var1, var2);
26 }
27 #[test]
28 fn test_compile_match_empty() {
29 let mut compiler = PatternCompiler::new();
30 let scrutinee = SurfaceExpr::Lit(Literal::Nat(42));
31 let result = compiler.compile_match(&scrutinee, &[]);
32 assert!(result.is_err());
33 }
34 #[test]
35 fn test_check_exhaustive_wildcard() {
36 let compiler = PatternCompiler::new();
37 let patterns = vec![Pattern::Wild];
38 assert!(compiler.check_exhaustive(&patterns).is_ok());
39 }
40 #[test]
41 fn test_check_exhaustive_none() {
42 let compiler = PatternCompiler::new();
43 let patterns = vec![];
44 assert!(compiler.check_exhaustive(&patterns).is_err());
45 }
46 #[test]
47 fn test_check_redundant() {
48 let compiler = PatternCompiler::new();
49 let patterns = vec![Pattern::Var("x".to_string()), Pattern::Wild];
50 let redundant = compiler.check_redundant(&patterns);
51 assert_eq!(redundant.len(), 1);
52 assert_eq!(redundant[0], 1);
53 }
54 #[test]
55 fn test_is_irrefutable_wild() {
56 let compiler = PatternCompiler::new();
57 assert!(compiler.is_irrefutable(&Pattern::Wild));
58 }
59 #[test]
60 fn test_is_irrefutable_var() {
61 let compiler = PatternCompiler::new();
62 assert!(compiler.is_irrefutable(&Pattern::Var("x".to_string())));
63 }
64 #[test]
65 fn test_is_irrefutable_ctor() {
66 let compiler = PatternCompiler::new();
67 assert!(!compiler.is_irrefutable(&Pattern::Ctor("Some".to_string(), vec![])));
68 }
69 #[test]
70 fn test_is_irrefutable_lit() {
71 let compiler = PatternCompiler::new();
72 assert!(!compiler.is_irrefutable(&Pattern::Lit(Literal::Nat(0))));
73 }
74 #[test]
75 fn test_is_irrefutable_or_with_wild() {
76 let compiler = PatternCompiler::new();
77 let pat = Pattern::Or(
78 Box::new(mk_located(Pattern::Ctor("Some".to_string(), vec![]))),
79 Box::new(mk_located(Pattern::Wild)),
80 );
81 assert!(compiler.is_irrefutable(&pat));
82 }
83 #[test]
84 fn test_count_bindings_wild() {
85 let compiler = PatternCompiler::new();
86 assert_eq!(compiler.count_bindings(&Pattern::Wild), 0);
87 }
88 #[test]
89 fn test_count_bindings_var() {
90 let compiler = PatternCompiler::new();
91 assert_eq!(compiler.count_bindings(&Pattern::Var("x".to_string())), 1);
92 }
93 #[test]
94 fn test_count_bindings_ctor() {
95 let compiler = PatternCompiler::new();
96 let pat = Pattern::Ctor(
97 "Pair".to_string(),
98 vec![
99 mk_located(Pattern::Var("a".to_string())),
100 mk_located(Pattern::Var("b".to_string())),
101 ],
102 );
103 assert_eq!(compiler.count_bindings(&pat), 2);
104 }
105 #[test]
106 fn test_count_bindings_or_pattern() {
107 let compiler = PatternCompiler::new();
108 let pat = Pattern::Or(
109 Box::new(mk_located(Pattern::Var("x".to_string()))),
110 Box::new(mk_located(Pattern::Var("y".to_string()))),
111 );
112 assert_eq!(compiler.count_bindings(&pat), 1);
113 }
114 #[test]
115 fn test_extract_bound_names_empty() {
116 let compiler = PatternCompiler::new();
117 assert!(compiler.extract_bound_names(&Pattern::Wild).is_empty());
118 }
119 #[test]
120 fn test_extract_bound_names_var() {
121 let compiler = PatternCompiler::new();
122 let names = compiler.extract_bound_names(&Pattern::Var("x".to_string()));
123 assert_eq!(names, vec!["x"]);
124 }
125 #[test]
126 fn test_extract_bound_names_nested() {
127 let compiler = PatternCompiler::new();
128 let pat = Pattern::Ctor(
129 "Pair".to_string(),
130 vec![
131 mk_located(Pattern::Var("a".to_string())),
132 mk_located(Pattern::Ctor(
133 "Some".to_string(),
134 vec![mk_located(Pattern::Var("b".to_string()))],
135 )),
136 ],
137 );
138 let names = compiler.extract_bound_names(&pat);
139 assert_eq!(names, vec!["a", "b"]);
140 }
141 #[test]
142 fn test_extract_bound_names_or() {
143 let compiler = PatternCompiler::new();
144 let pat = Pattern::Or(
145 Box::new(mk_located(Pattern::Var("x".to_string()))),
146 Box::new(mk_located(Pattern::Var("y".to_string()))),
147 );
148 let names = compiler.extract_bound_names(&pat);
149 assert_eq!(names, vec!["x"]);
150 }
151 #[test]
152 fn test_pattern_to_string_wild() {
153 let compiler = PatternCompiler::new();
154 assert_eq!(compiler.pattern_to_string(&Pattern::Wild), "_");
155 }
156 #[test]
157 fn test_pattern_to_string_var() {
158 let compiler = PatternCompiler::new();
159 assert_eq!(
160 compiler.pattern_to_string(&Pattern::Var("x".to_string())),
161 "x"
162 );
163 }
164 #[test]
165 fn test_pattern_to_string_ctor_no_args() {
166 let compiler = PatternCompiler::new();
167 assert_eq!(
168 compiler.pattern_to_string(&Pattern::Ctor("None".to_string(), vec![])),
169 "None"
170 );
171 }
172 #[test]
173 fn test_pattern_to_string_ctor_with_args() {
174 let compiler = PatternCompiler::new();
175 let pat = Pattern::Ctor(
176 "Some".to_string(),
177 vec![mk_located(Pattern::Var("x".to_string()))],
178 );
179 assert_eq!(compiler.pattern_to_string(&pat), "Some x");
180 }
181 #[test]
182 fn test_pattern_to_string_or() {
183 let compiler = PatternCompiler::new();
184 let pat = Pattern::Or(
185 Box::new(mk_located(Pattern::Ctor("None".to_string(), vec![]))),
186 Box::new(mk_located(Pattern::Var("x".to_string()))),
187 );
188 assert_eq!(compiler.pattern_to_string(&pat), "None | x");
189 }
190 #[test]
191 fn test_pattern_to_string_lit() {
192 let compiler = PatternCompiler::new();
193 assert_eq!(
194 compiler.pattern_to_string(&Pattern::Lit(Literal::Nat(42))),
195 "42"
196 );
197 }
198 #[test]
199 fn test_pattern_to_string_nested_ctor() {
200 let compiler = PatternCompiler::new();
201 let pat = Pattern::Ctor(
202 "Some".to_string(),
203 vec![mk_located(Pattern::Ctor(
204 "Pair".to_string(),
205 vec![
206 mk_located(Pattern::Var("a".to_string())),
207 mk_located(Pattern::Var("b".to_string())),
208 ],
209 ))],
210 );
211 let s = compiler.pattern_to_string(&pat);
212 assert_eq!(s, "Some (Pair a b)");
213 }
214 #[test]
215 fn test_simplify_pattern_wild() {
216 let compiler = PatternCompiler::new();
217 assert_eq!(compiler.simplify_pattern(&Pattern::Wild), Pattern::Wild);
218 }
219 #[test]
220 fn test_simplify_or_with_wild_becomes_wild() {
221 let compiler = PatternCompiler::new();
222 let pat = Pattern::Or(
223 Box::new(mk_located(Pattern::Ctor("Some".to_string(), vec![]))),
224 Box::new(mk_located(Pattern::Wild)),
225 );
226 let simplified = compiler.simplify_pattern(&pat);
227 assert_eq!(simplified, Pattern::Wild);
228 }
229 #[test]
230 fn test_simplify_ctor_with_args() {
231 let compiler = PatternCompiler::new();
232 let pat = Pattern::Ctor(
233 "Pair".to_string(),
234 vec![
235 mk_located(Pattern::Var("a".to_string())),
236 mk_located(Pattern::Wild),
237 ],
238 );
239 let simplified = compiler.simplify_pattern(&pat);
240 match simplified {
241 Pattern::Ctor(name, args) => {
242 assert_eq!(name, "Pair");
243 assert_eq!(args.len(), 2);
244 }
245 _ => panic!("Expected Ctor pattern"),
246 }
247 }
248 #[test]
249 fn test_collect_constructors_empty() {
250 let compiler = PatternCompiler::new();
251 let rows: Vec<PatternRow> = vec![];
252 let ctors = compiler.collect_constructors(&rows, 0);
253 assert!(ctors.is_empty());
254 }
255 #[test]
256 fn test_collect_constructors_single() {
257 let compiler = PatternCompiler::new();
258 let rows = vec![PatternRow {
259 patterns: vec![Pattern::Ctor(
260 "Some".to_string(),
261 vec![mk_located(Pattern::Wild)],
262 )],
263 body: SurfaceExpr::Hole,
264 guard: None,
265 }];
266 let ctors = compiler.collect_constructors(&rows, 0);
267 assert_eq!(ctors.len(), 1);
268 assert_eq!(ctors[0].0, "Some");
269 assert_eq!(ctors[0].1, 1);
270 }
271 #[test]
272 fn test_collect_constructors_dedup() {
273 let compiler = PatternCompiler::new();
274 let rows = vec![
275 PatternRow {
276 patterns: vec![Pattern::Ctor("A".to_string(), vec![])],
277 body: SurfaceExpr::Hole,
278 guard: None,
279 },
280 PatternRow {
281 patterns: vec![Pattern::Ctor("A".to_string(), vec![])],
282 body: SurfaceExpr::Hole,
283 guard: None,
284 },
285 ];
286 let ctors = compiler.collect_constructors(&rows, 0);
287 assert_eq!(ctors.len(), 1);
288 }
289 #[test]
290 fn test_select_column_prefers_constructors() {
291 let compiler = PatternCompiler::new();
292 let rows = vec![
293 PatternRow {
294 patterns: vec![Pattern::Wild, Pattern::Ctor("A".to_string(), vec![])],
295 body: SurfaceExpr::Hole,
296 guard: None,
297 },
298 PatternRow {
299 patterns: vec![Pattern::Wild, Pattern::Ctor("B".to_string(), vec![])],
300 body: SurfaceExpr::Hole,
301 guard: None,
302 },
303 ];
304 let col = compiler.select_column(&rows, 2);
305 assert_eq!(col, 1);
306 }
307 #[test]
308 fn test_default_rows_keeps_wildcards() {
309 let compiler = PatternCompiler::new();
310 let rows = vec![
311 PatternRow {
312 patterns: vec![Pattern::Ctor("A".to_string(), vec![])],
313 body: SurfaceExpr::Hole,
314 guard: None,
315 },
316 PatternRow {
317 patterns: vec![Pattern::Wild],
318 body: SurfaceExpr::Lit(Literal::Nat(1)),
319 guard: None,
320 },
321 ];
322 let defaults = compiler.default_rows(&rows, 0);
323 assert_eq!(defaults.len(), 1);
324 assert_eq!(defaults[0].body, SurfaceExpr::Lit(Literal::Nat(1)));
325 }
326 #[test]
327 fn test_specialize_ctor() {
328 let compiler = PatternCompiler::new();
329 let rows = vec![
330 PatternRow {
331 patterns: vec![Pattern::Ctor(
332 "Some".to_string(),
333 vec![mk_located(Pattern::Var("x".to_string()))],
334 )],
335 body: SurfaceExpr::Var("x".to_string()),
336 guard: None,
337 },
338 PatternRow {
339 patterns: vec![Pattern::Wild],
340 body: SurfaceExpr::Hole,
341 guard: None,
342 },
343 ];
344 let specialized = compiler.specialize(&rows, 0, "Some", 1);
345 assert_eq!(specialized.len(), 2);
346 assert_eq!(specialized[0].patterns.len(), 1);
347 assert!(matches!(specialized[0].patterns[0], Pattern::Var(_)));
348 assert_eq!(specialized[1].patterns.len(), 1);
349 assert!(matches!(specialized[1].patterns[0], Pattern::Wild));
350 }
351 #[test]
352 fn test_compile_matrix_empty() {
353 let mut compiler = PatternCompiler::new();
354 let tree = compiler.compile_matrix(&[], 1);
355 assert_eq!(tree, CaseTree::Failure);
356 }
357 #[test]
358 fn test_compile_matrix_all_wild() {
359 let mut compiler = PatternCompiler::new();
360 let rows = vec![PatternRow {
361 patterns: vec![Pattern::Wild],
362 body: SurfaceExpr::Hole,
363 guard: None,
364 }];
365 let tree = compiler.compile_matrix(&rows, 1);
366 assert_eq!(tree, CaseTree::Leaf { body_idx: 0 });
367 }
368 #[test]
369 fn test_compile_matrix_with_ctors() {
370 let mut compiler = PatternCompiler::new();
371 let rows = vec![
372 PatternRow {
373 patterns: vec![Pattern::Ctor("True".to_string(), vec![])],
374 body: SurfaceExpr::Lit(Literal::Nat(1)),
375 guard: None,
376 },
377 PatternRow {
378 patterns: vec![Pattern::Ctor("False".to_string(), vec![])],
379 body: SurfaceExpr::Lit(Literal::Nat(0)),
380 guard: None,
381 },
382 ];
383 let tree = compiler.compile_matrix(&rows, 1);
384 match tree {
385 CaseTree::Switch {
386 scrutinee,
387 branches,
388 ..
389 } => {
390 assert_eq!(scrutinee, 0);
391 assert_eq!(branches.len(), 2);
392 assert_eq!(branches[0].ctor, "True");
393 assert_eq!(branches[1].ctor, "False");
394 }
395 _ => panic!("Expected Switch"),
396 }
397 }
398 #[test]
399 fn test_check_exhaustive_with_ctors_all_covered() {
400 let compiler = PatternCompiler::new();
401 let ctors = TypeConstructors {
402 type_name: "Bool".to_string(),
403 constructors: vec![
404 ConstructorInfo {
405 name: "True".to_string(),
406 arity: 0,
407 },
408 ConstructorInfo {
409 name: "False".to_string(),
410 arity: 0,
411 },
412 ],
413 };
414 let patterns = vec![
415 Pattern::Ctor("True".to_string(), vec![]),
416 Pattern::Ctor("False".to_string(), vec![]),
417 ];
418 assert!(compiler
419 .check_exhaustive_with_ctors(&patterns, &ctors)
420 .is_ok());
421 }
422 #[test]
423 fn test_check_exhaustive_with_ctors_missing() {
424 let compiler = PatternCompiler::new();
425 let ctors = TypeConstructors {
426 type_name: "Bool".to_string(),
427 constructors: vec![
428 ConstructorInfo {
429 name: "True".to_string(),
430 arity: 0,
431 },
432 ConstructorInfo {
433 name: "False".to_string(),
434 arity: 0,
435 },
436 ],
437 };
438 let patterns = vec![Pattern::Ctor("True".to_string(), vec![])];
439 let result = compiler.check_exhaustive_with_ctors(&patterns, &ctors);
440 assert!(result.is_err());
441 let missing = result.unwrap_err();
442 assert_eq!(missing, vec!["False"]);
443 }
444 #[test]
445 fn test_check_exhaustive_with_ctors_wildcard() {
446 let compiler = PatternCompiler::new();
447 let ctors = TypeConstructors {
448 type_name: "Option".to_string(),
449 constructors: vec![
450 ConstructorInfo {
451 name: "Some".to_string(),
452 arity: 1,
453 },
454 ConstructorInfo {
455 name: "None".to_string(),
456 arity: 0,
457 },
458 ],
459 };
460 let patterns = vec![Pattern::Wild];
461 assert!(compiler
462 .check_exhaustive_with_ctors(&patterns, &ctors)
463 .is_ok());
464 }
465 #[test]
466 fn test_check_nested_exhaustive_ok() {
467 let compiler = PatternCompiler::new();
468 let bool_ctors = TypeConstructors {
469 type_name: "Bool".to_string(),
470 constructors: vec![
471 ConstructorInfo {
472 name: "True".to_string(),
473 arity: 0,
474 },
475 ConstructorInfo {
476 name: "False".to_string(),
477 arity: 0,
478 },
479 ],
480 };
481 let patterns = vec![
482 vec![Pattern::Ctor("True".to_string(), vec![])],
483 vec![Pattern::Ctor("False".to_string(), vec![])],
484 ];
485 assert!(compiler
486 .check_nested_exhaustive(&patterns, &[bool_ctors])
487 .is_ok());
488 }
489 #[test]
490 fn test_check_nested_exhaustive_missing() {
491 let compiler = PatternCompiler::new();
492 let bool_ctors = TypeConstructors {
493 type_name: "Bool".to_string(),
494 constructors: vec![
495 ConstructorInfo {
496 name: "True".to_string(),
497 arity: 0,
498 },
499 ConstructorInfo {
500 name: "False".to_string(),
501 arity: 0,
502 },
503 ],
504 };
505 let patterns = vec![vec![Pattern::Ctor("True".to_string(), vec![])]];
506 let result = compiler.check_nested_exhaustive(&patterns, &[bool_ctors]);
507 assert!(result.is_err());
508 }
509 #[test]
510 fn test_compile_matrix_no_cols() {
511 let mut compiler = PatternCompiler::new();
512 let rows = vec![PatternRow {
513 patterns: vec![],
514 body: SurfaceExpr::Hole,
515 guard: None,
516 }];
517 let tree = compiler.compile_matrix(&rows, 0);
518 assert_eq!(tree, CaseTree::Leaf { body_idx: 0 });
519 }
520 #[test]
521 fn test_specialize_drops_different_ctor() {
522 let compiler = PatternCompiler::new();
523 let rows = vec![PatternRow {
524 patterns: vec![Pattern::Ctor("A".to_string(), vec![])],
525 body: SurfaceExpr::Hole,
526 guard: None,
527 }];
528 let specialized = compiler.specialize(&rows, 0, "B", 0);
529 assert!(specialized.is_empty());
530 }
531 #[test]
532 fn test_default_rows_empty_on_all_ctors() {
533 let compiler = PatternCompiler::new();
534 let rows = vec![
535 PatternRow {
536 patterns: vec![Pattern::Ctor("A".to_string(), vec![])],
537 body: SurfaceExpr::Hole,
538 guard: None,
539 },
540 PatternRow {
541 patterns: vec![Pattern::Ctor("B".to_string(), vec![])],
542 body: SurfaceExpr::Hole,
543 guard: None,
544 },
545 ];
546 let defaults = compiler.default_rows(&rows, 0);
547 assert!(defaults.is_empty());
548 }
549 #[test]
550 fn test_max_pattern_depth_flat() {
551 let compiler = PatternCompiler::new();
552 let pat = Pattern::Var("x".to_string());
553 assert_eq!(compiler.max_pattern_depth(&pat), 0);
554 }
555 #[test]
556 fn test_max_pattern_depth_nested() {
557 let compiler = PatternCompiler::new();
558 let pat = Pattern::Ctor(
559 "Pair".to_string(),
560 vec![
561 mk_located(Pattern::Ctor(
562 "Some".to_string(),
563 vec![mk_located(Pattern::Var("x".to_string()))],
564 )),
565 mk_located(Pattern::Wild),
566 ],
567 );
568 assert_eq!(compiler.max_pattern_depth(&pat), 2);
569 }
570 #[test]
571 fn test_flatten_or_single() {
572 let compiler = PatternCompiler::new();
573 let pat = Pattern::Var("x".to_string());
574 let flat = compiler.flatten_or_pattern(&pat);
575 assert_eq!(flat.len(), 1);
576 }
577 #[test]
578 fn test_flatten_or_nested() {
579 let compiler = PatternCompiler::new();
580 let pat = Pattern::Or(
581 Box::new(mk_located(Pattern::Ctor("A".to_string(), vec![]))),
582 Box::new(mk_located(Pattern::Or(
583 Box::new(mk_located(Pattern::Ctor("B".to_string(), vec![]))),
584 Box::new(mk_located(Pattern::Ctor("C".to_string(), vec![]))),
585 ))),
586 );
587 let flat = compiler.flatten_or_pattern(&pat);
588 assert_eq!(flat.len(), 3);
589 }
590 #[test]
591 fn test_patterns_equivalent_wild() {
592 let compiler = PatternCompiler::new();
593 assert!(compiler.patterns_equivalent(&Pattern::Wild, &Pattern::Wild));
594 }
595 #[test]
596 fn test_patterns_equivalent_var() {
597 let compiler = PatternCompiler::new();
598 assert!(compiler.patterns_equivalent(
599 &Pattern::Var("x".to_string()),
600 &Pattern::Var("x".to_string())
601 ));
602 assert!(!compiler.patterns_equivalent(
603 &Pattern::Var("x".to_string()),
604 &Pattern::Var("y".to_string())
605 ));
606 }
607 #[test]
608 fn test_patterns_equivalent_ctor() {
609 let compiler = PatternCompiler::new();
610 let p1 = Pattern::Ctor(
611 "Some".to_string(),
612 vec![mk_located(Pattern::Var("x".to_string()))],
613 );
614 let p2 = Pattern::Ctor(
615 "Some".to_string(),
616 vec![mk_located(Pattern::Var("x".to_string()))],
617 );
618 let p3 = Pattern::Ctor("None".to_string(), vec![]);
619 assert!(compiler.patterns_equivalent(&p1, &p2));
620 assert!(!compiler.patterns_equivalent(&p1, &p3));
621 }
622 #[test]
623 fn test_patterns_equivalent_or() {
624 let compiler = PatternCompiler::new();
625 let p1 = Pattern::Or(
626 Box::new(mk_located(Pattern::Ctor("A".to_string(), vec![]))),
627 Box::new(mk_located(Pattern::Ctor("B".to_string(), vec![]))),
628 );
629 let p2 = Pattern::Or(
630 Box::new(mk_located(Pattern::Ctor("A".to_string(), vec![]))),
631 Box::new(mk_located(Pattern::Ctor("B".to_string(), vec![]))),
632 );
633 assert!(compiler.patterns_equivalent(&p1, &p2));
634 }
635 #[test]
636 fn test_bound_var_set_empty() {
637 let compiler = PatternCompiler::new();
638 let set = compiler.bound_var_set(&Pattern::Wild);
639 assert!(set.is_empty());
640 }
641 #[test]
642 fn test_bound_var_set_single() {
643 let compiler = PatternCompiler::new();
644 let set = compiler.bound_var_set(&Pattern::Var("x".to_string()));
645 assert_eq!(set.len(), 1);
646 assert!(set.contains("x"));
647 }
648 #[test]
649 fn test_bound_var_set_nested() {
650 let compiler = PatternCompiler::new();
651 let pat = Pattern::Ctor(
652 "Pair".to_string(),
653 vec![
654 mk_located(Pattern::Var("a".to_string())),
655 mk_located(Pattern::Var("b".to_string())),
656 ],
657 );
658 let set = compiler.bound_var_set(&pat);
659 assert_eq!(set.len(), 2);
660 assert!(set.contains("a"));
661 assert!(set.contains("b"));
662 }
663 #[test]
664 fn test_same_bindings_yes() {
665 let compiler = PatternCompiler::new();
666 let p1 = Pattern::Ctor(
667 "Pair".to_string(),
668 vec![
669 mk_located(Pattern::Var("a".to_string())),
670 mk_located(Pattern::Var("b".to_string())),
671 ],
672 );
673 let p2 = Pattern::Ctor(
674 "Pair".to_string(),
675 vec![
676 mk_located(Pattern::Var("a".to_string())),
677 mk_located(Pattern::Var("b".to_string())),
678 ],
679 );
680 assert!(compiler.same_bindings(&[p1, p2]));
681 }
682 #[test]
683 fn test_same_bindings_no() {
684 let compiler = PatternCompiler::new();
685 let p1 = Pattern::Ctor(
686 "Pair".to_string(),
687 vec![
688 mk_located(Pattern::Var("a".to_string())),
689 mk_located(Pattern::Var("b".to_string())),
690 ],
691 );
692 let p2 = Pattern::Ctor(
693 "Pair".to_string(),
694 vec![
695 mk_located(Pattern::Var("x".to_string())),
696 mk_located(Pattern::Var("y".to_string())),
697 ],
698 );
699 assert!(!compiler.same_bindings(&[p1, p2]));
700 }
701 #[test]
702 fn test_find_dead_patterns_none() {
703 let compiler = PatternCompiler::new();
704 let rows = vec![
705 PatternRow {
706 patterns: vec![Pattern::Ctor("A".to_string(), vec![])],
707 body: SurfaceExpr::Hole,
708 guard: None,
709 },
710 PatternRow {
711 patterns: vec![Pattern::Ctor("B".to_string(), vec![])],
712 body: SurfaceExpr::Hole,
713 guard: None,
714 },
715 ];
716 let dead = compiler.find_dead_patterns(&rows);
717 assert!(dead.is_empty());
718 }
719 #[test]
720 fn test_find_dead_patterns_with_wildcard() {
721 let compiler = PatternCompiler::new();
722 let rows = vec![
723 PatternRow {
724 patterns: vec![Pattern::Wild],
725 body: SurfaceExpr::Hole,
726 guard: None,
727 },
728 PatternRow {
729 patterns: vec![Pattern::Ctor("A".to_string(), vec![])],
730 body: SurfaceExpr::Hole,
731 guard: None,
732 },
733 ];
734 let dead = compiler.find_dead_patterns(&rows);
735 assert_eq!(dead.len(), 1);
736 assert_eq!(dead[0], 1);
737 }
738 #[test]
739 fn test_analyze_usefulness_empty() {
740 let compiler = PatternCompiler::new();
741 let new_pat = vec![Pattern::Ctor("A".to_string(), vec![])];
742 assert!(compiler.analyze_usefulness(&[], &new_pat));
743 }
744 #[test]
745 fn test_analyze_usefulness_nongeneral() {
746 let compiler = PatternCompiler::new();
747 let rows = vec![PatternRow {
748 patterns: vec![Pattern::Ctor("A".to_string(), vec![])],
749 body: SurfaceExpr::Hole,
750 guard: None,
751 }];
752 let new_pat = vec![Pattern::Ctor("B".to_string(), vec![])];
753 assert!(compiler.analyze_usefulness(&rows, &new_pat));
754 }
755 #[test]
756 fn test_extract_literal_range_single() {
757 let compiler = PatternCompiler::new();
758 let patterns = vec![Pattern::Lit(crate::Literal::Nat(5))];
759 let range = compiler.extract_literal_range(&patterns);
760 assert_eq!(range, Some((5, 5)));
761 }
762 #[test]
763 fn test_extract_literal_range_multiple() {
764 let compiler = PatternCompiler::new();
765 let patterns = vec![
766 Pattern::Lit(crate::Literal::Nat(1)),
767 Pattern::Lit(crate::Literal::Nat(3)),
768 Pattern::Lit(crate::Literal::Nat(2)),
769 ];
770 let range = compiler.extract_literal_range(&patterns);
771 assert_eq!(range, Some((1, 3)));
772 }
773 #[test]
774 fn test_extract_literal_range_none() {
775 let compiler = PatternCompiler::new();
776 let patterns = vec![Pattern::Wild];
777 let range = compiler.extract_literal_range(&patterns);
778 assert_eq!(range, None);
779 }
780 #[test]
781 fn test_check_range_coverage_full() {
782 let compiler = PatternCompiler::new();
783 let patterns = vec![
784 Pattern::Lit(crate::Literal::Nat(1)),
785 Pattern::Lit(crate::Literal::Nat(2)),
786 Pattern::Lit(crate::Literal::Nat(3)),
787 ];
788 assert!(compiler.check_range_coverage(&patterns, 1, 3));
789 }
790 #[test]
791 fn test_check_range_coverage_partial() {
792 let compiler = PatternCompiler::new();
793 let patterns = vec![
794 Pattern::Lit(crate::Literal::Nat(1)),
795 Pattern::Lit(crate::Literal::Nat(3)),
796 ];
797 assert!(!compiler.check_range_coverage(&patterns, 1, 3));
798 }
799 #[test]
800 fn test_check_range_coverage_with_wildcard() {
801 let compiler = PatternCompiler::new();
802 let patterns = vec![Pattern::Wild];
803 assert!(compiler.check_range_coverage(&patterns, 1, 100));
804 }
805 #[test]
806 fn test_canonicalize_wild() {
807 let compiler = PatternCompiler::new();
808 assert_eq!(compiler.canonicalize(&Pattern::Wild), "_");
809 }
810 #[test]
811 fn test_canonicalize_var() {
812 let compiler = PatternCompiler::new();
813 assert_eq!(compiler.canonicalize(&Pattern::Var("x".to_string())), "x");
814 }
815 #[test]
816 fn test_canonicalize_ctor() {
817 let compiler = PatternCompiler::new();
818 let pat = Pattern::Ctor(
819 "Cons".to_string(),
820 vec![
821 mk_located(Pattern::Var("x".to_string())),
822 mk_located(Pattern::Wild),
823 ],
824 );
825 let canonical = compiler.canonicalize(&pat);
826 assert_eq!(canonical, "Cons x _");
827 }
828 #[test]
829 fn test_nested_pattern_with_depth_limit() {
830 let compiler = PatternCompiler::new();
831 let deeply_nested = Pattern::Ctor(
832 "A".to_string(),
833 vec![mk_located(Pattern::Ctor(
834 "B".to_string(),
835 vec![mk_located(Pattern::Ctor(
836 "C".to_string(),
837 vec![mk_located(Pattern::Var("x".to_string()))],
838 ))],
839 ))],
840 );
841 assert!(compiler.max_pattern_depth(&deeply_nested) >= 2);
842 }
843 #[test]
844 fn test_or_pattern_flattening_complex() {
845 let compiler = PatternCompiler::new();
846 let complex_or = Pattern::Or(
847 Box::new(mk_located(Pattern::Or(
848 Box::new(mk_located(Pattern::Ctor("A".to_string(), vec![]))),
849 Box::new(mk_located(Pattern::Ctor("B".to_string(), vec![]))),
850 ))),
851 Box::new(mk_located(Pattern::Or(
852 Box::new(mk_located(Pattern::Ctor("C".to_string(), vec![]))),
853 Box::new(mk_located(Pattern::Ctor("D".to_string(), vec![]))),
854 ))),
855 );
856 let flat = compiler.flatten_or_pattern(&complex_or);
857 assert_eq!(flat.len(), 4);
858 }
859 #[test]
860 fn test_pattern_coverage_analysis() {
861 let compiler = PatternCompiler::new();
862 let patterns = vec![
863 Pattern::Lit(crate::Literal::Nat(0)),
864 Pattern::Lit(crate::Literal::Nat(1)),
865 ];
866 assert!(!compiler.check_range_coverage(&patterns, 0, 2));
867 }
868 #[test]
869 fn test_bound_names_or_pattern() {
870 let compiler = PatternCompiler::new();
871 let or_pat = Pattern::Or(
872 Box::new(mk_located(Pattern::Ctor(
873 "Some".to_string(),
874 vec![mk_located(Pattern::Var("a".to_string()))],
875 ))),
876 Box::new(mk_located(Pattern::Ctor(
877 "Some".to_string(),
878 vec![mk_located(Pattern::Var("a".to_string()))],
879 ))),
880 );
881 let names = compiler.extract_bound_names(&or_pat);
882 assert_eq!(names, vec!["a"]);
883 }
884 #[test]
885 fn test_collect_constructors_with_or() {
886 let compiler = PatternCompiler::new();
887 let rows = vec![PatternRow {
888 patterns: vec![Pattern::Or(
889 Box::new(mk_located(Pattern::Ctor("A".to_string(), vec![]))),
890 Box::new(mk_located(Pattern::Ctor("B".to_string(), vec![]))),
891 )],
892 body: SurfaceExpr::Hole,
893 guard: None,
894 }];
895 let ctors = compiler.collect_constructors(&rows, 0);
896 assert_eq!(ctors.len(), 2);
897 }
898 #[test]
899 fn test_pattern_simplification_nested_ctor() {
900 let compiler = PatternCompiler::new();
901 let pat = Pattern::Ctor(
902 "Cons".to_string(),
903 vec![
904 mk_located(Pattern::Var("x".to_string())),
905 mk_located(Pattern::Or(
906 Box::new(mk_located(Pattern::Ctor("Nil".to_string(), vec![]))),
907 Box::new(mk_located(Pattern::Wild)),
908 )),
909 ],
910 );
911 let simplified = compiler.simplify_pattern(&pat);
912 assert!(matches!(simplified, Pattern::Ctor(..)));
913 }
914 #[test]
915 fn test_pattern_counting_complex_nested() {
916 let compiler = PatternCompiler::new();
917 let pat = Pattern::Ctor(
918 "Triple".to_string(),
919 vec![
920 mk_located(Pattern::Var("a".to_string())),
921 mk_located(Pattern::Ctor(
922 "Pair".to_string(),
923 vec![
924 mk_located(Pattern::Var("b".to_string())),
925 mk_located(Pattern::Var("c".to_string())),
926 ],
927 )),
928 mk_located(Pattern::Wild),
929 ],
930 );
931 assert_eq!(compiler.count_bindings(&pat), 3);
932 }
933 #[test]
934 fn test_matrix_compilation_with_mixed_patterns() {
935 let mut compiler = PatternCompiler::new();
936 let rows = vec![
937 PatternRow {
938 patterns: vec![
939 Pattern::Lit(crate::Literal::Nat(0)),
940 Pattern::Var("x".to_string()),
941 ],
942 body: SurfaceExpr::Hole,
943 guard: None,
944 },
945 PatternRow {
946 patterns: vec![
947 Pattern::Lit(crate::Literal::Nat(1)),
948 Pattern::Ctor("Some".to_string(), vec![]),
949 ],
950 body: SurfaceExpr::Hole,
951 guard: None,
952 },
953 PatternRow {
954 patterns: vec![Pattern::Wild, Pattern::Wild],
955 body: SurfaceExpr::Hole,
956 guard: None,
957 },
958 ];
959 let tree = compiler.compile_matrix(&rows, 2);
960 assert!(!matches!(tree, CaseTree::Failure));
961 }
962 #[test]
963 fn test_specialize_with_or_pattern() {
964 let compiler = PatternCompiler::new();
965 let rows = vec![
966 PatternRow {
967 patterns: vec![Pattern::Or(
968 Box::new(mk_located(Pattern::Ctor("A".to_string(), vec![]))),
969 Box::new(mk_located(Pattern::Ctor("B".to_string(), vec![]))),
970 )],
971 body: SurfaceExpr::Hole,
972 guard: None,
973 },
974 PatternRow {
975 patterns: vec![Pattern::Wild],
976 body: SurfaceExpr::Hole,
977 guard: None,
978 },
979 ];
980 let spec_a = compiler.specialize(&rows, 0, "A", 0);
981 let spec_b = compiler.specialize(&rows, 0, "B", 0);
982 assert_eq!(spec_a.len(), 2);
983 assert_eq!(spec_b.len(), 2);
984 }
985 #[test]
986 fn test_exhaustiveness_with_or_patterns() {
987 let compiler = PatternCompiler::new();
988 let ctors = TypeConstructors {
989 type_name: "Status".to_string(),
990 constructors: vec![
991 ConstructorInfo {
992 name: "Ok".to_string(),
993 arity: 0,
994 },
995 ConstructorInfo {
996 name: "Err".to_string(),
997 arity: 0,
998 },
999 ],
1000 };
1001 let patterns = vec![Pattern::Or(
1002 Box::new(mk_located(Pattern::Ctor("Ok".to_string(), vec![]))),
1003 Box::new(mk_located(Pattern::Ctor("Err".to_string(), vec![]))),
1004 )];
1005 assert!(compiler
1006 .check_exhaustive_with_ctors(&patterns, &ctors)
1007 .is_ok());
1008 }
1009 #[test]
1010 fn test_multiple_bindings_same_var_or() {
1011 let compiler = PatternCompiler::new();
1012 let or_pat = Pattern::Or(
1013 Box::new(mk_located(Pattern::Var("x".to_string()))),
1014 Box::new(mk_located(Pattern::Ctor("Cons".to_string(), vec![]))),
1015 );
1016 let names = compiler.extract_bound_names(&or_pat);
1017 assert_eq!(names.len(), 1);
1018 }
1019 #[test]
1020 fn test_literal_pattern_nat() {
1021 let compiler = PatternCompiler::new();
1022 let lit_nat = Pattern::Lit(crate::Literal::Nat(42));
1023 assert_eq!(compiler.pattern_to_string(&lit_nat), "42");
1024 }
1025 #[test]
1026 fn test_pattern_equivalence_after_simplification() {
1027 let compiler = PatternCompiler::new();
1028 let original = Pattern::Or(
1029 Box::new(mk_located(Pattern::Var("x".to_string()))),
1030 Box::new(mk_located(Pattern::Wild)),
1031 );
1032 let simplified = compiler.simplify_pattern(&original);
1033 assert_eq!(simplified, Pattern::Wild);
1034 }
1035}
1036#[allow(dead_code)]
1038#[allow(missing_docs)]
1039pub fn classify_pattern_ext(s: &str) -> PatternTagExt {
1040 let s = s.trim();
1041 if s == "_" {
1042 return PatternTagExt::Wild;
1043 }
1044 if s.starts_with(char::is_lowercase) && !s.contains(' ') {
1045 return PatternTagExt::Var;
1046 }
1047 if s.starts_with(char::is_uppercase) {
1048 return PatternTagExt::Ctor;
1049 }
1050 if s.chars().all(|c| c.is_ascii_digit()) {
1051 return PatternTagExt::Lit;
1052 }
1053 if s.starts_with('"') {
1054 return PatternTagExt::Lit;
1055 }
1056 PatternTagExt::Ctor
1057}
1058#[cfg(test)]
1059mod pattern_ext_tests {
1060 use super::*;
1061 use crate::ast_impl::{Pattern, SurfaceExpr};
1062 use crate::pattern::*;
1063 #[test]
1064 fn test_classify_pattern() {
1065 assert_eq!(classify_pattern_ext("_"), PatternTagExt::Wild);
1066 assert_eq!(classify_pattern_ext("x"), PatternTagExt::Var);
1067 assert_eq!(classify_pattern_ext("Nat"), PatternTagExt::Ctor);
1068 assert_eq!(classify_pattern_ext("42"), PatternTagExt::Lit);
1069 }
1070 #[test]
1071 fn test_pattern_coverage() {
1072 let mut cov = PatternCoverageExt::new();
1073 cov.add_arm(PatternTagExt::Ctor);
1074 assert!(!cov.is_complete());
1075 cov.add_arm(PatternTagExt::Wild);
1076 assert!(cov.is_complete());
1077 }
1078 #[test]
1079 fn test_match_arm() {
1080 let arm = MatchArmExt::new("Nat.succ n", "n + 1").with_guard("n > 0");
1081 assert_eq!(arm.pattern, "Nat.succ n");
1082 assert!(arm.guard.is_some());
1083 }
1084}
1085#[cfg(test)]
1086mod pattern_ext2_tests {
1087 use super::*;
1088 use crate::ast_impl::{Pattern, SurfaceExpr};
1089 use crate::pattern::*;
1090 #[test]
1091 fn test_pattern_binding() {
1092 let b = PatternBinding::new("n", 0).with_type("Nat");
1093 assert_eq!(b.name, "n");
1094 assert_eq!(b.position, 0);
1095 assert_eq!(b.ty.as_deref(), Some("Nat"));
1096 }
1097 #[test]
1098 fn test_pattern_matrix_row_wildcard() {
1099 let row = PatternMatrixRow::new(vec!["_", "_"], "body");
1100 assert!(row.is_wildcard_row());
1101 let row2 = PatternMatrixRow::new(vec!["_", "x"], "body");
1102 assert!(!row2.is_wildcard_row());
1103 }
1104}
1105#[allow(dead_code)]
1107#[allow(missing_docs)]
1108pub fn count_pattern_vars(pattern: &str) -> usize {
1109 pattern
1110 .split_whitespace()
1111 .filter(|w| w.starts_with(|c: char| c.is_lowercase()) && !w.contains('.'))
1112 .count()
1113}
1114#[cfg(test)]
1115mod pattern_renamer_tests {
1116 use super::*;
1117 use crate::ast_impl::{Pattern, SurfaceExpr};
1118 use crate::pattern::*;
1119 #[test]
1120 fn test_pattern_renamer() {
1121 let mut r = PatternRenamer::new();
1122 r.add("x", "y");
1123 assert_eq!(r.rename("x"), "y");
1124 assert_eq!(r.rename("z"), "z");
1125 }
1126 #[test]
1127 fn test_count_pattern_vars() {
1128 assert_eq!(count_pattern_vars("Nat.succ n"), 1);
1129 assert_eq!(count_pattern_vars("Prod.mk a b"), 2);
1130 assert_eq!(count_pattern_vars("_"), 0);
1131 }
1132}
1133#[allow(dead_code)]
1135#[allow(missing_docs)]
1136pub fn normalise_or_pattern(pattern: &str) -> String {
1137 if !pattern.contains('|') {
1138 return pattern.to_string();
1139 }
1140 let parts: Vec<&str> = pattern.split('|').map(|s| s.trim()).collect();
1141 let mut sorted = parts.clone();
1142 sorted.sort();
1143 sorted.join(" | ")
1144}
1145#[allow(dead_code)]
1147#[allow(missing_docs)]
1148pub fn pattern_depth(pattern: &str) -> usize {
1149 let mut depth = 0usize;
1150 let mut max_depth = 0usize;
1151 for c in pattern.chars() {
1152 match c {
1153 '(' => {
1154 depth += 1;
1155 if depth > max_depth {
1156 max_depth = depth;
1157 }
1158 }
1159 ')' => {
1160 depth = depth.saturating_sub(1);
1161 }
1162 _ => {}
1163 }
1164 }
1165 max_depth
1166}
1167#[allow(dead_code)]
1169#[allow(missing_docs)]
1170pub fn count_all_vars(patterns: &[&str]) -> usize {
1171 patterns.iter().map(|p| count_pattern_vars(p)).sum()
1172}
1173#[cfg(test)]
1174mod pattern_final_tests {
1175 use super::*;
1176 use crate::ast_impl::{Pattern, SurfaceExpr};
1177 use crate::pattern::*;
1178 #[test]
1179 fn test_normalise_or_pattern() {
1180 let out = normalise_or_pattern("c | a | b");
1181 assert!(out.starts_with("a"));
1182 }
1183 #[test]
1184 fn test_pattern_depth() {
1185 assert_eq!(pattern_depth("Nat.succ n"), 0);
1186 assert_eq!(pattern_depth("Prod.mk (Nat.succ n) m"), 1);
1187 }
1188 #[test]
1189 fn test_count_all_vars() {
1190 let pats = ["Nat.succ n", "Prod.mk a b", "_"];
1191 assert_eq!(count_all_vars(&pats), 3);
1192 }
1193}
1194#[allow(dead_code)]
1196#[allow(missing_docs)]
1197pub fn is_ctor_pattern(s: &str) -> bool {
1198 let s = s.trim();
1199 s.starts_with(|c: char| c.is_uppercase()) || s.contains('.')
1200}
1201#[allow(dead_code)]
1203#[allow(missing_docs)]
1204pub fn extract_ctor_name(s: &str) -> &str {
1205 s.split_whitespace().next().unwrap_or(s)
1206}
1207#[cfg(test)]
1208mod pattern_pad {
1209 use super::*;
1210 use crate::ast_impl::{Pattern, SurfaceExpr};
1211 use crate::pattern::*;
1212 #[test]
1213 fn test_is_ctor_pattern() {
1214 assert!(is_ctor_pattern("Nat.succ n"));
1215 assert!(!is_ctor_pattern("x"));
1216 assert!(!is_ctor_pattern("_"));
1217 }
1218 #[test]
1219 fn test_extract_ctor_name() {
1220 assert_eq!(extract_ctor_name("Nat.succ n"), "Nat.succ");
1221 assert_eq!(extract_ctor_name("_"), "_");
1222 }
1223}
1224#[allow(dead_code)]
1226#[allow(missing_docs)]
1227pub fn pattern_vars(s: &str) -> Vec<&str> {
1228 s.split_whitespace()
1229 .filter(|t| t.starts_with(|c: char| c.is_lowercase()))
1230 .collect()
1231}
1232#[allow(dead_code)]
1234#[allow(missing_docs)]
1235pub fn is_wildcard_pattern(s: &str) -> bool {
1236 s.trim() == "_"
1237}
1238#[allow(dead_code)]
1240#[allow(missing_docs)]
1241pub fn same_ctor(a: &str, b: &str) -> bool {
1242 extract_ctor_name(a) == extract_ctor_name(b)
1243}
1244#[cfg(test)]
1245mod pattern_pad2 {
1246 use super::*;
1247 use crate::ast_impl::{Pattern, SurfaceExpr};
1248 use crate::pattern::*;
1249 #[test]
1250 fn test_pattern_vars() {
1251 let vars = pattern_vars("Nat.succ n");
1252 assert_eq!(vars, vec!["n"]);
1253 }
1254 #[test]
1255 fn test_is_wildcard_pattern() {
1256 assert!(is_wildcard_pattern("_"));
1257 assert!(is_wildcard_pattern(" _ "));
1258 assert!(!is_wildcard_pattern("x"));
1259 }
1260 #[test]
1261 fn test_same_ctor() {
1262 assert!(same_ctor("Nat.succ n", "Nat.succ m"));
1263 assert!(!same_ctor("Nat.succ n", "Nat.zero"));
1264 }
1265}
1266#[allow(dead_code)]
1268#[allow(missing_docs)]
1269pub fn is_pair_pattern(s: &str) -> bool {
1270 let s = s.trim();
1271 s.starts_with('(') && s.ends_with(')') && s.contains(',')
1272}
1273#[allow(dead_code)]
1275#[allow(missing_docs)]
1276pub fn ctor_arity(s: &str) -> usize {
1277 let parts: Vec<&str> = s.split_whitespace().collect();
1278 if parts.len() > 1 {
1279 parts.len() - 1
1280 } else {
1281 0
1282 }
1283}