use super::types::{CharClass, CharClassChar, TransitionLabel, TransitionLabelChar};
use super::{NFAChar, NFA};
#[derive(Debug, Default)]
pub struct ThompsonBuilderChar {
}
impl ThompsonBuilderChar {
pub fn new() -> Self {
Self {}
}
pub fn epsilon(&self) -> NFAChar {
NFAChar::with_initial_final(true)
}
pub fn single_char(&self, c: char) -> NFAChar {
let mut nfa = NFAChar::new();
let q1 = nfa.add_state(true);
nfa.add_transition_char(0, c, q1);
nfa
}
pub fn any_char(&self) -> NFAChar {
let mut nfa = NFAChar::new();
let q1 = nfa.add_state(true);
nfa.add_transition(0, TransitionLabelChar::Any, q1);
nfa
}
pub fn char_class(&self, class: CharClassChar) -> NFAChar {
let mut nfa = NFAChar::new();
let q1 = nfa.add_state(true);
nfa.add_transition_class(0, class, q1);
nfa
}
pub fn start_of_line(&self) -> NFAChar {
let mut nfa = NFAChar::new();
let q1 = nfa.add_state(true);
nfa.add_transition(0, TransitionLabelChar::StartOfLine, q1);
nfa
}
pub fn end_of_line(&self) -> NFAChar {
let mut nfa = NFAChar::new();
let q1 = nfa.add_state(true);
nfa.add_transition(0, TransitionLabelChar::EndOfLine, q1);
nfa
}
pub fn start_of_input(&self) -> NFAChar {
let mut nfa = NFAChar::new();
let q1 = nfa.add_state(true);
nfa.add_transition(0, TransitionLabelChar::StartOfInput, q1);
nfa
}
pub fn end_of_input(&self) -> NFAChar {
let mut nfa = NFAChar::new();
let q1 = nfa.add_state(true);
nfa.add_transition(0, TransitionLabelChar::EndOfInput, q1);
nfa
}
pub fn end_of_input_strict(&self) -> NFAChar {
let mut nfa = NFAChar::new();
let q1 = nfa.add_state(true);
nfa.add_transition(0, TransitionLabelChar::EndOfInputStrict, q1);
nfa
}
pub fn literal(&self, s: &str) -> NFAChar {
if s.is_empty() {
return self.epsilon();
}
let mut nfa = NFAChar::new();
let mut current = 0;
for (i, c) in s.chars().enumerate() {
let is_last = i == s.chars().count() - 1;
let next = nfa.add_state(is_last);
nfa.add_transition_char(current, c, next);
current = next;
}
nfa
}
#[inline]
pub fn concatenate(&self, a: NFAChar, b: NFAChar) -> NFAChar {
a.concatenate(b)
}
#[inline]
pub fn alternation(&self, a: NFAChar, b: NFAChar) -> NFAChar {
a.union(b)
}
#[inline]
pub fn kleene_star(&self, a: NFAChar) -> NFAChar {
a.kleene_star()
}
#[inline]
pub fn kleene_plus(&self, a: NFAChar) -> NFAChar {
a.kleene_plus()
}
#[inline]
pub fn optional(&self, a: NFAChar) -> NFAChar {
a.optional()
}
pub fn repeat_exact(&self, a: NFAChar, n: usize) -> NFAChar {
if n == 0 {
return self.epsilon();
}
let mut result = a.clone();
for _ in 1..n {
result = self.concatenate(result, a.clone());
}
result
}
pub fn repeat_range(&self, a: NFAChar, min: usize, max: Option<usize>) -> NFAChar {
match max {
Some(max_val) if max_val < min => {
NFAChar::new()
}
Some(max_val) => {
let required = self.repeat_exact(a.clone(), min);
let optional_count = max_val - min;
if optional_count == 0 {
required
} else {
let mut optional_part = self.optional(a.clone());
for _ in 1..optional_count {
optional_part = self.concatenate(optional_part, self.optional(a.clone()));
}
self.concatenate(required, optional_part)
}
}
None => {
let required = self.repeat_exact(a.clone(), min);
let star = self.kleene_star(a);
self.concatenate(required, star)
}
}
}
pub fn union_all(&self, nfas: Vec<NFAChar>) -> NFAChar {
if nfas.is_empty() {
return NFAChar::new(); }
let mut iter = nfas.into_iter();
let mut result = iter.next().expect("at least one NFA");
for nfa in iter {
result = self.alternation(result, nfa);
}
result
}
pub fn concat_all(&self, nfas: Vec<NFAChar>) -> NFAChar {
if nfas.is_empty() {
return self.epsilon();
}
let mut iter = nfas.into_iter();
let mut result = iter.next().expect("at least one NFA");
for nfa in iter {
result = self.concatenate(result, nfa);
}
result
}
}
#[derive(Debug, Default)]
pub struct ThompsonBuilder {
}
impl ThompsonBuilder {
pub fn new() -> Self {
Self {}
}
pub fn epsilon(&self) -> NFA {
NFA::with_initial_final(true)
}
pub fn single_byte(&self, b: u8) -> NFA {
let mut nfa = NFA::new();
let q1 = nfa.add_state(true);
nfa.add_transition_byte(0, b, q1);
nfa
}
pub fn any_byte(&self) -> NFA {
let mut nfa = NFA::new();
let q1 = nfa.add_state(true);
nfa.add_transition(0, TransitionLabel::Any, q1);
nfa
}
pub fn byte_class(&self, class: CharClass) -> NFA {
let mut nfa = NFA::new();
let q1 = nfa.add_state(true);
nfa.add_transition_class(0, class, q1);
nfa
}
pub fn start_of_line(&self) -> NFA {
let mut nfa = NFA::new();
let q1 = nfa.add_state(true);
nfa.add_transition(0, TransitionLabel::StartOfLine, q1);
nfa
}
pub fn end_of_line(&self) -> NFA {
let mut nfa = NFA::new();
let q1 = nfa.add_state(true);
nfa.add_transition(0, TransitionLabel::EndOfLine, q1);
nfa
}
pub fn start_of_input(&self) -> NFA {
let mut nfa = NFA::new();
let q1 = nfa.add_state(true);
nfa.add_transition(0, TransitionLabel::StartOfInput, q1);
nfa
}
pub fn end_of_input(&self) -> NFA {
let mut nfa = NFA::new();
let q1 = nfa.add_state(true);
nfa.add_transition(0, TransitionLabel::EndOfInput, q1);
nfa
}
pub fn end_of_input_strict(&self) -> NFA {
let mut nfa = NFA::new();
let q1 = nfa.add_state(true);
nfa.add_transition(0, TransitionLabel::EndOfInputStrict, q1);
nfa
}
pub fn literal(&self, s: &[u8]) -> NFA {
if s.is_empty() {
return self.epsilon();
}
let mut nfa = NFA::new();
let mut current = 0;
for (i, &b) in s.iter().enumerate() {
let is_last = i == s.len() - 1;
let next = nfa.add_state(is_last);
nfa.add_transition_byte(current, b, next);
current = next;
}
nfa
}
pub fn literal_str(&self, s: &str) -> NFA {
self.literal(s.as_bytes())
}
#[inline]
pub fn concatenate(&self, a: NFA, b: NFA) -> NFA {
a.concatenate(b)
}
#[inline]
pub fn alternation(&self, a: NFA, b: NFA) -> NFA {
a.union(b)
}
#[inline]
pub fn kleene_star(&self, a: NFA) -> NFA {
a.kleene_star()
}
#[inline]
pub fn kleene_plus(&self, a: NFA) -> NFA {
a.kleene_plus()
}
#[inline]
pub fn optional(&self, a: NFA) -> NFA {
a.optional()
}
pub fn repeat_exact(&self, a: NFA, n: usize) -> NFA {
if n == 0 {
return self.epsilon();
}
let mut result = a.clone();
for _ in 1..n {
result = self.concatenate(result, a.clone());
}
result
}
pub fn repeat_range(&self, a: NFA, min: usize, max: Option<usize>) -> NFA {
match max {
Some(max_val) if max_val < min => {
NFA::new() }
Some(max_val) => {
let required = self.repeat_exact(a.clone(), min);
let optional_count = max_val - min;
if optional_count == 0 {
required
} else {
let mut optional_part = self.optional(a.clone());
for _ in 1..optional_count {
optional_part = self.concatenate(optional_part, self.optional(a.clone()));
}
self.concatenate(required, optional_part)
}
}
None => {
let required = self.repeat_exact(a.clone(), min);
let star = self.kleene_star(a);
self.concatenate(required, star)
}
}
}
pub fn union_all(&self, nfas: Vec<NFA>) -> NFA {
if nfas.is_empty() {
return NFA::new();
}
let mut iter = nfas.into_iter();
let mut result = iter.next().expect("at least one NFA");
for nfa in iter {
result = self.alternation(result, nfa);
}
result
}
pub fn concat_all(&self, nfas: Vec<NFA>) -> NFA {
if nfas.is_empty() {
return self.epsilon();
}
let mut iter = nfas.into_iter();
let mut result = iter.next().expect("at least one NFA");
for nfa in iter {
result = self.concatenate(result, nfa);
}
result
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_thompson_epsilon() {
let builder = ThompsonBuilderChar::new();
let nfa = builder.epsilon();
assert!(nfa.accepts(""));
assert!(!nfa.accepts("a"));
}
#[test]
fn test_thompson_single_char() {
let builder = ThompsonBuilderChar::new();
let nfa = builder.single_char('a');
assert!(nfa.accepts("a"));
assert!(!nfa.accepts("b"));
assert!(!nfa.accepts(""));
assert!(!nfa.accepts("aa"));
}
#[test]
fn test_thompson_any_char() {
let builder = ThompsonBuilderChar::new();
let nfa = builder.any_char();
assert!(nfa.accepts("a"));
assert!(nfa.accepts("b"));
assert!(nfa.accepts("z"));
assert!(!nfa.accepts(""));
assert!(!nfa.accepts("ab"));
}
#[test]
fn test_thompson_char_class() {
let builder = ThompsonBuilderChar::new();
let vowels = CharClassChar::from_chars(&['a', 'e', 'i', 'o', 'u']);
let nfa = builder.char_class(vowels);
assert!(nfa.accepts("a"));
assert!(nfa.accepts("e"));
assert!(nfa.accepts("i"));
assert!(!nfa.accepts("b"));
assert!(!nfa.accepts(""));
}
#[test]
fn test_thompson_literal() {
let builder = ThompsonBuilderChar::new();
let nfa = builder.literal("hello");
assert!(nfa.accepts("hello"));
assert!(!nfa.accepts("hell"));
assert!(!nfa.accepts("helloo"));
assert!(!nfa.accepts(""));
}
#[test]
fn test_thompson_literal_empty() {
let builder = ThompsonBuilderChar::new();
let nfa = builder.literal("");
assert!(nfa.accepts(""));
assert!(!nfa.accepts("a"));
}
#[test]
fn test_thompson_concatenate() {
let builder = ThompsonBuilderChar::new();
let nfa_a = builder.single_char('a');
let nfa_b = builder.single_char('b');
let nfa = builder.concatenate(nfa_a, nfa_b);
assert!(nfa.accepts("ab"));
assert!(!nfa.accepts("a"));
assert!(!nfa.accepts("b"));
assert!(!nfa.accepts("ba"));
}
#[test]
fn test_thompson_alternation() {
let builder = ThompsonBuilderChar::new();
let nfa_a = builder.single_char('a');
let nfa_b = builder.single_char('b');
let nfa = builder.alternation(nfa_a, nfa_b);
assert!(nfa.accepts("a"));
assert!(nfa.accepts("b"));
assert!(!nfa.accepts("c"));
assert!(!nfa.accepts("ab"));
assert!(!nfa.accepts(""));
}
#[test]
fn test_thompson_kleene_star() {
let builder = ThompsonBuilderChar::new();
let nfa_a = builder.single_char('a');
let nfa = builder.kleene_star(nfa_a);
assert!(nfa.accepts(""));
assert!(nfa.accepts("a"));
assert!(nfa.accepts("aa"));
assert!(nfa.accepts("aaa"));
assert!(nfa.accepts("aaaa"));
assert!(!nfa.accepts("b"));
assert!(!nfa.accepts("ab"));
}
#[test]
fn test_thompson_kleene_plus() {
let builder = ThompsonBuilderChar::new();
let nfa_a = builder.single_char('a');
let nfa = builder.kleene_plus(nfa_a);
assert!(!nfa.accepts(""));
assert!(nfa.accepts("a"));
assert!(nfa.accepts("aa"));
assert!(nfa.accepts("aaa"));
assert!(!nfa.accepts("b"));
}
#[test]
fn test_thompson_optional() {
let builder = ThompsonBuilderChar::new();
let nfa_a = builder.single_char('a');
let nfa = builder.optional(nfa_a);
assert!(nfa.accepts(""));
assert!(nfa.accepts("a"));
assert!(!nfa.accepts("aa"));
assert!(!nfa.accepts("b"));
}
#[test]
fn test_thompson_repeat_exact() {
let builder = ThompsonBuilderChar::new();
let nfa_a = builder.single_char('a');
let nfa = builder.repeat_exact(nfa_a, 3);
assert!(!nfa.accepts(""));
assert!(!nfa.accepts("a"));
assert!(!nfa.accepts("aa"));
assert!(nfa.accepts("aaa"));
assert!(!nfa.accepts("aaaa"));
}
#[test]
fn test_thompson_repeat_range() {
let builder = ThompsonBuilderChar::new();
let nfa_a = builder.single_char('a');
let nfa = builder.repeat_range(nfa_a, 2, Some(4));
assert!(!nfa.accepts(""));
assert!(!nfa.accepts("a"));
assert!(nfa.accepts("aa"));
assert!(nfa.accepts("aaa"));
assert!(nfa.accepts("aaaa"));
assert!(!nfa.accepts("aaaaa"));
}
#[test]
fn test_thompson_repeat_range_unbounded() {
let builder = ThompsonBuilderChar::new();
let nfa_a = builder.single_char('a');
let nfa = builder.repeat_range(nfa_a, 2, None);
assert!(!nfa.accepts(""));
assert!(!nfa.accepts("a"));
assert!(nfa.accepts("aa"));
assert!(nfa.accepts("aaa"));
assert!(nfa.accepts("aaaa"));
assert!(nfa.accepts("aaaaa"));
}
#[test]
fn test_thompson_union_all() {
let builder = ThompsonBuilderChar::new();
let nfas = vec![
builder.single_char('a'),
builder.single_char('b'),
builder.single_char('c'),
];
let nfa = builder.union_all(nfas);
assert!(nfa.accepts("a"));
assert!(nfa.accepts("b"));
assert!(nfa.accepts("c"));
assert!(!nfa.accepts("d"));
assert!(!nfa.accepts("ab"));
}
#[test]
fn test_thompson_concat_all() {
let builder = ThompsonBuilderChar::new();
let nfas = vec![
builder.single_char('a'),
builder.single_char('b'),
builder.single_char('c'),
];
let nfa = builder.concat_all(nfas);
assert!(nfa.accepts("abc"));
assert!(!nfa.accepts("a"));
assert!(!nfa.accepts("ab"));
assert!(!nfa.accepts("abcd"));
}
#[test]
fn test_thompson_start_of_line() {
let builder = ThompsonBuilderChar::new();
let nfa = builder.start_of_line();
assert_eq!(nfa.state_count(), 2);
}
#[test]
fn test_thompson_end_of_line() {
let builder = ThompsonBuilderChar::new();
let nfa = builder.end_of_line();
assert_eq!(nfa.state_count(), 2);
}
#[test]
fn test_thompson_start_of_input() {
let builder = ThompsonBuilderChar::new();
let nfa = builder.start_of_input();
assert_eq!(nfa.state_count(), 2);
}
#[test]
fn test_thompson_end_of_input() {
let builder = ThompsonBuilderChar::new();
let nfa = builder.end_of_input();
assert_eq!(nfa.state_count(), 2);
}
#[test]
fn test_thompson_end_of_input_strict() {
let builder = ThompsonBuilderChar::new();
let nfa = builder.end_of_input_strict();
assert_eq!(nfa.state_count(), 2);
}
#[test]
fn test_thompson_anchor_concatenation() {
let builder = ThompsonBuilderChar::new();
let start = builder.start_of_line();
let hello = builder.literal("hello");
let end = builder.end_of_line();
let pattern = builder.concatenate(builder.concatenate(start, hello), end);
assert!(pattern.state_count() >= 4);
}
#[test]
fn test_thompson_complex_pattern() {
let builder = ThompsonBuilderChar::new();
let a = builder.single_char('a');
let b = builder.single_char('b');
let c = builder.single_char('c');
let ab = builder.alternation(a, b);
let ab_star = builder.kleene_star(ab);
let pattern = builder.concatenate(ab_star, c);
assert!(pattern.accepts("c"));
assert!(pattern.accepts("ac"));
assert!(pattern.accepts("bc"));
assert!(pattern.accepts("aac"));
assert!(pattern.accepts("abc"));
assert!(pattern.accepts("bac"));
assert!(pattern.accepts("aabbc"));
assert!(!pattern.accepts(""));
assert!(!pattern.accepts("a"));
assert!(!pattern.accepts("ab"));
assert!(!pattern.accepts("ca"));
}
#[test]
fn test_thompson_phonetic_pattern() {
let builder = ThompsonBuilderChar::new();
let ph = builder.literal("ph");
let f = builder.single_char('f');
let ph_or_f = builder.alternation(ph, f);
let one = builder.literal("one");
let pattern = builder.concatenate(ph_or_f, one);
assert!(pattern.accepts("phone"));
assert!(pattern.accepts("fone"));
assert!(!pattern.accepts("bone"));
assert!(!pattern.accepts("phon"));
assert!(!pattern.accepts(""));
}
#[test]
fn test_thompson_byte_literal() {
let builder = ThompsonBuilder::new();
let nfa = builder.literal_str("hello");
assert!(nfa.accepts_str("hello"));
assert!(!nfa.accepts_str("hell"));
}
#[test]
fn test_thompson_byte_alternation() {
let builder = ThompsonBuilder::new();
let a = builder.single_byte(b'a');
let b = builder.single_byte(b'b');
let nfa = builder.alternation(a, b);
assert!(nfa.accepts_str("a"));
assert!(nfa.accepts_str("b"));
assert!(!nfa.accepts_str("c"));
}
#[test]
fn test_thompson_byte_kleene_star() {
let builder = ThompsonBuilder::new();
let a = builder.single_byte(b'a');
let nfa = builder.kleene_star(a);
assert!(nfa.accepts_str(""));
assert!(nfa.accepts_str("a"));
assert!(nfa.accepts_str("aaa"));
}
}