dfa.rs (10240B)
1 use core::fmt::Display; 2 use petgraph::{Directed, Graph}; 3 use std::{ 4 collections::{BTreeMap, HashMap, HashSet}, 5 fmt, 6 }; 7 8 use crate::{Automaton, AutomatonError, Dot, Encodable, FiniteStateAutomaton}; 9 use serde_json::{from_str, Value}; 10 11 #[derive(Hash, Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)] 12 pub enum Transition { 13 Char(char), 14 Wildcard, 15 } 16 17 impl Display for Transition { 18 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 19 match self { 20 Transition::Char(c) => write!(f, "'{}'", c), 21 Transition::Wildcard => write!(f, "•"), 22 } 23 } 24 } 25 26 #[derive(Clone, Debug, PartialEq)] 27 pub struct DFA { 28 pub alphabet: HashSet<char>, 29 pub states: HashSet<u64>, 30 pub initial_state: u64, 31 pub final_states: HashSet<u64>, 32 pub transition_table: HashMap<(u64, Transition), u64>, 33 } 34 35 impl Automaton for DFA { 36 fn accepts(&self, input: &str) -> bool { 37 let mut current_state: u64 = self.initial_state; 38 for c in input.chars() { 39 match self 40 .transition_table 41 .get(&(current_state, Transition::Char(c))) 42 .or(self 43 .transition_table 44 .get(&(current_state, Transition::Wildcard))) 45 { 46 Some(state) => current_state = state.to_owned(), 47 None => { 48 eprintln!("The DFA's transition table is incomplete or malformed!"); 49 return false; 50 } 51 } 52 } 53 self.final_states.contains(¤t_state) 54 } 55 } 56 57 impl FiniteStateAutomaton for DFA { 58 fn as_dfa(&self) -> Result<DFA, AutomatonError> { 59 Ok(self.clone()) 60 } 61 62 fn from_dfa(dfa: self::DFA) -> Result<Self, AutomatonError> 63 where 64 Self: Sized, 65 { 66 Ok(dfa) 67 } 68 69 fn convert(automaton: Box<dyn FiniteStateAutomaton>) -> Result<Self, AutomatonError> 70 where 71 Self: Sized, 72 { 73 automaton.as_dfa() 74 } 75 } 76 77 impl Display for DFA { 78 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 79 let mut transition_vec: Vec<Transition> = 80 self.alphabet.iter().map(|&c| Transition::Char(c)).collect(); 81 let mut states_vec: Vec<u64> = self.states.iter().copied().collect(); 82 let mut final_states_vec: Vec<u64> = self.final_states.iter().copied().collect(); 83 84 transition_vec.push(Transition::Wildcard); 85 transition_vec.sort(); 86 states_vec.sort(); 87 final_states_vec.sort(); 88 89 let transition_str = format!("{:?}", transition_vec); 90 let states_str = format!("{:?}", states_vec); 91 let initial_state_str = self.initial_state.to_string(); 92 let final_states_str = format!("{:?}", final_states_vec); 93 94 let transition_tbl_hdr = transition_vec 95 .iter() 96 .fold("\t|".to_owned(), |acc, &t| format!("{}\t{}\t|", acc, t)); 97 98 let mut transition_map: HashMap<u64, Vec<Option<u64>>> = HashMap::new(); 99 for &state in states_vec.iter() { 100 let input_dests = transition_vec 101 .iter() 102 .map(|&transition| { 103 self.transition_table 104 .get(&(state, transition)) 105 .and_then(|v| Some(v.to_owned())) 106 }) 107 .collect(); 108 transition_map.insert(state, input_dests); 109 } 110 111 let transition_table_str = 112 transition_map 113 .iter() 114 .fold(transition_tbl_hdr, |acc, (from, dest_groups)| { 115 let line_str = dest_groups.into_iter().fold("".to_owned(), |acc, dests| { 116 let dest_str = match dests { 117 None => "∅".to_owned(), 118 Some(v) => v.to_string(), 119 }; 120 format!("{}{}\t|\t", acc, dest_str) 121 }); 122 format!("{}\n{}\t|\t{}", acc, from, line_str) 123 }); 124 writeln!(f, "Alphabet: {}", transition_str)?; 125 writeln!(f, "States: {}", states_str)?; 126 writeln!(f, "Initial State: {}", initial_state_str)?; 127 writeln!(f, "Final States: {}", final_states_str)?; 128 writeln!(f, "Transition Table:\n{}", transition_table_str)?; 129 let graphviz_repr: Result<Dot<Transition>, AutomatonError> = self.try_into(); 130 match graphviz_repr { 131 Ok(s) => writeln!(f, "Graphviz Representation:\n{}", s), 132 Err(e) => writeln!(f, "Error getting Graphviz representation: {}", e), 133 } 134 } 135 } 136 137 // This is the impl that should be used when reading DFA files 138 impl Encodable<&str> for DFA { 139 fn parse(json_str: &str) -> Result<DFA, AutomatonError> { 140 DFA::parse(from_str::<Value>(json_str)?) 141 } 142 } 143 144 impl Encodable<Value> for DFA { 145 fn parse(json: Value) -> Result<DFA, AutomatonError> { 146 let alphabet_vec: Vec<char> = json["alphabet"] 147 .as_array() 148 .ok_or(AutomatonError::parse_error( 149 "Could not get the alphabet from the JSON", 150 ))? 151 .iter() 152 .map(|v| v.to_string().chars().next()) 153 .collect::<Option<Vec<char>>>() 154 .ok_or(AutomatonError::parse_error( 155 "Could not convert the alphabet to UTF-8 characters", 156 ))?; 157 158 let alphabet: HashSet<char> = alphabet_vec.iter().map(char::to_owned).collect(); 159 160 let states: HashSet<u64> = json["states"] 161 .as_array() 162 .ok_or(AutomatonError::parse_error( 163 "Could not get the states from the JSON", 164 ))? 165 .iter() 166 .map(|v| v.as_u64()) 167 .collect::<Option<HashSet<u64>>>() 168 .ok_or(AutomatonError::parse_error( 169 "Could not convert the states to u64", 170 ))?; 171 172 let initial_state: u64 = 173 json["initial_state"] 174 .as_u64() 175 .ok_or(AutomatonError::parse_error( 176 "Initial state incorrectly formatted", 177 ))?; 178 179 let final_states: HashSet<u64> = json["final_states"] 180 .as_array() 181 .ok_or(AutomatonError::parse_error( 182 "Could not get the final states from the JSON", 183 ))? 184 .iter() 185 .map(|v| v.as_u64()) 186 .collect::<Option<_>>() 187 .ok_or(AutomatonError::parse_error( 188 "Could not convert the final states to u64", 189 ))?; 190 191 let mut transition_table: HashMap<(u64, Transition), u64> = HashMap::new(); 192 for row in json["transition_table"] 193 .as_array() 194 .ok_or(AutomatonError::parse_error( 195 "Unable to parse rows in transition table", 196 ))? 197 .iter() 198 { 199 let transitions = row 200 .as_array() 201 .ok_or(AutomatonError::parse_error( 202 "Unable to get a row in the transition table", 203 ))? 204 .to_owned(); 205 206 let from: u64 = transitions 207 .get(0) 208 .ok_or(AutomatonError::parse_error( 209 "Unable to get the \"from\" state of a row", 210 ))? 211 .as_u64() 212 .ok_or(AutomatonError::parse_error( 213 "Unable to parse the \"from\" state of a row as a u64", 214 ))?; 215 216 //let dests: Vec<(char, u64)> = transitions[1..] 217 let dests: Vec<(char, u64)> = alphabet_vec 218 .iter() 219 .zip(transitions.iter().skip(1).map(Value::as_u64)) 220 .map(|(input, dest)| dest.and_then(|v| Some((*input, v)))) 221 .collect::<Option<_>>() 222 .ok_or(AutomatonError::parse_error( 223 "Unable to parse the transition destinations in the table", 224 ))?; 225 for (input, dest) in dests.iter() { 226 transition_table.insert((from, Transition::Char(*input)), *dest); 227 } 228 } 229 230 Ok(DFA { 231 alphabet, 232 states, 233 final_states, 234 transition_table, 235 initial_state, 236 }) 237 } 238 } 239 240 impl TryFrom<&DFA> for Dot<Transition> { 241 type Error = AutomatonError; 242 fn try_from(dfa: &DFA) -> Result<Self, Self::Error> { 243 let mut graph: Graph<u64, Transition, Directed> = Graph::new(); 244 245 let states: HashMap<u64, _> = dfa.states.iter().map(|&s| (s, graph.add_node(s))).collect(); 246 247 let mut alphabet: HashSet<Transition> = 248 dfa.alphabet.iter().map(|&c| Transition::Char(c)).collect(); 249 alphabet.insert(Transition::Wildcard); 250 251 for transition in alphabet { 252 for (from, from_id, dest) in states.iter().filter_map(|(&from, &from_id)| { 253 dfa.transition_table 254 .get(&(from, transition)) 255 .map(|dest| (from, from_id, dest)) 256 }) { 257 let dest_id = *states.get(dest).ok_or(AutomatonError::dot_error( 258 "The destination of a transition does not exist in the dfa states!", 259 ))?; // Maybe not really necessary... 260 if transition == Transition::Wildcard 261 || !dfa 262 .transition_table 263 .get(&(from, Transition::Wildcard)) 264 .is_some_and(|wildcard_dest| dest == wildcard_dest) 265 { 266 graph.add_edge(from_id, dest_id, transition); 267 } 268 } 269 } 270 271 let initial_state_id = *states 272 .get(&dfa.initial_state) 273 .ok_or(AutomatonError::dot_error( 274 "Unable to get initial state graph index!", 275 ))?; 276 277 let final_states = dfa 278 .final_states 279 .iter() 280 .map(|s| states.get(s).map(|&ix| (*s, ix))) 281 .collect::<Option<BTreeMap<u64, _>>>() 282 .ok_or(AutomatonError::dot_error( 283 "Unable to get the graph index of a final state!", 284 ))?; 285 286 Ok(Self::new( 287 graph, 288 (dfa.initial_state, initial_state_id), 289 final_states, 290 )) 291 } 292 }