1use std::{fmt, sync::Arc};
2
3use regast_syntax::Pattern;
4
5use crate::{
6 Backend, Disambiguation, IrKind, IrPool, Lowering, Value,
7 antimirov::partial_derivative_at,
8 deriv::{deriv_at, nullable_at},
9 inj::inj_at,
10 lower::lower,
11 mkeps::mkeps_at,
12 simp::simp,
13 tagged_nfa::TaggedNfa,
14 tree::ParseTree,
15 value_parser::parse_ir_value,
16};
17
18#[derive(Clone, Debug, PartialEq, Eq)]
19pub struct NoMatch {
20 pub char_index: usize,
21 pub at_end: bool,
22}
23
24impl fmt::Display for NoMatch {
25 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
26 if self.at_end {
27 write!(formatter, "input ended before the pattern could match")
28 } else {
29 write!(
30 formatter,
31 "pattern cannot match at character {}",
32 self.char_index
33 )
34 }
35 }
36}
37
38impl std::error::Error for NoMatch {}
39
40#[derive(Clone, Debug, PartialEq, Eq)]
41pub enum MatchError {
42 NoMatch(NoMatch),
43 SizeLimitExceeded { limit: usize, states: usize },
44}
45
46impl fmt::Display for MatchError {
47 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
48 match self {
49 Self::NoMatch(error) => error.fmt(formatter),
50 Self::SizeLimitExceeded { limit, states } => write!(
51 formatter,
52 "matching state limit exceeded: {states} states (limit {limit})"
53 ),
54 }
55 }
56}
57
58impl std::error::Error for MatchError {}
59
60impl From<NoMatch> for MatchError {
61 fn from(error: NoMatch) -> Self {
62 Self::NoMatch(error)
63 }
64}
65
66#[derive(Clone, Debug, PartialEq, Eq)]
67pub struct TraceStep {
68 pub char_index: usize,
69 pub c: char,
70 pub before: String,
71 pub after: String,
72}
73
74#[derive(Clone, Debug)]
75pub struct Matcher {
76 pattern: Arc<Pattern>,
77 pool: IrPool,
78 lowering: Lowering,
79 disambiguation: Disambiguation,
80 size_limit: usize,
81 backend: Backend,
82 tagged_nfa: Option<TaggedNfa>,
83}
84
85impl Matcher {
86 #[must_use]
87 pub fn new(pattern: Pattern, disambiguation: Disambiguation, size_limit: usize) -> Self {
88 Self::new_with_backend(pattern, disambiguation, size_limit, Backend::Derivative)
89 }
90
91 #[must_use]
92 pub fn new_with_backend(
93 pattern: Pattern,
94 disambiguation: Disambiguation,
95 size_limit: usize,
96 backend: Backend,
97 ) -> Self {
98 let mut pool = IrPool::new();
99 let lowering = lower(&pattern, &mut pool, disambiguation);
100 let tagged_nfa =
101 (backend == Backend::TaggedNfa).then(|| TaggedNfa::compile(&pool, lowering.root));
102 Self {
103 pattern: Arc::new(pattern),
104 pool,
105 lowering,
106 disambiguation,
107 size_limit,
108 backend,
109 tagged_nfa,
110 }
111 }
112
113 #[must_use]
114 pub fn pattern(&self) -> &Pattern {
115 &self.pattern
116 }
117
118 #[must_use]
119 pub const fn disambiguation(&self) -> Disambiguation {
120 self.disambiguation
121 }
122
123 #[must_use]
124 pub fn lowering(&self) -> &Lowering {
125 &self.lowering
126 }
127
128 #[must_use]
129 pub fn pool(&self) -> &IrPool {
130 &self.pool
131 }
132
133 pub fn parse_value(&mut self, input: &str) -> Result<Value, MatchError> {
140 self.parse_value_at(input, 0, input.len())
141 }
142
143 fn parse_value_at(
144 &mut self,
145 input: &str,
146 base: usize,
147 full_len: usize,
148 ) -> Result<Value, MatchError> {
149 if self.backend == Backend::Derivative && self.disambiguation == Disambiguation::Posix {
150 return self.parse_value_derivative_at(input, base, full_len);
151 }
152 let matched = match self.backend {
153 Backend::Derivative => self.try_is_match_at(input, base, full_len)?,
154 Backend::Antimirov => self.try_is_match_antimirov(input, base, full_len)?,
155 Backend::TaggedNfa => self.try_is_match_tagged_nfa(input, base, full_len)?,
156 };
157 if !matched {
158 return Err(NoMatch {
159 char_index: input.chars().count(),
160 at_end: true,
161 }
162 .into());
163 }
164 self.parse_value_direct_at(input, base, full_len)
165 }
166
167 fn parse_value_derivative_at(
168 &mut self,
169 input: &str,
170 base: usize,
171 full_len: usize,
172 ) -> Result<Value, MatchError> {
173 let mut trace = Vec::with_capacity(input.chars().count());
174 let mut root = self.lowering.root;
175 for (index, (byte_offset, c)) in input.char_indices().enumerate() {
176 let position = base + byte_offset;
177 let derivative = deriv_at(&mut self.pool, root, c, position, full_len);
178 let (simple, rect) = simp(&mut self.pool, derivative);
179 trace.push((root, c, rect, position));
180 root = simple;
181 if root == self.pool.zero() {
182 return Err(NoMatch {
183 char_index: index,
184 at_end: false,
185 }
186 .into());
187 }
188 if self.pool.len() > self.size_limit {
189 return Err(MatchError::SizeLimitExceeded {
190 limit: self.size_limit,
191 states: self.pool.len(),
192 });
193 }
194 }
195 let final_position = base + input.len();
196 let (final_root, final_rect) = simp(&mut self.pool, root);
197 if !nullable_at(&mut self.pool, final_root, final_position, full_len) {
198 return Err(NoMatch {
199 char_index: input.chars().count(),
200 at_end: true,
201 }
202 .into());
203 }
204 let final_value = mkeps_at(&mut self.pool, final_root, final_position, full_len);
205 let mut value = self.pool.rect(final_rect).apply(&self.pool, final_value);
206 for (previous, c, rect, position) in trace.into_iter().rev() {
207 value = self.pool.rect(rect).apply(&self.pool, value);
208 value = inj_at(&mut self.pool, previous, c, value, position, full_len);
209 }
210 debug_assert!(value.typed_by(&self.pool, self.lowering.root));
211 debug_assert_eq!(value.flatten(), input);
212 Ok(value)
213 }
214
215 fn parse_value_direct_at(
216 &self,
217 input: &str,
218 base: usize,
219 full_len: usize,
220 ) -> Result<Value, MatchError> {
221 let value = parse_ir_value(
222 &self.pool,
223 self.lowering.root,
224 input,
225 base,
226 full_len,
227 self.disambiguation,
228 self.size_limit,
229 )
230 .map_err(|states| MatchError::SizeLimitExceeded {
231 limit: self.size_limit,
232 states,
233 })?
234 .ok_or_else(|| {
235 MatchError::from(NoMatch {
236 char_index: input.chars().count(),
237 at_end: true,
238 })
239 })?;
240 debug_assert!(
241 value.typed_by(&self.pool, self.lowering.root),
242 "direct value {value:?} is not typed by {:?}",
243 self.pool.kind(self.lowering.root)
244 );
245 debug_assert_eq!(value.flatten(), input);
246 Ok(value)
247 }
248
249 pub fn parse(&mut self, input: &str) -> Result<ParseTree, MatchError> {
256 let value = self.parse_value(input)?;
257 Ok(ParseTree::build(
258 Arc::clone(&self.pattern),
259 &self.lowering,
260 &self.pool,
261 value,
262 input,
263 self.disambiguation,
264 ))
265 }
266
267 pub fn find_parse(&mut self, input: &str) -> Result<Option<ParseTree>, MatchError> {
273 let mut boundaries: Vec<_> = input.char_indices().map(|(index, _)| index).collect();
274 boundaries.push(input.len());
275 for &start in &boundaries {
276 for &end in boundaries.iter().rev() {
277 if end < start {
278 break;
279 }
280 let matched = &input[start..end];
281 if !self.try_is_match_at(matched, start, input.len())? {
282 continue;
283 }
284 let value = match (self.backend, self.disambiguation) {
285 (Backend::Derivative, Disambiguation::Posix) => {
286 self.parse_value_derivative_at(matched, start, input.len())?
287 }
288 (Backend::Derivative, Disambiguation::Greedy)
289 | (Backend::Antimirov | Backend::TaggedNfa, _) => {
290 self.parse_value_direct_at(matched, start, input.len())?
291 }
292 };
293 return Ok(Some(ParseTree::build_at(
294 Arc::clone(&self.pattern),
295 &self.lowering,
296 &self.pool,
297 value,
298 matched,
299 input,
300 start,
301 self.disambiguation,
302 )));
303 }
304 }
305 Ok(None)
306 }
307
308 pub fn try_is_match(&mut self, input: &str) -> Result<bool, MatchError> {
314 self.try_is_match_at(input, 0, input.len())
315 }
316
317 fn try_is_match_at(
318 &mut self,
319 input: &str,
320 base: usize,
321 full_len: usize,
322 ) -> Result<bool, MatchError> {
323 match self.backend {
324 Backend::Antimirov => return self.try_is_match_antimirov(input, base, full_len),
325 Backend::TaggedNfa => return self.try_is_match_tagged_nfa(input, base, full_len),
326 Backend::Derivative => {}
327 }
328 let mut root = self.lowering.root;
329 for (byte_offset, c) in input.char_indices() {
330 let derivative = deriv_at(&mut self.pool, root, c, base + byte_offset, full_len);
331 root = simp(&mut self.pool, derivative).0;
332 if matches!(self.pool.kind(root), IrKind::Zero) {
333 return Ok(false);
334 }
335 if self.pool.len() > self.size_limit {
336 return Err(MatchError::SizeLimitExceeded {
337 limit: self.size_limit,
338 states: self.pool.len(),
339 });
340 }
341 }
342 let final_root = simp(&mut self.pool, root).0;
343 Ok(nullable_at(
344 &mut self.pool,
345 final_root,
346 base + input.len(),
347 full_len,
348 ))
349 }
350
351 fn try_is_match_antimirov(
352 &mut self,
353 input: &str,
354 base: usize,
355 full_len: usize,
356 ) -> Result<bool, MatchError> {
357 let mut states = vec![self.lowering.root];
358 for (byte_offset, c) in input.char_indices() {
359 let mut next = Vec::new();
360 for state in states {
361 next.extend(partial_derivative_at(
362 &mut self.pool,
363 state,
364 c,
365 base + byte_offset,
366 full_len,
367 ));
368 }
369 next.sort_unstable_by_key(|state| state.index());
370 next.dedup();
371 if next.is_empty() {
372 return Ok(false);
373 }
374 states = next;
375 self.check_size_limit()?;
376 }
377 let position = base + input.len();
378 Ok(states
379 .into_iter()
380 .any(|state| nullable_at(&mut self.pool, state, position, full_len)))
381 }
382
383 fn try_is_match_tagged_nfa(
384 &self,
385 input: &str,
386 base: usize,
387 full_len: usize,
388 ) -> Result<bool, MatchError> {
389 let nfa = self
390 .tagged_nfa
391 .as_ref()
392 .expect("tagged NFA is compiled for its backend");
393 let states = nfa.state_count();
394 if states > self.size_limit {
395 return Err(MatchError::SizeLimitExceeded {
396 limit: self.size_limit,
397 states,
398 });
399 }
400 Ok(nfa.is_match(&self.pool, input, base, full_len))
401 }
402
403 fn check_size_limit(&self) -> Result<(), MatchError> {
404 if self.pool.len() <= self.size_limit {
405 return Ok(());
406 }
407 Err(MatchError::SizeLimitExceeded {
408 limit: self.size_limit,
409 states: self.pool.len(),
410 })
411 }
412
413 pub fn is_match(&mut self, input: &str) -> bool {
414 self.try_is_match(input).unwrap_or(false)
415 }
416
417 pub fn trace(&mut self, input: &str) -> Vec<TraceStep> {
418 let mut result = Vec::new();
419 let mut root = self.lowering.root;
420 for (index, (byte_offset, c)) in input.char_indices().enumerate() {
421 let before = self.pool.display(root);
422 let derivative = deriv_at(&mut self.pool, root, c, byte_offset, input.len());
423 root = simp(&mut self.pool, derivative).0;
424 result.push(TraceStep {
425 char_index: index,
426 c,
427 before,
428 after: self.pool.display(root),
429 });
430 }
431 result
432 }
433}
434
435#[cfg(test)]
436#[path = "matcher_tests.rs"]
437mod tests;