1 #!/usr/bin/ruby 2 # encoding: utf-8 3 4 =begin LICENSE 5 6 [The "BSD licence"] 7 Copyright (c) 2009-2010 Kyle Yetter 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 14 1. Redistributions of source code must retain the above copyright 15 notice, this list of conditions and the following disclaimer. 16 2. Redistributions in binary form must reproduce the above copyright 17 notice, this list of conditions and the following disclaimer in the 18 documentation and/or other materials provided with the distribution. 19 3. The name of the author may not be used to endorse or promote products 20 derived from this software without specific prior written permission. 21 22 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 23 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 26 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 27 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 31 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 33 =end 34 35 require 'antlr3' 36 require 'erb' 37 38 module ANTLR3 39 40 =begin rdoc ANTLR3::DOT 41 42 An extra utility for generating graphviz DOT file representations of ANTLR 43 Abstract Syntax Tree nodes. 44 45 This module has been directly ported to Ruby from the ANTLR Python runtime 46 library. 47 48 =end 49 50 module DOT 51 class Context 52 def []=( var, value ) 53 instance_variable_set( :"@#{ var }", value ) 54 end 55 def []( var ) 56 instance_variable_get( :"@#{ var }" ) 57 end 58 59 def initialize( template, vars = {} ) 60 @__template__ = template 61 vars.each do |var, value| 62 self[ var ] = value 63 end 64 end 65 66 def to_s 67 @__template__.result( binding ) 68 end 69 end 70 class TreeGenerator 71 TREE_TEMPLATE = ERB.new( Util.tidy( <<-END ) ) 72 | digraph { 73 | ordering=out; 74 | ranksep=.4; 75 | node [shape=plaintext, fixedsize=true, fontsize=11, fontname="Courier", 76 | width=.25, height=.25]; 77 | edge [arrowsize=.5]; 78 | <%= @nodes.join("\n ") %> 79 | <%= @edges.join("\n ") %> 80 | } 81 END 82 83 NODE_TEMPLATE = ERB.new( Util.tidy( <<-END ) ) 84 | <%= @name %> [label="<%= @text %>"]; 85 END 86 87 EDGE_TEMPLATE = ERB.new( Util.tidy( <<-END ) ) 88 | <%= @parent %> -> <%= @child %>; // "<%= @parent_text %>" -> "<%= @child_text %>" 89 END 90 91 def self.generate( tree, adaptor = nil, tree_template = TREE_TEMPLATE, 92 edge_template = EDGE_TEMPLATE ) 93 new.to_dot( tree, adaptor, tree_template, edge_template ) 94 end 95 96 def initialize 97 @node_number = 0 98 @node_to_number_map = Hash.new do |map, node| 99 map[ node ] = @node_number 100 @node_number += 1 101 @node_number - 1 102 end 103 end 104 105 def to_dot( tree, adaptor = nil, tree_template = TREE_TEMPLATE, 106 edge_template = EDGE_TEMPLATE ) 107 adaptor ||= AST::CommonTreeAdaptor.new 108 @node_number = 0 109 tree_template = Context.new( tree_template, :nodes => [], :edges => [] ) 110 define_nodes( tree, adaptor, tree_template ) 111 112 @node_number = 0 113 define_edges( tree, adaptor, tree_template, edge_template ) 114 return tree_template.to_s 115 end 116 117 def define_nodes( tree, adaptor, tree_template, known_nodes = nil ) 118 known_nodes ||= Set.new 119 tree.nil? and return 120 n = adaptor.child_count( tree ) 121 n == 0 and return 122 number = node_number( tree ) 123 unless known_nodes.include?( number ) 124 parent_node_template = node_template_for( adaptor, child ) 125 tree_template[ :nodes ] << parent_node_template 126 known_nodes.add( number ) 127 end 128 129 n.times do |index| 130 child = adaptor.child_of( tree, index ) 131 number = @node_to_number_map[ child ] 132 unless known_nodes.include?( number ) 133 node_template = node_template_for( adaptor, child ) 134 tree_template[ :nodes ] << node_template 135 known_nodes.add( number ) 136 end 137 138 define_nodes( child, adaptor, tree_template, edge_template ) 139 end 140 end 141 142 def define_edges( tree, adaptor, tree_template, edge_template ) 143 tree.nil? or return 144 145 n = adaptor.child_count( tree ) 146 n == 0 and return 147 148 parent_name = 'n%i' % @node_to_number_map[ tree ] 149 parent_text = adaptor.text_of( tree ) 150 n.times do |index| 151 child = adaptor.child_of( tree, index ) 152 child_text = adaptor.text_of( child ) 153 child_name = 'n%i' % @node_to_number_map[ tree ] 154 edge_template = Context.new( edge_template, 155 :parent => parent_name, :child => child_name, 156 :parent_text => parent_text, :child_text => child_text 157 ) 158 tree_template[ :edges ] << edge_template 159 define_edges( child, adaptor, tree_template, edge_template ) 160 end 161 end 162 163 def node_template_for( adaptor, tree ) 164 text = adaptor.text_of( tree ) 165 node_template = Context.new( NODE_TEMPLATE ) 166 unique_name = 'n%i' % @node_to_number_map[ tree ] 167 node_template[ :name ] = unique_name 168 text and text = text.gsub( /"/, '\\"' ) 169 node_template[ :text ] = text 170 return node_template 171 end 172 end 173 end 174 end 175