175 lines
5.5 KiB
Ruby
175 lines
5.5 KiB
Ruby
#!/usr/bin/ruby
|
|
# encoding: utf-8
|
|
|
|
=begin LICENSE
|
|
|
|
[The "BSD licence"]
|
|
Copyright (c) 2009-2010 Kyle Yetter
|
|
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
|
|
|
|
require 'antlr3'
|
|
require 'erb'
|
|
|
|
module ANTLR3
|
|
|
|
=begin rdoc ANTLR3::DOT
|
|
|
|
An extra utility for generating graphviz DOT file representations of ANTLR
|
|
Abstract Syntax Tree nodes.
|
|
|
|
This module has been directly ported to Ruby from the ANTLR Python runtime
|
|
library.
|
|
|
|
=end
|
|
|
|
module DOT
|
|
class Context
|
|
def []=( var, value )
|
|
instance_variable_set( :"@#{ var }", value )
|
|
end
|
|
def []( var )
|
|
instance_variable_get( :"@#{ var }" )
|
|
end
|
|
|
|
def initialize( template, vars = {} )
|
|
@__template__ = template
|
|
vars.each do |var, value|
|
|
self[ var ] = value
|
|
end
|
|
end
|
|
|
|
def to_s
|
|
@__template__.result( binding )
|
|
end
|
|
end
|
|
class TreeGenerator
|
|
TREE_TEMPLATE = ERB.new( Util.tidy( <<-END ) )
|
|
| digraph {
|
|
| ordering=out;
|
|
| ranksep=.4;
|
|
| node [shape=plaintext, fixedsize=true, fontsize=11, fontname="Courier",
|
|
| width=.25, height=.25];
|
|
| edge [arrowsize=.5];
|
|
| <%= @nodes.join("\n ") %>
|
|
| <%= @edges.join("\n ") %>
|
|
| }
|
|
END
|
|
|
|
NODE_TEMPLATE = ERB.new( Util.tidy( <<-END ) )
|
|
| <%= @name %> [label="<%= @text %>"];
|
|
END
|
|
|
|
EDGE_TEMPLATE = ERB.new( Util.tidy( <<-END ) )
|
|
| <%= @parent %> -> <%= @child %>; // "<%= @parent_text %>" -> "<%= @child_text %>"
|
|
END
|
|
|
|
def self.generate( tree, adaptor = nil, tree_template = TREE_TEMPLATE,
|
|
edge_template = EDGE_TEMPLATE )
|
|
new.to_dot( tree, adaptor, tree_template, edge_template )
|
|
end
|
|
|
|
def initialize
|
|
@node_number = 0
|
|
@node_to_number_map = Hash.new do |map, node|
|
|
map[ node ] = @node_number
|
|
@node_number += 1
|
|
@node_number - 1
|
|
end
|
|
end
|
|
|
|
def to_dot( tree, adaptor = nil, tree_template = TREE_TEMPLATE,
|
|
edge_template = EDGE_TEMPLATE )
|
|
adaptor ||= AST::CommonTreeAdaptor.new
|
|
@node_number = 0
|
|
tree_template = Context.new( tree_template, :nodes => [], :edges => [] )
|
|
define_nodes( tree, adaptor, tree_template )
|
|
|
|
@node_number = 0
|
|
define_edges( tree, adaptor, tree_template, edge_template )
|
|
return tree_template.to_s
|
|
end
|
|
|
|
def define_nodes( tree, adaptor, tree_template, known_nodes = nil )
|
|
known_nodes ||= Set.new
|
|
tree.nil? and return
|
|
n = adaptor.child_count( tree )
|
|
n == 0 and return
|
|
number = node_number( tree )
|
|
unless known_nodes.include?( number )
|
|
parent_node_template = node_template_for( adaptor, child )
|
|
tree_template[ :nodes ] << parent_node_template
|
|
known_nodes.add( number )
|
|
end
|
|
|
|
n.times do |index|
|
|
child = adaptor.child_of( tree, index )
|
|
number = @node_to_number_map[ child ]
|
|
unless known_nodes.include?( number )
|
|
node_template = node_template_for( adaptor, child )
|
|
tree_template[ :nodes ] << node_template
|
|
known_nodes.add( number )
|
|
end
|
|
|
|
define_nodes( child, adaptor, tree_template, edge_template )
|
|
end
|
|
end
|
|
|
|
def define_edges( tree, adaptor, tree_template, edge_template )
|
|
tree.nil? or return
|
|
|
|
n = adaptor.child_count( tree )
|
|
n == 0 and return
|
|
|
|
parent_name = 'n%i' % @node_to_number_map[ tree ]
|
|
parent_text = adaptor.text_of( tree )
|
|
n.times do |index|
|
|
child = adaptor.child_of( tree, index )
|
|
child_text = adaptor.text_of( child )
|
|
child_name = 'n%i' % @node_to_number_map[ tree ]
|
|
edge_template = Context.new( edge_template,
|
|
:parent => parent_name, :child => child_name,
|
|
:parent_text => parent_text, :child_text => child_text
|
|
)
|
|
tree_template[ :edges ] << edge_template
|
|
define_edges( child, adaptor, tree_template, edge_template )
|
|
end
|
|
end
|
|
|
|
def node_template_for( adaptor, tree )
|
|
text = adaptor.text_of( tree )
|
|
node_template = Context.new( NODE_TEMPLATE )
|
|
unique_name = 'n%i' % @node_to_number_map[ tree ]
|
|
node_template[ :name ] = unique_name
|
|
text and text = text.gsub( /"/, '\\"' )
|
|
node_template[ :text ] = text
|
|
return node_template
|
|
end
|
|
end
|
|
end
|
|
end
|