1use super::functions::*;
6use crate::Node;
7use crate::{Expr, Name};
8use std::collections::HashMap;
9use std::rc::Rc;
10
11#[derive(Debug, Clone)]
13pub enum DecisionTree {
14 Leaf {
16 arm_idx: usize,
18 bindings: Vec<(Name, Expr)>,
20 },
21 Switch {
23 scrutinee: Expr,
25 branches: Vec<(Name, Vec<Name>, DecisionTree)>,
27 default: Option<Box<DecisionTree>>,
29 },
30 Failure,
32}
33#[allow(dead_code)]
35pub struct StringPool {
36 free: Vec<String>,
37}
38#[allow(dead_code)]
39impl StringPool {
40 pub fn new() -> Self {
42 Self { free: Vec::new() }
43 }
44 pub fn take(&mut self) -> String {
46 self.free.pop().unwrap_or_default()
47 }
48 pub fn give(&mut self, mut s: String) {
50 s.clear();
51 self.free.push(s);
52 }
53 pub fn free_count(&self) -> usize {
55 self.free.len()
56 }
57}
58#[derive(Debug, Clone)]
60pub struct MatchStats {
61 pub num_arms: usize,
63 pub reachable_arms: Vec<usize>,
65 pub unreachable_arms: Vec<usize>,
67 pub is_exhaustive: bool,
69 pub tree_depth: usize,
71}
72impl MatchStats {
73 pub fn is_exhaustive(&self) -> bool {
75 self.is_exhaustive
76 }
77 pub fn has_redundant_arms(&self) -> bool {
79 !self.unreachable_arms.is_empty()
80 }
81}
82#[allow(dead_code)]
84pub enum Either2<A, B> {
85 First(A),
87 Second(B),
89}
90#[allow(dead_code)]
91impl<A, B> Either2<A, B> {
92 pub fn is_first(&self) -> bool {
94 matches!(self, Either2::First(_))
95 }
96 pub fn is_second(&self) -> bool {
98 matches!(self, Either2::Second(_))
99 }
100 pub fn first(self) -> Option<A> {
102 match self {
103 Either2::First(a) => Some(a),
104 _ => None,
105 }
106 }
107 pub fn second(self) -> Option<B> {
109 match self {
110 Either2::Second(b) => Some(b),
111 _ => None,
112 }
113 }
114 pub fn map_first<C, F: FnOnce(A) -> C>(self, f: F) -> Either2<C, B> {
116 match self {
117 Either2::First(a) => Either2::First(f(a)),
118 Either2::Second(b) => Either2::Second(b),
119 }
120 }
121}
122#[allow(dead_code)]
124pub struct LabelSet {
125 labels: Vec<String>,
126}
127#[allow(dead_code)]
128impl LabelSet {
129 pub fn new() -> Self {
131 Self { labels: Vec::new() }
132 }
133 pub fn add(&mut self, label: impl Into<String>) {
135 let s = label.into();
136 if !self.labels.contains(&s) {
137 self.labels.push(s);
138 }
139 }
140 pub fn has(&self, label: &str) -> bool {
142 self.labels.iter().any(|l| l == label)
143 }
144 pub fn count(&self) -> usize {
146 self.labels.len()
147 }
148 pub fn all(&self) -> &[String] {
150 &self.labels
151 }
152}
153#[allow(dead_code)]
155pub struct TransitiveClosure {
156 adj: Vec<Vec<usize>>,
157 n: usize,
158}
159#[allow(dead_code)]
160impl TransitiveClosure {
161 pub fn new(n: usize) -> Self {
163 Self {
164 adj: vec![Vec::new(); n],
165 n,
166 }
167 }
168 pub fn add_edge(&mut self, from: usize, to: usize) {
170 if from < self.n {
171 self.adj[from].push(to);
172 }
173 }
174 pub fn reachable_from(&self, start: usize) -> Vec<usize> {
176 let mut visited = vec![false; self.n];
177 let mut queue = std::collections::VecDeque::new();
178 queue.push_back(start);
179 while let Some(node) = queue.pop_front() {
180 if node >= self.n || visited[node] {
181 continue;
182 }
183 visited[node] = true;
184 for &next in &self.adj[node] {
185 queue.push_back(next);
186 }
187 }
188 (0..self.n).filter(|&i| visited[i]).collect()
189 }
190 pub fn can_reach(&self, from: usize, to: usize) -> bool {
192 self.reachable_from(from).contains(&to)
193 }
194}
195#[allow(dead_code)]
197pub struct SlidingSum {
198 window: Vec<f64>,
199 capacity: usize,
200 pos: usize,
201 sum: f64,
202 count: usize,
203}
204#[allow(dead_code)]
205impl SlidingSum {
206 pub fn new(capacity: usize) -> Self {
208 Self {
209 window: vec![0.0; capacity],
210 capacity,
211 pos: 0,
212 sum: 0.0,
213 count: 0,
214 }
215 }
216 pub fn push(&mut self, val: f64) {
218 let oldest = self.window[self.pos];
219 self.sum -= oldest;
220 self.sum += val;
221 self.window[self.pos] = val;
222 self.pos = (self.pos + 1) % self.capacity;
223 if self.count < self.capacity {
224 self.count += 1;
225 }
226 }
227 pub fn sum(&self) -> f64 {
229 self.sum
230 }
231 pub fn mean(&self) -> Option<f64> {
233 if self.count == 0 {
234 None
235 } else {
236 Some(self.sum / self.count as f64)
237 }
238 }
239 pub fn count(&self) -> usize {
241 self.count
242 }
243}
244#[derive(Debug, Clone)]
246pub struct MatchArm {
247 pub patterns: Vec<Pattern>,
249 pub rhs: Expr,
251 pub guard: Option<Expr>,
253}
254#[derive(Debug, Clone)]
256pub struct CompileResult {
257 pub tree: DecisionTree,
259 pub unreachable_arms: Vec<usize>,
261 pub missing_patterns: Vec<Vec<Pattern>>,
263}
264#[allow(dead_code)]
266pub struct SmallMap<K: Ord + Clone, V: Clone> {
267 entries: Vec<(K, V)>,
268}
269#[allow(dead_code)]
270impl<K: Ord + Clone, V: Clone> SmallMap<K, V> {
271 pub fn new() -> Self {
273 Self {
274 entries: Vec::new(),
275 }
276 }
277 pub fn insert(&mut self, key: K, val: V) {
279 match self.entries.binary_search_by_key(&&key, |(k, _)| k) {
280 Ok(i) => self.entries[i].1 = val,
281 Err(i) => self.entries.insert(i, (key, val)),
282 }
283 }
284 pub fn get(&self, key: &K) -> Option<&V> {
286 self.entries
287 .binary_search_by_key(&key, |(k, _)| k)
288 .ok()
289 .map(|i| &self.entries[i].1)
290 }
291 pub fn len(&self) -> usize {
293 self.entries.len()
294 }
295 pub fn is_empty(&self) -> bool {
297 self.entries.is_empty()
298 }
299 pub fn keys(&self) -> Vec<&K> {
301 self.entries.iter().map(|(k, _)| k).collect()
302 }
303 pub fn values(&self) -> Vec<&V> {
305 self.entries.iter().map(|(_, v)| v).collect()
306 }
307}
308#[allow(dead_code)]
310pub struct SimpleDag {
311 edges: Vec<Vec<usize>>,
313}
314#[allow(dead_code)]
315impl SimpleDag {
316 pub fn new(n: usize) -> Self {
318 Self {
319 edges: vec![Vec::new(); n],
320 }
321 }
322 pub fn add_edge(&mut self, from: usize, to: usize) {
324 if from < self.edges.len() {
325 self.edges[from].push(to);
326 }
327 }
328 pub fn successors(&self, node: usize) -> &[usize] {
330 self.edges.get(node).map(|v| v.as_slice()).unwrap_or(&[])
331 }
332 pub fn can_reach(&self, from: usize, to: usize) -> bool {
334 let mut visited = vec![false; self.edges.len()];
335 self.dfs(from, to, &mut visited)
336 }
337 fn dfs(&self, cur: usize, target: usize, visited: &mut Vec<bool>) -> bool {
338 if cur == target {
339 return true;
340 }
341 if cur >= visited.len() || visited[cur] {
342 return false;
343 }
344 visited[cur] = true;
345 for &next in self.successors(cur) {
346 if self.dfs(next, target, visited) {
347 return true;
348 }
349 }
350 false
351 }
352 pub fn topological_sort(&self) -> Option<Vec<usize>> {
354 let n = self.edges.len();
355 let mut in_degree = vec![0usize; n];
356 for succs in &self.edges {
357 for &s in succs {
358 if s < n {
359 in_degree[s] += 1;
360 }
361 }
362 }
363 let mut queue: std::collections::VecDeque<usize> =
364 (0..n).filter(|&i| in_degree[i] == 0).collect();
365 let mut order = Vec::new();
366 while let Some(node) = queue.pop_front() {
367 order.push(node);
368 for &s in self.successors(node) {
369 if s < n {
370 in_degree[s] -= 1;
371 if in_degree[s] == 0 {
372 queue.push_back(s);
373 }
374 }
375 }
376 }
377 if order.len() == n {
378 Some(order)
379 } else {
380 None
381 }
382 }
383 pub fn num_nodes(&self) -> usize {
385 self.edges.len()
386 }
387}
388#[allow(dead_code)]
390pub struct NonEmptyVec<T> {
391 head: T,
392 tail: Vec<T>,
393}
394#[allow(dead_code)]
395impl<T> NonEmptyVec<T> {
396 pub fn singleton(val: T) -> Self {
398 Self {
399 head: val,
400 tail: Vec::new(),
401 }
402 }
403 pub fn push(&mut self, val: T) {
405 self.tail.push(val);
406 }
407 pub fn first(&self) -> &T {
409 &self.head
410 }
411 pub fn last(&self) -> &T {
413 self.tail.last().unwrap_or(&self.head)
414 }
415 pub fn len(&self) -> usize {
417 1 + self.tail.len()
418 }
419 pub fn is_empty(&self) -> bool {
421 self.len() == 0
422 }
423 pub fn to_vec(&self) -> Vec<&T> {
425 let mut v = vec![&self.head];
426 v.extend(self.tail.iter());
427 v
428 }
429}
430#[allow(dead_code)]
432pub struct PathBuf {
433 components: Vec<String>,
434}
435#[allow(dead_code)]
436impl PathBuf {
437 pub fn new() -> Self {
439 Self {
440 components: Vec::new(),
441 }
442 }
443 pub fn push(&mut self, comp: impl Into<String>) {
445 self.components.push(comp.into());
446 }
447 pub fn pop(&mut self) {
449 self.components.pop();
450 }
451 pub fn as_str(&self) -> String {
453 self.components.join("/")
454 }
455 pub fn depth(&self) -> usize {
457 self.components.len()
458 }
459 pub fn clear(&mut self) {
461 self.components.clear();
462 }
463}
464#[allow(dead_code)]
466pub struct FlatSubstitution {
467 pairs: Vec<(String, String)>,
468}
469#[allow(dead_code)]
470impl FlatSubstitution {
471 pub fn new() -> Self {
473 Self { pairs: Vec::new() }
474 }
475 pub fn add(&mut self, from: impl Into<String>, to: impl Into<String>) {
477 self.pairs.push((from.into(), to.into()));
478 }
479 pub fn apply(&self, s: &str) -> String {
481 let mut result = s.to_string();
482 for (from, to) in &self.pairs {
483 result = result.replace(from.as_str(), to.as_str());
484 }
485 result
486 }
487 pub fn len(&self) -> usize {
489 self.pairs.len()
490 }
491 pub fn is_empty(&self) -> bool {
493 self.pairs.is_empty()
494 }
495}
496#[allow(dead_code)]
498pub struct Stopwatch {
499 start: crate::wall_clock::Instant,
500 splits: Vec<f64>,
501}
502#[allow(dead_code)]
503impl Stopwatch {
504 pub fn start() -> Self {
506 Self {
507 start: crate::wall_clock::Instant::now(),
508 splits: Vec::new(),
509 }
510 }
511 pub fn split(&mut self) {
513 self.splits.push(self.elapsed_ms());
514 }
515 pub fn elapsed_ms(&self) -> f64 {
517 self.start.elapsed().as_secs_f64() * 1000.0
518 }
519 pub fn splits(&self) -> &[f64] {
521 &self.splits
522 }
523 pub fn num_splits(&self) -> usize {
525 self.splits.len()
526 }
527}
528#[allow(dead_code)]
530pub struct WriteOnce<T> {
531 value: std::cell::Cell<Option<T>>,
532}
533#[allow(dead_code)]
534impl<T: Copy> WriteOnce<T> {
535 pub fn new() -> Self {
537 Self {
538 value: std::cell::Cell::new(None),
539 }
540 }
541 pub fn write(&self, val: T) -> bool {
543 if self.value.get().is_some() {
544 return false;
545 }
546 self.value.set(Some(val));
547 true
548 }
549 pub fn read(&self) -> Option<T> {
551 self.value.get()
552 }
553 pub fn is_written(&self) -> bool {
555 self.value.get().is_some()
556 }
557}
558#[allow(dead_code)]
560pub struct WindowIterator<'a, T> {
561 pub(super) data: &'a [T],
562 pub(super) pos: usize,
563 pub(super) window: usize,
564}
565#[allow(dead_code)]
566impl<'a, T> WindowIterator<'a, T> {
567 pub fn new(data: &'a [T], window: usize) -> Self {
569 Self {
570 data,
571 pos: 0,
572 window,
573 }
574 }
575}
576#[allow(dead_code)]
578#[allow(missing_docs)]
579pub struct RewriteRule {
580 pub name: String,
582 pub lhs: String,
584 pub rhs: String,
586 pub conditional: bool,
588}
589#[allow(dead_code)]
590impl RewriteRule {
591 pub fn unconditional(
593 name: impl Into<String>,
594 lhs: impl Into<String>,
595 rhs: impl Into<String>,
596 ) -> Self {
597 Self {
598 name: name.into(),
599 lhs: lhs.into(),
600 rhs: rhs.into(),
601 conditional: false,
602 }
603 }
604 pub fn conditional(
606 name: impl Into<String>,
607 lhs: impl Into<String>,
608 rhs: impl Into<String>,
609 ) -> Self {
610 Self {
611 name: name.into(),
612 lhs: lhs.into(),
613 rhs: rhs.into(),
614 conditional: true,
615 }
616 }
617 pub fn display(&self) -> String {
619 format!("{}: {} → {}", self.name, self.lhs, self.rhs)
620 }
621}
622#[allow(dead_code)]
624pub struct StackCalc {
625 stack: Vec<i64>,
626}
627#[allow(dead_code)]
628impl StackCalc {
629 pub fn new() -> Self {
631 Self { stack: Vec::new() }
632 }
633 pub fn push(&mut self, n: i64) {
635 self.stack.push(n);
636 }
637 fn apply_binary(&mut self, op: impl FnOnce(i64, i64) -> i64) -> Option<i64> {
641 let (rest_len, a, b) = match self.stack[..] {
642 [ref rest @ .., a, b] => (rest.len(), a, b),
643 _ => return None,
644 };
645 let value = op(a, b);
646 self.stack.truncate(rest_len);
647 self.stack.push(value);
648 Some(value)
649 }
650 pub fn add(&mut self) -> Option<i64> {
654 self.apply_binary(|a, b| a + b)
655 }
656 pub fn sub(&mut self) -> Option<i64> {
660 self.apply_binary(|a, b| a - b)
661 }
662 pub fn mul(&mut self) -> Option<i64> {
666 self.apply_binary(|a, b| a * b)
667 }
668 pub fn peek(&self) -> Option<i64> {
670 self.stack.last().copied()
671 }
672 pub fn depth(&self) -> usize {
674 self.stack.len()
675 }
676}
677#[allow(dead_code)]
679pub struct TokenBucket {
680 capacity: u64,
681 tokens: u64,
682 refill_per_ms: u64,
683 last_refill: crate::wall_clock::Instant,
684}
685#[allow(dead_code)]
686impl TokenBucket {
687 pub fn new(capacity: u64, refill_per_ms: u64) -> Self {
689 Self {
690 capacity,
691 tokens: capacity,
692 refill_per_ms,
693 last_refill: crate::wall_clock::Instant::now(),
694 }
695 }
696 pub fn try_consume(&mut self, n: u64) -> bool {
698 self.refill();
699 if self.tokens >= n {
700 self.tokens -= n;
701 true
702 } else {
703 false
704 }
705 }
706 fn refill(&mut self) {
707 let now = crate::wall_clock::Instant::now();
708 let elapsed_ms = now.duration_since(self.last_refill).as_millis() as u64;
709 if elapsed_ms > 0 {
710 let new_tokens = elapsed_ms * self.refill_per_ms;
711 self.tokens = (self.tokens + new_tokens).min(self.capacity);
712 self.last_refill = now;
713 }
714 }
715 pub fn available(&self) -> u64 {
717 self.tokens
718 }
719 pub fn capacity(&self) -> u64 {
721 self.capacity
722 }
723}
724#[derive(Debug, Clone, PartialEq)]
726pub enum Pattern {
727 Wildcard,
729 Var(Name),
731 Constructor(Name, Vec<Pattern>),
733 Literal(crate::Literal),
735 As(Name, Box<Pattern>),
737 Or(Vec<Pattern>),
739 Inaccessible(Expr),
741}
742#[allow(dead_code)]
744pub struct RewriteRuleSet {
745 rules: Vec<RewriteRule>,
746}
747#[allow(dead_code)]
748impl RewriteRuleSet {
749 pub fn new() -> Self {
751 Self { rules: Vec::new() }
752 }
753 pub fn add(&mut self, rule: RewriteRule) {
755 self.rules.push(rule);
756 }
757 pub fn len(&self) -> usize {
759 self.rules.len()
760 }
761 pub fn is_empty(&self) -> bool {
763 self.rules.is_empty()
764 }
765 pub fn conditional_rules(&self) -> Vec<&RewriteRule> {
767 self.rules.iter().filter(|r| r.conditional).collect()
768 }
769 pub fn unconditional_rules(&self) -> Vec<&RewriteRule> {
771 self.rules.iter().filter(|r| !r.conditional).collect()
772 }
773 pub fn get(&self, name: &str) -> Option<&RewriteRule> {
775 self.rules.iter().find(|r| r.name == name)
776 }
777}
778#[allow(dead_code)]
780pub struct FocusStack<T> {
781 items: Vec<T>,
782}
783#[allow(dead_code)]
784impl<T> FocusStack<T> {
785 pub fn new() -> Self {
787 Self { items: Vec::new() }
788 }
789 pub fn focus(&mut self, item: T) {
791 self.items.push(item);
792 }
793 pub fn blur(&mut self) -> Option<T> {
795 self.items.pop()
796 }
797 pub fn current(&self) -> Option<&T> {
799 self.items.last()
800 }
801 pub fn depth(&self) -> usize {
803 self.items.len()
804 }
805 pub fn is_empty(&self) -> bool {
807 self.items.is_empty()
808 }
809}
810#[allow(dead_code)]
812pub struct RawFnPtr {
813 ptr: usize,
815 arity: usize,
816 name: String,
817}
818#[allow(dead_code)]
819impl RawFnPtr {
820 pub fn new(ptr: usize, arity: usize, name: impl Into<String>) -> Self {
822 Self {
823 ptr,
824 arity,
825 name: name.into(),
826 }
827 }
828 pub fn arity(&self) -> usize {
830 self.arity
831 }
832 pub fn name(&self) -> &str {
834 &self.name
835 }
836 pub fn raw(&self) -> usize {
838 self.ptr
839 }
840}
841#[allow(dead_code)]
843#[allow(missing_docs)]
844pub enum DecisionNode {
845 Leaf(String),
847 Branch {
849 key: String,
850 val: String,
851 yes_branch: Box<DecisionNode>,
852 no_branch: Box<DecisionNode>,
853 },
854}
855#[allow(dead_code)]
856impl DecisionNode {
857 pub fn evaluate(&self, ctx: &std::collections::HashMap<String, String>) -> &str {
859 match self {
860 DecisionNode::Leaf(action) => action.as_str(),
861 DecisionNode::Branch {
862 key,
863 val,
864 yes_branch,
865 no_branch,
866 } => {
867 let actual = ctx.get(key).map(|s| s.as_str()).unwrap_or("");
868 if actual == val.as_str() {
869 yes_branch.evaluate(ctx)
870 } else {
871 no_branch.evaluate(ctx)
872 }
873 }
874 }
875 }
876 pub fn depth(&self) -> usize {
878 match self {
879 DecisionNode::Leaf(_) => 0,
880 DecisionNode::Branch {
881 yes_branch,
882 no_branch,
883 ..
884 } => 1 + yes_branch.depth().max(no_branch.depth()),
885 }
886 }
887}
888#[allow(dead_code)]
890pub struct StatSummary {
891 count: u64,
892 sum: f64,
893 min: f64,
894 max: f64,
895}
896#[allow(dead_code)]
897impl StatSummary {
898 pub fn new() -> Self {
900 Self {
901 count: 0,
902 sum: 0.0,
903 min: f64::INFINITY,
904 max: f64::NEG_INFINITY,
905 }
906 }
907 pub fn record(&mut self, val: f64) {
909 self.count += 1;
910 self.sum += val;
911 if val < self.min {
912 self.min = val;
913 }
914 if val > self.max {
915 self.max = val;
916 }
917 }
918 pub fn mean(&self) -> Option<f64> {
920 if self.count == 0 {
921 None
922 } else {
923 Some(self.sum / self.count as f64)
924 }
925 }
926 pub fn min(&self) -> Option<f64> {
928 if self.count == 0 {
929 None
930 } else {
931 Some(self.min)
932 }
933 }
934 pub fn max(&self) -> Option<f64> {
936 if self.count == 0 {
937 None
938 } else {
939 Some(self.max)
940 }
941 }
942 pub fn count(&self) -> u64 {
944 self.count
945 }
946}
947#[allow(dead_code)]
949pub struct SparseVec<T: Default + Clone + PartialEq> {
950 entries: std::collections::HashMap<usize, T>,
951 default_: T,
952 logical_len: usize,
953}
954#[allow(dead_code)]
955impl<T: Default + Clone + PartialEq> SparseVec<T> {
956 pub fn new(len: usize) -> Self {
958 Self {
959 entries: std::collections::HashMap::new(),
960 default_: T::default(),
961 logical_len: len,
962 }
963 }
964 pub fn set(&mut self, idx: usize, val: T) {
966 if val == self.default_ {
967 self.entries.remove(&idx);
968 } else {
969 self.entries.insert(idx, val);
970 }
971 }
972 pub fn get(&self, idx: usize) -> &T {
974 self.entries.get(&idx).unwrap_or(&self.default_)
975 }
976 pub fn len(&self) -> usize {
978 self.logical_len
979 }
980 pub fn is_empty(&self) -> bool {
982 self.len() == 0
983 }
984 pub fn nnz(&self) -> usize {
986 self.entries.len()
987 }
988}
989#[allow(dead_code)]
991pub struct VersionedRecord<T: Clone> {
992 earlier: Vec<T>,
994 current: T,
996}
997#[allow(dead_code)]
998impl<T: Clone> VersionedRecord<T> {
999 pub fn new(initial: T) -> Self {
1001 Self {
1002 earlier: Vec::new(),
1003 current: initial,
1004 }
1005 }
1006 pub fn update(&mut self, val: T) {
1008 let previous = std::mem::replace(&mut self.current, val);
1009 self.earlier.push(previous);
1010 }
1011 pub fn current(&self) -> &T {
1013 &self.current
1014 }
1015 pub fn at_version(&self, n: usize) -> Option<&T> {
1017 match self.earlier.get(n) {
1018 Some(value) => Some(value),
1019 None if n == self.earlier.len() => Some(&self.current),
1020 None => None,
1021 }
1022 }
1023 pub fn version(&self) -> usize {
1025 self.earlier.len()
1026 }
1027 pub fn has_history(&self) -> bool {
1029 !self.earlier.is_empty()
1030 }
1031}
1032#[derive(Debug, Clone)]
1034pub struct ConstructorInfo {
1035 pub name: Name,
1037 pub num_fields: u32,
1039 pub inductive: Name,
1041}
1042#[derive(Clone, Debug, Default)]
1044pub struct PatternStats {
1045 pub total_patterns: usize,
1047 pub wildcards: usize,
1049 pub constructors: usize,
1051 pub literals: usize,
1053 pub or_patterns: usize,
1055 pub redundant_arms: usize,
1057}
1058impl PatternStats {
1059 pub fn new() -> Self {
1061 Self::default()
1062 }
1063 pub fn from_patterns(patterns: &[Pattern]) -> Self {
1065 let mut stats = Self::new();
1066 for p in patterns {
1067 stats.total_patterns += 1;
1068 stats.count_pattern(p);
1069 }
1070 stats
1071 }
1072 fn count_pattern(&mut self, pattern: &Pattern) {
1073 match pattern {
1074 Pattern::Wildcard => self.wildcards += 1,
1075 Pattern::Var(_) => self.wildcards += 1,
1076 Pattern::Constructor(_, sub) => {
1077 self.constructors += 1;
1078 for p in sub {
1079 self.count_pattern(p);
1080 }
1081 }
1082 Pattern::Literal(_) => self.literals += 1,
1083 Pattern::As(_, inner) => self.count_pattern(inner),
1084 Pattern::Or(pats) => {
1085 self.or_patterns += 1;
1086 for p in pats {
1087 self.count_pattern(p);
1088 }
1089 }
1090 Pattern::Inaccessible(_) => {}
1091 }
1092 }
1093}
1094#[allow(dead_code)]
1096pub struct TransformStat {
1097 before: StatSummary,
1098 after: StatSummary,
1099}
1100#[allow(dead_code)]
1101impl TransformStat {
1102 pub fn new() -> Self {
1104 Self {
1105 before: StatSummary::new(),
1106 after: StatSummary::new(),
1107 }
1108 }
1109 pub fn record_before(&mut self, v: f64) {
1111 self.before.record(v);
1112 }
1113 pub fn record_after(&mut self, v: f64) {
1115 self.after.record(v);
1116 }
1117 pub fn mean_ratio(&self) -> Option<f64> {
1119 let b = self.before.mean()?;
1120 let a = self.after.mean()?;
1121 if b.abs() < f64::EPSILON {
1122 return None;
1123 }
1124 Some(a / b)
1125 }
1126}
1127#[allow(dead_code)]
1129pub struct ConfigNode {
1130 key: String,
1131 value: Option<String>,
1132 children: Vec<ConfigNode>,
1133}
1134#[allow(dead_code)]
1135impl ConfigNode {
1136 pub fn leaf(key: impl Into<String>, value: impl Into<String>) -> Self {
1138 Self {
1139 key: key.into(),
1140 value: Some(value.into()),
1141 children: Vec::new(),
1142 }
1143 }
1144 pub fn section(key: impl Into<String>) -> Self {
1146 Self {
1147 key: key.into(),
1148 value: None,
1149 children: Vec::new(),
1150 }
1151 }
1152 pub fn add_child(&mut self, child: ConfigNode) {
1154 self.children.push(child);
1155 }
1156 pub fn key(&self) -> &str {
1158 &self.key
1159 }
1160 pub fn value(&self) -> Option<&str> {
1162 self.value.as_deref()
1163 }
1164 pub fn num_children(&self) -> usize {
1166 self.children.len()
1167 }
1168 pub fn lookup(&self, path: &str) -> Option<&str> {
1170 let mut parts = path.splitn(2, '.');
1171 let head = parts.next()?;
1172 let tail = parts.next();
1173 if head != self.key {
1174 return None;
1175 }
1176 match tail {
1177 None => self.value.as_deref(),
1178 Some(rest) => self.children.iter().find_map(|c| c.lookup_relative(rest)),
1179 }
1180 }
1181 fn lookup_relative(&self, path: &str) -> Option<&str> {
1182 let mut parts = path.splitn(2, '.');
1183 let head = parts.next()?;
1184 let tail = parts.next();
1185 if head != self.key {
1186 return None;
1187 }
1188 match tail {
1189 None => self.value.as_deref(),
1190 Some(rest) => self.children.iter().find_map(|c| c.lookup_relative(rest)),
1191 }
1192 }
1193}
1194pub struct MatchCompiler {
1196 next_var: u32,
1198 constructors: HashMap<Name, Vec<ConstructorInfo>>,
1200}
1201impl MatchCompiler {
1202 pub fn new() -> Self {
1204 Self {
1205 next_var: 0,
1206 constructors: HashMap::new(),
1207 }
1208 }
1209 pub fn fresh_var(&mut self) -> Name {
1211 let n = self.next_var;
1212 self.next_var += 1;
1213 Name::str(format!("_match{}", n))
1214 }
1215 pub fn register_constructors(&mut self, ind_name: Name, ctors: Vec<ConstructorInfo>) {
1217 self.constructors.insert(ind_name, ctors);
1218 }
1219 pub fn get_constructors(&self, ind_name: &Name) -> Option<&Vec<ConstructorInfo>> {
1221 self.constructors.get(ind_name)
1222 }
1223 pub fn compile_match(
1227 &mut self,
1228 scrutinees: &[Expr],
1229 arms: &[MatchArm],
1230 ) -> Result<CompileResult, String> {
1231 if arms.is_empty() {
1232 return Err("Match expression has no arms".to_string());
1233 }
1234 for (i, arm) in arms.iter().enumerate() {
1235 if arm.patterns.len() != scrutinees.len() {
1236 return Err(format!(
1237 "Arm {} has {} patterns but expected {}",
1238 i,
1239 arm.patterns.len(),
1240 scrutinees.len()
1241 ));
1242 }
1243 }
1244 let matrix: Vec<(Vec<&Pattern>, usize)> = arms
1245 .iter()
1246 .enumerate()
1247 .map(|(i, arm)| (arm.patterns.iter().collect(), i))
1248 .collect();
1249 let tree = self.compile_matrix(scrutinees, &matrix)?;
1250 let mut reachable = vec![false; arms.len()];
1251 self.mark_reachable(&tree, &mut reachable);
1252 let unreachable_arms: Vec<usize> = reachable
1253 .iter()
1254 .enumerate()
1255 .filter(|(_, &r)| !r)
1256 .map(|(i, _)| i)
1257 .collect();
1258 let missing_patterns = self.check_exhaustiveness_tree(&tree);
1259 Ok(CompileResult {
1260 tree,
1261 unreachable_arms,
1262 missing_patterns,
1263 })
1264 }
1265 fn compile_matrix(
1267 &mut self,
1268 scrutinees: &[Expr],
1269 matrix: &[(Vec<&Pattern>, usize)],
1270 ) -> Result<DecisionTree, String> {
1271 if scrutinees.is_empty() {
1272 if let Some((_, arm_idx)) = matrix.first() {
1273 return Ok(DecisionTree::Leaf {
1274 arm_idx: *arm_idx,
1275 bindings: Vec::new(),
1276 });
1277 }
1278 return Ok(DecisionTree::Failure);
1279 }
1280 if matrix.is_empty() {
1281 return Ok(DecisionTree::Failure);
1282 }
1283 let col = self.find_best_column(matrix);
1284 let all_wildcards = matrix
1285 .iter()
1286 .all(|(pats, _)| matches!(pats[col], Pattern::Wildcard | Pattern::Var(_)));
1287 if all_wildcards {
1288 let (_, arm_idx) = &matrix[0];
1289 return Ok(DecisionTree::Leaf {
1290 arm_idx: *arm_idx,
1291 bindings: Vec::new(),
1292 });
1293 }
1294 let ctors_used: Vec<Name> = matrix
1295 .iter()
1296 .filter_map(|(pats, _)| {
1297 if let Pattern::Constructor(name, _) = &pats[col] {
1298 Some(name.clone())
1299 } else {
1300 None
1301 }
1302 })
1303 .collect::<std::collections::HashSet<_>>()
1304 .into_iter()
1305 .collect();
1306 if ctors_used.is_empty() {
1307 let has_literals = matrix
1308 .iter()
1309 .any(|(pats, _)| matches!(pats[col], Pattern::Literal(_)));
1310 if has_literals {
1311 return self.compile_literal_column(scrutinees, matrix, col);
1312 }
1313 let (_, arm_idx) = &matrix[0];
1314 return Ok(DecisionTree::Leaf {
1315 arm_idx: *arm_idx,
1316 bindings: Vec::new(),
1317 });
1318 }
1319 let mut branches = Vec::new();
1320 let mut has_default = false;
1321 for ctor_name in &ctors_used {
1322 let ctor_arity = self.get_ctor_arity(ctor_name);
1323 let field_names: Vec<Name> = (0..ctor_arity).map(|_| self.fresh_var()).collect();
1324 let sub_matrix: Vec<(Vec<&Pattern>, usize)> = matrix
1325 .iter()
1326 .filter_map(|(pats, arm_idx)| match &pats[col] {
1327 Pattern::Constructor(name, sub_pats) if name == ctor_name => {
1328 let mut new_pats = Vec::new();
1329 for (i, p) in pats.iter().enumerate() {
1330 if i == col {
1331 for sp in sub_pats {
1332 new_pats.push(sp);
1333 }
1334 } else {
1335 new_pats.push(p);
1336 }
1337 }
1338 Some((new_pats, *arm_idx))
1339 }
1340 Pattern::Wildcard | Pattern::Var(_) => {
1341 let mut new_pats = Vec::new();
1342 for (i, p) in pats.iter().enumerate() {
1343 if i == col {
1344 for _ in 0..ctor_arity {
1345 new_pats.push(&Pattern::Wildcard);
1346 }
1347 } else {
1348 new_pats.push(p);
1349 }
1350 }
1351 Some((new_pats, *arm_idx))
1352 }
1353 _ => None,
1354 })
1355 .collect();
1356 let mut sub_scrutinees = Vec::new();
1357 for (i, s) in scrutinees.iter().enumerate() {
1358 if i == col {
1359 for (j, fname) in field_names.iter().enumerate() {
1360 sub_scrutinees.push(Expr::Proj(
1361 ctor_name.clone(),
1362 j as u32,
1363 Node::new(s.clone()),
1364 ));
1365 let _ = fname;
1366 }
1367 } else {
1368 sub_scrutinees.push(s.clone());
1369 }
1370 }
1371 let sub_tree = self.compile_matrix(&sub_scrutinees, &sub_matrix)?;
1372 branches.push((ctor_name.clone(), field_names, sub_tree));
1373 }
1374 let default_matrix: Vec<(Vec<&Pattern>, usize)> = matrix
1375 .iter()
1376 .filter_map(|(pats, arm_idx)| match &pats[col] {
1377 Pattern::Wildcard | Pattern::Var(_) => {
1378 let mut new_pats: Vec<&Pattern> = pats
1379 .iter()
1380 .enumerate()
1381 .filter(|(i, _)| *i != col)
1382 .map(|(_, p)| *p)
1383 .collect();
1384 if new_pats.is_empty() && !pats.is_empty() {
1385 new_pats.push(&Pattern::Wildcard);
1386 }
1387 Some((new_pats, *arm_idx))
1388 }
1389 _ => None,
1390 })
1391 .collect();
1392 let default = if !default_matrix.is_empty() {
1393 has_default = true;
1394 let remaining: Vec<Expr> = scrutinees
1395 .iter()
1396 .enumerate()
1397 .filter(|(i, _)| *i != col)
1398 .map(|(_, s)| s.clone())
1399 .collect();
1400 Some(Box::new(self.compile_matrix(&remaining, &default_matrix)?))
1401 } else {
1402 None
1403 };
1404 let _ = has_default;
1405 Ok(DecisionTree::Switch {
1406 scrutinee: scrutinees[col].clone(),
1407 branches,
1408 default,
1409 })
1410 }
1411 fn find_best_column(&self, matrix: &[(Vec<&Pattern>, usize)]) -> usize {
1413 if matrix.is_empty() || matrix[0].0.is_empty() {
1414 return 0;
1415 }
1416 let ncols = matrix[0].0.len();
1417 let mut best_col = 0;
1418 let mut best_score = 0;
1419 for col in 0..ncols {
1420 let mut score = 0;
1421 for (pats, _) in matrix {
1422 match pats[col] {
1423 Pattern::Constructor(_, _) => score += 2,
1424 Pattern::Literal(_) => score += 1,
1425 _ => {}
1426 }
1427 }
1428 if score > best_score {
1429 best_score = score;
1430 best_col = col;
1431 }
1432 }
1433 best_col
1434 }
1435 fn get_ctor_arity(&self, ctor_name: &Name) -> u32 {
1437 for ctors in self.constructors.values() {
1438 for ctor in ctors {
1439 if &ctor.name == ctor_name {
1440 return ctor.num_fields;
1441 }
1442 }
1443 }
1444 0
1445 }
1446 fn compile_literal_column(
1452 &mut self,
1453 scrutinees: &[Expr],
1454 matrix: &[(Vec<&Pattern>, usize)],
1455 col: usize,
1456 ) -> Result<DecisionTree, String> {
1457 if matrix.is_empty() {
1458 return Ok(DecisionTree::Failure);
1459 }
1460 let mut seen_lits: Vec<crate::Literal> = Vec::new();
1461 for (pats, _) in matrix {
1462 if let Pattern::Literal(lit) = &pats[col] {
1463 if !seen_lits.contains(lit) {
1464 seen_lits.push(lit.clone());
1465 }
1466 }
1467 }
1468 let sub_scrutinees: Vec<Expr> = scrutinees
1469 .iter()
1470 .enumerate()
1471 .filter(|(i, _)| *i != col)
1472 .map(|(_, s)| s.clone())
1473 .collect();
1474 let mut branches = Vec::new();
1475 for lit in &seen_lits {
1476 let sub_matrix: Vec<(Vec<&Pattern>, usize)> = matrix
1477 .iter()
1478 .filter_map(|(pats, arm_idx)| match &pats[col] {
1479 Pattern::Literal(l) if l == lit => {
1480 let new_pats: Vec<&Pattern> = pats
1481 .iter()
1482 .enumerate()
1483 .filter(|(i, _)| *i != col)
1484 .map(|(_, p)| *p)
1485 .collect();
1486 Some((new_pats, *arm_idx))
1487 }
1488 Pattern::Wildcard | Pattern::Var(_) => {
1489 let new_pats: Vec<&Pattern> = pats
1490 .iter()
1491 .enumerate()
1492 .filter(|(i, _)| *i != col)
1493 .map(|(_, p)| *p)
1494 .collect();
1495 Some((new_pats, *arm_idx))
1496 }
1497 _ => None,
1498 })
1499 .collect();
1500 let sub_tree = self.compile_matrix(&sub_scrutinees, &sub_matrix)?;
1501 let lit_name = Name::str(format!("{:?}", lit));
1502 branches.push((lit_name, Vec::new(), sub_tree));
1503 }
1504 let default_matrix: Vec<(Vec<&Pattern>, usize)> = matrix
1505 .iter()
1506 .filter_map(|(pats, arm_idx)| match &pats[col] {
1507 Pattern::Wildcard | Pattern::Var(_) => {
1508 let new_pats: Vec<&Pattern> = pats
1509 .iter()
1510 .enumerate()
1511 .filter(|(i, _)| *i != col)
1512 .map(|(_, p)| *p)
1513 .collect();
1514 Some((new_pats, *arm_idx))
1515 }
1516 _ => None,
1517 })
1518 .collect();
1519 let default = if !default_matrix.is_empty() {
1520 Some(Box::new(
1521 self.compile_matrix(&sub_scrutinees, &default_matrix)?,
1522 ))
1523 } else {
1524 None
1525 };
1526 Ok(DecisionTree::Switch {
1527 scrutinee: scrutinees[col].clone(),
1528 branches,
1529 default,
1530 })
1531 }
1532 fn mark_reachable(&self, tree: &DecisionTree, reachable: &mut [bool]) {
1534 match tree {
1535 DecisionTree::Leaf { arm_idx, .. } => {
1536 if *arm_idx < reachable.len() {
1537 reachable[*arm_idx] = true;
1538 }
1539 }
1540 DecisionTree::Switch {
1541 branches, default, ..
1542 } => {
1543 for (_, _, sub_tree) in branches {
1544 self.mark_reachable(sub_tree, reachable);
1545 }
1546 if let Some(d) = default {
1547 self.mark_reachable(d, reachable);
1548 }
1549 }
1550 DecisionTree::Failure => {}
1551 }
1552 }
1553 fn check_exhaustiveness_tree(&self, tree: &DecisionTree) -> Vec<Vec<Pattern>> {
1555 match tree {
1556 DecisionTree::Failure => vec![vec![Pattern::Wildcard]],
1557 DecisionTree::Leaf { .. } => Vec::new(),
1558 DecisionTree::Switch {
1559 branches, default, ..
1560 } => {
1561 let mut missing = Vec::new();
1562 for (_, _, sub_tree) in branches {
1563 missing.extend(self.check_exhaustiveness_tree(sub_tree));
1564 }
1565 if let Some(d) = default {
1566 missing.extend(self.check_exhaustiveness_tree(d));
1567 }
1568 missing
1569 }
1570 }
1571 }
1572 pub fn check_exhaustive(&self, patterns: &[Pattern], ind_name: &Name) -> Result<(), String> {
1577 let mut flat: Vec<&Pattern> = Vec::new();
1578 for pat in patterns {
1579 Self::collect_top_patterns(pat, &mut flat);
1580 }
1581 if flat.iter().any(|p| Self::is_irrefutable(p)) {
1582 return Ok(());
1583 }
1584 if let Some(ctors) = self.constructors.get(ind_name) {
1585 let mut covered: std::collections::HashSet<Name> = std::collections::HashSet::new();
1586 for pat in &flat {
1587 if let Pattern::Constructor(name, _) = pat {
1588 covered.insert(name.clone());
1589 }
1590 }
1591 let missing: Vec<&Name> = ctors
1592 .iter()
1593 .filter(|c| !covered.contains(&c.name))
1594 .map(|c| &c.name)
1595 .collect();
1596 if missing.is_empty() {
1597 Ok(())
1598 } else {
1599 Err(format!(
1600 "Non-exhaustive patterns: missing constructors {:?}",
1601 missing
1602 ))
1603 }
1604 } else {
1605 Ok(())
1606 }
1607 }
1608 pub fn check_redundant(&self, patterns: &[Pattern]) -> Vec<usize> {
1614 let mut seen_wildcard = false;
1615 let mut seen_ctors: std::collections::HashSet<Name> = std::collections::HashSet::new();
1616 let mut seen_lits: std::collections::HashSet<String> = std::collections::HashSet::new();
1617 let mut redundant = Vec::new();
1618 for (i, pat) in patterns.iter().enumerate() {
1619 if seen_wildcard {
1620 redundant.push(i);
1621 continue;
1622 }
1623 if Self::is_covered_by(pat, &seen_ctors, &seen_lits) {
1624 redundant.push(i);
1625 }
1626 Self::record_pattern(pat, &mut seen_wildcard, &mut seen_ctors, &mut seen_lits);
1627 }
1628 redundant
1629 }
1630 fn collect_top_patterns<'a>(pat: &'a Pattern, out: &mut Vec<&'a Pattern>) {
1632 match pat {
1633 Pattern::Or(pats) => {
1634 for p in pats {
1635 Self::collect_top_patterns(p, out);
1636 }
1637 }
1638 Pattern::As(_, inner) => Self::collect_top_patterns(inner, out),
1639 _ => out.push(pat),
1640 }
1641 }
1642 fn is_irrefutable(pat: &Pattern) -> bool {
1644 match pat {
1645 Pattern::Wildcard | Pattern::Var(_) => true,
1646 Pattern::As(_, inner) => Self::is_irrefutable(inner),
1647 Pattern::Or(pats) => pats.iter().any(Self::is_irrefutable),
1648 _ => false,
1649 }
1650 }
1651 fn is_covered_by(
1653 pat: &Pattern,
1654 seen_ctors: &std::collections::HashSet<Name>,
1655 seen_lits: &std::collections::HashSet<String>,
1656 ) -> bool {
1657 match pat {
1658 Pattern::Constructor(name, _) => seen_ctors.contains(name),
1659 Pattern::Literal(lit) => seen_lits.contains(&format!("{:?}", lit)),
1660 Pattern::As(_, inner) => Self::is_covered_by(inner, seen_ctors, seen_lits),
1661 Pattern::Or(pats) => pats
1662 .iter()
1663 .all(|p| Self::is_covered_by(p, seen_ctors, seen_lits)),
1664 Pattern::Wildcard | Pattern::Var(_) | Pattern::Inaccessible(_) => false,
1665 }
1666 }
1667 fn record_pattern(
1669 pat: &Pattern,
1670 seen_wildcard: &mut bool,
1671 seen_ctors: &mut std::collections::HashSet<Name>,
1672 seen_lits: &mut std::collections::HashSet<String>,
1673 ) {
1674 match pat {
1675 Pattern::Wildcard | Pattern::Var(_) => *seen_wildcard = true,
1676 Pattern::As(_, inner) => {
1677 Self::record_pattern(inner, seen_wildcard, seen_ctors, seen_lits)
1678 }
1679 Pattern::Constructor(name, _) => {
1680 seen_ctors.insert(name.clone());
1681 }
1682 Pattern::Literal(lit) => {
1683 seen_lits.insert(format!("{:?}", lit));
1684 }
1685 Pattern::Or(pats) => {
1686 for p in pats {
1687 Self::record_pattern(p, seen_wildcard, seen_ctors, seen_lits);
1688 }
1689 }
1690 Pattern::Inaccessible(_) => {}
1691 }
1692 }
1693}