automaton.rs (4957B)
1 use petgraph::{graph::NodeIndex, Directed, Graph}; 2 use std::collections::BTreeMap; 3 use std::error::Error; 4 use std::fmt::Display; 5 use std::panic::Location; 6 7 pub mod web_automaton; 8 9 pub mod dfa; 10 pub mod enfa; 11 pub mod graph_enfa; 12 pub mod nfa; 13 pub mod regex; 14 15 #[derive(Debug)] 16 pub enum AutomatonError { 17 Tokenise(String), 18 Parse(String), 19 Convertion(String), 20 Builder(String), 21 Dot(String), 22 } 23 24 impl AutomatonError { 25 pub fn cause(&self) -> String { 26 match self { 27 Self::Tokenise(s) => s.to_owned(), 28 Self::Parse(s) => s.to_owned(), 29 Self::Convertion(s) => s.to_owned(), 30 Self::Builder(s) => s.to_owned(), 31 Self::Dot(s) => s.to_owned(), 32 } 33 } 34 35 #[track_caller] 36 fn error_str(err_type: &str, reason: &str, loc: Option<&Location>) -> String { 37 let location = loc.unwrap_or(Location::caller()); 38 format!("[{}::{} ERROR] {}", location, err_type, reason) 39 } 40 41 #[track_caller] 42 fn tokenise_error(reason: &str) -> Self { 43 let location = Location::caller(); 44 AutomatonError::Tokenise(AutomatonError::error_str( 45 "Tokenisation", 46 reason, 47 Some(location), 48 )) 49 } 50 51 #[track_caller] 52 fn parse_error(reason: &str) -> Self { 53 let location = Location::caller(); 54 AutomatonError::Parse(AutomatonError::error_str("Parsing", reason, Some(location))) 55 } 56 57 #[track_caller] 58 fn conversion_error(reason: &str) -> Self { 59 let location = Location::caller(); 60 AutomatonError::Convertion(AutomatonError::error_str( 61 "Conversion", 62 reason, 63 Some(location), 64 )) 65 } 66 67 #[track_caller] 68 fn builder_error(reason: &str) -> Self { 69 let location = Location::caller(); 70 AutomatonError::Builder(AutomatonError::error_str("Builder", reason, Some(location))) 71 } 72 73 #[track_caller] 74 fn dot_error(reason: &str) -> Self { 75 let location = Location::caller(); 76 AutomatonError::Dot(AutomatonError::error_str("Dot", reason, Some(location))) 77 } 78 } 79 80 impl Error for AutomatonError {} 81 82 impl Display for AutomatonError { 83 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 84 write!(f, "{}", self.cause()) 85 } 86 } 87 88 impl From<serde_json::Error> for AutomatonError { 89 fn from(err: serde_json::Error) -> Self { 90 Self::parse_error(&err.to_string()) 91 } 92 } 93 94 pub trait Automaton: Display { 95 fn accepts(&self, input: &str) -> bool; 96 } 97 98 pub trait FiniteStateAutomaton: Automaton { 99 fn as_dfa(&self) -> Result<dfa::DFA, AutomatonError>; 100 fn from_dfa(dfa: dfa::DFA) -> Result<Self, AutomatonError> 101 where 102 Self: Sized; 103 fn convert(automaton: Box<dyn FiniteStateAutomaton>) -> Result<Self, AutomatonError> 104 where 105 Self: Sized, 106 { 107 Self::from_dfa(automaton.as_dfa()?) 108 } 109 } 110 111 pub trait Encodable<T> 112 where 113 Self: Sized, 114 { 115 fn parse(encoding: T) -> Result<Self, AutomatonError>; 116 } 117 118 pub struct Dot<V> 119 where 120 V: Display, 121 { 122 graph: Graph<u64, V, Directed>, 123 initial_state: (u64, NodeIndex), 124 final_states: BTreeMap<u64, NodeIndex>, 125 } 126 127 impl<V> Dot<V> 128 where 129 V: Display, 130 { 131 pub fn new( 132 graph: Graph<u64, V, Directed>, 133 initial_state: (u64, NodeIndex), 134 final_states: BTreeMap<u64, NodeIndex>, 135 ) -> Dot<V> { 136 Dot { 137 graph, 138 initial_state, 139 final_states, 140 } 141 } 142 } 143 144 impl<V> Display for Dot<V> 145 where 146 V: Display, 147 { 148 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 149 writeln!(f, "digraph {{")?; 150 writeln!(f, " entrypoint [ label = \"\", shape = none ]")?; 151 152 for (id, label) in self 153 .graph 154 .node_indices() 155 .filter_map(|id| Some((id, *self.graph.node_weight(id)?))) 156 { 157 let shape = if self.final_states.contains_key(&label) { 158 "doublecircle" 159 } else { 160 "circle" 161 }; 162 writeln!( 163 f, 164 " {} [ label = \"{}\", shape = {} ]", 165 id.index(), 166 label, 167 shape 168 )?; 169 } 170 writeln!(f, " entrypoint -> {}", self.initial_state.1.index())?; 171 for (from, to, transition) in self.graph.edge_indices().filter_map(|ex| { 172 let (from_ix, to_ix) = self.graph.edge_endpoints(ex)?; 173 let label = self.graph.edge_weight(ex)?.to_string(); 174 Some((from_ix, to_ix, label)) 175 }) { 176 writeln!( 177 f, 178 " {} -> {} [ label = \"{}\" ]", 179 from.index(), 180 to.index(), 181 transition 182 )?; 183 } 184 writeln!(f, "}}") 185 } 186 } 187 188 #[cfg(test)] 189 mod tests { 190 mod testlib { 191 pub mod util; 192 } 193 194 mod dfa_tests; 195 mod enfa_tests; 196 mod nfa_tests; 197 mod regex_tests; 198 }