use crate::alphabet::Alphabet;
use crate::dfa::{Dfa, MinDfa};
use crate::limits::{Budget, Limits};
use crate::lower::Lower;
use crate::term::{TermId, TermStore};
use rexact_syntax::error::{ErrorCode, RexactError};
use rexact_syntax::Pattern;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Mode {
Full,
Search,
}
pub struct Language {
store: TermStore,
dfa: Dfa,
root: TermId,
alphabet: Alphabet,
limits: Limits,
}
fn limit(code: ErrorCode) -> RexactError {
RexactError::new(code)
}
impl Language {
pub fn compile(
p: &Pattern,
alphabet: Option<Alphabet>,
mode: Mode,
limits: &Limits,
) -> Result<Language, RexactError> {
let alphabet = alphabet.unwrap_or_else(|| Alphabet::default_for(p.dialect, p.flags));
let mut store = TermStore::new(alphabet.universe());
let root = lower_into(&mut store, p, alphabet, mode, limits)?;
Self::from_parts(store, root, alphabet, limits)
}
pub fn from_parts(
mut store: TermStore,
root: TermId,
alphabet: Alphabet,
limits: &Limits,
) -> Result<Language, RexactError> {
let mut budget = Budget::new(limits.clone());
let dfa = Dfa::new(&mut store, root, alphabet.name(), &mut budget).map_err(limit)?;
Ok(Language {
store,
dfa,
root,
alphabet,
limits: limits.clone(),
})
}
pub fn alphabet(&self) -> Alphabet {
self.alphabet
}
pub fn root(&self) -> TermId {
self.root
}
pub fn store(&self) -> &TermStore {
&self.store
}
pub fn matches(&mut self, input: &str) -> Result<bool, RexactError> {
let units = self.alphabet.encode(input);
self.matches_units(&units)
}
pub fn matches_units(&mut self, input: &[u32]) -> Result<bool, RexactError> {
let mut budget = Budget::new(self.limits.clone());
self.dfa
.matches(&mut self.store, input, &mut budget)
.map_err(limit)
}
pub fn minimal(&mut self) -> Result<MinDfa, RexactError> {
let mut budget = Budget::new(self.limits.clone());
self.dfa
.minimize(&mut self.store, &mut budget)
.map_err(limit)
}
pub fn fingerprint(&mut self) -> Result<String, RexactError> {
Ok(self.minimal()?.fingerprint())
}
pub fn stats(&self) -> (usize, usize) {
(self.dfa.num_states(), self.dfa.num_classes())
}
}
pub fn fingerprint_term(
store: &mut TermStore,
root: TermId,
alphabet: Alphabet,
limits: &Limits,
) -> Result<String, RexactError> {
let mut budget = Budget::new(limits.clone());
let mut dfa = Dfa::new(store, root, alphabet.name(), &mut budget).map_err(limit)?;
Ok(dfa
.minimize(store, &mut budget)
.map_err(limit)?
.fingerprint())
}
pub fn lower_into(
store: &mut TermStore,
p: &Pattern,
alphabet: Alphabet,
mode: Mode,
limits: &Limits,
) -> Result<TermId, RexactError> {
let mut budget = Budget::new(limits.clone());
budget
.check_length(p.source.chars().count() as u64)
.map_err(limit)?;
let (term, _) = {
let mut l = Lower {
store,
alphabet,
dialect: p.dialect,
chars: p.source.chars().collect(),
budget: &mut budget,
};
l.pattern(p)?
};
Ok(match mode {
Mode::Full => term,
Mode::Search => {
let any = store.any_symbol();
let all = store.star(any);
store.concat_all(&[all, term, all])
}
})
}
pub fn minimal_of_term(
store: &mut TermStore,
root: TermId,
alphabet: Alphabet,
limits: &Limits,
) -> Result<crate::dfa::MinDfa, RexactError> {
let mut budget = Budget::new(limits.clone());
let mut dfa = Dfa::new(store, root, alphabet.name(), &mut budget).map_err(limit)?;
dfa.minimize(store, &mut budget).map_err(limit)
}
pub struct Resolved {
pub root: crate::resolved::RNode,
pub store: TermStore,
pub alphabet: Alphabet,
}
pub fn resolve(
p: &Pattern,
alphabet: Option<Alphabet>,
limits: &Limits,
) -> Result<Resolved, RexactError> {
let alphabet = alphabet.unwrap_or_else(|| Alphabet::default_for(p.dialect, p.flags));
let mut store = TermStore::new(alphabet.universe());
let mut budget = Budget::new(limits.clone());
budget
.check_length(p.source.chars().count() as u64)
.map_err(limit)?;
let root = {
let mut l = Lower {
store: &mut store,
alphabet,
dialect: p.dialect,
chars: p.source.chars().collect(),
budget: &mut budget,
};
l.resolve_pattern(p)?
};
Ok(Resolved {
root,
store,
alphabet,
})
}