use std::mem;
use exec::Search;
use input::{Input, InputAt};
use prog::{Program, InstPtr};
use sparse::SparseSet;
#[derive(Debug)]
pub struct Nfa<'r, I> {
prog: &'r Program,
stack: &'r mut Vec<FollowEpsilon>,
input: I,
}
#[derive(Debug)]
pub struct NfaCache {
clist: Threads,
nlist: Threads,
stack: Vec<FollowEpsilon>,
}
#[derive(Debug)]
struct Threads {
set: SparseSet,
caps: Vec<Option<usize>>,
slots_per_thread: usize,
}
#[derive(Debug)]
enum FollowEpsilon {
IP(InstPtr),
Capture { slot: usize, pos: Option<usize> },
}
impl NfaCache {
pub fn new() -> Self {
NfaCache {
clist: Threads::new(),
nlist: Threads::new(),
stack: vec![],
}
}
}
impl<'r, I: Input> Nfa<'r, I> {
pub fn exec(
prog: &'r Program,
search: &mut Search,
input: I,
start: usize,
) -> bool {
let mut _cache = prog.cache_nfa();
let mut cache = &mut **_cache;
cache.clist.resize(prog.len(), prog.captures.len());
cache.nlist.resize(prog.len(), prog.captures.len());
let at = input.at(start);
Nfa {
prog: prog,
stack: &mut cache.stack,
input: input,
}.exec_(&mut cache.clist, &mut cache.nlist, search, at)
}
fn exec_(
&mut self,
mut clist: &mut Threads,
mut nlist: &mut Threads,
mut search: &mut Search,
mut at: InputAt,
) -> bool {
let mut matched = false;
clist.set.clear();
nlist.set.clear();
'LOOP: loop {
if clist.set.is_empty() {
if matched || (!at.is_start() && self.prog.is_anchored_start) {
break;
}
if !self.prog.prefixes.is_empty() {
at = match self.input.prefix_at(&self.prog.prefixes, at) {
None => break,
Some(at) => at,
};
}
}
if clist.set.is_empty() || (!self.prog.is_anchored_start && !matched) {
self.add(&mut clist, &mut search.captures, 0, at)
}
let at_next = self.input.at(at.next_pos());
for i in 0..clist.set.len() {
let ip = clist.set[i];
let step = self.step(
&mut nlist,
search,
clist.caps(ip),
ip,
at,
at_next,
);
if step {
if !matched {
matched = search.matched_all();
}
if search.quit_after_first_match() {
break 'LOOP;
}
if !search.find_many_matches() {
break;
}
}
}
if at.is_end() {
break;
}
at = at_next;
mem::swap(clist, nlist);
nlist.set.clear();
}
matched
}
fn step(
&mut self,
nlist: &mut Threads,
search: &mut Search,
thread_caps: &mut [Option<usize>],
ip: usize,
at: InputAt,
at_next: InputAt,
) -> bool {
use prog::Inst::*;
match self.prog[ip] {
Match(match_slot) => {
search.copy_captures_from(thread_caps);
search.set_match(match_slot);
true
}
Char(ref inst) => {
if inst.c == at.char() {
self.add(nlist, thread_caps, inst.goto, at_next);
}
false
}
Ranges(ref inst) => {
if inst.matches(at.char()) {
self.add(nlist, thread_caps, inst.goto, at_next);
}
false
}
Bytes(ref inst) => {
if let Some(b) = at.byte() {
if inst.matches(b) {
self.add(nlist, thread_caps, inst.goto, at_next);
}
}
false
}
EmptyLook(_) | Save(_) | Split(_) => false,
}
}
#[inline(always)]
fn add(
&mut self,
nlist: &mut Threads,
thread_caps: &mut [Option<usize>],
ip: usize,
at: InputAt,
) {
self.stack.push(FollowEpsilon::IP(ip));
while let Some(frame) = self.stack.pop() {
match frame {
FollowEpsilon::IP(ip) => {
self.add_step(nlist, thread_caps, ip, at);
}
FollowEpsilon::Capture { slot, pos } => {
thread_caps[slot] = pos;
}
}
}
}
fn add_step(
&mut self,
nlist: &mut Threads,
thread_caps: &mut [Option<usize>],
mut ip: usize,
at: InputAt,
) {
use prog::Inst::*;
loop {
if nlist.set.contains_ip(ip) {
return;
}
nlist.set.add(ip);
match self.prog[ip] {
EmptyLook(ref inst) => {
let prev = self.input.previous_char(at);
let next = self.input.next_char(at);
if inst.matches(prev, next) {
ip = inst.goto;
}
}
Save(ref inst) => {
if inst.slot < thread_caps.len() {
self.stack.push(FollowEpsilon::Capture {
slot: inst.slot,
pos: thread_caps[inst.slot],
});
thread_caps[inst.slot] = Some(at.pos());
}
ip = inst.goto;
}
Split(ref inst) => {
self.stack.push(FollowEpsilon::IP(inst.goto2));
ip = inst.goto1;
}
Match(_) | Char(_) | Ranges(_) | Bytes(_) => {
let mut t = &mut nlist.caps(ip);
for (slot, val) in t.iter_mut().zip(thread_caps.iter()) {
*slot = *val;
}
return;
}
}
}
}
}
impl Threads {
fn new() -> Self {
Threads {
set: SparseSet::new(0),
caps: vec![],
slots_per_thread: 0,
}
}
fn resize(&mut self, num_insts: usize, ncaps: usize) {
if num_insts == self.set.capacity() {
return;
}
self.slots_per_thread = ncaps * 2;
self.set = SparseSet::new(num_insts);
self.caps = vec![None; self.slots_per_thread * num_insts];
}
fn caps(&mut self, pc: usize) -> &mut [Option<usize>] {
let i = pc * self.slots_per_thread;
&mut self.caps[i..i + self.slots_per_thread]
}
}