dcbor_pattern/pattern/meta/
repeat_pattern.rs

1use dcbor::prelude::*;
2
3use crate::{
4    Quantifier,
5    pattern::{Matcher, Path, Pattern, vm::Instr},
6};
7
8/// A pattern that matches with repetition using a quantifier.
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct RepeatPattern {
11    pattern: Box<Pattern>,
12    quantifier: Quantifier,
13}
14
15impl RepeatPattern {
16    /// Creates a new `RepeatPattern` with the specified sub-pattern and
17    /// quantifier.
18    pub fn repeat(pattern: Pattern, quantifier: Quantifier) -> Self {
19        RepeatPattern { pattern: Box::new(pattern), quantifier }
20    }
21
22    /// Creates a new `RepeatPattern` with a quantifier that matches exactly
23    /// once.
24    pub fn new(pattern: Pattern) -> Self {
25        RepeatPattern {
26            pattern: Box::new(pattern),
27            quantifier: Quantifier::default(),
28        }
29    }
30
31    /// Returns the sub-pattern of this repeat pattern.
32    pub fn pattern(&self) -> &Pattern { &self.pattern }
33
34    /// Returns the quantifier of this repeat pattern.
35    pub fn quantifier(&self) -> &Quantifier { &self.quantifier }
36}
37
38impl Matcher for RepeatPattern {
39    fn paths(&self, haystack: &CBOR) -> Vec<Path> {
40        // For repeat patterns, we need to handle the quantifier logic
41        // This is a simplified implementation that doesn't use the full VM
42        // capability
43
44        let inner_paths = self.pattern.paths(haystack);
45        let matches = !inner_paths.is_empty();
46
47        if matches {
48            // Inner pattern matches
49            if self.quantifier.contains(1) {
50                inner_paths
51            } else {
52                vec![]
53            }
54        } else {
55            // Inner pattern doesn't match
56            if self.quantifier.contains(0) {
57                // Zero matches are allowed, so succeed
58                vec![vec![haystack.clone()]]
59            } else {
60                // Zero matches not allowed, so fail
61                vec![]
62            }
63        }
64    }
65
66    fn paths_with_captures(
67        &self,
68        haystack: &CBOR,
69    ) -> (Vec<Path>, std::collections::HashMap<String, Vec<Path>>) {
70        // Check if the inner pattern has any captures
71        let mut capture_names = Vec::new();
72        self.pattern.collect_capture_names(&mut capture_names);
73
74        if capture_names.is_empty() {
75            // No captures in the inner pattern, use basic implementation
76            return (self.paths(haystack), std::collections::HashMap::new());
77        }
78
79        // For patterns with captures, we need to handle different quantifier
80        // cases
81        match haystack.as_case() {
82            CBORCase::Array(arr) => {
83                // For array inputs, the repeat pattern should match against
84                // elements
85                let mut all_captures = std::collections::HashMap::new();
86                let mut valid_match = false;
87
88                // Check if this pattern can match the array length
89                if self.quantifier.contains(arr.len()) {
90                    valid_match = true;
91
92                    // If minimum is 0 and array is empty, we have an empty
93                    // capture
94                    if arr.is_empty() && self.quantifier.contains(0) {
95                        // For empty arrays, captures should be empty but
96                        // present
97                        for name in &capture_names {
98                            all_captures.insert(name.clone(), vec![]);
99                        }
100                    } else {
101                        // For non-empty arrays, collect captures from each
102                        // element
103                        for element in arr {
104                            let (_element_paths, element_captures) =
105                                self.pattern.paths_with_captures(element);
106
107                            for (capture_name, captured_paths) in
108                                element_captures
109                            {
110                                all_captures
111                                    .entry(capture_name)
112                                    .or_insert_with(Vec::new)
113                                    .extend(captured_paths);
114                            }
115                        }
116                    }
117                }
118
119                if valid_match {
120                    (vec![vec![haystack.clone()]], all_captures)
121                } else {
122                    (vec![], std::collections::HashMap::new())
123                }
124            }
125            _ => {
126                // For non-array inputs, use the basic repeat logic
127                let inner_paths = self.pattern.paths(haystack);
128                let matches = !inner_paths.is_empty();
129
130                if matches && self.quantifier.contains(1) {
131                    // Inner pattern matches and quantifier allows 1 match
132                    let (_paths, captures) =
133                        self.pattern.paths_with_captures(haystack);
134                    (vec![vec![haystack.clone()]], captures)
135                } else if !matches && self.quantifier.contains(0) {
136                    // Inner pattern doesn't match but quantifier allows 0
137                    // matches
138                    let mut empty_captures = std::collections::HashMap::new();
139                    for name in &capture_names {
140                        empty_captures.insert(name.clone(), vec![]);
141                    }
142                    (vec![vec![haystack.clone()]], empty_captures)
143                } else {
144                    // No valid match
145                    (vec![], std::collections::HashMap::new())
146                }
147            }
148        }
149    }
150
151    fn compile(
152        &self,
153        code: &mut Vec<Instr>,
154        literals: &mut Vec<Pattern>,
155        _captures: &mut Vec<String>,
156    ) {
157        // Emit a high-level `Repeat` instruction for the VM
158        let idx = literals.len();
159        literals.push((*self.pattern).clone());
160        code.push(Instr::Repeat { pat_idx: idx, quantifier: self.quantifier });
161    }
162
163    fn collect_capture_names(&self, names: &mut Vec<String>) {
164        // Collect captures from the repeated pattern
165        self.pattern.collect_capture_names(names);
166    }
167}
168
169impl std::fmt::Display for RepeatPattern {
170    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
171        let formatted_range = self.quantifier.to_string();
172        write!(f, "({}){}", self.pattern, formatted_range)
173    }
174}