Home | History | Annotate | Download | only in antlr3
      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