use std::fmt;
use classes::ByteClasses;
pub use nfa::compiler::Builder;
mod compiler;
mod map;
mod range_trie;
pub type StateID = usize;
#[derive(Clone)]
pub struct NFA {
anchored: bool,
start: StateID,
states: Vec<State>,
byte_classes: ByteClasses,
}
impl NFA {
pub fn always_match() -> NFA {
NFA {
anchored: false,
start: 0,
states: vec![State::Match],
byte_classes: ByteClasses::empty(),
}
}
pub fn never_match() -> NFA {
NFA {
anchored: false,
start: 0,
states: vec![State::Fail],
byte_classes: ByteClasses::empty(),
}
}
pub fn is_anchored(&self) -> bool {
self.anchored
}
pub fn len(&self) -> usize {
self.states.len()
}
pub fn start(&self) -> StateID {
self.start
}
pub fn state(&self, id: StateID) -> &State {
&self.states[id]
}
pub fn byte_classes(&self) -> &ByteClasses {
&self.byte_classes
}
}
impl fmt::Debug for NFA {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for (i, state) in self.states.iter().enumerate() {
let status = if i == self.start { '>' } else { ' ' };
writeln!(f, "{}{:06}: {:?}", status, i, state)?;
}
Ok(())
}
}
#[derive(Clone, Eq, PartialEq)]
pub enum State {
Range { range: Transition },
Sparse { ranges: Box<[Transition]> },
Union { alternates: Box<[StateID]> },
Fail,
Match,
}
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
pub struct Transition {
pub start: u8,
pub end: u8,
pub next: StateID,
}
impl State {
pub fn is_epsilon(&self) -> bool {
match *self {
State::Range { .. }
| State::Sparse { .. }
| State::Fail
| State::Match => false,
State::Union { .. } => true,
}
}
fn remap(&mut self, remap: &[StateID]) {
match *self {
State::Range { ref mut range } => range.next = remap[range.next],
State::Sparse { ref mut ranges } => {
for r in ranges.iter_mut() {
r.next = remap[r.next];
}
}
State::Union { ref mut alternates } => {
for alt in alternates.iter_mut() {
*alt = remap[*alt];
}
}
State::Fail => {}
State::Match => {}
}
}
}
impl fmt::Debug for State {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
State::Range { ref range } => range.fmt(f),
State::Sparse { ref ranges } => {
let rs = ranges
.iter()
.map(|t| format!("{:?}", t))
.collect::<Vec<String>>()
.join(", ");
write!(f, "sparse({})", rs)
}
State::Union { ref alternates } => {
let alts = alternates
.iter()
.map(|id| format!("{}", id))
.collect::<Vec<String>>()
.join(", ");
write!(f, "alt({})", alts)
}
State::Fail => write!(f, "FAIL"),
State::Match => write!(f, "MATCH"),
}
}
}
impl fmt::Debug for Transition {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let Transition { start, end, next } = *self;
if self.start == self.end {
write!(f, "{} => {}", escape(start), next)
} else {
write!(f, "{}-{} => {}", escape(start), escape(end), next)
}
}
}
fn escape(b: u8) -> String {
use std::ascii;
String::from_utf8(ascii::escape_default(b).collect::<Vec<_>>()).unwrap()
}
#[cfg(test)]
mod tests {
use super::*;
use dense;
use dfa::DFA;
#[test]
fn always_match() {
let nfa = NFA::always_match();
let dfa = dense::Builder::new().build_from_nfa::<usize>(&nfa).unwrap();
assert_eq!(Some(0), dfa.find_at(b"", 0));
assert_eq!(Some(0), dfa.find_at(b"a", 0));
assert_eq!(Some(1), dfa.find_at(b"a", 1));
assert_eq!(Some(0), dfa.find_at(b"ab", 0));
assert_eq!(Some(1), dfa.find_at(b"ab", 1));
assert_eq!(Some(2), dfa.find_at(b"ab", 2));
}
#[test]
fn never_match() {
let nfa = NFA::never_match();
let dfa = dense::Builder::new().build_from_nfa::<usize>(&nfa).unwrap();
assert_eq!(None, dfa.find_at(b"", 0));
assert_eq!(None, dfa.find_at(b"a", 0));
assert_eq!(None, dfa.find_at(b"a", 1));
assert_eq!(None, dfa.find_at(b"ab", 0));
assert_eq!(None, dfa.find_at(b"ab", 1));
assert_eq!(None, dfa.find_at(b"ab", 2));
}
}