-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdotty.go
135 lines (124 loc) · 3.57 KB
/
dotty.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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package cords
import (
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"testing"
)
type nodeids struct {
idTable map[*cordNode]int
max int
}
func newtable() nodeids {
return nodeids{
idTable: make(map[*cordNode]int),
max: 1,
}
}
func (ids nodeids) find(node *cordNode) int {
return ids.idTable[node]
}
func (ids *nodeids) alloc(node *cordNode) int {
if id := ids.find(node); id > 0 {
return id
}
ids.idTable[node] = ids.max
ids.max++
return ids.max - 1
}
// Cord2Dot outputs the internal structure of a Cord in Graphviz DOT format
// (for debugging purposes). Outputs to writer `w`.
//
func Cord2Dot(text Cord, w io.Writer) {
io.WriteString(w, "strict digraph {\n")
io.WriteString(w, "\tnode [fontname=Arial,fontsize=12];\n")
ids := newtable()
nodelist, edgelist := "", ""
err := text.each(func(node *cordNode, pos uint64, depth int) error {
ID := ids.alloc(node)
styles := nodeDotStyles(node, node.IsLeaf(), false)
if node.IsLeaf() {
leaf := node.AsLeaf()
strstart(leaf)
label := fmt.Sprintf("%d @%d\\n“%s”", node.Weight(), pos, strstart(leaf))
nodelist += fmt.Sprintf("\"%d\" [label=\"%s\" %s];\n", ID, label, styles)
} else {
inner := node.AsInner()
if inner.Left() == nil {
nilid := ID + 10000
nodelist += fmt.Sprintf("\"%d\" %s;\n", nilid, emptyNode(nilid))
edgelist += fmt.Sprintf("\"%d\" -> \"%d\";\n", ID, nilid)
} else {
edgelist += fmt.Sprintf("\"%d\" -> \"%d\";\n", ID, ids.find(inner.left))
}
if inner.Right() == nil {
nilid := ID + 10000
nodelist += fmt.Sprintf("\"%d\" %s;\n", nilid, emptyNode(nilid))
edgelist += fmt.Sprintf("\"%d\" -> \"%d\";\n", ID, nilid)
} else {
_ = ids.alloc(inner.right)
edgelist += fmt.Sprintf("\"%d\" -> \"%d\";\n", ID, ids.find(inner.right))
}
nodelist += fmt.Sprintf("\"%d\" [label=%d %s];\n", ID, node.Weight(), styles)
}
return nil
})
if err != nil {
tracer().Errorf("cord DOT: %s", err.Error())
}
io.WriteString(w, nodelist)
io.WriteString(w, edgelist)
io.WriteString(w, "}\n")
}
// Dotty is a helper for testing. It writes the internal representation of a Cord
// to an SVG image file in the current directory. If an error occurs, `t.Error(…)`
// will be called and the test fails.
//
func Dotty(text Cord, t *testing.T) {
tmpfile, err := ioutil.TempFile(".", "cord.*.dot")
if err != nil {
t.Error(err)
return
}
defer func() {
tmpfile.Close()
os.Remove(tmpfile.Name()) // clean up
}()
t.Logf("writing Cord digraph to %s\n", tmpfile.Name())
Cord2Dot(text, tmpfile)
outOption := fmt.Sprintf("-o%s.svg", tmpfile.Name())
cmd := exec.Command("dot", "-Tsvg", outOption, tmpfile.Name())
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
t.Log("writing SVG cord tree\n")
if err := cmd.Run(); err != nil {
t.Error(err.Error())
}
}
func emptyNode(id int) string {
s := "[label=\"\",color=black,shape=circle,fixedsize=true,width=.4]"
//s = fmt.Sprintf(s, id)
return s
}
func nodeDotStyles(node *cordNode, isleaf bool, highlight bool) string {
s := ",style=filled"
if isleaf {
//s += ",fillcolor=\"#a3d7e4\""
s += ",shape=box"
} else {
s += ",color=black,fillcolor=\"#a3d7e4\""
s += ",shape=circle"
}
// if highlight {
// s = s + fmt.Sprintf(",fillcolor=\"%s\"", hexhlcolors[node.pathcnt])
// } else {
// s = s + fmt.Sprintf(",fillcolor=\"%s\"", hexcolors[node.pathcnt])
// }
return s
}
var hexhlcolors = [...]string{"#FFEEDD", "#FFDDCC", "#FFCCAA", "#FFBB88", "#FFAA66",
"#FF9944", "#FF8822", "#FF7700", "#ff6600"}
var hexcolors = [...]string{"white", "#CCDDFF", "#AACCFF", "#88BBFF", "#66AAFF",
"#4499FF", "#2288FF", "#0077FF", "#0066FF"}