1 /*
2  * [The "BSD licence"]
3  * Copyright (c) 2005-2008 Terence Parr
4  * All rights reserved.
5  *
6  * Conversion to C#:
7  * Copyright (c) 2008 Sam Harwell, Pixel Mine, Inc.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 namespace Antlr.Runtime.Tree {
34     using System.Collections.Generic;
35     using StringBuilder = System.Text.StringBuilder;
36 
37     /** A utility class to generate DOT diagrams (graphviz) from
38      *  arbitrary trees.  You can pass in your own templates and
39      *  can pass in any kind of tree or use Tree interface method.
40      *  I wanted this separator so that you don't have to include
41      *  ST just to use the org.antlr.runtime.tree.* package.
42      *  This is a set of non-static methods so you can subclass
43      *  to override.  For example, here is an invocation:
44      *
45      *      CharStream input = new ANTLRInputStream(System.in);
46      *      TLexer lex = new TLexer(input);
47      *      CommonTokenStream tokens = new CommonTokenStream(lex);
48      *      TParser parser = new TParser(tokens);
49      *      TParser.e_return r = parser.e();
50      *      Tree t = (Tree)r.tree;
51      *      System.out.println(t.toStringTree());
52      *      DOTTreeGenerator gen = new DOTTreeGenerator();
53      *      StringTemplate st = gen.toDOT(t);
54      *      System.out.println(st);
55      */
56     public class DotTreeGenerator {
57         readonly string[] HeaderLines =
58             {
59                 "digraph {",
60                 "",
61                 "\tordering=out;",
62                 "\tranksep=.4;",
63                 "\tbgcolor=\"lightgrey\"; node [shape=box, fixedsize=false, fontsize=12, fontname=\"Helvetica-bold\", fontcolor=\"blue\"",
64                 "\t\twidth=.25, height=.25, color=\"black\", fillcolor=\"white\", style=\"filled, solid, bold\"];",
65                 "\tedge [arrowsize=.5, color=\"black\", style=\"bold\"]",
66                 ""
67             };
68         const string Footer = "}";
69         const string NodeFormat = "  {0} [label=\"{1}\"];";
70         const string EdgeFormat = "  {0} -> {1} // \"{2}\" -> \"{3}\"";
71 
72         /** Track node to number mapping so we can get proper node name back */
73         Dictionary<object, int> nodeToNumberMap = new Dictionary<object, int>();
74 
75         /** Track node number so we can get unique node names */
76         int nodeNumber = 0;
77 
78         /** Generate DOT (graphviz) for a whole tree not just a node.
79          *  For example, 3+4*5 should generate:
80          *
81          * digraph {
82          *   node [shape=plaintext, fixedsize=true, fontsize=11, fontname="Courier",
83          *         width=.4, height=.2];
84          *   edge [arrowsize=.7]
85          *   "+"->3
86          *   "+"->"*"
87          *   "*"->4
88          *   "*"->5
89          * }
90          *
91          * Takes a Tree interface object.
92          */
ToDot(object tree, ITreeAdaptor adaptor)93         public virtual string ToDot(object tree, ITreeAdaptor adaptor) {
94             StringBuilder builder = new StringBuilder();
95             foreach (string line in HeaderLines)
96                 builder.AppendLine(line);
97 
98             nodeNumber = 0;
99             var nodes = DefineNodes(tree, adaptor);
100             nodeNumber = 0;
101             var edges = DefineEdges(tree, adaptor);
102 
103             foreach (var s in nodes)
104                 builder.AppendLine(s);
105 
106             builder.AppendLine();
107 
108             foreach (var s in edges)
109                 builder.AppendLine(s);
110 
111             builder.AppendLine();
112 
113             builder.AppendLine(Footer);
114             return builder.ToString();
115         }
116 
ToDot(ITree tree)117         public virtual string ToDot(ITree tree) {
118             return ToDot(tree, new CommonTreeAdaptor());
119         }
DefineNodes(object tree, ITreeAdaptor adaptor)120         protected virtual IEnumerable<string> DefineNodes(object tree, ITreeAdaptor adaptor) {
121             if (tree == null)
122                 yield break;
123 
124             int n = adaptor.GetChildCount(tree);
125             if (n == 0) {
126                 // must have already dumped as child from previous
127                 // invocation; do nothing
128                 yield break;
129             }
130 
131             // define parent node
132             yield return GetNodeText(adaptor, tree);
133 
134             // for each child, do a "<unique-name> [label=text]" node def
135             for (int i = 0; i < n; i++) {
136                 object child = adaptor.GetChild(tree, i);
137                 yield return GetNodeText(adaptor, child);
138                 foreach (var t in DefineNodes(child, adaptor))
139                     yield return t;
140             }
141         }
142 
DefineEdges(object tree, ITreeAdaptor adaptor)143         protected virtual IEnumerable<string> DefineEdges(object tree, ITreeAdaptor adaptor) {
144             if (tree == null)
145                 yield break;
146 
147             int n = adaptor.GetChildCount(tree);
148             if (n == 0) {
149                 // must have already dumped as child from previous
150                 // invocation; do nothing
151                 yield break;
152             }
153 
154             string parentName = "n" + GetNodeNumber(tree);
155 
156             // for each child, do a parent -> child edge using unique node names
157             string parentText = adaptor.GetText(tree);
158             for (int i = 0; i < n; i++) {
159                 object child = adaptor.GetChild(tree, i);
160                 string childText = adaptor.GetText(child);
161                 string childName = "n" + GetNodeNumber(child);
162                 yield return string.Format(EdgeFormat, parentName, childName, FixString(parentText), FixString(childText));
163                 foreach (var t in DefineEdges(child, adaptor))
164                     yield return t;
165             }
166         }
167 
GetNodeText(ITreeAdaptor adaptor, object t)168         protected virtual string GetNodeText(ITreeAdaptor adaptor, object t) {
169             string text = adaptor.GetText(t);
170             string uniqueName = "n" + GetNodeNumber(t);
171             return string.Format(NodeFormat, uniqueName, FixString(text));
172         }
173 
GetNodeNumber(object t)174         protected virtual int GetNodeNumber(object t) {
175             int i;
176             if (nodeToNumberMap.TryGetValue(t, out i)) {
177                 return i;
178             } else {
179                 nodeToNumberMap[t] = nodeNumber;
180                 nodeNumber++;
181                 return nodeNumber - 1;
182             }
183         }
184 
FixString(string text)185         protected virtual string FixString(string text) {
186             if (text != null) {
187                 text = System.Text.RegularExpressions.Regex.Replace(text, "\"", "\\\\\"");
188                 text = System.Text.RegularExpressions.Regex.Replace(text, "\\t", "    ");
189                 text = System.Text.RegularExpressions.Regex.Replace(text, "\\n", "\\\\n");
190                 text = System.Text.RegularExpressions.Regex.Replace(text, "\\r", "\\\\r");
191 
192                 if (text.Length > 20)
193                     text = text.Substring(0, 8) + "..." + text.Substring(text.Length - 8);
194             }
195 
196             return text;
197         }
198     }
199 }
200