1use std::io::Write;
5
6use dot_writer::{Attributes, DotWriter, RankDirection, Scope};
7
8use crate::internal::compiled_dfa::CompiledDfa;
9
10use super::{nfa::Nfa, CharClassID, CharacterClassRegistry, MultiPatternNfa, StateID};
11
12#[allow(dead_code)]
14pub(crate) fn nfa_render<W: Write>(nfa: &Nfa, label: &str, output: &mut W) {
15 let mut writer = DotWriter::from(output);
16 writer.set_pretty_print(true);
17 let mut digraph = writer.digraph();
18 digraph
19 .set_label(format!("{}: {}", label, nfa.pattern()).as_str())
20 .set_rank_direction(RankDirection::LeftRight);
21 for state in nfa.states() {
22 let source_id = {
23 let mut source_node = digraph.node_auto();
24 source_node.set_label(&state.id().as_usize().to_string());
25 if state.id() == nfa.start_state() {
26 source_node
27 .set_shape(dot_writer::Shape::Circle)
28 .set_color(dot_writer::Color::Blue)
29 .set_pen_width(3.0);
30 }
31 if state.id() == nfa.end_state() {
32 source_node
33 .set_shape(dot_writer::Shape::Circle)
34 .set_color(dot_writer::Color::Red)
35 .set_pen_width(3.0);
36 }
37 source_node.id()
38 };
39 for transition in state.transitions() {
40 let target_state = transition.target_state();
41 digraph
42 .edge(
43 source_id.clone(),
44 format!("node_{}", target_state.as_usize()),
45 )
46 .attributes()
47 .set_label(
48 &format!("#{}:'{}'", transition.char_class(), transition.ast())
49 .escape_default()
50 .to_string(),
51 );
52 }
53 for epsilon_transition in state.epsilon_transitions() {
54 let target_state = epsilon_transition.target_state();
55 digraph
56 .edge(
57 source_id.clone(),
58 format!("node_{}", target_state.as_usize()),
59 )
60 .attributes()
61 .set_label("ε");
62 }
63 }
64}
65
66fn render_compiled_dfa(
67 compiled_dfa: &CompiledDfa,
68 node_prefix: &str,
69 character_class_registry: &CharacterClassRegistry,
70 graph: &mut Scope,
71) {
72 for id in 0..compiled_dfa.states.len() {
74 let node_name = format!("\"{}{}\"", node_prefix, id);
75 let mut source_node = graph.node_named(&node_name);
76 if id == 0 {
77 source_node
79 .set_shape(dot_writer::Shape::Circle)
80 .set_color(dot_writer::Color::Blue)
81 .set_pen_width(3.0);
82 source_node.set_label(&id.to_string());
83 } else if compiled_dfa.end_states[id].0 {
84 source_node
85 .set_shape(dot_writer::Shape::Circle)
86 .set_color(dot_writer::Color::Red)
87 .set_pen_width(3.0);
88 source_node.set_label(&format!("{} T{}", id, compiled_dfa.end_states[id].1));
89 } else {
90 source_node.set_label(&id.to_string());
91 }
92 }
93 for (id, state) in compiled_dfa.states.iter().enumerate() {
95 for (cc, next) in state.transitions.iter() {
96 graph
98 .edge(
99 format!("\"{}{}\"", node_prefix, id),
100 format!("\"{}{}\"", node_prefix, next.as_usize()),
101 )
102 .attributes()
103 .set_label(&format!(
104 "{} (C#{})",
105 character_class_registry.get_character_class(*cc).map_or(
106 "-".to_string(),
107 |cc| cc.ast().to_string().escape_debug().to_string()
108 ),
109 cc.id()
110 ));
111 }
112 }
113}
114
115pub(crate) fn compiled_dfa_render<W: Write>(
117 compiled_dfa: &CompiledDfa,
118 label: &str,
119 character_class_registry: &CharacterClassRegistry,
120 output: &mut W,
121) {
122 let mut writer = DotWriter::from(output);
123 writer.set_pretty_print(true);
124 let mut digraph = writer.digraph();
125 digraph
126 .set_label(
127 format!(
128 "{}: {}...",
129 label,
130 compiled_dfa.pattern(0.into()).escape_default()
131 )
132 .as_str(),
133 )
134 .set_rank_direction(RankDirection::LeftRight);
135
136 render_compiled_dfa(compiled_dfa, "", character_class_registry, &mut digraph);
137
138 for (terminal_id, lookahead) in compiled_dfa.lookaheads.iter() {
140 let mut cluster = digraph.cluster();
141 cluster.set_label(&format!(
142 "LA for T{}({})",
143 terminal_id,
144 if lookahead.is_positive { "Pos" } else { "Neg" }
145 ));
146 let node_prefix = format!("{}_", terminal_id);
147 render_compiled_dfa(
148 &lookahead.nfa,
149 &node_prefix,
150 character_class_registry,
151 &mut cluster,
152 );
153 }
154}
155
156#[allow(dead_code)]
158pub(crate) fn multi_pattern_nfa_render<W: Write>(
159 multi_pattern_nfa: &MultiPatternNfa,
160 label: &str,
161 character_class_registry: &CharacterClassRegistry,
162 output: &mut W,
163) {
164 let mut writer = DotWriter::from(output);
165 writer.set_pretty_print(true);
166 let mut digraph = writer.digraph();
167 digraph
168 .set_label(label)
169 .set_rank_direction(RankDirection::LeftRight);
170
171 let node_ids = multi_pattern_nfa.nfas.iter().fold(vec![0], |mut acc, nfa| {
172 nfa.states()
173 .iter()
174 .for_each(|state| acc.push(state.id().id()));
175 acc
176 });
177
178 let mut epsilon_transitions = multi_pattern_nfa.start_transitions().iter().fold(
180 Vec::<(usize, usize)>::new(),
181 |mut acc, t| {
182 acc.push((0, t.target_state().as_usize()));
183 acc
184 },
185 );
186 multi_pattern_nfa.nfas.iter().for_each(|nfa| {
188 nfa.states().iter().for_each(|state| {
189 state.epsilon_transitions().iter().for_each(|t| {
190 epsilon_transitions.push((state.id().as_usize(), t.target_state().as_usize()));
191 });
192 });
193 });
194
195 let transitions = multi_pattern_nfa.nfas.iter().fold(
197 Vec::<(StateID, StateID, CharClassID)>::new(),
198 |mut acc, nfa| {
199 nfa.states().iter().for_each(|state| {
200 state.transitions().iter().for_each(|t| {
201 acc.push((state.id(), t.target_state(), t.char_class()));
202 });
203 });
204 acc
205 },
206 );
207
208 for node_id in node_ids {
209 let mut source_node = digraph.node_auto();
210 source_node.set_label(&node_id.to_string());
211 if node_id == 0 {
212 source_node
213 .set_shape(dot_writer::Shape::Circle)
214 .set_color(dot_writer::Color::Blue)
215 .set_pen_width(3.0);
216 }
217 if multi_pattern_nfa.is_accepting_state(node_id.into()) {
218 source_node
219 .set_shape(dot_writer::Shape::Circle)
220 .set_color(dot_writer::Color::Red)
221 .set_pen_width(3.0);
222 }
223 }
224
225 for (source_id, target_id) in epsilon_transitions {
226 digraph
227 .edge(format!("node_{}", source_id), format!("node_{}", target_id))
228 .attributes()
229 .set_label("ε");
230 }
231
232 for (source_id, target_id, char_class_id) in transitions {
233 digraph
234 .edge(
235 format!("node_{}", source_id.as_usize()),
236 format!("node_{}", target_id.as_usize()),
237 )
238 .attributes()
239 .set_label(&format!(
240 "{}:{}",
241 character_class_registry
242 .get_character_class(char_class_id)
243 .map_or("-".to_string(), |cc| cc
244 .ast()
245 .to_string()
246 .escape_default()
247 .to_string()),
248 char_class_id.id()
249 ));
250 }
251}