use dfa::Dfa;
use error::Error;
use itertools::Itertools;
use look::Look;
use nfa::{Accept, Nfa, NoLooks, State, StateIdx, StateSet};
use num::traits::PrimInt;
use range_map::{Range, RangeMap, RangeMultiMap};
use std::{char, u8, usize};
use std::cmp::max;
use std::collections::{HashMap, HashSet};
use std::fmt::Debug;
use std::marker::PhantomData;
use std::mem::swap;
use utf8_ranges::{Utf8Range, Utf8Sequence, Utf8Sequences};
struct MergedUtf8Sequences {
pub head: Vec<Utf8Range>,
pub last_byte: Vec<Utf8Range>,
}
fn to_char_pair(r: Range<u32>) -> Option<(char, char)> {
let start = if r.start > 0xD7FF && r.start < 0xE000 {
0xE000
} else {
r.start
};
let end = if r.end > 0x10FFFF {
0x10FFFF
} else if r.end < 0xE000 && r.end > 0xD7FF {
0xD7FF
} else {
r.end
};
if start > end {
None
} else {
Some((char::from_u32(start).unwrap(), char::from_u32(end).unwrap()))
}
}
impl MergedUtf8Sequences {
fn merge<I>(iter: I) -> MergedUtf8Sequences where I: Iterator<Item=Utf8Sequence> {
let mut head = Vec::new();
let mut last_byte = Vec::new();
for seq in iter {
let len = seq.len();
let h = &seq.as_slice()[..len-1];
if head.is_empty() {
head.extend_from_slice(h);
} else if &head[..] != h {
panic!("invalid sequences to merge");
}
last_byte.push(seq.as_slice()[len-1]);
}
MergedUtf8Sequences {
head: head,
last_byte: last_byte,
}
}
fn from_sequences<'a, I>(iter: I) -> Box<Iterator<Item=MergedUtf8Sequences> + 'a>
where I: Iterator<Item=Utf8Sequence> + 'a {
fn head(u: &Utf8Sequence) -> Vec<Utf8Range> {
let len = u.len();
u.as_slice()[..len-1].to_owned()
}
Box::new(iter
.group_by(head)
.into_iter()
.map(|(_, seqs)| MergedUtf8Sequences::merge(seqs.into_iter())))
}
fn from_ranges<'a, I>(iter: I) -> Box<Iterator<Item=MergedUtf8Sequences> + 'a>
where I: Iterator<Item=Range<u32>> + 'a {
MergedUtf8Sequences::from_sequences(
iter.filter_map(to_char_pair)
.flat_map(|r| Utf8Sequences::new(r.0, r.1)))
}
fn num_bytes(&self) -> u8 {
(self.head.len() + 1) as u8
}
}
fn make_char_dfa(look: Look) -> Dfa<(Look, u8)> {
let mut nfa: Nfa<u32, NoLooks> = Nfa::with_capacity(2);
nfa.add_state(Accept::Never);
nfa.add_look_ahead_state(look, 1, 0);
nfa.init.push((Look::Full, 0));
nfa.init.push((Look::Boundary, 0));
nfa.states[0].consuming
= RangeMultiMap::from_vec(look.as_set().ranges().map(|x| (x, 1)).collect());
nfa.byte_me(usize::MAX).unwrap()
.determinize(usize::MAX).unwrap()
.optimize()
}
fn make_rev_char_dfa(look: Look) -> Dfa<(Look, u8)> {
let mut nfa: Nfa<u8, NoLooks> = Nfa::with_capacity(0); nfa.add_state(Accept::Never);
nfa.init.push((Look::Full, 0));
nfa.init.push((Look::Boundary, 0));
for seq in MergedUtf8Sequences::from_ranges(look.as_set().ranges()) {
let mut last_state = nfa.add_state(Accept::Never);
for range in &seq.last_byte {
nfa.add_transition(0, last_state, Range::new(range.start, range.end));
}
for range in seq.head.iter().rev() {
let cur_state = nfa.add_state(Accept::Never);
nfa.add_transition(last_state, cur_state, Range::new(range.start, range.end));
last_state = cur_state;
}
nfa.states[last_state].accept = Accept::Always;
nfa.states[last_state].accept_look = look;
nfa.states[last_state].accept_state = 0;
nfa.states[last_state].accept_tokens = seq.num_bytes();
}
nfa.determinize(usize::MAX).unwrap()
.optimize()
}
lazy_static! {
static ref WORD_CHAR_DFA: Dfa<(Look, u8)> = make_char_dfa(Look::WordChar);
static ref NOT_WORD_CHAR_DFA: Dfa<(Look, u8)> = make_char_dfa(Look::NotWordChar);
static ref REV_WORD_CHAR_DFA: Dfa<(Look, u8)> = make_rev_char_dfa(Look::WordChar);
static ref REV_NOT_WORD_CHAR_DFA: Dfa<(Look, u8)> = make_rev_char_dfa(Look::NotWordChar);
}
impl<Tok: Debug + PrimInt> Nfa<Tok, NoLooks> {
fn reachable_from<I>(&self, states: I) -> HashSet<StateIdx> where I: Iterator<Item=StateIdx> {
let mut active: HashSet<StateIdx> = states.collect();
let mut next_active: HashSet<StateIdx> = HashSet::new();
let mut ret = active.clone();
while !active.is_empty() {
for &s in &active {
for &(_, t) in self.states[s].consuming.ranges_values() {
if !ret.contains(&t) {
ret.insert(t);
next_active.insert(t);
}
}
}
swap(&mut active, &mut next_active);
next_active.clear();
}
ret
}
fn reversed_simple(&self) -> Nfa<Tok, NoLooks> {
let rev_transitions = self.reversed_transitions();
let mut ret: Nfa<Tok, NoLooks> = Nfa::with_capacity(self.states.len());
for trans in rev_transitions {
let idx = ret.add_state(Accept::Never);
ret.states[idx].consuming = trans;
}
ret
}
fn reachable_states(&self) -> HashSet<StateIdx> {
let init_states = self.init.iter().map(|pair| pair.1);
let final_states = self.states.iter().enumerate()
.filter(|&(_, state)| state.accept != Accept::Never)
.map(|(idx, _)| idx);
let forward = self.reachable_from(init_states);
let backward = self.reversed_simple().reachable_from(final_states);
forward.intersection(&backward).cloned().collect()
}
pub fn trim_unreachable(&mut self) {
let reachable = self.reachable_states();
let mut old_states = Vec::new();
swap(&mut self.states, &mut old_states);
let mut old_to_new = vec![None; old_states.len()];
let (new_to_old, new_states): (Vec<_>, Vec<State<Tok>>) = old_states.into_iter()
.enumerate()
.filter(|&(i, _)| reachable.contains(&i))
.unzip();
self.states = new_states;
for (new, &old) in new_to_old.iter().enumerate() {
old_to_new[old] = Some(new);
}
self.map_states(|s| old_to_new[s]);
}
fn accept_union(&self, states: &StateSet) -> Accept {
states.iter().map(|s| self.states[*s].accept).max().unwrap_or(Accept::Never)
}
}
impl Nfa<u32, NoLooks> {
pub fn byte_me(self, max_states: usize) -> ::Result<Nfa<u8, NoLooks>> {
let mut ret = Nfa::<u8, NoLooks> {
states: self.states.iter().map(|s| State {
accept: s.accept,
accept_look: s.accept_look,
accept_state: s.accept_state,
accept_tokens: s.accept_tokens,
consuming: RangeMultiMap::new(),
looking: Vec::new(),
}).collect(),
init: self.init,
phantom: PhantomData,
};
for (i, state) in self.states.into_iter().enumerate() {
for (tgt, transitions) in state.consuming.ranges_values().group_by(|x| x.1) {
try!(ret.add_utf8_sequences(i, transitions.into_iter().map(|x| x.0), tgt, max_states));
}
}
Ok(ret)
}
}
impl Nfa<u8, NoLooks> {
pub fn determinize(&self, max_states: usize) -> ::Result<Dfa<(Look, u8)>> {
Determinizer::determinize(self, max_states, MatchChoice::TransitionOrder, self.init.clone())
}
pub fn determinize_longest(&self, max_states: usize) -> ::Result<Dfa<(Look, u8)>> {
Determinizer::determinize(self, max_states, MatchChoice::LongestMatch, self.init.clone())
}
pub fn reverse(&self, max_states: usize) -> ::Result<Nfa<u8, NoLooks>> {
let mut ret = self.reversed_simple();
for &(look, i) in &self.init {
match look {
Look::Full => {
ret.states[i].accept = Accept::Always;
ret.states[i].accept_look = Look::Full;
},
Look::Boundary => {
ret.states[i].accept = max(ret.states[i].accept, Accept::AtEoi);
ret.states[i].accept_look = max(ret.states[i].accept_look, Look::Boundary);
},
Look::NewLine => {
let accept_state = ret.add_look_ahead_state(Look::NewLine, 1, i);
ret.add_transition(i, accept_state, Range::new(b'\n', b'\n'));
ret.states[i].accept = max(ret.states[i].accept, Accept::AtEoi);
ret.states[i].accept_look = max(ret.states[i].accept_look, Look::Boundary);
},
Look::WordChar | Look::NotWordChar => {
let dfa: &Dfa<_> = if look == Look::WordChar {
&REV_WORD_CHAR_DFA
} else {
ret.states[i].accept = max(ret.states[i].accept, Accept::AtEoi);
ret.states[i].accept_look = max(ret.states[i].accept_look, Look::Boundary);
&REV_NOT_WORD_CHAR_DFA
};
let accept_state = ret.add_look_ahead_state(look, 1, i);
try!(ret.add_min_utf8_sequences(i, dfa, accept_state, max_states));
},
Look::Empty => {
panic!("Empty cannot be an init look");
},
}
}
ret.init.clear();
for st in &self.states {
if st.accept != Accept::Never {
ret.init.push((st.accept_look, st.accept_state));
}
}
Ok(ret)
}
fn init_accept(&self, look: Look) -> Accept {
let set = self.init.iter()
.filter(|pair| look <= pair.0)
.map(|pair| pair.1)
.collect::<Vec<_>>();
self.accept_union(&set)
}
pub fn anchor(mut self, max_states: usize) -> ::Result<Nfa<u8, NoLooks>> {
let loop_accept = self.init_accept(Look::Full);
let loop_state = self.add_state(loop_accept);
let init_accept = self.init_accept(Look::Boundary);
let init_state = self.add_state(init_accept);
let mut init = Vec::new();
swap(&mut init, &mut self.init);
for &(look, st_idx) in &init {
if look.allows_eoi() {
for &(range, target) in self.states[st_idx].consuming.clone().ranges_values() {
self.add_transition(init_state, target, range);
}
}
match look {
Look::Boundary => {},
Look::Full => {
for &(range, target) in self.states[st_idx].consuming.clone().ranges_values() {
self.add_transition(loop_state, target, range);
}
},
Look::NewLine => {
self.add_transition(init_state, st_idx, Range::new(b'\n', b'\n'));
self.add_transition(loop_state, st_idx, Range::new(b'\n', b'\n'));
},
Look::WordChar | Look::NotWordChar => {
let dfa: &Dfa<_> =
if look == Look::WordChar { &WORD_CHAR_DFA } else { &NOT_WORD_CHAR_DFA };
try!(self.add_min_utf8_sequences(loop_state, dfa, st_idx, max_states));
try!(self.add_min_utf8_sequences(init_state, dfa, st_idx, max_states));
},
Look::Empty => {
panic!("Cannot start with an empty look");
},
}
if self.states[st_idx].accept == Accept::Always {
break;
}
}
if init_accept != Accept::Always {
self.add_transition(init_state, loop_state, Range::full());
}
if loop_accept != Accept::Always {
self.add_transition(loop_state, loop_state, Range::full());
}
self.init.push((Look::Boundary, init_state));
self.trim_unreachable();
Ok(self)
}
fn add_min_utf8_sequences(
&mut self,
start_state: StateIdx,
dfa: &Dfa<(Look, u8)>,
end_state: StateIdx,
max_states: usize,
) -> ::Result<()> {
let offset = self.states.len();
let end_accept = self.states[end_state].accept_tokens > 0;
if self.states.len() + dfa.num_states() > max_states {
return Err(Error::TooManyStates);
}
for _ in 0..dfa.num_states() {
self.add_state(Accept::Never);
}
for d_idx in 0..dfa.num_states() {
let n_src = if d_idx == 0 { start_state } else { d_idx + offset };
for &(range, d_tgt) in dfa.transitions(d_idx).ranges_values() {
let n_tgt = if dfa.accept(d_tgt) == &Accept::Always && !end_accept {
end_state
} else {
let n_tgt = d_tgt + offset;
self.states[n_tgt].accept = *dfa.accept(d_tgt);
if let Some(&(look, bytes)) = dfa.ret(d_tgt) {
self.states[n_tgt].accept_look = look;
self.states[n_tgt].accept_state = start_state;
self.states[n_tgt].accept_tokens = bytes;
}
n_tgt
};
self.add_transition(n_src, n_tgt, range);
}
}
Ok(())
}
fn add_utf8_sequence(
&mut self,
start_state: StateIdx,
mut end_state: StateIdx,
seq: MergedUtf8Sequences
) {
let mut last_state = start_state;
for range in &seq.head {
let cur_state = self.add_state(Accept::Never);
self.add_transition(last_state, cur_state, Range::new(range.start, range.end));
last_state = cur_state;
}
if self.states[end_state].accept_tokens > 0 {
let look = self.states[end_state].accept_look;
let acc_state = self.states[end_state].accept_state;
end_state = self.add_look_ahead_state(look, seq.num_bytes(), acc_state);
}
for range in &seq.last_byte {
self.add_transition(last_state, end_state, Range::new(range.start, range.end));
}
}
fn add_utf8_sequences<I>(
&mut self,
start_state: StateIdx,
ranges: I,
end_state: StateIdx,
max_states: usize
) -> ::Result<()>
where I: Iterator<Item=Range<u32>> {
for m in MergedUtf8Sequences::from_ranges(ranges) {
self.add_utf8_sequence(start_state, end_state, m);
if self.states.len() > max_states {
return Err(Error::TooManyStates);
}
}
Ok(())
}
fn transition_map(&self, states: &[StateIdx]) -> RangeMap<u8, Vec<usize>> {
let mut transitions = states.into_iter()
.flat_map(|s| self.states[*s].consuming.ranges_values().cloned())
.collect::<RangeMultiMap<u8, StateIdx>>()
.group();
fn uniquify(elts: &mut Vec<StateIdx>, scratch: &mut Vec<bool>) {
elts.retain(|&e| {
let ret = !scratch[e];
scratch[e] = true;
ret
});
for e in elts {
scratch[*e] = false;
}
}
let mut scratch = vec![false; self.num_states()];
for pair in transitions.as_mut_slice() {
uniquify(&mut pair.1, &mut scratch);
}
transitions
}
}
#[derive(PartialEq)]
enum MatchChoice {
TransitionOrder,
LongestMatch,
}
struct Determinizer<'a> {
nfa: &'a Nfa<u8, NoLooks>,
dfa: Dfa<(Look, u8)>,
state_map: HashMap<StateSet, StateIdx>,
active_states: Vec<StateSet>,
max_states: usize,
match_choice: MatchChoice,
}
impl<'a> Determinizer<'a> {
fn determinize(nfa: &Nfa<u8, NoLooks>,
max_states: usize,
match_choice: MatchChoice,
init: Vec<(Look, StateIdx)>) -> ::Result<Dfa<(Look, u8)>> {
let mut det = Determinizer::new(nfa, max_states, match_choice);
try!(det.run(init));
Ok(det.dfa)
}
fn new(nfa: &'a Nfa<u8, NoLooks>,
max_states: usize,
match_choice: MatchChoice) -> Determinizer<'a> {
Determinizer {
nfa: nfa,
dfa: Dfa::new(),
state_map: HashMap::new(),
active_states: Vec::new(),
max_states: max_states,
match_choice: match_choice,
}
}
fn accept(&self, s: &[StateIdx]) -> (Accept, Look, u8) {
let mut accept_states = s.iter().cloned()
.filter(|i| self.nfa.states[*i].accept != Accept::Never);
let mut accept_always_states = s.iter().cloned()
.filter(|i| self.nfa.states[*i].accept == Accept::Always);
let (first_accept, other_accept) = if self.match_choice == MatchChoice::TransitionOrder {
(accept_states.next(), accept_always_states.next())
} else {
(accept_states.min_by_key(|i| self.nfa.states[*i].accept_tokens),
accept_always_states.min_by_key(|i| self.nfa.states[*i].accept_tokens))
};
let look_intersection = |toks: u8| {
s.iter().cloned()
.filter(|i| self.nfa.states[*i].accept == Accept::Always)
.filter(|i| self.nfa.states[*i].accept_tokens == toks)
.fold(Look::Full, |x, y| x.intersection(&self.nfa.states[y].accept_look))
};
if let Some(first_accept) = first_accept {
let st = &self.nfa.states[first_accept];
if st.accept == Accept::AtEoi {
if let Some(other_accept) = other_accept {
let other_st = &self.nfa.states[other_accept];
if other_st.accept_tokens > 0 {
let look = look_intersection(other_st.accept_tokens);
return (Accept::AtEoi, look, other_st.accept_tokens);
}
}
(Accept::AtEoi, Look::Boundary, 0)
} else {
(Accept::Always, look_intersection(st.accept_tokens), st.accept_tokens)
}
} else {
(Accept::Never, Look::Empty, 0)
}
}
fn add_state(&mut self, mut s: StateSet) -> ::Result<StateIdx> {
if self.match_choice == MatchChoice::TransitionOrder {
if let Some(accept_idx) = s.iter().position(|&i| self.nfa.states[i].accept == Accept::Always) {
s.truncate(accept_idx + 1);
}
}
if self.state_map.contains_key(&s) {
Ok(*self.state_map.get(&s).unwrap())
} else if self.dfa.num_states() >= self.max_states {
Err(Error::TooManyStates)
} else {
let (acc, look, bytes_ago) = self.accept(&s);
let ret = if acc != Accept::Never { Some ((look, bytes_ago)) } else { None };
let new_state = self.dfa.add_state(acc, ret);
self.active_states.push(s.clone());
self.state_map.insert(s, new_state);
Ok(new_state)
}
}
fn run(&mut self, init: Vec<(Look, StateIdx)>) -> ::Result<()> {
if self.nfa.states.is_empty() {
return Ok(());
}
for &look in Look::all() {
let init_states: StateSet = init.iter().cloned()
.filter(|&(x, _)| look == x)
.map(|(_, y)| y)
.collect();
if !init_states.is_empty() {
let new_state_idx = try!(self.add_state(init_states));
self.dfa.init[look.as_usize()] = Some(new_state_idx);
}
}
while !self.active_states.is_empty() {
let state = self.active_states.pop().unwrap();
let state_idx = *self.state_map.get(&state).unwrap();
let trans = self.nfa.transition_map(&state);
let mut dfa_trans = Vec::new();
for &(range, ref target) in trans.ranges_values() {
let target_idx = try!(self.add_state(target.clone()));
dfa_trans.push((range, target_idx));
}
self.dfa.set_transitions(state_idx, dfa_trans.into_iter().collect());
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use look::Look;
use dfa::Dfa;
use nfa::{Accept, Nfa, NoLooks};
use nfa::tests::{re_nfa, trans_nfa, trans_range_nfa};
use range_map::Range;
use std::usize;
fn re_nfa_anchored(re: &str) -> Nfa<u8, NoLooks> {
re_nfa(re).byte_me(usize::MAX).unwrap().anchor(usize::MAX).unwrap()
}
fn re_dfa(re: &str) -> Dfa<(Look, u8)> {
re_nfa(re).byte_me(usize::MAX).unwrap().determinize(usize::MAX).unwrap()
}
#[test]
fn anchor_simple() {
let nfa = re_nfa_anchored("a");
let mut target = trans_range_nfa(3, &[(2, 0, Range::new(b'a', b'a')),
(2, 1, Range::full()),
(1, 0, Range::new(b'a', b'a')),
(1, 1, Range::full())]);
target.init.push((Look::Boundary, 2));
target.states[0].accept = Accept::Always;
assert_eq!(nfa, target);
}
#[test]
fn anchor_nl() {
let nfa = re_nfa_anchored(r"(?m)^a");
let mut target = trans_nfa(4, &[(3, 1, 'a'),
(0, 1, 'a'),
(2, 0, '\n'),
(3, 0, '\n')]);
target.init.push((Look::Boundary, 3));
target.states[1].accept = Accept::Always;
let mut target = target.byte_me(usize::MAX).unwrap();
target.states[2].consuming.insert(Range::full(), 2);
target.states[3].consuming.insert(Range::full(), 2);
assert_eq!(nfa, target);
}
#[test]
fn anchor_already_anchored() {
let nfa = re_nfa_anchored("^a");
let mut target = trans_nfa(2, &[(1, 0, 'a')]);
target.init.push((Look::Boundary, 1));
target.states[0].accept = Accept::Always;
assert_eq!(nfa, target);
}
#[test]
fn determinize_pruning() {
assert_eq!(re_dfa("a|aa"), re_dfa("a"));
}
macro_rules! check_rev_inits {
($name:ident, $re:expr, $inits:expr) => {
#[test]
fn $name() {
let rev = re_nfa($re).byte_me(usize::MAX).unwrap().reverse(usize::MAX).unwrap();
println!("{:?}", rev.init);
for &look in Look::all() {
println!("checking look {:?}", look);
if $inits.contains(&look) {
assert!(rev.init.iter().any(|pair| pair.0 == look));
} else {
assert!(!rev.init.iter().any(|pair| pair.0 == look));
}
}
}
};
}
check_rev_inits!(rev_init_simple, "abc", [Look::Full]);
check_rev_inits!(rev_init_boundary, "abc$", [Look::Boundary]);
check_rev_inits!(rev_init_simple_and_boundary, "(abc$|abc)", [Look::Full, Look::Boundary]);
check_rev_inits!(rev_init_new_line, "(?m)abc$", [Look::Boundary, Look::NewLine]);
check_rev_inits!(rev_init_word, r" \b", [Look::WordChar]);
check_rev_inits!(rev_init_not_word, r"abc\b", [Look::Boundary, Look::NotWordChar]);
check_rev_inits!(rev_init_word_or_not_word, r".\b", [Look::Boundary, Look::NotWordChar, Look::WordChar]);
}