1
use itertools::Itertools;
2

            
3
use crate::set_automaton::SetAutomaton;
4
use crate::set_automaton::State;
5
use core::fmt;
6

            
7
use super::MatchAnnouncement;
8
use super::MatchObligation;
9
use super::Transition;
10

            
11
impl<M> fmt::Debug for SetAutomaton<M> {
12
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
13
        write!(f, "{}", self)
14
    }
15
}
16

            
17
/// Implement display for a transition with a term pool
18
impl<M> fmt::Display for Transition<M> {
19
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20
        write!(
21
            f,
22
            "Transition {{ {}, announce: [{}], dest: [{}] }}",
23
            self.symbol,
24
            self.announcements.iter().map(|(x, _)| { x }).format(", "),
25
            self.destinations.iter().format_with(", ", |element, f| {
26
                f(&format_args!("{} -> {}", element.0, element.1))
27
            })
28
        )
29
    }
30
}
31

            
32
/// Implement display for a match announcement
33
impl fmt::Display for MatchAnnouncement {
34
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35
        write!(f, "({})@{}", self.rule, self.position)
36
    }
37
}
38

            
39
impl fmt::Display for MatchObligation {
40
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41
        write!(f, "{}@{}", self.pattern, self.position)
42
    }
43
}
44

            
45
/// Implement display for a state with a term pool
46
impl fmt::Display for State {
47
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48
        writeln!(f, "Label: {}, ", self.label)?;
49
        writeln!(f, "Match goals: [")?;
50
        for m in &self.match_goals {
51
            writeln!(f, "\t {}", m)?;
52
        }
53
        write!(f, "]")
54
    }
55
}
56

            
57
impl<M> fmt::Display for SetAutomaton<M> {
58
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59
        writeln!(f, "States: {{")?;
60

            
61
        for (state_index, s) in self.states.iter().enumerate() {
62
            writeln!(f, "State {} {{\n{}", state_index, s)?;
63

            
64
            writeln!(f, "Transitions: {{")?;
65
            for ((from, _), tr) in self.transitions.iter() {
66
                if state_index == *from {
67
                    writeln!(f, "\t {}", tr)?;
68
                }
69
            }
70
            writeln!(f, "}}")?;
71
        }
72

            
73
        writeln!(f, "}}")
74
    }
75
}
76

            
77
pub struct DotFormatter<'a, M> {
78
    pub(crate) automaton: &'a SetAutomaton<M>,
79
    pub(crate) show_backtransitions: bool,
80
    pub(crate) show_final: bool,
81
}
82

            
83
impl<M> fmt::Display for DotFormatter<'_, M> {
84
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85
        // Write the header anf final states.
86
        writeln!(f, "digraph{{")?;
87

            
88
        if self.show_final {
89
            writeln!(f, "  final[label=\"💩\"];")?;
90
        }
91

            
92
        for (i, s) in self.automaton.states.iter().enumerate() {
93
            let match_goals = s.match_goals.iter().format_with("\\n", |goal, f| {
94
                f(&format_args!("{}", html_escape::encode_safe(&format!("{}", goal))))
95
            });
96

            
97
            writeln!(
98
                f,
99
                "  s{}[shape=record label=\"{{{{s{} | {}}} | {}}}\"]",
100
                i, i, s.label, match_goals
101
            )?;
102
        }
103

            
104
        for ((i, _), tr) in &self.automaton.transitions {
105
            let announcements = tr.announcements.iter().format_with(", ", |(announcement, _), f| {
106
                f(&format_args!("{}@{}", announcement.rule.rhs, announcement.position))
107
            });
108

            
109
            if tr.destinations.is_empty() {
110
                if self.show_final {
111
                    writeln!(f, "  s{} -> final [label=\"{} \\[{}\\]\"]", i, tr.symbol, announcements)?;
112
                }
113
            } else {
114
                writeln!(f, "  \"s{}{}\" [shape=point]", i, tr.symbol,).unwrap();
115
                writeln!(
116
                    f,
117
                    "  s{} -> \"s{}{}\" [label=\"{} \\[{}\\]\"]",
118
                    i, i, tr.symbol, tr.symbol, announcements
119
                )?;
120

            
121
                for (pos, des) in &tr.destinations {
122
                    if self.show_backtransitions || *des != 0 {
123
                        // Hide backpointers to the initial state.
124
                        writeln!(f, "  \"s{}{}\" -> s{} [label = \"{}\"]", i, tr.symbol, des, pos)?;
125
                    }
126
                }
127
            }
128
        }
129
        writeln!(f, "}}")
130
    }
131
}