1use crate::SymbolDomain;
4use core::fmt;
5use core::marker::PhantomData;
6use std::collections::{BTreeMap, BTreeSet};
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
10pub struct CaptureId(pub u32);
11
12#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
14pub struct AssertionId(pub u32);
15
16#[derive(Clone, Copy, Debug, PartialEq, Eq)]
18pub enum Anchor {
19 SubjectStart,
21 SubjectEnd,
23}
24
25#[derive(Clone, Copy, Debug, PartialEq, Eq)]
27pub struct RepeatBounds {
28 min: usize,
29 max: Option<usize>,
30}
31
32impl RepeatBounds {
33 pub fn new(min: usize, max: Option<usize>) -> Result<Self, IrError> {
45 if let Some(max) = max
46 && min > max
47 {
48 return Err(IrError::InvalidRepeatBounds { min, max });
49 }
50 Ok(Self { min, max })
51 }
52
53 pub const fn min(self) -> usize {
55 self.min
56 }
57
58 pub const fn max(self) -> Option<usize> {
60 self.max
61 }
62}
63
64#[derive(Clone, Debug, PartialEq, Eq)]
66pub enum IrNode<S, E> {
67 Symbol(S),
69 Any,
71 Concat(Vec<Self>),
73 Alternation(Vec<Self>),
75 Repeat {
77 node: Box<Self>,
79 bounds: RepeatBounds,
81 greedy: bool,
83 },
84 Group(Box<Self>),
86 Capture {
88 id: CaptureId,
90 node: Box<Self>,
92 },
93 Anchor(Anchor),
95 Assertion(AssertionId),
97 Extension(E),
99}
100
101#[derive(Clone, Debug, PartialEq, Eq)]
103pub struct EnginePolicy<E> {
104 admitted_extensions: BTreeSet<E>,
105}
106
107impl<E: Ord> EnginePolicy<E> {
108 pub fn new(admitted_extensions: impl IntoIterator<Item = E>) -> Self {
110 Self {
111 admitted_extensions: admitted_extensions.into_iter().collect(),
112 }
113 }
114}
115
116#[derive(Clone, Debug, PartialEq, Eq)]
118pub struct PatternIr<D: SymbolDomain, E> {
119 root: IrNode<D::Symbol, E>,
120 assertions: BTreeMap<AssertionId, IrNode<D::Symbol, E>>,
121 domain: PhantomData<fn() -> D>,
122}
123
124impl<D, E> PatternIr<D, E>
125where
126 D: SymbolDomain,
127 E: Clone + fmt::Debug + Ord,
128{
129 pub fn new(
131 root: IrNode<D::Symbol, E>,
132 assertions: BTreeMap<AssertionId, IrNode<D::Symbol, E>>,
133 policy: &EnginePolicy<E>,
134 ) -> Result<Self, IrError> {
135 let mut captures = BTreeSet::new();
136 validate_node(&root, &assertions, policy, &mut captures)?;
137 for definition in assertions.values() {
138 validate_node(definition, &assertions, policy, &mut captures)?;
139 }
140 validate_assertion_cycles(&root, &assertions, &mut Vec::new())?;
141 for (id, definition) in &assertions {
142 validate_assertion_cycles(definition, &assertions, &mut vec![*id])?;
143 }
144 Ok(Self {
145 root,
146 assertions,
147 domain: PhantomData,
148 })
149 }
150
151 pub fn root(&self) -> &IrNode<D::Symbol, E> {
153 &self.root
154 }
155
156 pub fn assertions(&self) -> &BTreeMap<AssertionId, IrNode<D::Symbol, E>> {
158 &self.assertions
159 }
160}
161
162#[derive(Clone, Debug, PartialEq, Eq)]
164pub enum IrError {
165 InvalidRepeatBounds {
167 min: usize,
169 max: usize,
171 },
172 DuplicateCapture(CaptureId),
174 MissingAssertion(AssertionId),
176 AssertionCycle(Vec<AssertionId>),
178 UnsupportedExtension(String),
180}
181
182impl fmt::Display for IrError {
183 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184 match self {
185 Self::InvalidRepeatBounds { min, max } => {
186 write!(
187 f,
188 "invalid repeat bounds: minimum {min} exceeds maximum {max}"
189 )
190 }
191 Self::DuplicateCapture(id) => write!(f, "duplicate capture id {}", id.0),
192 Self::MissingAssertion(id) => write!(f, "missing assertion id {}", id.0),
193 Self::AssertionCycle(path) => write!(f, "assertion cycle: {path:?}"),
194 Self::UnsupportedExtension(extension) => {
195 write!(f, "target engine does not admit extension {extension}")
196 }
197 }
198 }
199}
200
201impl std::error::Error for IrError {}
202
203fn validate_node<S, E>(
204 node: &IrNode<S, E>,
205 assertions: &BTreeMap<AssertionId, IrNode<S, E>>,
206 policy: &EnginePolicy<E>,
207 captures: &mut BTreeSet<CaptureId>,
208) -> Result<(), IrError>
209where
210 E: fmt::Debug + Ord,
211{
212 match node {
213 IrNode::Concat(nodes) | IrNode::Alternation(nodes) => {
214 for node in nodes {
215 validate_node(node, assertions, policy, captures)?;
216 }
217 }
218 IrNode::Repeat { node, .. } | IrNode::Group(node) => {
219 validate_node(node, assertions, policy, captures)?;
220 }
221 IrNode::Capture { id, node } => {
222 if !captures.insert(*id) {
223 return Err(IrError::DuplicateCapture(*id));
224 }
225 validate_node(node, assertions, policy, captures)?;
226 }
227 IrNode::Assertion(id) => {
228 assertions.get(id).ok_or(IrError::MissingAssertion(*id))?;
229 }
230 IrNode::Extension(extension) if !policy.admitted_extensions.contains(extension) => {
231 return Err(IrError::UnsupportedExtension(format!("{extension:?}")));
232 }
233 IrNode::Symbol(_) | IrNode::Any | IrNode::Anchor(_) | IrNode::Extension(_) => {}
234 }
235 Ok(())
236}
237
238fn validate_assertion_cycles<S, E>(
239 node: &IrNode<S, E>,
240 assertions: &BTreeMap<AssertionId, IrNode<S, E>>,
241 path: &mut Vec<AssertionId>,
242) -> Result<(), IrError> {
243 match node {
244 IrNode::Concat(nodes) | IrNode::Alternation(nodes) => {
245 for node in nodes {
246 validate_assertion_cycles(node, assertions, path)?;
247 }
248 }
249 IrNode::Repeat { node, .. } | IrNode::Group(node) | IrNode::Capture { node, .. } => {
250 validate_assertion_cycles(node, assertions, path)?;
251 }
252 IrNode::Assertion(id) => {
253 if let Some(cycle_start) = path.iter().position(|seen| seen == id) {
254 let mut cycle = path[cycle_start..].to_vec();
255 cycle.push(*id);
256 return Err(IrError::AssertionCycle(cycle));
257 }
258 let definition = assertions.get(id).ok_or(IrError::MissingAssertion(*id))?;
259 path.push(*id);
260 let result = validate_assertion_cycles(definition, assertions, path);
261 path.pop();
262 result?;
263 }
264 IrNode::Symbol(_) | IrNode::Any | IrNode::Anchor(_) | IrNode::Extension(_) => {}
265 }
266 Ok(())
267}
268
269#[cfg(test)]
270mod tests {
271 use super::*;
272 use crate::ByteDomain;
273
274 #[test]
275 fn invalid_repeat_names_both_bounds() {
276 let error = RepeatBounds::new(4, Some(3)).unwrap_err();
277 assert_eq!(
278 error.to_string(),
279 "invalid repeat bounds: minimum 4 exceeds maximum 3"
280 );
281 }
282
283 #[test]
284 fn rejects_duplicate_capture_ids() {
285 let capture = |symbol| IrNode::Capture {
286 id: CaptureId(7),
287 node: Box::new(IrNode::Symbol(symbol)),
288 };
289 let root = IrNode::Concat(vec![capture(b'a'), capture(b'b')]);
290 let error =
291 PatternIr::<ByteDomain, &str>::new(root, BTreeMap::new(), &EnginePolicy::new([]))
292 .unwrap_err();
293 assert_eq!(error, IrError::DuplicateCapture(CaptureId(7)));
294 }
295
296 #[test]
297 fn rejects_assertion_cycles() {
298 let assertions = BTreeMap::from([
299 (AssertionId(1), IrNode::Assertion(AssertionId(2))),
300 (AssertionId(2), IrNode::Assertion(AssertionId(1))),
301 ]);
302 let error = PatternIr::<ByteDomain, &str>::new(
303 IrNode::Assertion(AssertionId(1)),
304 assertions,
305 &EnginePolicy::new([]),
306 )
307 .unwrap_err();
308 assert_eq!(
309 error,
310 IrError::AssertionCycle(vec![AssertionId(1), AssertionId(2), AssertionId(1)])
311 );
312 }
313
314 #[test]
315 fn target_controls_dialect_extensions() {
316 let denied = PatternIr::<ByteDomain, &str>::new(
317 IrNode::Extension("backreference"),
318 BTreeMap::new(),
319 &EnginePolicy::new([]),
320 );
321 assert!(matches!(denied, Err(IrError::UnsupportedExtension(_))));
322
323 let admitted = PatternIr::<ByteDomain, &str>::new(
324 IrNode::Extension("backreference"),
325 BTreeMap::new(),
326 &EnginePolicy::new(["backreference"]),
327 );
328 assert!(admitted.is_ok());
329 }
330}