use core::mem;
use unconst::unconst;
use crate::compile::{Index, Program, Inst};
use crate::context::Context;
use crate::exec::ProgramCache;
use crate::sparse::SparseSet;
use crate::traits::Integral;
#[derive(Debug)]
pub struct Fsm<'r, I: Integral> {
prog: &'r Program<I>,
stack: &'r mut Vec<Index>,
context: Context<I>,
}
type Thread = SparseSet<usize, usize>;
#[derive(Clone, Debug)]
pub struct Cache {
clist: Thread,
nlist: Thread,
stack: Vec<Index>,
}
#[unconst]
impl Cache {
pub const fn new<I: Integral>(_prog: &Program<I>) -> Self {
Cache {
clist: Thread::new(0),
nlist: Thread::new(0),
stack: Vec::new()
}
}
}
#[unconst]
impl<'r, I: ~const Integral> Fsm<'r, I> {
pub fn exec(
prog: &'r Program<I>,
cache: &ProgramCache<I>,
matches: &mut [bool],
quit_after_match: bool,
context: &Context<I>,
start: usize,
end: usize,
) -> bool {
let mut cache = cache.borrow_mut();
let cache = &mut cache.pikevm;
cache.clist.resize(prog.len());
cache.nlist.resize(prog.len());
let at = context[start];
Fsm { prog, stack: &mut cache.stack, context }.exec_(
&mut cache.clist,
&mut cache.nlist,
matches,
quit_after_match,
at,
end,
)
}
fn exec_(
&mut self,
mut clist: &mut Thread,
mut nlist: &mut Thread,
matches: &mut [bool],
quit_after_match: bool,
mut at: I,
end: usize,
) -> bool {
let mut matched = false;
let mut all_matched = false;
clist.clear();
nlist.clear();
'LOOP: loop {
if clist.is_empty() {
if (matched && matches.len() <= 1) || all_matched {
break;
}
if !self.prog.prefixes.is_empty() {
at = match self.context.prefix_at(&self.prog.prefixes, at) {
None => break,
Some(at) => at,
};
}
}
if clist.is_empty()
|| (!self.prog.is_anchored_start && !all_matched)
{
self.add(&mut clist, 0, at);
}
let at_next = self.context[at + 1];
for i in 0..clist.len() {
let ip = clist[i];
if self.step(&mut nlist, matches, 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.pos() >= end {
break;
}
at = at_next;
mem::swap(clist, nlist);
nlist.clear();
}
matched
}
fn step(
&mut self,
nlist: &mut Thread,
matches: &mut [bool],
ip: usize,
at: I,
at_next: I,
) -> bool {
match self.prog[ip] {
Inst::True(match_slot) => {
if match_slot < matches.len() {
matches[match_slot] = true;
}
true
}
Inst::One { goto, seq } => {
if seq == at.char() {
self.add(nlist, goto, at_next);
}
false
}
Inst::Interval { goto, interval } => {
if interval.has(at.char()) {
self.add(nlist, goto, at_next);
}
false
}
_ => false,
}
}
fn add(&mut self, nlist: &mut Thread, ip: Index, at: I) {
self.stack.push(ip);
while let Some(ip) = self.stack.pop() {
self.add_step(nlist, ip, at);
}
}
fn add_step(&mut self, nlist: &mut Thread, mut ip: usize, at: I) {
loop {
if nlist.contains(&ip) {
return;
}
nlist.insert(ip);
match self.prog[ip] {
Inst::Zero { goto, zero } => {
if self.context.is_empty_match(at, zero) {
ip = goto;
}
}
Inst::Or { goto1, goto2 } => {
self.stack.push(goto2);
ip = goto1;
}
_ => {
return;
}
}
}
}
}