use super::interpreter::TaggedNfa;
use super::shared::PatternStep;
use super::steps::StepExtractor;
use crate::nfa::Nfa;
use crate::vm::{PikeVm, PikeVmContext};
use std::sync::RwLock;
pub struct TaggedNfaEngine {
steps: Option<Vec<PatternStep>>,
pike_vm: PikeVm,
pike_ctx: RwLock<PikeVmContext>,
}
impl TaggedNfaEngine {
pub fn new(nfa: Nfa) -> Self {
let steps = StepExtractor::new(&nfa).extract();
let pike_vm = PikeVm::new(nfa);
let pike_ctx = RwLock::new(pike_vm.create_context());
Self {
steps,
pike_vm,
pike_ctx,
}
}
pub fn is_match(&self, input: &[u8]) -> bool {
self.find(input).is_some()
}
pub fn find(&self, input: &[u8]) -> Option<(usize, usize)> {
if let Some(ref steps) = self.steps {
return TaggedNfa::find(steps, input);
}
self.pike_vm.find(input)
}
pub fn find_at(&self, input: &[u8], start: usize) -> Option<(usize, usize)> {
if let Some(ref steps) = self.steps {
return TaggedNfa::find_at(steps, input, start);
}
self.pike_vm.find_from(input, start)
}
pub(crate) fn match_at(&self, input: &[u8], pos: usize) -> Option<(usize, usize)> {
if pos > input.len() {
return None;
}
if !crate::nfa::is_utf8_boundary(input, pos) {
return None;
}
if let Some(ref steps) = self.steps {
return TaggedNfa::match_at(steps, input, pos).map(|end| (pos, end));
}
self.pike_vm.find_at(input, pos)
}
pub fn captures(&self, input: &[u8]) -> Option<Vec<Option<(usize, usize)>>> {
self.captures_from(input, 0)
}
pub fn captures_from(&self, input: &[u8], start: usize) -> Option<Vec<Option<(usize, usize)>>> {
let mut ctx = self.pike_ctx.write().unwrap();
self.pike_vm
.captures_unanchored_with_context(input, &mut ctx, start)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::hir::translate;
use crate::parser::parse;
fn engine(pattern: &str) -> TaggedNfaEngine {
let ast = parse(pattern).unwrap();
let hir = translate(&ast).unwrap();
let nfa = crate::nfa::compile(&hir).unwrap();
TaggedNfaEngine::new(nfa)
}
#[test]
fn match_at_is_anchored_to_the_position() {
let engine = engine(r"\d+");
let input: &[u8] = b"abc 42";
assert_eq!(engine.match_at(input, 0), None);
assert_eq!(engine.match_at(input, 4), Some((4, 6)));
assert_eq!(engine.find_at(input, 0), Some((4, 6)));
assert_eq!(engine.match_at(input, input.len() + 1), None);
}
#[test]
fn match_at_is_anchored_with_lookbehind() {
let engine = engine(r"(?<=@)\w+");
let input: &[u8] = b"user @name";
assert_eq!(engine.match_at(input, 0), None);
assert_eq!(engine.match_at(input, 6), Some((6, 10)));
assert_eq!(engine.find_at(input, 0), Some((6, 10)));
}
}