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