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