xref: /aosp_15_r20/external/antlr/runtime/Python/antlr3/dottreegen.py (revision 16467b971bd3e2009fad32dd79016f2c7e421deb)
1*16467b97STreehugger Robot""" @package antlr3.dottreegenerator
2*16467b97STreehugger Robot@brief ANTLR3 runtime package, tree module
3*16467b97STreehugger Robot
4*16467b97STreehugger RobotThis module contains all support classes for AST construction and tree parsers.
5*16467b97STreehugger Robot
6*16467b97STreehugger Robot"""
7*16467b97STreehugger Robot
8*16467b97STreehugger Robot# begin[licence]
9*16467b97STreehugger Robot#
10*16467b97STreehugger Robot# [The "BSD licence"]
11*16467b97STreehugger Robot# Copyright (c) 2005-2008 Terence Parr
12*16467b97STreehugger Robot# All rights reserved.
13*16467b97STreehugger Robot#
14*16467b97STreehugger Robot# Redistribution and use in source and binary forms, with or without
15*16467b97STreehugger Robot# modification, are permitted provided that the following conditions
16*16467b97STreehugger Robot# are met:
17*16467b97STreehugger Robot# 1. Redistributions of source code must retain the above copyright
18*16467b97STreehugger Robot#    notice, this list of conditions and the following disclaimer.
19*16467b97STreehugger Robot# 2. Redistributions in binary form must reproduce the above copyright
20*16467b97STreehugger Robot#    notice, this list of conditions and the following disclaimer in the
21*16467b97STreehugger Robot#    documentation and/or other materials provided with the distribution.
22*16467b97STreehugger Robot# 3. The name of the author may not be used to endorse or promote products
23*16467b97STreehugger Robot#    derived from this software without specific prior written permission.
24*16467b97STreehugger Robot#
25*16467b97STreehugger Robot# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
26*16467b97STreehugger Robot# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27*16467b97STreehugger Robot# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28*16467b97STreehugger Robot# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
29*16467b97STreehugger Robot# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30*16467b97STreehugger Robot# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31*16467b97STreehugger Robot# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32*16467b97STreehugger Robot# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33*16467b97STreehugger Robot# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34*16467b97STreehugger Robot# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35*16467b97STreehugger Robot#
36*16467b97STreehugger Robot# end[licence]
37*16467b97STreehugger Robot
38*16467b97STreehugger Robot# lot's of docstrings are missing, don't complain for now...
39*16467b97STreehugger Robot# pylint: disable-msg=C0111
40*16467b97STreehugger Robot
41*16467b97STreehugger Robotfrom antlr3.tree import CommonTreeAdaptor
42*16467b97STreehugger Robotimport stringtemplate3
43*16467b97STreehugger Robot
44*16467b97STreehugger Robotclass DOTTreeGenerator(object):
45*16467b97STreehugger Robot    """
46*16467b97STreehugger Robot    A utility class to generate DOT diagrams (graphviz) from
47*16467b97STreehugger Robot    arbitrary trees.  You can pass in your own templates and
48*16467b97STreehugger Robot    can pass in any kind of tree or use Tree interface method.
49*16467b97STreehugger Robot    """
50*16467b97STreehugger Robot
51*16467b97STreehugger Robot    _treeST = stringtemplate3.StringTemplate(
52*16467b97STreehugger Robot        template=(
53*16467b97STreehugger Robot        "digraph {\n" +
54*16467b97STreehugger Robot        "  ordering=out;\n" +
55*16467b97STreehugger Robot        "  ranksep=.4;\n" +
56*16467b97STreehugger Robot        "  node [shape=plaintext, fixedsize=true, fontsize=11, fontname=\"Courier\",\n" +
57*16467b97STreehugger Robot        "        width=.25, height=.25];\n" +
58*16467b97STreehugger Robot        "  edge [arrowsize=.5]\n" +
59*16467b97STreehugger Robot        "  $nodes$\n" +
60*16467b97STreehugger Robot        "  $edges$\n" +
61*16467b97STreehugger Robot        "}\n")
62*16467b97STreehugger Robot        )
63*16467b97STreehugger Robot
64*16467b97STreehugger Robot    _nodeST = stringtemplate3.StringTemplate(
65*16467b97STreehugger Robot        template="$name$ [label=\"$text$\"];\n"
66*16467b97STreehugger Robot        )
67*16467b97STreehugger Robot
68*16467b97STreehugger Robot    _edgeST = stringtemplate3.StringTemplate(
69*16467b97STreehugger Robot        template="$parent$ -> $child$ // \"$parentText$\" -> \"$childText$\"\n"
70*16467b97STreehugger Robot        )
71*16467b97STreehugger Robot
72*16467b97STreehugger Robot    def __init__(self):
73*16467b97STreehugger Robot        ## Track node to number mapping so we can get proper node name back
74*16467b97STreehugger Robot        self.nodeToNumberMap = {}
75*16467b97STreehugger Robot
76*16467b97STreehugger Robot        ## Track node number so we can get unique node names
77*16467b97STreehugger Robot        self.nodeNumber = 0
78*16467b97STreehugger Robot
79*16467b97STreehugger Robot
80*16467b97STreehugger Robot    def toDOT(self, tree, adaptor=None, treeST=_treeST, edgeST=_edgeST):
81*16467b97STreehugger Robot        if adaptor is None:
82*16467b97STreehugger Robot            adaptor = CommonTreeAdaptor()
83*16467b97STreehugger Robot
84*16467b97STreehugger Robot        treeST = treeST.getInstanceOf()
85*16467b97STreehugger Robot
86*16467b97STreehugger Robot        self.nodeNumber = 0
87*16467b97STreehugger Robot        self.toDOTDefineNodes(tree, adaptor, treeST)
88*16467b97STreehugger Robot
89*16467b97STreehugger Robot        self.nodeNumber = 0
90*16467b97STreehugger Robot        self.toDOTDefineEdges(tree, adaptor, treeST, edgeST)
91*16467b97STreehugger Robot        return treeST
92*16467b97STreehugger Robot
93*16467b97STreehugger Robot
94*16467b97STreehugger Robot    def toDOTDefineNodes(self, tree, adaptor, treeST, knownNodes=None):
95*16467b97STreehugger Robot        if knownNodes is None:
96*16467b97STreehugger Robot            knownNodes = set()
97*16467b97STreehugger Robot
98*16467b97STreehugger Robot        if tree is None:
99*16467b97STreehugger Robot            return
100*16467b97STreehugger Robot
101*16467b97STreehugger Robot        n = adaptor.getChildCount(tree)
102*16467b97STreehugger Robot        if n == 0:
103*16467b97STreehugger Robot            # must have already dumped as child from previous
104*16467b97STreehugger Robot            # invocation; do nothing
105*16467b97STreehugger Robot            return
106*16467b97STreehugger Robot
107*16467b97STreehugger Robot        # define parent node
108*16467b97STreehugger Robot        number = self.getNodeNumber(tree)
109*16467b97STreehugger Robot        if number not in knownNodes:
110*16467b97STreehugger Robot            parentNodeST = self.getNodeST(adaptor, tree)
111*16467b97STreehugger Robot            treeST.setAttribute("nodes", parentNodeST)
112*16467b97STreehugger Robot            knownNodes.add(number)
113*16467b97STreehugger Robot
114*16467b97STreehugger Robot        # for each child, do a "<unique-name> [label=text]" node def
115*16467b97STreehugger Robot        for i in range(n):
116*16467b97STreehugger Robot            child = adaptor.getChild(tree, i)
117*16467b97STreehugger Robot
118*16467b97STreehugger Robot            number = self.getNodeNumber(child)
119*16467b97STreehugger Robot            if number not in knownNodes:
120*16467b97STreehugger Robot                nodeST = self.getNodeST(adaptor, child)
121*16467b97STreehugger Robot                treeST.setAttribute("nodes", nodeST)
122*16467b97STreehugger Robot                knownNodes.add(number)
123*16467b97STreehugger Robot
124*16467b97STreehugger Robot            self.toDOTDefineNodes(child, adaptor, treeST, knownNodes)
125*16467b97STreehugger Robot
126*16467b97STreehugger Robot
127*16467b97STreehugger Robot    def toDOTDefineEdges(self, tree, adaptor, treeST, edgeST):
128*16467b97STreehugger Robot        if tree is None:
129*16467b97STreehugger Robot            return
130*16467b97STreehugger Robot
131*16467b97STreehugger Robot        n = adaptor.getChildCount(tree)
132*16467b97STreehugger Robot        if n == 0:
133*16467b97STreehugger Robot            # must have already dumped as child from previous
134*16467b97STreehugger Robot            # invocation; do nothing
135*16467b97STreehugger Robot            return
136*16467b97STreehugger Robot
137*16467b97STreehugger Robot        parentName = "n%d" % self.getNodeNumber(tree)
138*16467b97STreehugger Robot
139*16467b97STreehugger Robot        # for each child, do a parent -> child edge using unique node names
140*16467b97STreehugger Robot        parentText = adaptor.getText(tree)
141*16467b97STreehugger Robot        for i in range(n):
142*16467b97STreehugger Robot            child = adaptor.getChild(tree, i)
143*16467b97STreehugger Robot            childText = adaptor.getText(child)
144*16467b97STreehugger Robot            childName = "n%d" % self.getNodeNumber(child)
145*16467b97STreehugger Robot            edgeST = edgeST.getInstanceOf()
146*16467b97STreehugger Robot            edgeST.setAttribute("parent", parentName)
147*16467b97STreehugger Robot            edgeST.setAttribute("child", childName)
148*16467b97STreehugger Robot            edgeST.setAttribute("parentText", parentText)
149*16467b97STreehugger Robot            edgeST.setAttribute("childText", childText)
150*16467b97STreehugger Robot            treeST.setAttribute("edges", edgeST)
151*16467b97STreehugger Robot            self.toDOTDefineEdges(child, adaptor, treeST, edgeST)
152*16467b97STreehugger Robot
153*16467b97STreehugger Robot
154*16467b97STreehugger Robot    def getNodeST(self, adaptor, t):
155*16467b97STreehugger Robot        text = adaptor.getText(t)
156*16467b97STreehugger Robot        nodeST = self._nodeST.getInstanceOf()
157*16467b97STreehugger Robot        uniqueName = "n%d" % self.getNodeNumber(t)
158*16467b97STreehugger Robot        nodeST.setAttribute("name", uniqueName)
159*16467b97STreehugger Robot        if text is not None:
160*16467b97STreehugger Robot            text = text.replace('"', r'\"')
161*16467b97STreehugger Robot        nodeST.setAttribute("text", text)
162*16467b97STreehugger Robot        return nodeST
163*16467b97STreehugger Robot
164*16467b97STreehugger Robot
165*16467b97STreehugger Robot    def getNodeNumber(self, t):
166*16467b97STreehugger Robot        try:
167*16467b97STreehugger Robot            return self.nodeToNumberMap[t]
168*16467b97STreehugger Robot        except KeyError:
169*16467b97STreehugger Robot            self.nodeToNumberMap[t] = self.nodeNumber
170*16467b97STreehugger Robot            self.nodeNumber += 1
171*16467b97STreehugger Robot            return self.nodeNumber - 1
172*16467b97STreehugger Robot
173*16467b97STreehugger Robot
174*16467b97STreehugger Robotdef toDOT(tree, adaptor=None, treeST=DOTTreeGenerator._treeST, edgeST=DOTTreeGenerator._edgeST):
175*16467b97STreehugger Robot    """
176*16467b97STreehugger Robot    Generate DOT (graphviz) for a whole tree not just a node.
177*16467b97STreehugger Robot    For example, 3+4*5 should generate:
178*16467b97STreehugger Robot
179*16467b97STreehugger Robot    digraph {
180*16467b97STreehugger Robot        node [shape=plaintext, fixedsize=true, fontsize=11, fontname="Courier",
181*16467b97STreehugger Robot            width=.4, height=.2];
182*16467b97STreehugger Robot        edge [arrowsize=.7]
183*16467b97STreehugger Robot        "+"->3
184*16467b97STreehugger Robot        "+"->"*"
185*16467b97STreehugger Robot        "*"->4
186*16467b97STreehugger Robot        "*"->5
187*16467b97STreehugger Robot    }
188*16467b97STreehugger Robot
189*16467b97STreehugger Robot    Return the ST not a string in case people want to alter.
190*16467b97STreehugger Robot
191*16467b97STreehugger Robot    Takes a Tree interface object.
192*16467b97STreehugger Robot
193*16467b97STreehugger Robot    Example of invokation:
194*16467b97STreehugger Robot
195*16467b97STreehugger Robot        import antlr3
196*16467b97STreehugger Robot        import antlr3.extras
197*16467b97STreehugger Robot
198*16467b97STreehugger Robot        input = antlr3.ANTLRInputStream(sys.stdin)
199*16467b97STreehugger Robot        lex = TLexer(input)
200*16467b97STreehugger Robot        tokens = antlr3.CommonTokenStream(lex)
201*16467b97STreehugger Robot        parser = TParser(tokens)
202*16467b97STreehugger Robot        tree = parser.e().tree
203*16467b97STreehugger Robot        print tree.toStringTree()
204*16467b97STreehugger Robot        st = antlr3.extras.toDOT(t)
205*16467b97STreehugger Robot        print st
206*16467b97STreehugger Robot
207*16467b97STreehugger Robot    """
208*16467b97STreehugger Robot
209*16467b97STreehugger Robot    gen = DOTTreeGenerator()
210*16467b97STreehugger Robot    return gen.toDOT(tree, adaptor, treeST, edgeST)
211