1use super::functions::*;
6use crate::{Expr, Name};
7use std::collections::HashMap;
8
9#[derive(Debug, Clone)]
11pub enum DecisionTree {
12 Leaf {
14 arm_idx: usize,
16 bindings: Vec<(Name, Expr)>,
18 },
19 Switch {
21 scrutinee: Expr,
23 branches: Vec<(Name, Vec<Name>, DecisionTree)>,
25 default: Option<Box<DecisionTree>>,
27 },
28 Failure,
30}
31#[allow(dead_code)]
33pub struct StringPool {
34 free: Vec<String>,
35}
36#[allow(dead_code)]
37impl StringPool {
38 pub fn new() -> Self {
40 Self { free: Vec::new() }
41 }
42 pub fn take(&mut self) -> String {
44 self.free.pop().unwrap_or_default()
45 }
46 pub fn give(&mut self, mut s: String) {
48 s.clear();
49 self.free.push(s);
50 }
51 pub fn free_count(&self) -> usize {
53 self.free.len()
54 }
55}
56#[derive(Debug, Clone)]
58pub struct MatchStats {
59 pub num_arms: usize,
61 pub reachable_arms: Vec<usize>,
63 pub unreachable_arms: Vec<usize>,
65 pub is_exhaustive: bool,
67 pub tree_depth: usize,
69}
70impl MatchStats {
71 pub fn is_exhaustive(&self) -> bool {
73 self.is_exhaustive
74 }
75 pub fn has_redundant_arms(&self) -> bool {
77 !self.unreachable_arms.is_empty()
78 }
79}
80#[allow(dead_code)]
82pub enum Either2<A, B> {
83 First(A),
85 Second(B),
87}
88#[allow(dead_code)]
89impl<A, B> Either2<A, B> {
90 pub fn is_first(&self) -> bool {
92 matches!(self, Either2::First(_))
93 }
94 pub fn is_second(&self) -> bool {
96 matches!(self, Either2::Second(_))
97 }
98 pub fn first(self) -> Option<A> {
100 match self {
101 Either2::First(a) => Some(a),
102 _ => None,
103 }
104 }
105 pub fn second(self) -> Option<B> {
107 match self {
108 Either2::Second(b) => Some(b),
109 _ => None,
110 }
111 }
112 pub fn map_first<C, F: FnOnce(A) -> C>(self, f: F) -> Either2<C, B> {
114 match self {
115 Either2::First(a) => Either2::First(f(a)),
116 Either2::Second(b) => Either2::Second(b),
117 }
118 }
119}
120#[allow(dead_code)]
122pub struct LabelSet {
123 labels: Vec<String>,
124}
125#[allow(dead_code)]
126impl LabelSet {
127 pub fn new() -> Self {
129 Self { labels: Vec::new() }
130 }
131 pub fn add(&mut self, label: impl Into<String>) {
133 let s = label.into();
134 if !self.labels.contains(&s) {
135 self.labels.push(s);
136 }
137 }
138 pub fn has(&self, label: &str) -> bool {
140 self.labels.iter().any(|l| l == label)
141 }
142 pub fn count(&self) -> usize {
144 self.labels.len()
145 }
146 pub fn all(&self) -> &[String] {
148 &self.labels
149 }
150}
151#[allow(dead_code)]
153pub struct TransitiveClosure {
154 adj: Vec<Vec<usize>>,
155 n: usize,
156}
157#[allow(dead_code)]
158impl TransitiveClosure {
159 pub fn new(n: usize) -> Self {
161 Self {
162 adj: vec![Vec::new(); n],
163 n,
164 }
165 }
166 pub fn add_edge(&mut self, from: usize, to: usize) {
168 if from < self.n {
169 self.adj[from].push(to);
170 }
171 }
172 pub fn reachable_from(&self, start: usize) -> Vec<usize> {
174 let mut visited = vec![false; self.n];
175 let mut queue = std::collections::VecDeque::new();
176 queue.push_back(start);
177 while let Some(node) = queue.pop_front() {
178 if node >= self.n || visited[node] {
179 continue;
180 }
181 visited[node] = true;
182 for &next in &self.adj[node] {
183 queue.push_back(next);
184 }
185 }
186 (0..self.n).filter(|&i| visited[i]).collect()
187 }
188 pub fn can_reach(&self, from: usize, to: usize) -> bool {
190 self.reachable_from(from).contains(&to)
191 }
192}
193#[allow(dead_code)]
195pub struct SlidingSum {
196 window: Vec<f64>,
197 capacity: usize,
198 pos: usize,
199 sum: f64,
200 count: usize,
201}
202#[allow(dead_code)]
203impl SlidingSum {
204 pub fn new(capacity: usize) -> Self {
206 Self {
207 window: vec![0.0; capacity],
208 capacity,
209 pos: 0,
210 sum: 0.0,
211 count: 0,
212 }
213 }
214 pub fn push(&mut self, val: f64) {
216 let oldest = self.window[self.pos];
217 self.sum -= oldest;
218 self.sum += val;
219 self.window[self.pos] = val;
220 self.pos = (self.pos + 1) % self.capacity;
221 if self.count < self.capacity {
222 self.count += 1;
223 }
224 }
225 pub fn sum(&self) -> f64 {
227 self.sum
228 }
229 pub fn mean(&self) -> Option<f64> {
231 if self.count == 0 {
232 None
233 } else {
234 Some(self.sum / self.count as f64)
235 }
236 }
237 pub fn count(&self) -> usize {
239 self.count
240 }
241}
242#[derive(Debug, Clone)]
244pub struct MatchArm {
245 pub patterns: Vec<Pattern>,
247 pub rhs: Expr,
249 pub guard: Option<Expr>,
251}
252#[derive(Debug, Clone)]
254pub struct CompileResult {
255 pub tree: DecisionTree,
257 pub unreachable_arms: Vec<usize>,
259 pub missing_patterns: Vec<Vec<Pattern>>,
261}
262#[allow(dead_code)]
264pub struct SmallMap<K: Ord + Clone, V: Clone> {
265 entries: Vec<(K, V)>,
266}
267#[allow(dead_code)]
268impl<K: Ord + Clone, V: Clone> SmallMap<K, V> {
269 pub fn new() -> Self {
271 Self {
272 entries: Vec::new(),
273 }
274 }
275 pub fn insert(&mut self, key: K, val: V) {
277 match self.entries.binary_search_by_key(&&key, |(k, _)| k) {
278 Ok(i) => self.entries[i].1 = val,
279 Err(i) => self.entries.insert(i, (key, val)),
280 }
281 }
282 pub fn get(&self, key: &K) -> Option<&V> {
284 self.entries
285 .binary_search_by_key(&key, |(k, _)| k)
286 .ok()
287 .map(|i| &self.entries[i].1)
288 }
289 pub fn len(&self) -> usize {
291 self.entries.len()
292 }
293 pub fn is_empty(&self) -> bool {
295 self.entries.is_empty()
296 }
297 pub fn keys(&self) -> Vec<&K> {
299 self.entries.iter().map(|(k, _)| k).collect()
300 }
301 pub fn values(&self) -> Vec<&V> {
303 self.entries.iter().map(|(_, v)| v).collect()
304 }
305}
306#[allow(dead_code)]
308pub struct SimpleDag {
309 edges: Vec<Vec<usize>>,
311}
312#[allow(dead_code)]
313impl SimpleDag {
314 pub fn new(n: usize) -> Self {
316 Self {
317 edges: vec![Vec::new(); n],
318 }
319 }
320 pub fn add_edge(&mut self, from: usize, to: usize) {
322 if from < self.edges.len() {
323 self.edges[from].push(to);
324 }
325 }
326 pub fn successors(&self, node: usize) -> &[usize] {
328 self.edges.get(node).map(|v| v.as_slice()).unwrap_or(&[])
329 }
330 pub fn can_reach(&self, from: usize, to: usize) -> bool {
332 let mut visited = vec![false; self.edges.len()];
333 self.dfs(from, to, &mut visited)
334 }
335 fn dfs(&self, cur: usize, target: usize, visited: &mut Vec<bool>) -> bool {
336 if cur == target {
337 return true;
338 }
339 if cur >= visited.len() || visited[cur] {
340 return false;
341 }
342 visited[cur] = true;
343 for &next in self.successors(cur) {
344 if self.dfs(next, target, visited) {
345 return true;
346 }
347 }
348 false
349 }
350 pub fn topological_sort(&self) -> Option<Vec<usize>> {
352 let n = self.edges.len();
353 let mut in_degree = vec![0usize; n];
354 for succs in &self.edges {
355 for &s in succs {
356 if s < n {
357 in_degree[s] += 1;
358 }
359 }
360 }
361 let mut queue: std::collections::VecDeque<usize> =
362 (0..n).filter(|&i| in_degree[i] == 0).collect();
363 let mut order = Vec::new();
364 while let Some(node) = queue.pop_front() {
365 order.push(node);
366 for &s in self.successors(node) {
367 if s < n {
368 in_degree[s] -= 1;
369 if in_degree[s] == 0 {
370 queue.push_back(s);
371 }
372 }
373 }
374 }
375 if order.len() == n {
376 Some(order)
377 } else {
378 None
379 }
380 }
381 pub fn num_nodes(&self) -> usize {
383 self.edges.len()
384 }
385}
386#[allow(dead_code)]
388pub struct NonEmptyVec<T> {
389 head: T,
390 tail: Vec<T>,
391}
392#[allow(dead_code)]
393impl<T> NonEmptyVec<T> {
394 pub fn singleton(val: T) -> Self {
396 Self {
397 head: val,
398 tail: Vec::new(),
399 }
400 }
401 pub fn push(&mut self, val: T) {
403 self.tail.push(val);
404 }
405 pub fn first(&self) -> &T {
407 &self.head
408 }
409 pub fn last(&self) -> &T {
411 self.tail.last().unwrap_or(&self.head)
412 }
413 pub fn len(&self) -> usize {
415 1 + self.tail.len()
416 }
417 pub fn is_empty(&self) -> bool {
419 self.len() == 0
420 }
421 pub fn to_vec(&self) -> Vec<&T> {
423 let mut v = vec![&self.head];
424 v.extend(self.tail.iter());
425 v
426 }
427}
428#[allow(dead_code)]
430pub struct PathBuf {
431 components: Vec<String>,
432}
433#[allow(dead_code)]
434impl PathBuf {
435 pub fn new() -> Self {
437 Self {
438 components: Vec::new(),
439 }
440 }
441 pub fn push(&mut self, comp: impl Into<String>) {
443 self.components.push(comp.into());
444 }
445 pub fn pop(&mut self) {
447 self.components.pop();
448 }
449 pub fn as_str(&self) -> String {
451 self.components.join("/")
452 }
453 pub fn depth(&self) -> usize {
455 self.components.len()
456 }
457 pub fn clear(&mut self) {
459 self.components.clear();
460 }
461}
462#[allow(dead_code)]
464pub struct FlatSubstitution {
465 pairs: Vec<(String, String)>,
466}
467#[allow(dead_code)]
468impl FlatSubstitution {
469 pub fn new() -> Self {
471 Self { pairs: Vec::new() }
472 }
473 pub fn add(&mut self, from: impl Into<String>, to: impl Into<String>) {
475 self.pairs.push((from.into(), to.into()));
476 }
477 pub fn apply(&self, s: &str) -> String {
479 let mut result = s.to_string();
480 for (from, to) in &self.pairs {
481 result = result.replace(from.as_str(), to.as_str());
482 }
483 result
484 }
485 pub fn len(&self) -> usize {
487 self.pairs.len()
488 }
489 pub fn is_empty(&self) -> bool {
491 self.pairs.is_empty()
492 }
493}
494#[allow(dead_code)]
496pub struct Stopwatch {
497 start: std::time::Instant,
498 splits: Vec<f64>,
499}
500#[allow(dead_code)]
501impl Stopwatch {
502 pub fn start() -> Self {
504 Self {
505 start: std::time::Instant::now(),
506 splits: Vec::new(),
507 }
508 }
509 pub fn split(&mut self) {
511 self.splits.push(self.elapsed_ms());
512 }
513 pub fn elapsed_ms(&self) -> f64 {
515 self.start.elapsed().as_secs_f64() * 1000.0
516 }
517 pub fn splits(&self) -> &[f64] {
519 &self.splits
520 }
521 pub fn num_splits(&self) -> usize {
523 self.splits.len()
524 }
525}
526#[allow(dead_code)]
528pub struct WriteOnce<T> {
529 value: std::cell::Cell<Option<T>>,
530}
531#[allow(dead_code)]
532impl<T: Copy> WriteOnce<T> {
533 pub fn new() -> Self {
535 Self {
536 value: std::cell::Cell::new(None),
537 }
538 }
539 pub fn write(&self, val: T) -> bool {
541 if self.value.get().is_some() {
542 return false;
543 }
544 self.value.set(Some(val));
545 true
546 }
547 pub fn read(&self) -> Option<T> {
549 self.value.get()
550 }
551 pub fn is_written(&self) -> bool {
553 self.value.get().is_some()
554 }
555}
556#[allow(dead_code)]
558pub struct WindowIterator<'a, T> {
559 pub(super) data: &'a [T],
560 pub(super) pos: usize,
561 pub(super) window: usize,
562}
563#[allow(dead_code)]
564impl<'a, T> WindowIterator<'a, T> {
565 pub fn new(data: &'a [T], window: usize) -> Self {
567 Self {
568 data,
569 pos: 0,
570 window,
571 }
572 }
573}
574#[allow(dead_code)]
576#[allow(missing_docs)]
577pub struct RewriteRule {
578 pub name: String,
580 pub lhs: String,
582 pub rhs: String,
584 pub conditional: bool,
586}
587#[allow(dead_code)]
588impl RewriteRule {
589 pub fn unconditional(
591 name: impl Into<String>,
592 lhs: impl Into<String>,
593 rhs: impl Into<String>,
594 ) -> Self {
595 Self {
596 name: name.into(),
597 lhs: lhs.into(),
598 rhs: rhs.into(),
599 conditional: false,
600 }
601 }
602 pub fn conditional(
604 name: impl Into<String>,
605 lhs: impl Into<String>,
606 rhs: impl Into<String>,
607 ) -> Self {
608 Self {
609 name: name.into(),
610 lhs: lhs.into(),
611 rhs: rhs.into(),
612 conditional: true,
613 }
614 }
615 pub fn display(&self) -> String {
617 format!("{}: {} → {}", self.name, self.lhs, self.rhs)
618 }
619}
620#[allow(dead_code)]
622pub struct StackCalc {
623 stack: Vec<i64>,
624}
625#[allow(dead_code)]
626impl StackCalc {
627 pub fn new() -> Self {
629 Self { stack: Vec::new() }
630 }
631 pub fn push(&mut self, n: i64) {
633 self.stack.push(n);
634 }
635 pub fn add(&mut self) {
637 let b = self
638 .stack
639 .pop()
640 .expect("stack must have at least two values for add");
641 let a = self
642 .stack
643 .pop()
644 .expect("stack must have at least two values for add");
645 self.stack.push(a + b);
646 }
647 pub fn sub(&mut self) {
649 let b = self
650 .stack
651 .pop()
652 .expect("stack must have at least two values for sub");
653 let a = self
654 .stack
655 .pop()
656 .expect("stack must have at least two values for sub");
657 self.stack.push(a - b);
658 }
659 pub fn mul(&mut self) {
661 let b = self
662 .stack
663 .pop()
664 .expect("stack must have at least two values for mul");
665 let a = self
666 .stack
667 .pop()
668 .expect("stack must have at least two values for mul");
669 self.stack.push(a * b);
670 }
671 pub fn peek(&self) -> Option<i64> {
673 self.stack.last().copied()
674 }
675 pub fn depth(&self) -> usize {
677 self.stack.len()
678 }
679}
680#[allow(dead_code)]
682pub struct TokenBucket {
683 capacity: u64,
684 tokens: u64,
685 refill_per_ms: u64,
686 last_refill: std::time::Instant,
687}
688#[allow(dead_code)]
689impl TokenBucket {
690 pub fn new(capacity: u64, refill_per_ms: u64) -> Self {
692 Self {
693 capacity,
694 tokens: capacity,
695 refill_per_ms,
696 last_refill: std::time::Instant::now(),
697 }
698 }
699 pub fn try_consume(&mut self, n: u64) -> bool {
701 self.refill();
702 if self.tokens >= n {
703 self.tokens -= n;
704 true
705 } else {
706 false
707 }
708 }
709 fn refill(&mut self) {
710 let now = std::time::Instant::now();
711 let elapsed_ms = now.duration_since(self.last_refill).as_millis() as u64;
712 if elapsed_ms > 0 {
713 let new_tokens = elapsed_ms * self.refill_per_ms;
714 self.tokens = (self.tokens + new_tokens).min(self.capacity);
715 self.last_refill = now;
716 }
717 }
718 pub fn available(&self) -> u64 {
720 self.tokens
721 }
722 pub fn capacity(&self) -> u64 {
724 self.capacity
725 }
726}
727#[derive(Debug, Clone, PartialEq)]
729pub enum Pattern {
730 Wildcard,
732 Var(Name),
734 Constructor(Name, Vec<Pattern>),
736 Literal(crate::Literal),
738 As(Name, Box<Pattern>),
740 Or(Vec<Pattern>),
742 Inaccessible(Expr),
744}
745#[allow(dead_code)]
747pub struct RewriteRuleSet {
748 rules: Vec<RewriteRule>,
749}
750#[allow(dead_code)]
751impl RewriteRuleSet {
752 pub fn new() -> Self {
754 Self { rules: Vec::new() }
755 }
756 pub fn add(&mut self, rule: RewriteRule) {
758 self.rules.push(rule);
759 }
760 pub fn len(&self) -> usize {
762 self.rules.len()
763 }
764 pub fn is_empty(&self) -> bool {
766 self.rules.is_empty()
767 }
768 pub fn conditional_rules(&self) -> Vec<&RewriteRule> {
770 self.rules.iter().filter(|r| r.conditional).collect()
771 }
772 pub fn unconditional_rules(&self) -> Vec<&RewriteRule> {
774 self.rules.iter().filter(|r| !r.conditional).collect()
775 }
776 pub fn get(&self, name: &str) -> Option<&RewriteRule> {
778 self.rules.iter().find(|r| r.name == name)
779 }
780}
781#[allow(dead_code)]
783pub struct FocusStack<T> {
784 items: Vec<T>,
785}
786#[allow(dead_code)]
787impl<T> FocusStack<T> {
788 pub fn new() -> Self {
790 Self { items: Vec::new() }
791 }
792 pub fn focus(&mut self, item: T) {
794 self.items.push(item);
795 }
796 pub fn blur(&mut self) -> Option<T> {
798 self.items.pop()
799 }
800 pub fn current(&self) -> Option<&T> {
802 self.items.last()
803 }
804 pub fn depth(&self) -> usize {
806 self.items.len()
807 }
808 pub fn is_empty(&self) -> bool {
810 self.items.is_empty()
811 }
812}
813#[allow(dead_code)]
815pub struct RawFnPtr {
816 ptr: usize,
818 arity: usize,
819 name: String,
820}
821#[allow(dead_code)]
822impl RawFnPtr {
823 pub fn new(ptr: usize, arity: usize, name: impl Into<String>) -> Self {
825 Self {
826 ptr,
827 arity,
828 name: name.into(),
829 }
830 }
831 pub fn arity(&self) -> usize {
833 self.arity
834 }
835 pub fn name(&self) -> &str {
837 &self.name
838 }
839 pub fn raw(&self) -> usize {
841 self.ptr
842 }
843}
844#[allow(dead_code)]
846#[allow(missing_docs)]
847pub enum DecisionNode {
848 Leaf(String),
850 Branch {
852 key: String,
853 val: String,
854 yes_branch: Box<DecisionNode>,
855 no_branch: Box<DecisionNode>,
856 },
857}
858#[allow(dead_code)]
859impl DecisionNode {
860 pub fn evaluate(&self, ctx: &std::collections::HashMap<String, String>) -> &str {
862 match self {
863 DecisionNode::Leaf(action) => action.as_str(),
864 DecisionNode::Branch {
865 key,
866 val,
867 yes_branch,
868 no_branch,
869 } => {
870 let actual = ctx.get(key).map(|s| s.as_str()).unwrap_or("");
871 if actual == val.as_str() {
872 yes_branch.evaluate(ctx)
873 } else {
874 no_branch.evaluate(ctx)
875 }
876 }
877 }
878 }
879 pub fn depth(&self) -> usize {
881 match self {
882 DecisionNode::Leaf(_) => 0,
883 DecisionNode::Branch {
884 yes_branch,
885 no_branch,
886 ..
887 } => 1 + yes_branch.depth().max(no_branch.depth()),
888 }
889 }
890}
891#[allow(dead_code)]
893pub struct StatSummary {
894 count: u64,
895 sum: f64,
896 min: f64,
897 max: f64,
898}
899#[allow(dead_code)]
900impl StatSummary {
901 pub fn new() -> Self {
903 Self {
904 count: 0,
905 sum: 0.0,
906 min: f64::INFINITY,
907 max: f64::NEG_INFINITY,
908 }
909 }
910 pub fn record(&mut self, val: f64) {
912 self.count += 1;
913 self.sum += val;
914 if val < self.min {
915 self.min = val;
916 }
917 if val > self.max {
918 self.max = val;
919 }
920 }
921 pub fn mean(&self) -> Option<f64> {
923 if self.count == 0 {
924 None
925 } else {
926 Some(self.sum / self.count as f64)
927 }
928 }
929 pub fn min(&self) -> Option<f64> {
931 if self.count == 0 {
932 None
933 } else {
934 Some(self.min)
935 }
936 }
937 pub fn max(&self) -> Option<f64> {
939 if self.count == 0 {
940 None
941 } else {
942 Some(self.max)
943 }
944 }
945 pub fn count(&self) -> u64 {
947 self.count
948 }
949}
950#[allow(dead_code)]
952pub struct SparseVec<T: Default + Clone + PartialEq> {
953 entries: std::collections::HashMap<usize, T>,
954 default_: T,
955 logical_len: usize,
956}
957#[allow(dead_code)]
958impl<T: Default + Clone + PartialEq> SparseVec<T> {
959 pub fn new(len: usize) -> Self {
961 Self {
962 entries: std::collections::HashMap::new(),
963 default_: T::default(),
964 logical_len: len,
965 }
966 }
967 pub fn set(&mut self, idx: usize, val: T) {
969 if val == self.default_ {
970 self.entries.remove(&idx);
971 } else {
972 self.entries.insert(idx, val);
973 }
974 }
975 pub fn get(&self, idx: usize) -> &T {
977 self.entries.get(&idx).unwrap_or(&self.default_)
978 }
979 pub fn len(&self) -> usize {
981 self.logical_len
982 }
983 pub fn is_empty(&self) -> bool {
985 self.len() == 0
986 }
987 pub fn nnz(&self) -> usize {
989 self.entries.len()
990 }
991}
992#[allow(dead_code)]
994pub struct VersionedRecord<T: Clone> {
995 history: Vec<T>,
996}
997#[allow(dead_code)]
998impl<T: Clone> VersionedRecord<T> {
999 pub fn new(initial: T) -> Self {
1001 Self {
1002 history: vec![initial],
1003 }
1004 }
1005 pub fn update(&mut self, val: T) {
1007 self.history.push(val);
1008 }
1009 pub fn current(&self) -> &T {
1011 self.history
1012 .last()
1013 .expect("VersionedRecord history is always non-empty after construction")
1014 }
1015 pub fn at_version(&self, n: usize) -> Option<&T> {
1017 self.history.get(n)
1018 }
1019 pub fn version(&self) -> usize {
1021 self.history.len() - 1
1022 }
1023 pub fn has_history(&self) -> bool {
1025 self.history.len() > 1
1026 }
1027}
1028#[derive(Debug, Clone)]
1030pub struct ConstructorInfo {
1031 pub name: Name,
1033 pub num_fields: u32,
1035 pub inductive: Name,
1037}
1038#[derive(Clone, Debug, Default)]
1040pub struct PatternStats {
1041 pub total_patterns: usize,
1043 pub wildcards: usize,
1045 pub constructors: usize,
1047 pub literals: usize,
1049 pub or_patterns: usize,
1051 pub redundant_arms: usize,
1053}
1054impl PatternStats {
1055 pub fn new() -> Self {
1057 Self::default()
1058 }
1059 pub fn from_patterns(patterns: &[Pattern]) -> Self {
1061 let mut stats = Self::new();
1062 for p in patterns {
1063 stats.total_patterns += 1;
1064 stats.count_pattern(p);
1065 }
1066 stats
1067 }
1068 fn count_pattern(&mut self, pattern: &Pattern) {
1069 match pattern {
1070 Pattern::Wildcard => self.wildcards += 1,
1071 Pattern::Var(_) => self.wildcards += 1,
1072 Pattern::Constructor(_, sub) => {
1073 self.constructors += 1;
1074 for p in sub {
1075 self.count_pattern(p);
1076 }
1077 }
1078 Pattern::Literal(_) => self.literals += 1,
1079 Pattern::As(_, inner) => self.count_pattern(inner),
1080 Pattern::Or(pats) => {
1081 self.or_patterns += 1;
1082 for p in pats {
1083 self.count_pattern(p);
1084 }
1085 }
1086 Pattern::Inaccessible(_) => {}
1087 }
1088 }
1089}
1090#[allow(dead_code)]
1092pub struct TransformStat {
1093 before: StatSummary,
1094 after: StatSummary,
1095}
1096#[allow(dead_code)]
1097impl TransformStat {
1098 pub fn new() -> Self {
1100 Self {
1101 before: StatSummary::new(),
1102 after: StatSummary::new(),
1103 }
1104 }
1105 pub fn record_before(&mut self, v: f64) {
1107 self.before.record(v);
1108 }
1109 pub fn record_after(&mut self, v: f64) {
1111 self.after.record(v);
1112 }
1113 pub fn mean_ratio(&self) -> Option<f64> {
1115 let b = self.before.mean()?;
1116 let a = self.after.mean()?;
1117 if b.abs() < f64::EPSILON {
1118 return None;
1119 }
1120 Some(a / b)
1121 }
1122}
1123#[allow(dead_code)]
1125pub struct ConfigNode {
1126 key: String,
1127 value: Option<String>,
1128 children: Vec<ConfigNode>,
1129}
1130#[allow(dead_code)]
1131impl ConfigNode {
1132 pub fn leaf(key: impl Into<String>, value: impl Into<String>) -> Self {
1134 Self {
1135 key: key.into(),
1136 value: Some(value.into()),
1137 children: Vec::new(),
1138 }
1139 }
1140 pub fn section(key: impl Into<String>) -> Self {
1142 Self {
1143 key: key.into(),
1144 value: None,
1145 children: Vec::new(),
1146 }
1147 }
1148 pub fn add_child(&mut self, child: ConfigNode) {
1150 self.children.push(child);
1151 }
1152 pub fn key(&self) -> &str {
1154 &self.key
1155 }
1156 pub fn value(&self) -> Option<&str> {
1158 self.value.as_deref()
1159 }
1160 pub fn num_children(&self) -> usize {
1162 self.children.len()
1163 }
1164 pub fn lookup(&self, path: &str) -> Option<&str> {
1166 let mut parts = path.splitn(2, '.');
1167 let head = parts.next()?;
1168 let tail = parts.next();
1169 if head != self.key {
1170 return None;
1171 }
1172 match tail {
1173 None => self.value.as_deref(),
1174 Some(rest) => self.children.iter().find_map(|c| c.lookup_relative(rest)),
1175 }
1176 }
1177 fn lookup_relative(&self, path: &str) -> Option<&str> {
1178 let mut parts = path.splitn(2, '.');
1179 let head = parts.next()?;
1180 let tail = parts.next();
1181 if head != self.key {
1182 return None;
1183 }
1184 match tail {
1185 None => self.value.as_deref(),
1186 Some(rest) => self.children.iter().find_map(|c| c.lookup_relative(rest)),
1187 }
1188 }
1189}
1190pub struct MatchCompiler {
1192 next_var: u32,
1194 constructors: HashMap<Name, Vec<ConstructorInfo>>,
1196}
1197impl MatchCompiler {
1198 pub fn new() -> Self {
1200 Self {
1201 next_var: 0,
1202 constructors: HashMap::new(),
1203 }
1204 }
1205 pub fn fresh_var(&mut self) -> Name {
1207 let n = self.next_var;
1208 self.next_var += 1;
1209 Name::str(format!("_match{}", n))
1210 }
1211 pub fn register_constructors(&mut self, ind_name: Name, ctors: Vec<ConstructorInfo>) {
1213 self.constructors.insert(ind_name, ctors);
1214 }
1215 pub fn get_constructors(&self, ind_name: &Name) -> Option<&Vec<ConstructorInfo>> {
1217 self.constructors.get(ind_name)
1218 }
1219 pub fn compile_match(
1223 &mut self,
1224 scrutinees: &[Expr],
1225 arms: &[MatchArm],
1226 ) -> Result<CompileResult, String> {
1227 if arms.is_empty() {
1228 return Err("Match expression has no arms".to_string());
1229 }
1230 for (i, arm) in arms.iter().enumerate() {
1231 if arm.patterns.len() != scrutinees.len() {
1232 return Err(format!(
1233 "Arm {} has {} patterns but expected {}",
1234 i,
1235 arm.patterns.len(),
1236 scrutinees.len()
1237 ));
1238 }
1239 }
1240 let matrix: Vec<(Vec<&Pattern>, usize)> = arms
1241 .iter()
1242 .enumerate()
1243 .map(|(i, arm)| (arm.patterns.iter().collect(), i))
1244 .collect();
1245 let tree = self.compile_matrix(scrutinees, &matrix)?;
1246 let mut reachable = vec![false; arms.len()];
1247 self.mark_reachable(&tree, &mut reachable);
1248 let unreachable_arms: Vec<usize> = reachable
1249 .iter()
1250 .enumerate()
1251 .filter(|(_, &r)| !r)
1252 .map(|(i, _)| i)
1253 .collect();
1254 let missing_patterns = self.check_exhaustiveness_tree(&tree);
1255 Ok(CompileResult {
1256 tree,
1257 unreachable_arms,
1258 missing_patterns,
1259 })
1260 }
1261 fn compile_matrix(
1263 &mut self,
1264 scrutinees: &[Expr],
1265 matrix: &[(Vec<&Pattern>, usize)],
1266 ) -> Result<DecisionTree, String> {
1267 if scrutinees.is_empty() {
1268 if let Some((_, arm_idx)) = matrix.first() {
1269 return Ok(DecisionTree::Leaf {
1270 arm_idx: *arm_idx,
1271 bindings: Vec::new(),
1272 });
1273 }
1274 return Ok(DecisionTree::Failure);
1275 }
1276 if matrix.is_empty() {
1277 return Ok(DecisionTree::Failure);
1278 }
1279 let col = self.find_best_column(matrix);
1280 let all_wildcards = matrix
1281 .iter()
1282 .all(|(pats, _)| matches!(pats[col], Pattern::Wildcard | Pattern::Var(_)));
1283 if all_wildcards {
1284 let (_, arm_idx) = &matrix[0];
1285 return Ok(DecisionTree::Leaf {
1286 arm_idx: *arm_idx,
1287 bindings: Vec::new(),
1288 });
1289 }
1290 let ctors_used: Vec<Name> = matrix
1291 .iter()
1292 .filter_map(|(pats, _)| {
1293 if let Pattern::Constructor(name, _) = &pats[col] {
1294 Some(name.clone())
1295 } else {
1296 None
1297 }
1298 })
1299 .collect::<std::collections::HashSet<_>>()
1300 .into_iter()
1301 .collect();
1302 if ctors_used.is_empty() {
1303 let has_literals = matrix
1304 .iter()
1305 .any(|(pats, _)| matches!(pats[col], Pattern::Literal(_)));
1306 if has_literals {
1307 return self.compile_literal_column(scrutinees, matrix, col);
1308 }
1309 let (_, arm_idx) = &matrix[0];
1310 return Ok(DecisionTree::Leaf {
1311 arm_idx: *arm_idx,
1312 bindings: Vec::new(),
1313 });
1314 }
1315 let mut branches = Vec::new();
1316 let mut has_default = false;
1317 for ctor_name in &ctors_used {
1318 let ctor_arity = self.get_ctor_arity(ctor_name);
1319 let field_names: Vec<Name> = (0..ctor_arity).map(|_| self.fresh_var()).collect();
1320 let sub_matrix: Vec<(Vec<&Pattern>, usize)> = matrix
1321 .iter()
1322 .filter_map(|(pats, arm_idx)| match &pats[col] {
1323 Pattern::Constructor(name, sub_pats) if name == ctor_name => {
1324 let mut new_pats = Vec::new();
1325 for (i, p) in pats.iter().enumerate() {
1326 if i == col {
1327 for sp in sub_pats {
1328 new_pats.push(sp);
1329 }
1330 } else {
1331 new_pats.push(p);
1332 }
1333 }
1334 Some((new_pats, *arm_idx))
1335 }
1336 Pattern::Wildcard | Pattern::Var(_) => {
1337 let mut new_pats = Vec::new();
1338 for (i, p) in pats.iter().enumerate() {
1339 if i == col {
1340 for _ in 0..ctor_arity {
1341 new_pats.push(&Pattern::Wildcard);
1342 }
1343 } else {
1344 new_pats.push(p);
1345 }
1346 }
1347 Some((new_pats, *arm_idx))
1348 }
1349 _ => None,
1350 })
1351 .collect();
1352 let mut sub_scrutinees = Vec::new();
1353 for (i, s) in scrutinees.iter().enumerate() {
1354 if i == col {
1355 for (j, fname) in field_names.iter().enumerate() {
1356 sub_scrutinees.push(Expr::Proj(
1357 ctor_name.clone(),
1358 j as u32,
1359 Box::new(s.clone()),
1360 ));
1361 let _ = fname;
1362 }
1363 } else {
1364 sub_scrutinees.push(s.clone());
1365 }
1366 }
1367 let sub_tree = self.compile_matrix(&sub_scrutinees, &sub_matrix)?;
1368 branches.push((ctor_name.clone(), field_names, sub_tree));
1369 }
1370 let default_matrix: Vec<(Vec<&Pattern>, usize)> = matrix
1371 .iter()
1372 .filter_map(|(pats, arm_idx)| match &pats[col] {
1373 Pattern::Wildcard | Pattern::Var(_) => {
1374 let mut new_pats: Vec<&Pattern> = pats
1375 .iter()
1376 .enumerate()
1377 .filter(|(i, _)| *i != col)
1378 .map(|(_, p)| *p)
1379 .collect();
1380 if new_pats.is_empty() && !pats.is_empty() {
1381 new_pats.push(&Pattern::Wildcard);
1382 }
1383 Some((new_pats, *arm_idx))
1384 }
1385 _ => None,
1386 })
1387 .collect();
1388 let default = if !default_matrix.is_empty() {
1389 has_default = true;
1390 let remaining: Vec<Expr> = scrutinees
1391 .iter()
1392 .enumerate()
1393 .filter(|(i, _)| *i != col)
1394 .map(|(_, s)| s.clone())
1395 .collect();
1396 Some(Box::new(self.compile_matrix(&remaining, &default_matrix)?))
1397 } else {
1398 None
1399 };
1400 let _ = has_default;
1401 Ok(DecisionTree::Switch {
1402 scrutinee: scrutinees[col].clone(),
1403 branches,
1404 default,
1405 })
1406 }
1407 fn find_best_column(&self, matrix: &[(Vec<&Pattern>, usize)]) -> usize {
1409 if matrix.is_empty() || matrix[0].0.is_empty() {
1410 return 0;
1411 }
1412 let ncols = matrix[0].0.len();
1413 let mut best_col = 0;
1414 let mut best_score = 0;
1415 for col in 0..ncols {
1416 let mut score = 0;
1417 for (pats, _) in matrix {
1418 match pats[col] {
1419 Pattern::Constructor(_, _) => score += 2,
1420 Pattern::Literal(_) => score += 1,
1421 _ => {}
1422 }
1423 }
1424 if score > best_score {
1425 best_score = score;
1426 best_col = col;
1427 }
1428 }
1429 best_col
1430 }
1431 fn get_ctor_arity(&self, ctor_name: &Name) -> u32 {
1433 for ctors in self.constructors.values() {
1434 for ctor in ctors {
1435 if &ctor.name == ctor_name {
1436 return ctor.num_fields;
1437 }
1438 }
1439 }
1440 0
1441 }
1442 fn compile_literal_column(
1448 &mut self,
1449 scrutinees: &[Expr],
1450 matrix: &[(Vec<&Pattern>, usize)],
1451 col: usize,
1452 ) -> Result<DecisionTree, String> {
1453 if matrix.is_empty() {
1454 return Ok(DecisionTree::Failure);
1455 }
1456 let mut seen_lits: Vec<crate::Literal> = Vec::new();
1457 for (pats, _) in matrix {
1458 if let Pattern::Literal(lit) = &pats[col] {
1459 if !seen_lits.contains(lit) {
1460 seen_lits.push(lit.clone());
1461 }
1462 }
1463 }
1464 let sub_scrutinees: Vec<Expr> = scrutinees
1465 .iter()
1466 .enumerate()
1467 .filter(|(i, _)| *i != col)
1468 .map(|(_, s)| s.clone())
1469 .collect();
1470 let mut branches = Vec::new();
1471 for lit in &seen_lits {
1472 let sub_matrix: Vec<(Vec<&Pattern>, usize)> = matrix
1473 .iter()
1474 .filter_map(|(pats, arm_idx)| match &pats[col] {
1475 Pattern::Literal(l) if l == lit => {
1476 let new_pats: Vec<&Pattern> = pats
1477 .iter()
1478 .enumerate()
1479 .filter(|(i, _)| *i != col)
1480 .map(|(_, p)| *p)
1481 .collect();
1482 Some((new_pats, *arm_idx))
1483 }
1484 Pattern::Wildcard | Pattern::Var(_) => {
1485 let new_pats: Vec<&Pattern> = pats
1486 .iter()
1487 .enumerate()
1488 .filter(|(i, _)| *i != col)
1489 .map(|(_, p)| *p)
1490 .collect();
1491 Some((new_pats, *arm_idx))
1492 }
1493 _ => None,
1494 })
1495 .collect();
1496 let sub_tree = self.compile_matrix(&sub_scrutinees, &sub_matrix)?;
1497 let lit_name = Name::str(format!("{:?}", lit));
1498 branches.push((lit_name, Vec::new(), sub_tree));
1499 }
1500 let default_matrix: Vec<(Vec<&Pattern>, usize)> = matrix
1501 .iter()
1502 .filter_map(|(pats, arm_idx)| match &pats[col] {
1503 Pattern::Wildcard | Pattern::Var(_) => {
1504 let new_pats: Vec<&Pattern> = pats
1505 .iter()
1506 .enumerate()
1507 .filter(|(i, _)| *i != col)
1508 .map(|(_, p)| *p)
1509 .collect();
1510 Some((new_pats, *arm_idx))
1511 }
1512 _ => None,
1513 })
1514 .collect();
1515 let default = if !default_matrix.is_empty() {
1516 Some(Box::new(
1517 self.compile_matrix(&sub_scrutinees, &default_matrix)?,
1518 ))
1519 } else {
1520 None
1521 };
1522 Ok(DecisionTree::Switch {
1523 scrutinee: scrutinees[col].clone(),
1524 branches,
1525 default,
1526 })
1527 }
1528 fn mark_reachable(&self, tree: &DecisionTree, reachable: &mut [bool]) {
1530 match tree {
1531 DecisionTree::Leaf { arm_idx, .. } => {
1532 if *arm_idx < reachable.len() {
1533 reachable[*arm_idx] = true;
1534 }
1535 }
1536 DecisionTree::Switch {
1537 branches, default, ..
1538 } => {
1539 for (_, _, sub_tree) in branches {
1540 self.mark_reachable(sub_tree, reachable);
1541 }
1542 if let Some(d) = default {
1543 self.mark_reachable(d, reachable);
1544 }
1545 }
1546 DecisionTree::Failure => {}
1547 }
1548 }
1549 fn check_exhaustiveness_tree(&self, tree: &DecisionTree) -> Vec<Vec<Pattern>> {
1551 match tree {
1552 DecisionTree::Failure => vec![vec![Pattern::Wildcard]],
1553 DecisionTree::Leaf { .. } => Vec::new(),
1554 DecisionTree::Switch {
1555 branches, default, ..
1556 } => {
1557 let mut missing = Vec::new();
1558 for (_, _, sub_tree) in branches {
1559 missing.extend(self.check_exhaustiveness_tree(sub_tree));
1560 }
1561 if let Some(d) = default {
1562 missing.extend(self.check_exhaustiveness_tree(d));
1563 }
1564 missing
1565 }
1566 }
1567 }
1568 pub fn check_exhaustive(&self, patterns: &[Pattern], ind_name: &Name) -> Result<(), String> {
1573 let mut flat: Vec<&Pattern> = Vec::new();
1574 for pat in patterns {
1575 Self::collect_top_patterns(pat, &mut flat);
1576 }
1577 if flat.iter().any(|p| Self::is_irrefutable(p)) {
1578 return Ok(());
1579 }
1580 if let Some(ctors) = self.constructors.get(ind_name) {
1581 let mut covered: std::collections::HashSet<Name> = std::collections::HashSet::new();
1582 for pat in &flat {
1583 if let Pattern::Constructor(name, _) = pat {
1584 covered.insert(name.clone());
1585 }
1586 }
1587 let missing: Vec<&Name> = ctors
1588 .iter()
1589 .filter(|c| !covered.contains(&c.name))
1590 .map(|c| &c.name)
1591 .collect();
1592 if missing.is_empty() {
1593 Ok(())
1594 } else {
1595 Err(format!(
1596 "Non-exhaustive patterns: missing constructors {:?}",
1597 missing
1598 ))
1599 }
1600 } else {
1601 Ok(())
1602 }
1603 }
1604 pub fn check_redundant(&self, patterns: &[Pattern]) -> Vec<usize> {
1610 let mut seen_wildcard = false;
1611 let mut seen_ctors: std::collections::HashSet<Name> = std::collections::HashSet::new();
1612 let mut seen_lits: std::collections::HashSet<String> = std::collections::HashSet::new();
1613 let mut redundant = Vec::new();
1614 for (i, pat) in patterns.iter().enumerate() {
1615 if seen_wildcard {
1616 redundant.push(i);
1617 continue;
1618 }
1619 if Self::is_covered_by(pat, &seen_ctors, &seen_lits) {
1620 redundant.push(i);
1621 }
1622 Self::record_pattern(pat, &mut seen_wildcard, &mut seen_ctors, &mut seen_lits);
1623 }
1624 redundant
1625 }
1626 fn collect_top_patterns<'a>(pat: &'a Pattern, out: &mut Vec<&'a Pattern>) {
1628 match pat {
1629 Pattern::Or(pats) => {
1630 for p in pats {
1631 Self::collect_top_patterns(p, out);
1632 }
1633 }
1634 Pattern::As(_, inner) => Self::collect_top_patterns(inner, out),
1635 _ => out.push(pat),
1636 }
1637 }
1638 fn is_irrefutable(pat: &Pattern) -> bool {
1640 match pat {
1641 Pattern::Wildcard | Pattern::Var(_) => true,
1642 Pattern::As(_, inner) => Self::is_irrefutable(inner),
1643 Pattern::Or(pats) => pats.iter().any(Self::is_irrefutable),
1644 _ => false,
1645 }
1646 }
1647 fn is_covered_by(
1649 pat: &Pattern,
1650 seen_ctors: &std::collections::HashSet<Name>,
1651 seen_lits: &std::collections::HashSet<String>,
1652 ) -> bool {
1653 match pat {
1654 Pattern::Constructor(name, _) => seen_ctors.contains(name),
1655 Pattern::Literal(lit) => seen_lits.contains(&format!("{:?}", lit)),
1656 Pattern::As(_, inner) => Self::is_covered_by(inner, seen_ctors, seen_lits),
1657 Pattern::Or(pats) => pats
1658 .iter()
1659 .all(|p| Self::is_covered_by(p, seen_ctors, seen_lits)),
1660 Pattern::Wildcard | Pattern::Var(_) | Pattern::Inaccessible(_) => false,
1661 }
1662 }
1663 fn record_pattern(
1665 pat: &Pattern,
1666 seen_wildcard: &mut bool,
1667 seen_ctors: &mut std::collections::HashSet<Name>,
1668 seen_lits: &mut std::collections::HashSet<String>,
1669 ) {
1670 match pat {
1671 Pattern::Wildcard | Pattern::Var(_) => *seen_wildcard = true,
1672 Pattern::As(_, inner) => {
1673 Self::record_pattern(inner, seen_wildcard, seen_ctors, seen_lits)
1674 }
1675 Pattern::Constructor(name, _) => {
1676 seen_ctors.insert(name.clone());
1677 }
1678 Pattern::Literal(lit) => {
1679 seen_lits.insert(format!("{:?}", lit));
1680 }
1681 Pattern::Or(pats) => {
1682 for p in pats {
1683 Self::record_pattern(p, seen_wildcard, seen_ctors, seen_lits);
1684 }
1685 }
1686 Pattern::Inaccessible(_) => {}
1687 }
1688 }
1689}