Skip to main content

oxilean_parse/pattern/
patterncompiler_queries.rs

1//! # PatternCompiler - queries Methods
2//!
3//! This module contains method implementations for `PatternCompiler`.
4//!
5//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
6
7use crate::{Located, MatchArm, Pattern, Span, SurfaceExpr};
8use std::collections::HashSet;
9
10use super::types::{CaseBranch, CaseTree, MatchClause, PatternRow, TypeConstructors};
11
12use super::patterncompiler_type::PatternCompiler;
13
14impl PatternCompiler {
15    /// Create a new pattern compiler.
16    pub fn new() -> Self {
17        Self { next_var: 0 }
18    }
19    /// Compile a pattern match to a case tree.
20    ///
21    /// Builds a `SurfaceExpr::Match` over the given scrutinee with one
22    /// arm per clause.  All clauses are included in textual order; the
23    /// elaborator handles the actual case-tree construction.
24    pub fn compile_match(
25        &mut self,
26        scrutinee: &SurfaceExpr,
27        clauses: &[MatchClause],
28    ) -> Result<SurfaceExpr, String> {
29        if clauses.is_empty() {
30            return Err("Match with no clauses".to_string());
31        }
32        let dummy = Span::new(0, 0, 0, 0);
33        let arms: Vec<MatchArm> = clauses
34            .iter()
35            .map(|clause| MatchArm {
36                pattern: Located::new(clause.pattern.clone(), dummy.clone()),
37                guard: None,
38                rhs: Located::new(clause.body.clone(), dummy.clone()),
39            })
40            .collect();
41        Ok(SurfaceExpr::Match(
42            Box::new(Located::new(scrutinee.clone(), dummy)),
43            arms,
44        ))
45    }
46    /// Return the indices of redundant (unreachable) patterns.
47    ///
48    /// A pattern at index `i` is redundant when some earlier pattern in the
49    /// list is irrefutable (wildcard or variable), meaning it would always
50    /// match before the pattern at `i` is reached.
51    pub fn check_redundant(&self, patterns: &[Pattern]) -> Vec<usize> {
52        let mut redundant = Vec::new();
53        let mut has_irrefutable = false;
54        for (i, pattern) in patterns.iter().enumerate() {
55            if has_irrefutable {
56                redundant.push(i);
57            }
58            if matches!(pattern, Pattern::Wild | Pattern::Var(_)) {
59                has_irrefutable = true;
60            }
61        }
62        redundant
63    }
64    /// Compile a pattern matrix into a case tree.
65    ///
66    /// This implements the standard pattern matrix compilation algorithm.
67    /// Each row contains a vector of patterns (one per column) and a body.
68    /// The result is a decision tree that tests columns in an efficient order.
69    #[allow(dead_code)]
70    pub fn compile_matrix(&mut self, rows: &[PatternRow], num_cols: usize) -> CaseTree {
71        if rows.is_empty() {
72            return CaseTree::Failure;
73        }
74        if num_cols == 0 {
75            return CaseTree::Leaf { body_idx: 0 };
76        }
77        let first_row = &rows[0];
78        let all_wild = first_row.patterns.iter().all(|p| self.is_irrefutable(p));
79        if all_wild && first_row.guard.is_none() {
80            return CaseTree::Leaf { body_idx: 0 };
81        }
82        let col = self.select_column(rows, num_cols);
83        let ctors = self.collect_constructors(rows, col);
84        if ctors.is_empty() {
85            let defaults = self.default_rows(rows, col);
86            let new_cols = num_cols.saturating_sub(1);
87            return self.compile_matrix(&defaults, new_cols);
88        }
89        let mut branches = Vec::new();
90        for (ctor_name, arity) in &ctors {
91            let specialized = self.specialize(rows, col, ctor_name, *arity);
92            let new_cols = num_cols - 1 + arity;
93            let subtree = self.compile_matrix(&specialized, new_cols);
94            branches.push(CaseBranch {
95                ctor: ctor_name.clone(),
96                num_fields: *arity,
97                subtree,
98            });
99        }
100        let defaults = self.default_rows(rows, col);
101        let default = if defaults.is_empty() {
102            None
103        } else {
104            let new_cols = num_cols.saturating_sub(1);
105            Some(Box::new(self.compile_matrix(&defaults, new_cols)))
106        };
107        CaseTree::Switch {
108            scrutinee: col,
109            branches,
110            default,
111        }
112    }
113    /// Specialize the pattern matrix for a particular constructor.
114    ///
115    /// For each row where column `col` matches constructor `ctor`:
116    ///   - If the pattern is `Ctor(ctor, args)`, replace column `col` with `args`
117    ///   - If the pattern is a wildcard/variable, expand it to `arity` wildcards
118    ///
119    /// Rows that have a different constructor in column `col` are removed.
120    #[allow(dead_code)]
121    pub fn specialize(
122        &self,
123        rows: &[PatternRow],
124        col: usize,
125        ctor: &str,
126        arity: usize,
127    ) -> Vec<PatternRow> {
128        let mut result = Vec::new();
129        for row in rows {
130            if col >= row.patterns.len() {
131                continue;
132            }
133            match &row.patterns[col] {
134                Pattern::Ctor(name, args) => {
135                    if name == ctor {
136                        let mut new_patterns = Vec::new();
137                        for (i, p) in row.patterns.iter().enumerate() {
138                            if i == col {
139                                for arg in args {
140                                    new_patterns.push(arg.value.clone());
141                                }
142                            } else {
143                                new_patterns.push(p.clone());
144                            }
145                        }
146                        result.push(PatternRow {
147                            patterns: new_patterns,
148                            body: row.body.clone(),
149                            guard: row.guard.clone(),
150                        });
151                    }
152                }
153                Pattern::Wild | Pattern::Var(_) => {
154                    let mut new_patterns = Vec::new();
155                    for (i, p) in row.patterns.iter().enumerate() {
156                        if i == col {
157                            for _ in 0..arity {
158                                new_patterns.push(Pattern::Wild);
159                            }
160                        } else {
161                            new_patterns.push(p.clone());
162                        }
163                    }
164                    result.push(PatternRow {
165                        patterns: new_patterns,
166                        body: row.body.clone(),
167                        guard: row.guard.clone(),
168                    });
169                }
170                Pattern::Lit(_) => {}
171                Pattern::Or(left, right) => {
172                    let mut left_row = row.clone();
173                    left_row.patterns[col] = left.value.clone();
174                    let mut right_row = row.clone();
175                    right_row.patterns[col] = right.value.clone();
176                    let left_spec = self.specialize(&[left_row], col, ctor, arity);
177                    let right_spec = self.specialize(&[right_row], col, ctor, arity);
178                    result.extend(left_spec);
179                    result.extend(right_spec);
180                }
181            }
182        }
183        result
184    }
185    /// Compute the default matrix.
186    ///
187    /// Rows where column `col` is a wildcard or variable are kept (with the
188    /// column removed). Rows with a specific constructor are dropped.
189    #[allow(dead_code)]
190    pub fn default_rows(&self, rows: &[PatternRow], col: usize) -> Vec<PatternRow> {
191        let mut result = Vec::new();
192        for row in rows {
193            if col >= row.patterns.len() {
194                continue;
195            }
196            match &row.patterns[col] {
197                Pattern::Wild | Pattern::Var(_) => {
198                    let new_patterns: Vec<Pattern> = row
199                        .patterns
200                        .iter()
201                        .enumerate()
202                        .filter(|(i, _)| *i != col)
203                        .map(|(_, p)| p.clone())
204                        .collect();
205                    result.push(PatternRow {
206                        patterns: new_patterns,
207                        body: row.body.clone(),
208                        guard: row.guard.clone(),
209                    });
210                }
211                Pattern::Or(left, right) => {
212                    if self.is_irrefutable(&left.value) || self.is_irrefutable(&right.value) {
213                        let new_patterns: Vec<Pattern> = row
214                            .patterns
215                            .iter()
216                            .enumerate()
217                            .filter(|(i, _)| *i != col)
218                            .map(|(_, p)| p.clone())
219                            .collect();
220                        result.push(PatternRow {
221                            patterns: new_patterns,
222                            body: row.body.clone(),
223                            guard: row.guard.clone(),
224                        });
225                    }
226                }
227                _ => {}
228            }
229        }
230        result
231    }
232    /// Collect constructors appearing in a particular column.
233    ///
234    /// Returns a deduplicated list of `(constructor_name, arity)` pairs.
235    #[allow(dead_code)]
236    pub fn collect_constructors(&self, rows: &[PatternRow], col: usize) -> Vec<(String, usize)> {
237        let mut seen = Vec::new();
238        for row in rows {
239            if col >= row.patterns.len() {
240                continue;
241            }
242            self.collect_ctors_from_pattern(&row.patterns[col], &mut seen);
243        }
244        seen
245    }
246    /// Check exhaustiveness given known type constructors.
247    ///
248    /// Returns `Ok(())` if the patterns are exhaustive, or `Err(missing)` with
249    /// a list of constructor names that are not covered.
250    #[allow(dead_code)]
251    pub fn check_exhaustive_with_ctors(
252        &self,
253        patterns: &[Pattern],
254        ctors: &TypeConstructors,
255    ) -> Result<(), Vec<String>> {
256        for pat in patterns {
257            if self.is_irrefutable(pat) {
258                return Ok(());
259            }
260        }
261        let mut covered = Vec::new();
262        for pat in patterns {
263            self.collect_pattern_ctors(pat, &mut covered);
264        }
265        let mut missing = Vec::new();
266        for ctor_info in &ctors.constructors {
267            if !covered.contains(&ctor_info.name) {
268                missing.push(ctor_info.name.clone());
269            }
270        }
271        if missing.is_empty() {
272            Ok(())
273        } else {
274            Err(missing)
275        }
276    }
277    /// Check exhaustiveness for multi-column pattern matching.
278    ///
279    /// Each inner `Vec<Pattern>` represents a row of patterns (one per scrutinee).
280    /// `ctors` provides constructor info for each column's type.
281    ///
282    /// Returns `Ok(())` if exhaustive, or `Err` with missing pattern combinations.
283    #[allow(dead_code)]
284    pub fn check_nested_exhaustive(
285        &self,
286        patterns: &[Vec<Pattern>],
287        ctors: &[TypeConstructors],
288    ) -> Result<(), Vec<Vec<String>>> {
289        if patterns.is_empty() {
290            if ctors.is_empty() {
291                return Ok(());
292            }
293            let missing: Vec<Vec<String>> = ctors[0]
294                .constructors
295                .iter()
296                .map(|c| vec![c.name.clone()])
297                .collect();
298            return if missing.is_empty() {
299                Ok(())
300            } else {
301                Err(missing)
302            };
303        }
304        if ctors.is_empty() {
305            return Ok(());
306        }
307        let mut all_missing: Vec<Vec<String>> = Vec::new();
308        for (col_idx, type_ctors) in ctors.iter().enumerate() {
309            let col_patterns: Vec<Pattern> = patterns
310                .iter()
311                .filter_map(|row| row.get(col_idx).cloned())
312                .collect();
313            if let Err(missing) = self.check_exhaustive_with_ctors(&col_patterns, type_ctors) {
314                for m in &missing {
315                    let mut combo = vec!["_".to_string(); ctors.len()];
316                    combo[col_idx] = m.clone();
317                    all_missing.push(combo);
318                }
319            }
320        }
321        if all_missing.is_empty() {
322            Ok(())
323        } else {
324            Err(all_missing)
325        }
326    }
327    /// Simplify a pattern by flattening nested or-patterns.
328    ///
329    /// Transforms `(a | b) | c` into a flat structure and removes
330    /// redundant wildcards in or-patterns.
331    #[allow(dead_code)]
332    pub fn simplify_pattern(&self, pattern: &Pattern) -> Pattern {
333        match pattern {
334            Pattern::Or(left, right) => {
335                let sl = self.simplify_pattern(&left.value);
336                let sr = self.simplify_pattern(&right.value);
337                if self.is_irrefutable(&sl) || self.is_irrefutable(&sr) {
338                    return Pattern::Wild;
339                }
340                Pattern::Or(
341                    Box::new(crate::Located::new(sl, left.span.clone())),
342                    Box::new(crate::Located::new(sr, right.span.clone())),
343                )
344            }
345            Pattern::Ctor(name, args) => {
346                let simplified_args: Vec<crate::Located<Pattern>> = args
347                    .iter()
348                    .map(|a| crate::Located::new(self.simplify_pattern(&a.value), a.span.clone()))
349                    .collect();
350                Pattern::Ctor(name.clone(), simplified_args)
351            }
352            Pattern::Wild => Pattern::Wild,
353            Pattern::Var(v) => Pattern::Var(v.clone()),
354            Pattern::Lit(l) => Pattern::Lit(l.clone()),
355        }
356    }
357    /// Extract literal values from a pattern for range analysis.
358    pub fn extract_literal_range(&self, patterns: &[Pattern]) -> Option<(i64, i64)> {
359        let mut values = Vec::new();
360        for pat in patterns {
361            self.collect_literals(pat, &mut values);
362        }
363        if values.is_empty() {
364            return None;
365        }
366        values.sort();
367        Some((values[0], values[values.len() - 1]))
368    }
369    /// Check if patterns cover all values in a given range.
370    pub fn check_range_coverage(&self, patterns: &[Pattern], min: i64, max: i64) -> bool {
371        let mut covered = HashSet::new();
372        for pat in patterns {
373            self.collect_literal_set(pat, &mut covered);
374        }
375        for pat in patterns {
376            if self.is_irrefutable(pat) {
377                return true;
378            }
379        }
380        for i in min..=max {
381            if !covered.contains(&(i as u64)) {
382                return false;
383            }
384        }
385        true
386    }
387    /// Analyze pattern matrix for dead code.
388    pub fn find_dead_patterns(&self, rows: &[PatternRow]) -> Vec<usize> {
389        let mut dead = Vec::new();
390        for (i, _row) in rows.iter().enumerate() {
391            if i > 0 && self.all_irrefutable(&rows[..i]) {
392                dead.push(i);
393            }
394        }
395        dead
396    }
397    /// Extract all bound variable names from a pattern.
398    #[allow(dead_code)]
399    pub fn extract_bound_names(&self, pattern: &Pattern) -> Vec<String> {
400        let mut names = Vec::new();
401        self.collect_bound_names(pattern, &mut names);
402        names
403    }
404}