This repository has been archived by the owner on Dec 3, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
/
grapher.go
93 lines (73 loc) · 2.09 KB
/
grapher.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
package ahocorasick
import (
"fmt"
"io"
"os"
)
// A TrieGrapher is used to output a Trie in the DOT graph description language.
type TrieGrapher struct {
trie *Trie // The Trie to be graphed.
w io.Writer // A writer to print output to.
drawFailLinks bool // Whether to include fail links in the graph.
}
// Create a new TrieGrapher.
func NewTrieGrapher(trie *Trie) *TrieGrapher {
return &TrieGrapher{
trie: trie,
}
}
// Toggle inclusion of fail links in the graph.
func (tg *TrieGrapher) DrawFailLinks(b bool) *TrieGrapher {
tg.drawFailLinks = b
return tg
}
// Output the DOT graph to a file.
func (tg *TrieGrapher) Graph(path string) error {
f, err := os.Create(path)
if err != nil {
return err
}
defer f.Close()
tg.w = f
fmt.Fprintln(f, "digraph T {")
fmt.Fprintln(f, "\tnodesep=0.2; ranksep=0.4; splines=false; outputorder=edgesfirst;")
fmt.Fprintln(f, "\tnode [shape=circle, style=filled, fillcolor=white, fixedsize=true];")
fmt.Fprintln(f, "\tedge [arrowsize=0.5];")
// Will recursivelly call graphState on every state (which is in use).
tg.graphState(RootState, EmptyCell)
fmt.Fprintln(f, "}")
return nil
}
func (tg *TrieGrapher) graphState(s, c int64) {
if tg.trie.dict[s] != 0 {
fmt.Fprintf(tg.w, "\t%d [label=%q, shape=doublecircle];\n", s, label(c))
} else {
fmt.Fprintf(tg.w, "\t%d [label=%q];\n", s, label(c))
}
for c := int64(0); c < AlphabetSize+1; c++ {
t := tg.trie.base[s] + c
if t < int64(len(tg.trie.check)) && tg.trie.check[t] == s {
tg.graphState(t, c)
fmt.Fprintf(tg.w, "\t%d -> %d;\n", s, t)
}
}
if f := tg.trie.fail[s]; tg.drawFailLinks && f != EmptyCell && f != RootState {
fmt.Fprintf(tg.w, "\t%d -> %d [color=red, constraint=false];\n", s, f)
}
if f := tg.trie.suff[s]; f != EmptyCell {
fmt.Fprintf(tg.w, "\t%d -> %d [color=darkgreen, constraint=false];\n", s, f)
}
}
func label(c int64) string {
if c == EmptyCell {
return ""
}
b := DecodeByte(c)
if isAlphaNum(b) {
return fmt.Sprintf("%c", b)
}
return fmt.Sprintf("0x%02x", b)
}
func isAlphaNum(b byte) bool {
return b >= 0x20 && b <= 0x7e
}