| """ @package antlr3.dottreegenerator |
| @brief ANTLR3 runtime package, tree module |
| |
| This module contains all support classes for AST construction and tree parsers. |
| |
| """ |
| |
| # begin[licence] |
| # |
| # [The "BSD licence"] |
| # Copyright (c) 2005-2008 Terence Parr |
| # All rights reserved. |
| # |
| # Redistribution and use in source and binary forms, with or without |
| # modification, are permitted provided that the following conditions |
| # are met: |
| # 1. Redistributions of source code must retain the above copyright |
| # notice, this list of conditions and the following disclaimer. |
| # 2. Redistributions in binary form must reproduce the above copyright |
| # notice, this list of conditions and the following disclaimer in the |
| # documentation and/or other materials provided with the distribution. |
| # 3. The name of the author may not be used to endorse or promote products |
| # derived from this software without specific prior written permission. |
| # |
| # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
| # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
| # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| # |
| # end[licence] |
| |
| # lot's of docstrings are missing, don't complain for now... |
| # pylint: disable-msg=C0111 |
| |
| from antlr3.tree import CommonTreeAdaptor |
| import stringtemplate3 |
| |
| class DOTTreeGenerator(object): |
| """ |
| A utility class to generate DOT diagrams (graphviz) from |
| arbitrary trees. You can pass in your own templates and |
| can pass in any kind of tree or use Tree interface method. |
| """ |
| |
| _treeST = stringtemplate3.StringTemplate( |
| template=( |
| "digraph {\n" + |
| " ordering=out;\n" + |
| " ranksep=.4;\n" + |
| " node [shape=plaintext, fixedsize=true, fontsize=11, fontname=\"Courier\",\n" + |
| " width=.25, height=.25];\n" + |
| " edge [arrowsize=.5]\n" + |
| " $nodes$\n" + |
| " $edges$\n" + |
| "}\n") |
| ) |
| |
| _nodeST = stringtemplate3.StringTemplate( |
| template="$name$ [label=\"$text$\"];\n" |
| ) |
| |
| _edgeST = stringtemplate3.StringTemplate( |
| template="$parent$ -> $child$ // \"$parentText$\" -> \"$childText$\"\n" |
| ) |
| |
| def __init__(self): |
| ## Track node to number mapping so we can get proper node name back |
| self.nodeToNumberMap = {} |
| |
| ## Track node number so we can get unique node names |
| self.nodeNumber = 0 |
| |
| |
| def toDOT(self, tree, adaptor=None, treeST=_treeST, edgeST=_edgeST): |
| if adaptor is None: |
| adaptor = CommonTreeAdaptor() |
| |
| treeST = treeST.getInstanceOf() |
| |
| self.nodeNumber = 0 |
| self.toDOTDefineNodes(tree, adaptor, treeST) |
| |
| self.nodeNumber = 0 |
| self.toDOTDefineEdges(tree, adaptor, treeST, edgeST) |
| return treeST |
| |
| |
| def toDOTDefineNodes(self, tree, adaptor, treeST, knownNodes=None): |
| if knownNodes is None: |
| knownNodes = set() |
| |
| if tree is None: |
| return |
| |
| n = adaptor.getChildCount(tree) |
| if n == 0: |
| # must have already dumped as child from previous |
| # invocation; do nothing |
| return |
| |
| # define parent node |
| number = self.getNodeNumber(tree) |
| if number not in knownNodes: |
| parentNodeST = self.getNodeST(adaptor, tree) |
| treeST.setAttribute("nodes", parentNodeST) |
| knownNodes.add(number) |
| |
| # for each child, do a "<unique-name> [label=text]" node def |
| for i in range(n): |
| child = adaptor.getChild(tree, i) |
| |
| number = self.getNodeNumber(child) |
| if number not in knownNodes: |
| nodeST = self.getNodeST(adaptor, child) |
| treeST.setAttribute("nodes", nodeST) |
| knownNodes.add(number) |
| |
| self.toDOTDefineNodes(child, adaptor, treeST, knownNodes) |
| |
| |
| def toDOTDefineEdges(self, tree, adaptor, treeST, edgeST): |
| if tree is None: |
| return |
| |
| n = adaptor.getChildCount(tree) |
| if n == 0: |
| # must have already dumped as child from previous |
| # invocation; do nothing |
| return |
| |
| parentName = "n%d" % self.getNodeNumber(tree) |
| |
| # for each child, do a parent -> child edge using unique node names |
| parentText = adaptor.getText(tree) |
| for i in range(n): |
| child = adaptor.getChild(tree, i) |
| childText = adaptor.getText(child) |
| childName = "n%d" % self.getNodeNumber(child) |
| edgeST = edgeST.getInstanceOf() |
| edgeST.setAttribute("parent", parentName) |
| edgeST.setAttribute("child", childName) |
| edgeST.setAttribute("parentText", parentText) |
| edgeST.setAttribute("childText", childText) |
| treeST.setAttribute("edges", edgeST) |
| self.toDOTDefineEdges(child, adaptor, treeST, edgeST) |
| |
| |
| def getNodeST(self, adaptor, t): |
| text = adaptor.getText(t) |
| nodeST = self._nodeST.getInstanceOf() |
| uniqueName = "n%d" % self.getNodeNumber(t) |
| nodeST.setAttribute("name", uniqueName) |
| if text is not None: |
| text = text.replace('"', r'\\"') |
| nodeST.setAttribute("text", text) |
| return nodeST |
| |
| |
| def getNodeNumber(self, t): |
| try: |
| return self.nodeToNumberMap[t] |
| except KeyError: |
| self.nodeToNumberMap[t] = self.nodeNumber |
| self.nodeNumber += 1 |
| return self.nodeNumber - 1 |
| |
| |
| def toDOT(tree, adaptor=None, treeST=DOTTreeGenerator._treeST, edgeST=DOTTreeGenerator._edgeST): |
| """ |
| Generate DOT (graphviz) for a whole tree not just a node. |
| For example, 3+4*5 should generate: |
| |
| digraph { |
| node [shape=plaintext, fixedsize=true, fontsize=11, fontname="Courier", |
| width=.4, height=.2]; |
| edge [arrowsize=.7] |
| "+"->3 |
| "+"->"*" |
| "*"->4 |
| "*"->5 |
| } |
| |
| Return the ST not a string in case people want to alter. |
| |
| Takes a Tree interface object. |
| |
| Example of invokation: |
| |
| import antlr3 |
| import antlr3.extras |
| |
| input = antlr3.ANTLRInputStream(sys.stdin) |
| lex = TLexer(input) |
| tokens = antlr3.CommonTokenStream(lex) |
| parser = TParser(tokens) |
| tree = parser.e().tree |
| print tree.toStringTree() |
| st = antlr3.extras.toDOT(t) |
| print st |
| |
| """ |
| |
| gen = DOTTreeGenerator() |
| return gen.toDOT(tree, adaptor, treeST, edgeST) |