Skip to main content

oxilean_parse/pattern/
functions.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use 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/// Classify a pattern string by its leading token.
1037#[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/// A depth-first traversal of a pattern tree (string-based).
1106#[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/// A pattern normaliser that sorts or-patterns.
1134#[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/// A pattern depth counter.
1146#[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/// A pattern variable counter.
1168#[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/// Returns true if a pattern string is a constructor application.
1195#[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/// Returns the constructor name from a pattern string.
1202#[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/// Returns the variable names bound in a pattern string (simplified: lowercase idents).
1225#[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/// Returns true if a pattern string is a wildcard.
1233#[allow(dead_code)]
1234#[allow(missing_docs)]
1235pub fn is_wildcard_pattern(s: &str) -> bool {
1236    s.trim() == "_"
1237}
1238/// Returns true if two patterns have the same structure (same ctor and arity).
1239#[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/// Returns true if a pattern is a pair pattern "(a, b)".
1267#[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/// Returns the arity of a constructor pattern (number of arguments).
1274#[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}