use look::Look;
use num::traits::PrimInt;
use range_map::{Range, RangeMultiMap};
use std::fmt::{self, Debug, Formatter};
use std::marker::PhantomData;
mod has_looks;
mod no_looks;
pub type StateIdx = usize;
pub type StateSet = Vec<StateIdx>;
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
struct LookPair {
pub behind: Look,
pub ahead: Look,
pub target_state: StateIdx,
}
impl LookPair {
fn is_empty(&self) -> bool {
self.behind == Look::Empty || self.ahead == Look::Empty
}
fn intersection(&self, other: &LookPair) -> LookPair {
LookPair {
behind: self.behind.intersection(&other.behind),
ahead: self.ahead.intersection(&other.ahead),
target_state: self.target_state,
}
}
}
#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum Accept {
Never,
AtEoi,
Always,
}
#[derive(Clone, Eq, PartialEq)]
struct State<Tok> {
accept: Accept,
accept_state: StateIdx,
accept_look: Look,
accept_tokens: u8,
consuming: RangeMultiMap<Tok, StateIdx>,
looking: Vec<LookPair>,
}
#[derive(Clone, Eq, PartialEq)]
pub struct Nfa<Tok, Variant> {
states: Vec<State<Tok>>,
init: Vec<(Look, StateIdx)>,
phantom: PhantomData<Variant>,
}
pub trait Lookability {}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct HasLooks;
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct NoLooks;
impl Lookability for HasLooks {}
impl Lookability for NoLooks {}
impl<Tok: Debug + PrimInt, L: Lookability> Nfa<Tok, L> {
pub fn new() -> Nfa<Tok, L> {
Nfa::with_capacity(0)
}
pub fn with_capacity(n: usize) -> Nfa<Tok, L> {
Nfa {
states: Vec::with_capacity(n),
init: Vec::new(),
phantom: PhantomData,
}
}
pub fn reversed_transitions(&self) -> Vec<RangeMultiMap<Tok, StateIdx>> {
let mut ret = vec![RangeMultiMap::new(); self.states.len()];
for (source_idx, st) in self.states.iter().enumerate() {
for &(range, target_idx) in st.consuming.ranges_values() {
ret[target_idx].insert(range, source_idx);
}
}
ret
}
pub fn add_state(&mut self, accept: Accept) -> StateIdx {
let state_idx = self.states.len();
self.states.push(State {
accept: accept,
accept_state: state_idx,
accept_look: if accept == Accept::AtEoi { Look::Boundary } else { Look::Full },
accept_tokens: 0,
consuming: RangeMultiMap::new(),
looking: Vec::new(),
});
state_idx
}
pub fn add_look_ahead_state(&mut self, look: Look, tokens: u8, accept_state: StateIdx)
-> StateIdx {
debug_assert!(look != Look::Boundary && look != Look::Full && look != Look::Empty);
debug_assert!(tokens > 0);
let state_idx = self.states.len();
self.states.push(State {
accept: Accept::Always,
accept_state: accept_state,
accept_look: look,
accept_tokens: tokens,
consuming: RangeMultiMap::new(),
looking: Vec::new(),
});
state_idx
}
pub fn add_transition(&mut self, source: StateIdx, target: StateIdx, range: Range<Tok>) {
self.states[source].consuming.insert(range, target);
}
pub fn consuming(&self, i: StateIdx) -> &RangeMultiMap<Tok, StateIdx> {
&self.states[i].consuming
}
pub fn num_states(&self) -> usize {
self.states.len()
}
fn map_states<F>(&mut self, map: F) where F: Fn(StateIdx) -> Option<StateIdx> {
for st in &mut self.states {
st.consuming.retain_values(|x| map(*x).is_some());
st.consuming.map_values(|x| map(*x).unwrap());
st.looking = st.looking.iter()
.filter(|look| map(look.target_state).is_some())
.map(|look| LookPair { target_state: map(look.target_state).unwrap(), .. *look })
.collect();
st.accept_state = map(st.accept_state).expect("bug in map_states");
}
self.init = self.init.iter()
.filter_map(|pair| map(pair.1).map(|idx| (pair.0, idx)))
.collect();
}
fn transmuted<NewL: Lookability>(self) -> Nfa<Tok, NewL> {
Nfa {
states: self.states,
init: self.init,
phantom: PhantomData,
}
}
pub fn is_anchored(&self) -> bool {
self.init.iter().all(|pair| pair.0 == Look::Boundary)
}
pub fn is_empty(&self) -> bool {
self.states.is_empty()
}
}
impl<Tok: Debug + PrimInt, L: Lookability> Debug for Nfa<Tok, L> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
try!(f.write_fmt(format_args!("Nfa ({} states):\n", self.states.len())));
try!(f.write_fmt(format_args!("Init: {:?}\n", self.init)));
for (st_idx, st) in self.states.iter().enumerate().take(40) {
try!(f.write_fmt(format_args!("\tState {} ({:?}):\n", st_idx, st.accept)));
if st.accept != Accept::Never {
try!(f.write_fmt(format_args!("\t\tlook {:?}, tokens {:?}, state {:?}\n",
st.accept_look, st.accept_tokens, st.accept_state)));
}
if !st.consuming.is_empty() {
try!(f.write_str("\t\tConsuming:\n"));
for &(range, target) in st.consuming.ranges_values().take(10) {
try!(f.write_fmt(format_args!("\t\t\t{:?} -- {:?} => {}\n",
range.start, range.end, target)));
}
if st.consuming.num_ranges() > 10 {
try!(f.write_str("\t\t\t...\n"));
}
}
if !st.looking.is_empty() {
try!(f.write_str("\t\tLooking:\n"));
for look in &st.looking {
try!(f.write_fmt(format_args!("\t\t\t({:?},{:?}) => {}\n",
look.behind, look.ahead, look.target_state)));
}
}
}
if self.states.len() > 40 {
try!(f.write_fmt(format_args!("\t... ({} more states)\n", self.states.len() - 40)));
}
Ok(())
}
}
#[cfg(test)]
pub mod tests {
use nfa::{Accept, NoLooks, Nfa, StateIdx};
use num::traits::PrimInt;
use range_map::Range;
use std::fmt::Debug;
pub fn re_nfa(re: &str) -> Nfa<u32, NoLooks> {
let nfa = Nfa::from_regex(re).unwrap();
println!("before remove looks: {:?}", nfa);
let nfa = nfa.remove_looks();
println!("after remove looks: {:?}", nfa);
nfa
}
pub fn trans_range_nfa<Tok>(size: usize, transitions: &[(StateIdx, StateIdx, Range<Tok>)])
-> Nfa<Tok, NoLooks>
where Tok: Debug + PrimInt {
let mut ret: Nfa<Tok, NoLooks> = Nfa::with_capacity(size);
for _ in 0..size {
ret.add_state(Accept::Never);
}
for &(src, tgt, range) in transitions {
ret.add_transition(src, tgt, range);
}
ret
}
pub fn trans_nfa<Tok>(size: usize, transitions: &[(StateIdx, StateIdx, char)])
-> Nfa<Tok, NoLooks>
where Tok: Debug + PrimInt {
let tok = |x: char| -> Tok { Tok::from(x as u32).unwrap() };
let range_trans: Vec<_> = transitions.iter()
.map(|x| (x.0, x.1, Range::new(tok(x.2), tok(x.2))))
.collect();
trans_range_nfa(size, &range_trans)
}
}