use std::mem;
use exec::ProgramCache;
use input::{Input, InputAt};
use prog::{Program, InstPtr};
use re_trait::Slot;
use sparse::SparseSet;
#[derive(Debug)]
pub struct Fsm<'r, I> {
prog: &'r Program,
stack: &'r mut Vec<FollowEpsilon>,
input: I,
}
#[derive(Clone, Debug)]
pub struct Cache {
clist: Threads,
nlist: Threads,
stack: Vec<FollowEpsilon>,
}
#[derive(Clone, Debug)]
struct Threads {
set: SparseSet,
caps: Vec<Slot>,
slots_per_thread: usize,
}
#[derive(Clone, Debug)]
enum FollowEpsilon {
IP(InstPtr),
Capture { slot: usize, pos: Slot },
}
impl Cache {
pub fn new(_prog: &Program) -> Self {
Cache {
clist: Threads::new(),
nlist: Threads::new(),
stack: vec![],
}
}
}
impl<'r, I: Input> Fsm<'r, I> {
pub fn exec(
prog: &'r Program,
cache: &ProgramCache,
matches: &mut [bool],
slots: &mut [Slot],
quit_after_match: bool,
input: I,
start: usize,
) -> bool {
let mut cache = cache.borrow_mut();
let mut cache = &mut cache.pikevm;
cache.clist.resize(prog.len(), prog.captures.len());
cache.nlist.resize(prog.len(), prog.captures.len());
let at = input.at(start);
Fsm {
prog: prog,
stack: &mut cache.stack,
input: input,
}.exec_(
&mut cache.clist,
&mut cache.nlist,
matches,
slots,
quit_after_match,
at,
)
}
fn exec_(
&mut self,
mut clist: &mut Threads,
mut nlist: &mut Threads,
matches: &mut [bool],
slots: &mut [Slot],
quit_after_match: bool,
mut at: InputAt,
) -> bool {
let mut matched = false;
let mut all_matched = false;
clist.set.clear();
nlist.set.clear();
'LOOP: loop {
if clist.set.is_empty() {
if (matched && matches.len() <= 1)
|| all_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 && !all_matched) {
self.add(&mut clist, slots, 0, at);
}
let at_next = self.input.at(at.next_pos());
for i in 0..clist.set.len() {
let ip = clist.set[i];
if self.step(
&mut nlist,
matches,
slots,
clist.caps(ip),
ip,
at,
at_next,
) {
matched = true;
all_matched = all_matched || matches.iter().all(|&b| b);
if quit_after_match {
break 'LOOP;
}
if self.prog.matches.len() == 1 {
break;
}
}
}
if at.is_end() {
break;
}
at = at_next;
mem::swap(clist, nlist);
nlist.set.clear();
}
matched
}
fn step(
&mut self,
nlist: &mut Threads,
matches: &mut [bool],
slots: &mut [Slot],
thread_caps: &mut [Option<usize>],
ip: usize,
at: InputAt,
at_next: InputAt,
) -> bool {
use prog::Inst::*;
match self.prog[ip] {
Match(match_slot) => {
if match_slot < matches.len() {
matches[match_slot] = true;
}
for (slot, val) in slots.iter_mut().zip(thread_caps.iter()) {
*slot = *val;
}
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,
}
}
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) {
return;
}
nlist.set.insert(ip);
match self.prog[ip] {
EmptyLook(ref inst) => {
if self.input.is_empty_match(at, inst) {
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]
}
}