use input::{Input, InputAt, CharInput};
use program::Program;
use re::CaptureIdxs;
#[derive(Debug)]
pub struct Nfa<'r, 't> {
prog: &'r Program,
input: CharInput<'t>,
}
impl<'r, 't> Nfa<'r, 't> {
pub fn exec(
prog: &'r Program,
mut caps: &mut CaptureIdxs,
text: &'t str,
start: usize,
) -> bool {
let mut q = prog.nfa_threads.get();
let input = CharInput::new(text);
let at = input.at(start);
let matched = Nfa {
prog: prog,
input: input,
}.exec_(&mut q, &mut caps, at);
matched
}
fn exec_(
&mut self,
mut q: &mut NfaThreads,
mut caps: &mut CaptureIdxs,
mut at: InputAt,
) -> bool {
let mut matched = false;
q.clist.empty(); q.nlist.empty();
'LOOP: loop {
if q.clist.size == 0 {
if matched
|| (!at.is_beginning() && self.prog.anchored_begin) {
break;
}
if !self.prog.prefixes.is_empty() {
at = match self.input.prefix_at(&self.prog.prefixes, at) {
None => break,
Some(at) => at,
};
}
}
if q.clist.size == 0 || (!self.prog.anchored_begin && !matched) {
self.add(&mut q.clist, &mut caps, 0, at)
}
let at_next = self.input.at(at.next_pos());
for i in 0..q.clist.size {
let pc = q.clist.pc(i);
let tcaps = q.clist.caps(i);
if self.step(&mut q.nlist, caps, tcaps, pc, at, at_next) {
matched = true;
if caps.len() == 0 {
break 'LOOP;
}
break;
}
}
if at.char().is_none() {
break;
}
at = at_next;
q.swap();
q.nlist.empty();
}
matched
}
fn step(
&self,
nlist: &mut Threads,
caps: &mut [Option<usize>],
thread_caps: &mut [Option<usize>],
pc: usize,
at: InputAt,
at_next: InputAt,
) -> bool {
use program::Inst::*;
match self.prog.insts[pc] {
Match => {
for (slot, val) in caps.iter_mut().zip(thread_caps.iter()) {
*slot = *val;
}
true
}
Char(c) => {
if c == at.char() {
self.add(nlist, thread_caps, pc+1, at_next);
}
false
}
Ranges(ref inst) => {
if inst.matches(at.char()) {
self.add(nlist, thread_caps, pc+1, at_next);
}
false
}
EmptyLook(_) | Save(_) | Jump(_) | Split(_, _) => false,
}
}
fn add(
&self,
nlist: &mut Threads,
thread_caps: &mut [Option<usize>],
pc: usize,
at: InputAt,
) {
use program::Inst::*;
if nlist.contains(pc) {
return
}
let ti = nlist.add(pc);
match self.prog.insts[pc] {
EmptyLook(ref inst) => {
let prev = self.input.previous_at(at.pos());
if inst.matches(prev.char(), at.char()) {
self.add(nlist, thread_caps, pc+1, at);
}
}
Save(slot) => {
if slot >= thread_caps.len() {
self.add(nlist, thread_caps, pc+1, at);
} else {
let old = thread_caps[slot];
thread_caps[slot] = Some(at.pos());
self.add(nlist, thread_caps, pc+1, at);
thread_caps[slot] = old;
}
}
Jump(to) => {
self.add(nlist, thread_caps, to, at)
}
Split(x, y) => {
self.add(nlist, thread_caps, x, at);
self.add(nlist, thread_caps, y, at);
}
Match | Char(_) | Ranges(_) => {
let mut t = &mut nlist.thread(ti);
for (slot, val) in t.caps.iter_mut().zip(thread_caps.iter()) {
*slot = *val;
}
}
}
}
}
#[derive(Debug)]
pub struct NfaThreads {
clist: Threads,
nlist: Threads,
}
#[derive(Debug)]
struct Threads {
dense: Vec<Thread>,
sparse: Vec<usize>,
size: usize,
}
#[derive(Clone, Debug)]
struct Thread {
pc: usize,
caps: Vec<Option<usize>>,
}
impl NfaThreads {
pub fn new(num_insts: usize, ncaps: usize) -> NfaThreads {
NfaThreads {
clist: Threads::new(num_insts, ncaps),
nlist: Threads::new(num_insts, ncaps),
}
}
fn swap(&mut self) {
::std::mem::swap(&mut self.clist, &mut self.nlist);
}
}
impl Threads {
fn new(num_insts: usize, ncaps: usize) -> Threads {
let t = Thread { pc: 0, caps: vec![None; ncaps * 2] };
Threads {
dense: vec![t; num_insts],
sparse: vec![0; num_insts],
size: 0,
}
}
fn add(&mut self, pc: usize) -> usize {
let i = self.size;
self.dense[i].pc = pc;
self.sparse[pc] = i;
self.size += 1;
i
}
fn thread(&mut self, i: usize) -> &mut Thread {
&mut self.dense[i]
}
fn contains(&self, pc: usize) -> bool {
let s = self.sparse[pc];
s < self.size && self.dense[s].pc == pc
}
fn empty(&mut self) {
self.size = 0;
}
fn pc(&self, i: usize) -> usize {
self.dense[i].pc
}
fn caps(&mut self, i: usize) -> &mut [Option<usize>] {
&mut self.dense[i].caps
}
}