graph_enfa.rs (14774B)
1 use crate::{ 2 dfa, 3 enfa::{self, ENFA}, 4 Automaton, AutomatonError, Dot, FiniteStateAutomaton, 5 }; 6 use petgraph::{graph::NodeIndex, visit::EdgeRef, Directed, Graph}; 7 use std::{ 8 collections::{BTreeMap, HashMap, HashSet}, 9 fmt::Display, 10 }; 11 12 #[derive(Hash, Debug, PartialEq, Eq, Copy, Clone)] 13 pub enum Transition { 14 Char(char), 15 Wildcard, 16 Epsilon, 17 } 18 19 impl Display for Transition { 20 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 21 match self { 22 Transition::Char(c) => write!(f, "'{}'", c), 23 Transition::Wildcard => write!(f, "•"), 24 Transition::Epsilon => write!(f, "ε"), 25 } 26 } 27 } 28 29 impl From<Transition> for enfa::Transition { 30 fn from(transition: Transition) -> Self { 31 match transition { 32 Transition::Char(c) => enfa::Transition::Char(c), 33 Transition::Wildcard => enfa::Transition::Wildcard, 34 Transition::Epsilon => enfa::Transition::Epsilon, 35 } 36 } 37 } 38 39 impl From<enfa::Transition> for Transition { 40 fn from(transition: enfa::Transition) -> Self { 41 match transition { 42 enfa::Transition::Char(c) => Transition::Char(c), 43 enfa::Transition::Wildcard => Transition::Wildcard, 44 enfa::Transition::Epsilon => Transition::Epsilon, 45 } 46 } 47 } 48 49 impl From<dfa::Transition> for Transition { 50 fn from(transition: dfa::Transition) -> Self { 51 match transition { 52 dfa::Transition::Char(c) => Transition::Char(c), 53 dfa::Transition::Wildcard => Transition::Wildcard, 54 } 55 } 56 } 57 58 /// Intended as an easy way of constructing an ε-NFA in bits, adding onto it much like you'd build an ε-NFA on a whiteboard. 59 #[derive(Clone)] 60 pub struct GraphENFA { 61 alphabet: HashSet<char>, 62 states: HashSet<u64>, 63 initial_state: u64, 64 final_states: HashSet<u64>, 65 graph: Graph<u64, Transition, Directed>, 66 state_id_to_index_map: HashMap<u64, NodeIndex>, 67 current_state_id: u64, 68 } 69 70 impl GraphENFA { 71 /// Create a new graph ε-NFA 72 pub fn new() -> (Self, u64) { 73 let mut states = HashSet::new(); 74 let mut state_id_to_index_map = HashMap::new(); 75 let mut graph = Graph::new(); 76 77 let initial_state_id = 0; 78 79 let initial_state_index = graph.add_node(initial_state_id); 80 81 states.insert(initial_state_id); 82 state_id_to_index_map.insert(initial_state_id, initial_state_index); 83 84 ( 85 GraphENFA { 86 alphabet: HashSet::new(), 87 states, 88 initial_state: initial_state_id, 89 final_states: HashSet::new(), 90 graph, 91 state_id_to_index_map, 92 current_state_id: initial_state_id, 93 }, 94 initial_state_id, 95 ) 96 } 97 98 /// Set the acceptance of a state, will return an error if the state is already in the desired acceptance or the state doesn't exist in the ε-NFA. 99 pub fn set_state_acceptance( 100 &mut self, 101 state_id: u64, 102 accept: bool, 103 ) -> Result<(), AutomatonError> { 104 if !self.states.contains(&state_id) { 105 return Err(AutomatonError::builder_error( 106 "There's no state with that ID!", 107 )); 108 } 109 110 if accept && !self.final_states.contains(&state_id) && self.final_states.insert(state_id) // adding to final states 111 || !accept // Removing from final states 112 && self.final_states.contains(&state_id) 113 && self.final_states.remove(&state_id) 114 { 115 Ok(()) 116 } else { 117 Err(AutomatonError::builder_error( 118 "The state was already in the desired acceptance!", 119 )) 120 } 121 } 122 123 /// Changes the initial state of the ε-NFA to the state with ID `state_id`, this will remove the other initial state. Returns `Ok` of the previous initial state, or an error describing the failure 124 pub fn set_initial_state(&mut self, state_id: u64) -> Result<u64, AutomatonError> { 125 if !self.states.contains(&state_id) { 126 return Err(AutomatonError::builder_error( 127 "There is no state with the specified ID", 128 )); 129 } 130 131 let prev_initial_state_id = self.initial_state; 132 self.initial_state = state_id; 133 134 Ok(prev_initial_state_id) 135 } 136 137 /// Inserts a new state with no transitions into the ε-NFA with the ID `state_id` 138 fn insert_state(&mut self, state_id: u64) -> Result<(), AutomatonError> { 139 if self.states.contains(&state_id) { 140 return Err(AutomatonError::builder_error( 141 "There's already a state with that ID", 142 )); 143 } 144 145 let node_index = self.graph.add_node(state_id); 146 if !self 147 .state_id_to_index_map 148 .insert(state_id, node_index) 149 .is_none() 150 { 151 return Err(AutomatonError::builder_error( 152 "There's already a state with that ID", 153 )); 154 } 155 156 if !self.states.insert(state_id) { 157 return Err(AutomatonError::builder_error( 158 "There's already a state with that ID", 159 )); 160 } 161 Ok(()) 162 } 163 164 /// Inserts a new state with no transitions into the ε-NFA with the ID of the max existing ID + 1. 165 pub fn new_state(&mut self) -> Result<u64, AutomatonError> { 166 self.current_state_id += 1; 167 168 self.insert_state(self.current_state_id)?; 169 170 Ok(self.current_state_id) 171 } 172 173 /// Add a transition from one state to another, returns an error if either of the states doesn't exist. 174 /// An epsilon transition is done using the `None` of the `Option<char>` enum. 175 pub fn insert_transition( 176 &mut self, 177 from_id: u64, 178 to_id: u64, 179 input: Transition, 180 ) -> Result<(), AutomatonError> { 181 if !self.states.contains(&from_id) || !self.states.contains(&to_id) { 182 return Err(AutomatonError::builder_error( 183 "Tried adding a transition to/from a state that doesn't exist!", 184 )); 185 } 186 187 match input { 188 Transition::Char(c) => { 189 if !self.alphabet.contains(&c) { 190 self.alphabet.insert(c); 191 } 192 } 193 _ => (), 194 } 195 196 let from_index = self 197 .state_id_to_index_map 198 .get(&from_id) 199 .ok_or(AutomatonError::builder_error( 200 "Couldn't get the index of the from state!", 201 ))? 202 .to_owned(); 203 204 let to_index = self 205 .state_id_to_index_map 206 .get(&to_id) 207 .ok_or(AutomatonError::builder_error( 208 "Couldn't get the index of the to state!", 209 ))? 210 .to_owned(); 211 212 self.graph.add_edge(from_index, to_index, input); 213 214 Ok(()) 215 } 216 217 /// Convert into a regular ε-NFA backed by a `HashMap` instead of a `Graph` 218 /// Whilst this can be used, the `as_dfa` method should be used if you're going to evaluate string acceptance. 219 pub fn as_enfa(&self) -> Result<ENFA, AutomatonError> { 220 let mut transition_table: HashMap<(u64, enfa::Transition), Vec<u64>> = HashMap::new(); 221 let mut current_states = vec![self.initial_state]; 222 let mut next_states = vec![]; 223 let mut visited = HashSet::new(); 224 225 while current_states.len() != 0 { 226 for ¤t_state in current_states.iter() { 227 let current_state_index = self 228 .state_id_to_index_map 229 .get(¤t_state) 230 .ok_or(AutomatonError::conversion_error( 231 "Couldn't get graph index of a state!", 232 ))? 233 .to_owned(); 234 for edge in self.graph.edges(current_state_index) { 235 let mut transitions = transition_table 236 .get(&(current_state, Transition::into(*edge.weight()))) 237 .map_or(vec![], |v: &Vec<u64>| v.to_owned()); 238 let dest = self 239 .graph 240 .node_weight(edge.target()) 241 .ok_or(AutomatonError::conversion_error( 242 "Error converting from graph to regular NFA!", 243 ))? 244 .to_owned(); 245 if !transitions.contains(&dest) { 246 transitions.push(dest); 247 } 248 if !visited.contains(&dest) { 249 next_states.push(dest); 250 } 251 transition_table.insert( 252 (current_state, Transition::into(*edge.weight())), 253 transitions, 254 ); 255 } 256 257 visited.insert(current_state); 258 } 259 current_states = next_states; 260 next_states = vec![]; 261 } 262 263 Ok(ENFA::new( 264 self.alphabet.clone(), 265 self.states.clone(), 266 self.initial_state, 267 self.final_states.clone(), 268 transition_table, 269 )) 270 } 271 } 272 273 impl Automaton for GraphENFA { 274 // This should never be used 275 fn accepts(&self, input: &str) -> bool { 276 let dfa = self.as_dfa().expect("Woops!"); 277 dfa.accepts(input) 278 } 279 } 280 281 impl FiniteStateAutomaton for GraphENFA { 282 /// Converts the `Graph` ε-NFA into a regular ε-NFA backed by a `HashMap`, then converts that to a DFA 283 fn as_dfa(&self) -> std::result::Result<crate::dfa::DFA, AutomatonError> { 284 let enfa = self.as_enfa()?; 285 enfa.as_dfa() 286 } 287 288 /// This should never be used... Unless you really badly want to modify an existing DFA. 289 fn from_dfa(dfa: crate::dfa::DFA) -> std::result::Result<Self, AutomatonError> 290 where 291 Self: Sized, 292 { 293 let mut state_id_to_index_map: HashMap<u64, NodeIndex> = HashMap::new(); 294 let mut graph = Graph::new(); 295 296 for ((from, input), to) in dfa.transition_table { 297 let from_index = state_id_to_index_map 298 .get(&from) 299 .map_or_else(|| graph.add_node(from), |n| n.to_owned()); 300 state_id_to_index_map.insert(from, from_index); 301 let to_index = state_id_to_index_map 302 .get(&to) 303 .map_or_else(|| graph.add_node(to), |n| n.to_owned()); 304 state_id_to_index_map.insert(to, to_index); 305 graph.add_edge(from_index, to_index, Transition::from(input)); 306 } 307 308 let current_state_id = *dfa 309 .states 310 .iter() 311 .max() 312 .ok_or(AutomatonError::conversion_error( 313 "Unable to get the current state ID", 314 ))?; 315 316 Ok(GraphENFA { 317 alphabet: dfa.alphabet, 318 states: dfa.states, 319 initial_state: dfa.initial_state, 320 final_states: dfa.final_states, 321 graph, 322 state_id_to_index_map, 323 current_state_id, 324 }) 325 } 326 } 327 328 impl Display for GraphENFA { 329 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 330 let mut alphabet_vec: Vec<char> = self.alphabet.iter().copied().collect(); 331 let mut states_vec: Vec<u64> = self.states.iter().copied().collect(); 332 let mut final_states_vec: Vec<u64> = self.final_states.iter().copied().collect(); 333 334 alphabet_vec.sort(); 335 states_vec.sort(); 336 final_states_vec.sort(); 337 338 let alphabet_str: String = 339 alphabet_vec 340 .iter() 341 .enumerate() 342 .fold("[".to_owned(), |st, (index, c)| { 343 let ending = if index == self.alphabet.len() - 1 { 344 "]" 345 } else { 346 ", " 347 }; 348 format!("{}{}{}", st, c, ending) 349 }); 350 351 let states_str: String = 352 states_vec 353 .iter() 354 .enumerate() 355 .fold("[".to_owned(), |st, (index, n)| { 356 let ending = if index == self.states.len() - 1 { 357 "]" 358 } else { 359 ", " 360 }; 361 format!("{}{}{}", st, n, ending) 362 }); 363 364 let initial_state_str: String = self.initial_state.to_string(); 365 366 let final_states_str: String = 367 final_states_vec 368 .iter() 369 .enumerate() 370 .fold("[".to_owned(), |st, (index, n)| { 371 let ending = if index == self.final_states.len() - 1 { 372 "]" 373 } else { 374 ", " 375 }; 376 format!("{}{}{}", st, n, ending) 377 }); 378 379 writeln!(f, "Alphabet: {}", alphabet_str)?; 380 writeln!(f, "States: {}", states_str)?; 381 writeln!(f, "Initial State: {}", initial_state_str)?; 382 writeln!(f, "Final States: {}", final_states_str)?; 383 let graphviz_repr: Result<Dot<Transition>, AutomatonError> = self.try_into(); 384 match graphviz_repr { 385 Ok(s) => writeln!(f, "Graphviz Representation:\n{}", s), 386 Err(e) => writeln!(f, "Error getting Graphviz representation: {}", e), 387 } 388 } 389 } 390 391 impl From<&GraphENFA> for Graph<u64, Transition, Directed> { 392 fn from(graph_enfa: &GraphENFA) -> Self { 393 graph_enfa.graph.clone() 394 } 395 } 396 397 impl TryFrom<&GraphENFA> for Dot<Transition> { 398 type Error = AutomatonError; 399 fn try_from(graph_enfa: &GraphENFA) -> Result<Self, Self::Error> { 400 let states = graph_enfa 401 .states 402 .iter() 403 .map(|s| graph_enfa.state_id_to_index_map.get(s).map(|&ix| (*s, ix))) 404 .collect::<Option<BTreeMap<u64, _>>>() 405 .ok_or(AutomatonError::dot_error( 406 "Unable to get the graph index of one of the states!", 407 ))?; 408 let initial_state_id = 409 *states 410 .get(&graph_enfa.initial_state) 411 .ok_or(AutomatonError::dot_error( 412 "Unable to get the graph index of the initial state!", 413 ))?; 414 let final_states = graph_enfa 415 .final_states 416 .iter() 417 .map(|s| states.get(s).map(|&ix| (*s, ix))) 418 .collect::<Option<BTreeMap<u64, _>>>() 419 .ok_or(AutomatonError::dot_error( 420 "Unable to get the graph index of a final state!", 421 ))?; 422 Ok(Self::new( 423 graph_enfa.into(), 424 (graph_enfa.initial_state, initial_state_id), 425 final_states, 426 )) 427 } 428 }