1use crate::ast::Pattern;
19use crate::engine::Span;
20
21#[derive(Clone, Debug, Default, PartialEq, Eq)]
32pub struct CaptureSlots {
33 names: Vec<String>,
34 spans: Vec<Option<Span>>,
35 extents: Vec<Option<(usize, usize)>>,
36 matched: Option<Span>,
37 matched_extent: Option<(usize, usize)>,
38}
39
40impl CaptureSlots {
41 #[must_use]
43 pub fn of(pattern: &Pattern) -> Self {
44 let names = crate::cursor::capture_names(pattern);
45 let n = names.len();
46 Self {
47 names,
48 spans: vec![None; n],
49 extents: vec![None; n],
50 matched: None,
51 matched_extent: None,
52 }
53 }
54
55 #[must_use]
57 pub fn len(&self) -> usize {
58 self.names.len()
59 }
60
61 #[must_use]
63 pub fn is_empty(&self) -> bool {
64 self.names.is_empty()
65 }
66
67 #[must_use]
69 pub fn names(&self) -> &[String] {
70 &self.names
71 }
72
73 #[must_use]
75 pub fn name(&self, i: usize) -> Option<&str> {
76 self.names.get(i).map(String::as_str)
77 }
78
79 #[must_use]
81 pub fn index_of(&self, name: &str) -> Option<usize> {
82 self.names.iter().position(|n| n == name)
83 }
84
85 #[must_use]
87 pub fn get(&self, i: usize) -> Option<Span> {
88 *self.spans.get(i)?
89 }
90
91 #[must_use]
93 pub fn by_name(&self, name: &str) -> Option<Span> {
94 self.get(self.index_of(name)?)
95 }
96
97 #[must_use]
104 pub fn extent(&self, i: usize) -> Option<(usize, usize)> {
105 *self.extents.get(i)?
106 }
107
108 #[must_use]
110 pub fn tokens(&self, i: usize) -> Option<usize> {
111 self.extent(i).map(|(a, b)| b - a)
112 }
113
114 #[must_use]
116 pub fn matched(&self) -> Option<Span> {
117 self.matched
118 }
119
120 #[must_use]
122 pub fn matched_extent(&self) -> Option<(usize, usize)> {
123 self.matched_extent
124 }
125
126 #[must_use]
128 pub fn matched_tokens(&self) -> Option<usize> {
129 self.matched_extent.map(|(a, b)| b - a)
130 }
131
132 pub fn clear(&mut self) {
134 self.spans.fill(None);
135 self.extents.fill(None);
136 self.matched = None;
137 self.matched_extent = None;
138 }
139
140 fn take_match(&mut self, m: &crate::engine::Match, extent: Option<(usize, usize)>) {
142 self.clear();
143 self.matched = Some(m.span());
144 self.matched_extent = extent;
145 for (name, span) in m.names().iter().zip(m.captures()) {
146 if let Some(i) = self.index_of(name) {
147 self.spans[i] = Some(*span);
148 }
149 }
150 }
151
152 fn take_flat(&mut self, m: &crate::nfa::FlatMatch, names: &[String]) {
159 self.clear();
160 self.matched = Some(m.span);
161 for (i, name) in names.iter().enumerate() {
162 if let Some(k) = self.index_of(name) {
163 self.spans[k] = Some(m.regs[i]);
164 }
165 }
166 }
167
168 fn fill_from_walk(
174 &mut self,
175 w: &mut crate::nfa::SerialWalk<crate::nfa::OwnedStream>,
176 input: &[u8],
177 ) -> Option<Span> {
178 let Self { names, spans, extents, matched, matched_extent } = self;
179 let (span, ks, ke) =
180 w.next_into(input, names.as_slice(), spans.as_mut_slice(), extents.as_mut_slice())?;
181 *matched = Some(span);
182 *matched_extent = Some((ks, ke));
183 Some(span)
184 }
185}
186
187pub fn captures_read(pattern: &Pattern, input: &[u8], slots: &mut CaptureSlots) -> Option<Span> {
196 captures_read_at(pattern, input, 0, slots)
197}
198
199pub fn captures_read_at(
215 pattern: &Pattern,
216 input: &[u8],
217 at: usize,
218 slots: &mut CaptureSlots,
219) -> Option<Span> {
220 slots.clear();
221 if !pattern.binds() {
222 let span = crate::cursor::find_at(pattern, input, at)?;
223 slots.matched = Some(span);
224 return Some(span);
225 }
226 if let Some(first) = crate::engine::routed_first_at(pattern, input, at) {
227 let first = first?;
228 let m = crate::engine::captures(pattern, input, &[first]).into_iter().next()?;
229 slots.take_match(&m, None);
230 return Some(m.span());
231 }
232 if let Some(mut w) = crate::nfa::SerialWalk::over(pattern, input) {
233 w.seek(at);
234 return slots.fill_from_walk(&mut w, input);
235 }
236 let toks = crate::parallel_lex::lex_parallel(input);
237 let start = toks.partition_point(|t| t.start() < at);
238 let span = crate::engine::scan_tokens_from(pattern, input, &toks, start).into_iter().next()?;
239 let m = crate::engine::captures_over(pattern, input, &toks, &[span]).into_iter().next()?;
240 slots.take_match(&m, None);
241 Some(m.span())
242}
243
244pub struct SlotCursor<'h> {
250 input: &'h [u8],
251 source: SlotSource,
252}
253
254enum SlotSource {
256 Spans { spans: Vec<Span>, at: usize },
261 Resolved { ms: Vec<crate::engine::Match>, at: usize },
264 Flat { ms: Vec<crate::nfa::FlatMatch>, names: std::sync::Arc<[String]>, at: usize },
269 Walk(Box<crate::nfa::SerialWalk<crate::nfa::OwnedStream>>),
276}
277
278impl SlotCursor<'_> {
279 pub fn next_into(&mut self, slots: &mut CaptureSlots) -> Option<Span> {
287 match &mut self.source {
288 SlotSource::Spans { spans, at } => {
289 let s = *spans.get(*at)?;
290 *at += 1;
291 slots.clear();
292 slots.matched = Some(s);
293 Some(s)
294 }
295 SlotSource::Resolved { ms, at } => {
296 let m = ms.get(*at)?;
297 *at += 1;
298 slots.take_match(m, None);
299 Some(m.span())
300 }
301 SlotSource::Flat { ms, names, at } => {
302 let m = ms.get(*at)?;
303 *at += 1;
304 slots.take_flat(m, names);
305 Some(m.span)
306 }
307 SlotSource::Walk(w) => {
308 slots.clear();
309 slots.fill_from_walk(w, self.input)
310 }
311 }
312 }
313}
314
315#[must_use]
327pub fn captures_read_iter<'h>(pattern: &Pattern, input: &'h [u8]) -> Option<SlotCursor<'h>> {
328 if !pattern.binds()
334 && let Some(spans) = crate::engine::routed_spans(pattern, input)
335 {
336 return Some(SlotCursor { input, source: SlotSource::Spans { spans, at: 0 } });
337 }
338 if let Some((ms, names)) = crate::prefilter::scan_flat_by_literal_windows(pattern, input) {
346 return Some(SlotCursor { input, source: SlotSource::Flat { ms, names, at: 0 } });
347 }
348 if let Some(ms) = crate::prefilter::scan_captures_by_literal_windows(pattern, input) {
349 return Some(SlotCursor { input, source: SlotSource::Resolved { ms, at: 0 } });
350 }
351 if let Some(spans) = crate::engine::routed_spans(pattern, input) {
352 let source = if !pattern.binds() {
353 SlotSource::Spans { spans, at: 0 }
354 } else if let Some((ms, names)) =
355 crate::prefilter::flat_captures_by_byte_bounds(pattern, input, &spans)
356 {
357 SlotSource::Flat { ms, names, at: 0 }
358 } else if let Some((ms, names)) =
359 crate::prefilter::flat_captures_by_windows(pattern, input, &spans)
360 {
361 SlotSource::Flat { ms, names, at: 0 }
362 } else {
363 SlotSource::Resolved { ms: crate::engine::captures(pattern, input, &spans), at: 0 }
364 };
365 return Some(SlotCursor { input, source });
366 }
367 let walk = crate::nfa::SerialWalk::over(pattern, input)?;
368 Some(SlotCursor { input, source: SlotSource::Walk(Box::new(walk)) })
369}
370
371#[must_use]
380pub fn static_captures_len(pattern: &Pattern) -> Option<usize> {
381 let all = crate::cursor::capture_names(pattern);
382 let mut certain = Vec::new();
383 certain_names(pattern, &mut certain);
384 (certain.len() == all.len()).then_some(all.len())
385}
386
387fn certain_names(pattern: &Pattern, out: &mut Vec<String>) {
389 match pattern {
390 Pattern::Bind(name, _, inner) => {
391 if !out.iter().any(|n| n == name) {
392 out.push(name.clone());
393 }
394 certain_names(inner, out);
395 }
396 Pattern::Plus(p, _)
397 | Pattern::Atomic(p)
398 | Pattern::Balanced(_, p)
399 | Pattern::Field(_, p) => certain_names(p, out),
400 Pattern::Repeat(p, lo, _, _) if *lo >= 1 => certain_names(p, out),
402 Pattern::Concat(v) => {
403 for p in v {
404 certain_names(p, out);
405 }
406 }
407 Pattern::Alt(v, _) => {
409 let Some((first, rest)) = v.split_first() else { return };
410 let mut common = Vec::new();
411 certain_names(first, &mut common);
412 for p in rest {
413 let mut theirs = Vec::new();
414 certain_names(p, &mut theirs);
415 common.retain(|n| theirs.contains(n));
416 }
417 for n in common {
418 if !out.contains(&n) {
419 out.push(n);
420 }
421 }
422 }
423 Pattern::Within(..)
429 | Pattern::Star(..)
430 | Pattern::Opt(..)
431 | Pattern::Repeat(..)
432 | Pattern::Assert(..)
433 | Pattern::Empty
434 | Pattern::Atom(_)
435 | Pattern::Guard(..)
436 | Pattern::Anchor(_) => {}
437 }
438}
439
440#[must_use]
453pub fn static_token_extent(pattern: &Pattern) -> Option<usize> {
454 match pattern {
455 Pattern::Empty | Pattern::Guard(..) | Pattern::Anchor(_) | Pattern::Assert(..) => Some(0),
456 Pattern::Atom(_) => Some(1),
457 Pattern::Within(v, 0) => Some(v.len()),
460 Pattern::Within(..) => None,
461 Pattern::Bind(_, _, p) | Pattern::Atomic(p) | Pattern::Field(_, p) => {
462 static_token_extent(p)
463 }
464 Pattern::Balanced(_, p) => static_token_extent(p)?.checked_add(2),
467 Pattern::Concat(v) => {
468 let mut total = 0usize;
469 for p in v {
470 total = total.checked_add(static_token_extent(p)?)?;
471 }
472 Some(total)
473 }
474 Pattern::Alt(v, _) => {
475 let (first, rest) = v.split_first()?;
476 let width = static_token_extent(first)?;
477 for p in rest {
478 if static_token_extent(p)? != width {
479 return None;
480 }
481 }
482 Some(width)
483 }
484 Pattern::Repeat(p, lo, Some(hi), _) if lo == hi => {
485 static_token_extent(p)?.checked_mul(*lo)
486 }
487 Pattern::Star(..) | Pattern::Plus(..) | Pattern::Opt(..) | Pattern::Repeat(..) => None,
488 }
489}
490
491#[cfg(test)]
492mod tests {
493 use super::*;
494 use crate::parser::parse;
495
496 #[test]
497 fn a_refilled_buffer_answers_what_a_match_would_have() {
498 let hay = b"a 1 b 2 c 3";
503 let p = parse("\\W:k \\N:v").expect("parses");
504 let want: Vec<_> = crate::captures_iter(&p, hay).collect();
505 assert_eq!(want.len(), 3, "three pairs: {want:?}");
506
507 let mut slots = CaptureSlots::of(&p);
508 let mut at = 0usize;
509 let mut seen = 0usize;
510 while let Some(span) = captures_read_at(&p, hay, at, &mut slots) {
511 assert_eq!(span, want[seen].span(), "match {seen}");
512 assert_eq!(
513 slots.by_name("k"),
514 want[seen].group_span("k"),
515 "register k of match {seen}"
516 );
517 assert_eq!(
518 slots.by_name("v"),
519 want[seen].group_span("v"),
520 "register v of match {seen}"
521 );
522 assert_eq!(slots.matched(), Some(span));
523 at = span.end();
524 seen += 1;
525 }
526 assert_eq!(seen, want.len(), "the loop took every match");
527 }
528
529 #[test]
534 fn the_inline_registers_are_the_ones_the_eager_resolve_reports() {
535 let mut text = String::new();
536 for i in 0..80u32 {
537 text.push_str(&format!("let value_{i} = {} ; call_{i}(alpha, beta) ;\n", i * 37));
538 }
539 let hay = text.as_bytes();
540 for src in ["\"let\" \\W:v \"=\"", "\\W:name \"=\"", "\"let\" \\W:z \"=\" \\N:a"] {
543 let p = parse(src).expect("parses");
544 let spans = crate::scan(&p, hay);
545 let want = crate::captures(&p, hay, &spans);
546 assert!(!want.is_empty(), "{src} matches the corpus");
547
548 let got: Vec<_> = crate::captures_iter(&p, hay).collect();
549 assert_eq!(got, want, "captures_iter {src}");
550
551 let mut slots = CaptureSlots::of(&p);
552 let mut c = captures_read_iter(&p, hay).expect("a route or the walk takes this");
553 let mut seen = 0usize;
554 while let Some(span) = c.next_into(&mut slots) {
555 assert_eq!(span, want[seen].span(), "{src} match {seen}");
556 for name in slots.names().to_vec() {
557 assert_eq!(
558 slots.by_name(&name),
559 want[seen].group_span(&name),
560 "{src} register {name} of match {seen}"
561 );
562 }
563 seen += 1;
564 }
565 assert_eq!(seen, want.len(), "{src} took every match");
566 }
567 }
568
569 #[test]
570 fn a_cursor_refilling_one_buffer_takes_the_same_matches() {
571 let hay = b"a 1 b 2 c 3 d 4";
576 let p = parse("\\W:k \\N:v").expect("parses");
577 let want: Vec<_> = crate::captures_iter(&p, hay).collect();
578 assert_eq!(want.len(), 4, "four pairs: {want:?}");
579
580 let mut slots = CaptureSlots::of(&p);
581 let mut c = captures_read_iter(&p, hay).expect("the walk takes this pattern");
582 let mut seen = 0usize;
583 while let Some(span) = c.next_into(&mut slots) {
584 assert_eq!(span, want[seen].span(), "match {seen}");
585 assert_eq!(slots.by_name("k"), want[seen].group_span("k"), "k of match {seen}");
586 assert_eq!(slots.by_name("v"), want[seen].group_span("v"), "v of match {seen}");
587 seen += 1;
588 }
589 assert_eq!(seen, want.len(), "the cursor took every match");
590
591 let routed = parse("\"a\"").expect("parses");
595 let mut rs = CaptureSlots::of(&routed);
596 let mut rc = captures_read_iter(&routed, hay).expect("a route answers this");
597 let mut got = Vec::new();
598 while let Some(s) = rc.next_into(&mut rs) {
599 got.push(s);
600 }
601 assert_eq!(got, crate::scan(&routed, hay), "the routed cursor selects what the scan does");
602 assert!(rs.is_empty(), "the pattern binds nothing, so there are no slots");
603
604 let balanced = parse("\\B(\\N:v)").expect("parses");
607 assert!(captures_read_iter(&balanced, b"(1) (2)").is_none());
608 assert_eq!(crate::captures_iter(&balanced, b"(1) (2)").count(), 2);
609 }
610
611 #[test]
612 fn binding_a_register_does_not_key_the_thread_list_but_reading_one_does() {
613 let bind_only = parse("\\W:k \"=\"").expect("parses");
620 assert!(bind_only.binds(), "it writes a register");
621 assert!(!bind_only.reads_registers(), "and never reads one back");
622
623 let reads = parse("\\W:x \"=\" =x").expect("parses");
624 assert!(reads.reads_registers(), "a back-reference reads one");
625
626 let hay = b"alpha = beta ; gamma = gamma ;";
630 let got: Vec<_> = crate::captures_iter(&bind_only, hay)
631 .map(|m| m.group_span("k").map(|s| (s.start(), s.end())))
632 .collect();
633 assert_eq!(got, vec![Some((0, 5)), Some((15, 20))], "both assignments, both keys");
634
635 let back: Vec<_> = crate::captures_iter(&reads, hay)
636 .map(|m| (m.span().start(), m.span().end()))
637 .collect();
638 assert_eq!(back.len(), 1, "only gamma = gamma repeats its token: {back:?}");
639 assert_eq!(&hay[back[0].0..back[0].1], &b"gamma = gamma"[..]);
640 }
641
642 #[test]
643 fn a_slot_carries_the_tokens_as_well_as_the_bytes() {
644 let hay = b"alpha 42 beta 7";
648 let p = parse("\\W:k \\N:v").expect("parses");
649 let mut slots = CaptureSlots::of(&p);
650 assert!(captures_read(&p, hay, &mut slots).is_some(), "alpha 42 matches");
651
652 let k = slots.index_of("k").expect("k is a register");
653 let v = slots.index_of("v").expect("v is a register");
654 assert_eq!(slots.tokens(k), Some(1), "a word is one token");
655 assert_eq!(slots.tokens(v), Some(1), "a number is one token");
656 assert_eq!(slots.matched_tokens(), Some(2), "the match spans both");
657
658 let (ks, ke) = slots.extent(k).expect("k bound");
661 let (vs, ve) = slots.extent(v).expect("v bound");
662 assert_eq!(ke, vs, "adjacent tokens: k ends at {ke}, v starts at {vs}");
663 assert_eq!(slots.matched_extent(), Some((ks, ve)));
664 }
665
666 #[test]
667 fn clearing_keeps_the_names_and_forgets_the_positions() {
668 let p = parse("\\W:k \\N:v").expect("parses");
669 let mut slots = CaptureSlots::of(&p);
670 assert!(captures_read(&p, b"alpha 42", &mut slots).is_some());
671 slots.clear();
672 assert_eq!(slots.len(), 2, "the shape is the pattern's, not the match's");
673 assert_eq!(slots.get(0), None);
674 assert_eq!(slots.matched(), None);
675 assert_eq!(slots.names().to_vec(), vec!["k".to_string(), "v".to_string()]);
676 }
677
678 #[test]
679 fn a_register_that_need_not_bind_has_no_static_count() {
680 assert_eq!(static_captures_len(&parse("\\W:k \\N:v").expect("parses")), Some(2));
681 assert_eq!(
682 static_captures_len(&parse("\\W:k (\\N:v)?").expect("parses")),
683 None,
684 "v is bound by some matches and not others"
685 );
686 assert_eq!(
687 static_captures_len(&parse("\\W:k | \\N:k").expect("parses")),
688 Some(1),
689 "every branch binds k"
690 );
691 assert_eq!(
692 static_captures_len(&parse("\\W:k | \\N:v").expect("parses")),
693 None,
694 "neither name is bound by both branches"
695 );
696 let asserted = parse("\\W ~(\\N:v)").expect("parses");
700 assert_eq!(crate::capture_names(&asserted), Vec::<String>::new(), "v cannot bind");
701 assert_eq!(static_captures_len(&asserted), Some(0), "every match binds none");
702 }
703
704 #[test]
705 fn a_fixed_token_width_is_a_property_no_byte_matcher_has() {
706 assert_eq!(static_token_extent(&parse("\\W \\N").expect("parses")), Some(2));
707 assert_eq!(static_token_extent(&parse("\\W{3}").expect("parses")), Some(3));
708 assert_eq!(
709 static_token_extent(&parse("\\B(\\N)").expect("parses")),
710 Some(3),
711 "the open and the close are tokens of the match"
712 );
713 assert_eq!(
714 static_token_extent(&parse("\\W ~(\\N)").expect("parses")),
715 Some(1),
716 "an assertion consumes nothing"
717 );
718 assert_eq!(static_token_extent(&parse("\\W | \\N").expect("parses")), Some(1));
719 assert_eq!(
720 static_token_extent(&parse("\\W | \\N \\N").expect("parses")),
721 None,
722 "the branches are one token and two"
723 );
724 assert_eq!(static_token_extent(&parse("\\W*").expect("parses")), None);
725 assert_eq!(static_token_extent(&parse("\\W{2,4}").expect("parses")), None);
726 }
727
728 #[test]
729 fn anchoring_selects_where_a_match_may_begin_and_never_what_the_input_is() {
730 let hay = b"a 1 b 2 c 3";
731 let p = parse("\\W:k \\N:v").expect("parses");
732 let all: Vec<_> = crate::captures_iter(&p, hay).collect();
733 assert_eq!(all.len(), 3);
734
735 let after_first = all[0].span().end();
736 let got = crate::captures_at(&p, hay, after_first).expect("a match follows the first");
737 assert_eq!(got.span(), all[1].span(), "the next match, not the first again");
738 assert_eq!(got.group_span("k"), all[1].group_span("k"));
739
740 assert_eq!(crate::shortest_match_at(&p, hay, 0), crate::shortest_match(&p, hay));
741 assert_eq!(
742 crate::shortest_match_at(&p, hay, after_first),
743 Some(all[1].span().end()),
744 "the soonest end at or after the position"
745 );
746 assert_eq!(crate::captures_at(&p, hay, hay.len()), None, "nothing begins past the end");
747 }
748
749 #[test]
750 fn a_set_reports_where_each_member_matched() {
751 let hay = b"alpha 42";
757 let set = crate::PatternSet::new(vec![
758 parse("\\N").expect("parses"),
759 parse("\"zzzqqq\"").expect("parses"),
760 parse("\\W").expect("parses"),
761 ]);
762
763 let hits = set.matches_with_spans(hay);
764 assert_eq!(hits.iter().map(|&(i, _)| i).collect::<Vec<_>>(), vec![0, 2]);
765 assert_eq!(&hay[hits[0].1.start()..hits[0].1.end()], &b"42"[..]);
766 assert_eq!(&hay[hits[1].1.start()..hits[1].1.end()], &b"alpha"[..]);
767
768 let m = set.matched(hay);
769 assert!(m.matched(0) && !m.matched(1) && m.matched(2));
770 assert!(m.matched_any(), "two of three");
771 assert!(!m.matched_all(), "zzzqqq is nowhere in the input");
772 assert_eq!(m.iter().collect::<Vec<_>>(), vec![0, 2]);
773 assert_eq!(m.len(), 3, "every index carries a verdict, matched or not");
774
775 let at = set.matches_at(hay, 5);
777 assert!(at.matched(0), "42 begins at or after byte 5");
778 assert!(!at.matched(2), "alpha ends before it");
779 assert!(set.is_match_at(hay, 5));
780 assert!(!set.is_match_at(hay, hay.len()), "nothing begins past the end");
781 }
782}