code_node 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/cog/templates/code_node/generator.rb.erb +2 -2
- data/lib/code_node.rb +10 -46
- data/lib/code_node/graph_builder.rb +102 -0
- data/lib/code_node/ir/graph.rb +2 -182
- data/lib/code_node/ir/graph/builder_methods.rb +113 -0
- data/lib/code_node/ir/graph/template_methods.rb +97 -0
- data/lib/code_node/ir/node.rb +18 -212
- data/lib/code_node/ir/node/builder_methods.rb +83 -0
- data/lib/code_node/ir/node/query_methods.rb +55 -0
- data/lib/code_node/ir/node/template_methods.rb +73 -0
- data/lib/code_node/sexp_walker.rb +35 -26
- data/lib/code_node/spec_helpers/dot_file.rb +10 -12
- data/lib/code_node/version.rb +1 -1
- metadata +10 -4
@@ -1,5 +1,5 @@
|
|
1
|
-
<% if
|
2
|
-
<% else %>require '<%=
|
1
|
+
<% if Cog.active_tool.explicit_require %>require '<%= Cog.active_tool.path %>'
|
2
|
+
<% else %>require '<%= Cog.active_tool.name %>'
|
3
3
|
<% end %>
|
4
4
|
|
5
5
|
# Makes a graph of the project_source_path.
|
data/lib/code_node.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
$LOAD_PATH << File.join(File.dirname(__FILE__))
|
2
2
|
require 'code_node/version'
|
3
3
|
require 'code_node/dsl'
|
4
|
+
require 'code_node/graph_builder'
|
4
5
|
require 'code_node/ir'
|
5
6
|
require 'code_node/sexp_walker'
|
6
7
|
require 'cog'
|
@@ -13,57 +14,20 @@ module CodeNode
|
|
13
14
|
|
14
15
|
# @param graph_name [String] name of the dot file to generate (without +.dot+ extension)
|
15
16
|
# @option opt [Symbol] :ruby_version (:ruby19) either <tt>:ruby18</tt> or <tt>:ruby19</tt>, indicating which parser to use
|
16
|
-
# @
|
17
|
+
# @yieldparam graph [DSL::GraphDefiner] define rules for creating the graph
|
17
18
|
def self.graph(graph_name, opt={}, &block)
|
18
|
-
|
19
|
-
root = Cog.project_source_path
|
20
|
-
@graph = IR::Graph.new
|
21
|
-
graph_definer = DSL::GraphDefiner.new @graph
|
22
|
-
block.call graph_definer
|
23
|
-
|
24
|
-
rp = case opt[:ruby_version]
|
25
|
-
when :ruby18
|
19
|
+
parser = if opt[:ruby_version] == :ruby18
|
26
20
|
Ruby18Parser.new
|
27
21
|
else
|
28
22
|
Ruby19Parser.new
|
29
23
|
end
|
30
24
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
rescue Racc::ParseError
|
38
|
-
STDERR.write "#{filename.relative_to_project_root}, skipped...\n".color(:yellow)
|
39
|
-
nil
|
40
|
-
end
|
41
|
-
if sexp[i]
|
42
|
-
walker = SexpWalker.new @graph, sexp[i], :mode => mode
|
43
|
-
walker.walk
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
# Apply styles before pruning because some relations may be destroyed while pruning
|
49
|
-
puts "Applying styles".color(feedback_color)
|
50
|
-
@graph.apply_styles
|
51
|
-
|
52
|
-
# Prune the graph according to ignore rules.
|
53
|
-
# We keep pruning until there are no more changes because some rules don't apply the first time (for example: &:island?)
|
54
|
-
puts "Pruning nodes".color(feedback_color)
|
55
|
-
i = 1
|
56
|
-
while (x = @graph.prune) > 0
|
57
|
-
puts " #{x} nodes pruned on #{i.ordinalize} pass".color(feedback_color)
|
58
|
-
i += 1
|
59
|
-
end
|
60
|
-
|
61
|
-
# Activate code_node while rendering templates
|
62
|
-
# so that cog will be able to find code_node templates
|
63
|
-
Cog.activate_tool 'code_node' do
|
64
|
-
stamp 'code_node/graph.dot', "#{graph_name}.dot"
|
65
|
-
end
|
66
|
-
|
67
|
-
nil
|
25
|
+
GraphBuilder.new(graph_name, parser).
|
26
|
+
define(&block).
|
27
|
+
find_nodes.
|
28
|
+
find_relations.
|
29
|
+
finalize.
|
30
|
+
render
|
68
31
|
end
|
32
|
+
|
69
33
|
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'cog'
|
2
|
+
require 'code_node/ir/graph'
|
3
|
+
|
4
|
+
module CodeNode
|
5
|
+
|
6
|
+
# Helps to build an {IR::Graph}
|
7
|
+
class GraphBuilder
|
8
|
+
|
9
|
+
# @return [IR::Graph] the graph being built
|
10
|
+
attr_reader :graph
|
11
|
+
|
12
|
+
# @param name [String] the name of the graph
|
13
|
+
# @param parser [Ruby18Parser, Ruby19Parser] a ruby parser instance
|
14
|
+
def initialize(name, parser)
|
15
|
+
@name = name
|
16
|
+
@graph = IR::Graph.new
|
17
|
+
@parser = parser
|
18
|
+
@sexp = [] # array of file sexp, one per file
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [Array<String>] paths to ruby source files
|
22
|
+
def sources
|
23
|
+
Dir.glob("#{Cog.project_source_path}/**/*.rb")
|
24
|
+
end
|
25
|
+
|
26
|
+
# Define the custom graph generation rules
|
27
|
+
# @return [self]
|
28
|
+
def define(&block)
|
29
|
+
block.call DSL::GraphDefiner.new(@graph)
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
# Search the sources for nodes
|
34
|
+
# @return [self]
|
35
|
+
def find_nodes
|
36
|
+
puts '1st pass: find nodes'
|
37
|
+
find :nodes
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
# Search the sources for relations
|
42
|
+
# @return [nil]
|
43
|
+
def find_relations
|
44
|
+
puts '2nd pass: find relations'
|
45
|
+
find :relations
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
# Apply styles and prune the graph
|
50
|
+
# @return [nil]
|
51
|
+
def finalize
|
52
|
+
# Apply styles before pruning because some relations may be destroyed while pruning
|
53
|
+
puts "Applying styles"
|
54
|
+
@graph.apply_styles
|
55
|
+
|
56
|
+
# Prune the graph according to ignore rules.
|
57
|
+
# We keep pruning until there are no more changes because some rules don't apply the first time (for example: &:island?)
|
58
|
+
puts "Pruning nodes"
|
59
|
+
i = 1
|
60
|
+
while (x = @graph.prune) > 0
|
61
|
+
puts " #{x} nodes pruned on #{i.ordinalize} pass"
|
62
|
+
i += 1
|
63
|
+
end
|
64
|
+
self
|
65
|
+
end
|
66
|
+
|
67
|
+
# Render the graph
|
68
|
+
# @return [nil]
|
69
|
+
def render
|
70
|
+
# Activate code_node while rendering templates
|
71
|
+
# so that cog will be able to find code_node templates
|
72
|
+
Cog.activate_tool 'code_node' do
|
73
|
+
stamp 'code_node/graph.dot', "#{@name}.dot"
|
74
|
+
end
|
75
|
+
nil
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
# @param type [Symbol] one of <tt>:nodes</tt> or <tt>:relations</tt>
|
81
|
+
# @return [nil]
|
82
|
+
def find(type)
|
83
|
+
sources.each_with_index do |filename, i|
|
84
|
+
@sexp[i] ||= parse filename
|
85
|
+
if @sexp[i]
|
86
|
+
walker = SexpWalker.new @graph, @sexp[i], :mode => "find_#{type}".to_sym
|
87
|
+
walker.walk
|
88
|
+
end
|
89
|
+
end
|
90
|
+
nil
|
91
|
+
end
|
92
|
+
|
93
|
+
# @param filename [String] path to the file to parse
|
94
|
+
# @return [Sexp]
|
95
|
+
def parse(filename)
|
96
|
+
@parser.parse(File.read filename)
|
97
|
+
rescue Racc::ParseError
|
98
|
+
STDERR.write "#{filename.relative_to_project_root}, skipped...\n".color(:yellow)
|
99
|
+
nil
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
data/lib/code_node/ir/graph.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'code_node/ir/node'
|
2
|
+
require 'code_node/ir/graph/builder_methods'
|
3
|
+
require 'code_node/ir/graph/template_methods'
|
2
4
|
|
3
5
|
module CodeNode
|
4
6
|
module IR
|
@@ -6,188 +8,6 @@ module CodeNode
|
|
6
8
|
# A collection of {Node}s
|
7
9
|
class Graph
|
8
10
|
|
9
|
-
# {Graph} methods which are useful in templates
|
10
|
-
module TemplateMethods
|
11
|
-
|
12
|
-
# Iterate through each {Node} with {Node::QueryMethods#class?} in the graph
|
13
|
-
# @yieldparam node [Node::TemplateMethods] a class node. Does not yield ignored nodes.
|
14
|
-
# @return [nil]
|
15
|
-
def each_class(&block)
|
16
|
-
@nodes.values.select do |node|
|
17
|
-
node.class?
|
18
|
-
end.sort.each &block
|
19
|
-
end
|
20
|
-
|
21
|
-
# Iterate through each {Node} with {Node::QueryMethods#module?} in the graph
|
22
|
-
# @yieldparam node [Node::TemplateMethods] a module node. Does not yield ignored nodes.
|
23
|
-
# @return [nil]
|
24
|
-
def each_module(&block)
|
25
|
-
@nodes.values.select do |node|
|
26
|
-
node.module?
|
27
|
-
end.sort.each &block
|
28
|
-
end
|
29
|
-
|
30
|
-
# Iterate through each containment relation in the graph
|
31
|
-
# @example
|
32
|
-
# # a -> b (A contains B)
|
33
|
-
# module A
|
34
|
-
# module B
|
35
|
-
# end
|
36
|
-
# end
|
37
|
-
# @yieldparam a [Node::TemplateMethods] the container node
|
38
|
-
# @yieldparam b [Node::TemplateMethods] the contained node
|
39
|
-
# @return [nil]
|
40
|
-
def each_containment(&block)
|
41
|
-
@nodes.values.sort.each do |node|
|
42
|
-
if node.parent
|
43
|
-
block.call node.parent, node
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
# Iterate through each inheritance relation in the graph
|
49
|
-
# @example
|
50
|
-
# # a -> b (A inherits from B)
|
51
|
-
# class A < B
|
52
|
-
# end
|
53
|
-
# @yieldparam a [Node::TemplateMethods] the derived class node
|
54
|
-
# @yieldparam b [Node::TemplateMethods] the super class node
|
55
|
-
# @return [nil]
|
56
|
-
def each_inheritance(&block)
|
57
|
-
@nodes.values.sort.each do |node|
|
58
|
-
if node.super_class_node
|
59
|
-
block.call node, node.super_class_node
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
# Iterate through each inclusion relation in the graph
|
65
|
-
# @example
|
66
|
-
# # a -> b (A includes B)
|
67
|
-
# module A
|
68
|
-
# include B
|
69
|
-
# end
|
70
|
-
# @yieldparam a [Node::TemplateMethods] the which includes
|
71
|
-
# @yieldparam b [Node::TemplateMethods] the included node
|
72
|
-
# @return [nil]
|
73
|
-
def each_inclusion(&block)
|
74
|
-
@nodes.values.sort.each do |node|
|
75
|
-
node.inclusions.each do |other|
|
76
|
-
block.call node, other
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
# Iterate through each extension relation in the graph
|
82
|
-
# @example
|
83
|
-
# # a -> b (A extends B)
|
84
|
-
# module A
|
85
|
-
# extend B
|
86
|
-
# end
|
87
|
-
# @yieldparam a [Node::TemplateMethods] the which extends
|
88
|
-
# @yieldparam b [Node::TemplateMethods] the extended node
|
89
|
-
# @return [nil]
|
90
|
-
def each_extension(&block)
|
91
|
-
@nodes.values.sort.each do |node|
|
92
|
-
node.extensions.each do |other|
|
93
|
-
block.call node, other
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
end
|
99
|
-
|
100
|
-
# {Graph} methods used during the graph building phase
|
101
|
-
# @api developer
|
102
|
-
module BuilderMethods
|
103
|
-
|
104
|
-
attr_reader :scope
|
105
|
-
|
106
|
-
def apply_styles
|
107
|
-
@nodes.each_value do |node|
|
108
|
-
@style_matchers.each do |pair|
|
109
|
-
if pair[0].matches? node
|
110
|
-
node.style.update pair[1]
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
# @return [FixNum] were any more nodes pruned?
|
117
|
-
def prune
|
118
|
-
prunees = []
|
119
|
-
@nodes.each_value do |node|
|
120
|
-
if @exclude_matchers.any? {|m| m.matches? node}
|
121
|
-
prunees << node
|
122
|
-
end
|
123
|
-
end
|
124
|
-
prunees.each do |node|
|
125
|
-
puts " #{node.path}"
|
126
|
-
node.prune
|
127
|
-
@nodes.delete node.path
|
128
|
-
end
|
129
|
-
prunees.length
|
130
|
-
end
|
131
|
-
|
132
|
-
# Find a node or create it and add it to the graph
|
133
|
-
# @api developer
|
134
|
-
# @param node_type [Symbol] either <tt>:module</tt> or <tt>:class</tt>
|
135
|
-
# @param s [Symbol, Sexp] either flat name, or a Sexp representing a color (<tt>:</tt>) separated path.
|
136
|
-
# @yieldparam node [Node]
|
137
|
-
# @return [Node]
|
138
|
-
def node_for(node_type, s, opt={}, &block)
|
139
|
-
name = if s.is_a? Symbol
|
140
|
-
s
|
141
|
-
elsif s[0] == :const
|
142
|
-
s[1]
|
143
|
-
elsif s[0] == :colon2
|
144
|
-
x = []
|
145
|
-
while s[0] == :colon2
|
146
|
-
x << s[2] ; s = s[1]
|
147
|
-
end
|
148
|
-
x << s[1]
|
149
|
-
x.reverse
|
150
|
-
elsif s[0] == :self
|
151
|
-
@scope.last.mark_as_singleton
|
152
|
-
nil
|
153
|
-
end
|
154
|
-
return if name.nil?
|
155
|
-
|
156
|
-
node = if opt[:not_sure_if_nested]
|
157
|
-
candidate = if name.is_a?(Array)
|
158
|
-
parts = name.dup
|
159
|
-
n = !@scope.empty? && @scope.last.find(parts.shift)
|
160
|
-
while n && !parts.empty?
|
161
|
-
n = n.find parts.shift
|
162
|
-
end
|
163
|
-
n
|
164
|
-
else
|
165
|
-
!@scope.empty? && @scope.last.find(name)
|
166
|
-
end
|
167
|
-
candidate || Node.new(name, :node_type => node_type)
|
168
|
-
else
|
169
|
-
Node.new name, :parent => @scope.last, :node_type => node_type
|
170
|
-
end
|
171
|
-
|
172
|
-
node = self << node
|
173
|
-
unless block.nil? || node.nil?
|
174
|
-
@scope << node
|
175
|
-
block.call node
|
176
|
-
@scope.pop
|
177
|
-
end
|
178
|
-
node
|
179
|
-
end
|
180
|
-
|
181
|
-
# Add the given node to the graph and return it. If a node with the same path is already in the graph, do not add it again, and return the original node.
|
182
|
-
# @param node [Node] a node to add to the graph
|
183
|
-
# @return [Node] the newly added node, or another node with the same path which was already in the graph
|
184
|
-
def <<(node)
|
185
|
-
@nodes[node.path] ||= node
|
186
|
-
@nodes[node.path]
|
187
|
-
end
|
188
|
-
|
189
|
-
end
|
190
|
-
|
191
11
|
include BuilderMethods
|
192
12
|
include TemplateMethods
|
193
13
|
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module CodeNode
|
2
|
+
module IR
|
3
|
+
class Graph
|
4
|
+
|
5
|
+
# {Graph} methods used during the graph building phase
|
6
|
+
# @api developer
|
7
|
+
module BuilderMethods
|
8
|
+
|
9
|
+
attr_reader :scope
|
10
|
+
|
11
|
+
def apply_styles
|
12
|
+
@nodes.each_value do |node|
|
13
|
+
@style_matchers.each do |pair|
|
14
|
+
if pair[0].matches? node
|
15
|
+
node.style.update pair[1]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [FixNum] were any more nodes pruned?
|
22
|
+
def prune
|
23
|
+
prunees = []
|
24
|
+
@nodes.each_value do |node|
|
25
|
+
if @exclude_matchers.any? {|m| m.matches? node}
|
26
|
+
prunees << node
|
27
|
+
end
|
28
|
+
end
|
29
|
+
prunees.each do |node|
|
30
|
+
puts " #{node.path}"
|
31
|
+
node.prune
|
32
|
+
@nodes.delete node.path
|
33
|
+
end
|
34
|
+
prunees.length
|
35
|
+
end
|
36
|
+
|
37
|
+
# Find a node or create it and add it to the graph
|
38
|
+
# @api developer
|
39
|
+
# @param node_type [Symbol] either <tt>:module</tt> or <tt>:class</tt>
|
40
|
+
# @param s [Symbol, Sexp] either flat name, or a Sexp representing a color (<tt>:</tt>) separated path.
|
41
|
+
# @option opt [Boolean] :not_sure_if_nested (false)
|
42
|
+
# @yieldparam node [Node]
|
43
|
+
# @return [Node]
|
44
|
+
def node_for(node_type, s, opt={}, &block)
|
45
|
+
name = determine_name s
|
46
|
+
return if name.nil?
|
47
|
+
|
48
|
+
node = if opt[:not_sure_if_nested]
|
49
|
+
search_for_node_in_parent(@scope.last, name) ||
|
50
|
+
Node.new(name, :node_type => node_type)
|
51
|
+
else
|
52
|
+
Node.new name, :parent => @scope.last, :node_type => node_type
|
53
|
+
end
|
54
|
+
|
55
|
+
node = add_or_find_duplicate node
|
56
|
+
unless block.nil?
|
57
|
+
@scope << node
|
58
|
+
block.call node
|
59
|
+
@scope.pop
|
60
|
+
end
|
61
|
+
node
|
62
|
+
end
|
63
|
+
|
64
|
+
# Add the given node to the graph and return it. If a node with the same path is already in the graph, do not add it again, and return the original node.
|
65
|
+
# @param node [Node] a node to add to the graph
|
66
|
+
# @return [Node] the newly added node, or another node with the same path which was already in the graph
|
67
|
+
def add_or_find_duplicate(node)
|
68
|
+
@nodes[node.path] ||= node
|
69
|
+
@nodes[node.path]
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
# @param s [Sexp]
|
75
|
+
# @return [Symbol, Array<Symbol>, nil]
|
76
|
+
def determine_name(s)
|
77
|
+
name = if s.is_a? Symbol
|
78
|
+
s
|
79
|
+
elsif s[0] == :const
|
80
|
+
s[1]
|
81
|
+
elsif s[0] == :colon2
|
82
|
+
x = []
|
83
|
+
while s[0] == :colon2
|
84
|
+
x << s[2] ; s = s[1]
|
85
|
+
end
|
86
|
+
x << s[1]
|
87
|
+
x.reverse
|
88
|
+
elsif s[0] == :self
|
89
|
+
@scope.last.mark_as_singleton
|
90
|
+
nil
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# @param parent [Node, nil] the parent to search in
|
95
|
+
# @param name [Array<Symbol>, Symbol] name of the node to search for
|
96
|
+
# @return [Node, nil] the node, if found in the parent
|
97
|
+
def search_for_node_in_parent(parent, name)
|
98
|
+
if name.is_a?(Array)
|
99
|
+
parts = name.dup
|
100
|
+
n = parent && parent.find(parts.shift)
|
101
|
+
while n && !parts.empty?
|
102
|
+
n = n.find parts.shift
|
103
|
+
end
|
104
|
+
n
|
105
|
+
else
|
106
|
+
parent && parent.find(name)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module CodeNode
|
2
|
+
module IR
|
3
|
+
class Graph
|
4
|
+
|
5
|
+
# {Graph} methods which are useful in templates
|
6
|
+
module TemplateMethods
|
7
|
+
|
8
|
+
# Iterate through each {Node} with {Node::QueryMethods#class?} in the graph
|
9
|
+
# @yieldparam node [Node::TemplateMethods] a class node. Does not yield ignored nodes.
|
10
|
+
# @return [nil]
|
11
|
+
def each_class(&block)
|
12
|
+
@nodes.values.select do |node|
|
13
|
+
node.class?
|
14
|
+
end.sort.each &block
|
15
|
+
end
|
16
|
+
|
17
|
+
# Iterate through each {Node} with {Node::QueryMethods#module?} in the graph
|
18
|
+
# @yieldparam node [Node::TemplateMethods] a module node. Does not yield ignored nodes.
|
19
|
+
# @return [nil]
|
20
|
+
def each_module(&block)
|
21
|
+
@nodes.values.select do |node|
|
22
|
+
node.module?
|
23
|
+
end.sort.each &block
|
24
|
+
end
|
25
|
+
|
26
|
+
# Iterate through each containment relation in the graph
|
27
|
+
# @example
|
28
|
+
# # a -> b (A contains B)
|
29
|
+
# module A
|
30
|
+
# module B
|
31
|
+
# end
|
32
|
+
# end
|
33
|
+
# @yieldparam a [Node::TemplateMethods] the container node
|
34
|
+
# @yieldparam b [Node::TemplateMethods] the contained node
|
35
|
+
# @return [nil]
|
36
|
+
def each_containment(&block)
|
37
|
+
@nodes.values.sort.each do |node|
|
38
|
+
if node.parent
|
39
|
+
block.call node.parent, node
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Iterate through each inheritance relation in the graph
|
45
|
+
# @example
|
46
|
+
# # a -> b (A inherits from B)
|
47
|
+
# class A < B
|
48
|
+
# end
|
49
|
+
# @yieldparam a [Node::TemplateMethods] the derived class node
|
50
|
+
# @yieldparam b [Node::TemplateMethods] the super class node
|
51
|
+
# @return [nil]
|
52
|
+
def each_inheritance(&block)
|
53
|
+
@nodes.values.sort.each do |node|
|
54
|
+
if node.super_class_node
|
55
|
+
block.call node, node.super_class_node
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Iterate through each inclusion relation in the graph
|
61
|
+
# @example
|
62
|
+
# # a -> b (A includes B)
|
63
|
+
# module A
|
64
|
+
# include B
|
65
|
+
# end
|
66
|
+
# @yieldparam a [Node::TemplateMethods] the which includes
|
67
|
+
# @yieldparam b [Node::TemplateMethods] the included node
|
68
|
+
# @return [nil]
|
69
|
+
def each_inclusion(&block)
|
70
|
+
@nodes.values.sort.each do |node|
|
71
|
+
node.inclusions.each do |other|
|
72
|
+
block.call node, other
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Iterate through each extension relation in the graph
|
78
|
+
# @example
|
79
|
+
# # a -> b (A extends B)
|
80
|
+
# module A
|
81
|
+
# extend B
|
82
|
+
# end
|
83
|
+
# @yieldparam a [Node::TemplateMethods] the which extends
|
84
|
+
# @yieldparam b [Node::TemplateMethods] the extended node
|
85
|
+
# @return [nil]
|
86
|
+
def each_extension(&block)
|
87
|
+
@nodes.values.sort.each do |node|
|
88
|
+
node.extensions.each do |other|
|
89
|
+
block.call node, other
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
data/lib/code_node/ir/node.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
require 'cog'
|
2
|
+
require 'code_node/ir/node/builder_methods'
|
3
|
+
require 'code_node/ir/node/template_methods'
|
4
|
+
require 'code_node/ir/node/query_methods'
|
2
5
|
|
3
6
|
module CodeNode
|
4
7
|
module IR
|
@@ -8,210 +11,6 @@ module CodeNode
|
|
8
11
|
class Node
|
9
12
|
|
10
13
|
include Cog::Generator
|
11
|
-
|
12
|
-
# Node methods which are useful in templates
|
13
|
-
module TemplateMethods
|
14
|
-
|
15
|
-
# @return [String] the name of the node. Not necessarilly unique.
|
16
|
-
# @see {#path}
|
17
|
-
attr_reader :name
|
18
|
-
|
19
|
-
# @return [Node] node which contains this node
|
20
|
-
# @example
|
21
|
-
# module Foo # Foo is the parent
|
22
|
-
# module Bar # to Bar
|
23
|
-
# end
|
24
|
-
# end
|
25
|
-
attr_reader :parent
|
26
|
-
|
27
|
-
# The child nodes of this node
|
28
|
-
# @return [Hash<String,Node>] a mapping from node {#path} names to nodes
|
29
|
-
# @example
|
30
|
-
# module Foo # Foo has children
|
31
|
-
# module Bar # Bar
|
32
|
-
# end # and
|
33
|
-
# class Car # Car
|
34
|
-
# end
|
35
|
-
# end
|
36
|
-
attr_reader :children
|
37
|
-
|
38
|
-
# @return [Node,nil] the super class of this class. Will be +nil+ for modules.
|
39
|
-
def super_class_node
|
40
|
-
@inherits_from
|
41
|
-
end
|
42
|
-
|
43
|
-
# @return [Array<Node>] module nodes for which this node has an +include+ statement
|
44
|
-
def inclusions
|
45
|
-
@includes.values.sort
|
46
|
-
end
|
47
|
-
|
48
|
-
# @return [Array<Node>] module nodes for which this node has an +extend+ statement
|
49
|
-
def extensions
|
50
|
-
@extends.values.sort
|
51
|
-
end
|
52
|
-
|
53
|
-
# @return [String] fully qualified identifier for the node in the form <tt>Foo_Bar_Car</tt>. Ideal for graphviz identifiers.
|
54
|
-
def key
|
55
|
-
@path.join '_'
|
56
|
-
end
|
57
|
-
|
58
|
-
# @return [String] how the node will be labelled in the graph. Nodes without parents display their full {#path}, while nodes with parents only display their {#name}.
|
59
|
-
def label
|
60
|
-
@parent.nil? ? path : name
|
61
|
-
end
|
62
|
-
|
63
|
-
# Stamp the accumulated GraphViz styles in a format suitable for inclusion in a <tt>.dot</tt> file
|
64
|
-
# @return [String] style in the form <tt>key1="value1" key2="value2"</tt>...
|
65
|
-
def stamp_styles
|
66
|
-
x = []
|
67
|
-
style.each_pair do |key, value|
|
68
|
-
x << "#{key}=\"#{value}\""
|
69
|
-
end
|
70
|
-
x.join ' '
|
71
|
-
end
|
72
|
-
|
73
|
-
end
|
74
|
-
|
75
|
-
# {Node} methods which are useful for querying in matchers
|
76
|
-
module QueryMethods
|
77
|
-
|
78
|
-
# @return [Boolean] does this node represent a module?
|
79
|
-
def module?
|
80
|
-
@node_type == :module
|
81
|
-
end
|
82
|
-
|
83
|
-
# @return [Boolean] does this node represent a class?
|
84
|
-
def class?
|
85
|
-
@node_type == :class
|
86
|
-
end
|
87
|
-
|
88
|
-
# @return [String] fully qualified name of the node in the form <tt>'Foo::Bar::Car'</tt>. Not good as a graphviz identifier because of the colon (<tt>:</tt>) characters. Use {TemplateMethods#key} for graphviz identifiers instead.
|
89
|
-
def path
|
90
|
-
@path.join '::'
|
91
|
-
end
|
92
|
-
|
93
|
-
# @param path [String] a class or module path in the form <tt>Foo::Bar::Car</tt>
|
94
|
-
# @return [Boolean] does this node include a {#module?} node with the given {#path}?
|
95
|
-
def includes?(path)
|
96
|
-
@includes.member? path
|
97
|
-
end
|
98
|
-
|
99
|
-
# @param path [String] a class or module path in the form <tt>Foo::Bar::Car</tt>
|
100
|
-
# @return [Boolean] does this node extend a {#module?} node with the given {#path}?
|
101
|
-
def extends?(path)
|
102
|
-
@extends.member? path
|
103
|
-
end
|
104
|
-
|
105
|
-
# @param path [String] a class or module path in the form <tt>Foo::Bar::Car</tt>
|
106
|
-
# @return [Boolean] does this node inherit from (directly or indirectly) a {#class?} node with the given {#path}? Note that a node inherits from itself according to this method. Recursively checks the ancestry of the node.
|
107
|
-
def inherits_from?(path)
|
108
|
-
# TODO: need process all nodes first, marking for deletion on the first pass, because the superclass gets deleting and then the inherits from relation breaks down
|
109
|
-
self.path == path || @inherits_from && @inherits_from.inherits_from?(path)
|
110
|
-
end
|
111
|
-
|
112
|
-
# @return [Boolean] whether or not this node represents a singleton module. A singleton module is one which contains an <tt>extend self</tt> statement.
|
113
|
-
def singleton?
|
114
|
-
@singleton
|
115
|
-
end
|
116
|
-
|
117
|
-
# @return [Boolean] whether or not this node is an island. An island is a node with no connections to other nodes.
|
118
|
-
def island?
|
119
|
-
([@parent, @inherits_from].all?(&:nil?) &&
|
120
|
-
[@children, @inherited_by, @extends, @includes, @extended_by, @included_by].all?(&:empty?))
|
121
|
-
end
|
122
|
-
|
123
|
-
end
|
124
|
-
|
125
|
-
# {Node} methods used during the graph building phase
|
126
|
-
# @api developer
|
127
|
-
module BuilderMethods
|
128
|
-
|
129
|
-
attr_reader :style
|
130
|
-
|
131
|
-
# Find a node contained in this node, or contained in this nodes {#parent}, recursively.
|
132
|
-
# @return [Node, nil]
|
133
|
-
def find(name)
|
134
|
-
path = (@path + [name].flatten).join '::'
|
135
|
-
@children[path] || (@parent && @parent.find(name))
|
136
|
-
end
|
137
|
-
|
138
|
-
# Add other as a child of this node
|
139
|
-
# @param other [Node] another node
|
140
|
-
# @return [nil]
|
141
|
-
def contains(other)
|
142
|
-
this = self
|
143
|
-
@children[other.path] = other
|
144
|
-
other.instance_eval {@parent = this}
|
145
|
-
nil
|
146
|
-
end
|
147
|
-
|
148
|
-
# Add other as the super class of this node
|
149
|
-
# @param other [Node] another node
|
150
|
-
# @return [nil]
|
151
|
-
def inherits_from(other)
|
152
|
-
this = self
|
153
|
-
@inherits_from = other
|
154
|
-
other.instance_eval {@inherited_by[this.path] = this}
|
155
|
-
nil
|
156
|
-
end
|
157
|
-
|
158
|
-
# Add other to this nodes includes set
|
159
|
-
# @param other [Node] another node
|
160
|
-
# @return [nil]
|
161
|
-
def includes(other)
|
162
|
-
this = self
|
163
|
-
@includes[other.path] = other
|
164
|
-
other.instance_eval {@included_by[this.path] = this}
|
165
|
-
nil
|
166
|
-
end
|
167
|
-
|
168
|
-
# Add other to this nodes extends set
|
169
|
-
# @param other [Node] another node
|
170
|
-
# @return [nil]
|
171
|
-
def extends(other)
|
172
|
-
this = self
|
173
|
-
@extends[other.path] = other
|
174
|
-
other.instance_eval {@extended_by[this.path] = this}
|
175
|
-
nil
|
176
|
-
end
|
177
|
-
|
178
|
-
# Mark this module node as a singleton
|
179
|
-
# @return [nil]
|
180
|
-
def mark_as_singleton
|
181
|
-
throw :NodeNotAModule unless module?
|
182
|
-
@singleton = true
|
183
|
-
end
|
184
|
-
|
185
|
-
# Remove any relations involving this node
|
186
|
-
def prune
|
187
|
-
this = self
|
188
|
-
if @inherits_from
|
189
|
-
@inherits_from.instance_eval {@inherited_by.delete this.path}
|
190
|
-
end
|
191
|
-
@inherited_by.each_value do |other|
|
192
|
-
other.instance_eval {@inherits_from = nil}
|
193
|
-
end
|
194
|
-
if @parent
|
195
|
-
@parent.instance_eval {@children.delete this.path}
|
196
|
-
end
|
197
|
-
@children.each_value do |other|
|
198
|
-
other.instance_eval {@parent = nil}
|
199
|
-
end
|
200
|
-
@includes.each_value do |other|
|
201
|
-
other.instance_eval {@included_by.delete this.path}
|
202
|
-
end
|
203
|
-
@included_by.each_value do |other|
|
204
|
-
other.instance_eval {@includes.delete this.path}
|
205
|
-
end
|
206
|
-
@extends.each_value do |other|
|
207
|
-
other.instance_eval {@extended_by.delete this.path}
|
208
|
-
end
|
209
|
-
@extended_by.each_value do |other|
|
210
|
-
other.instance_eval {@extends.delete this.path}
|
211
|
-
end
|
212
|
-
end
|
213
|
-
end
|
214
|
-
|
215
14
|
include BuilderMethods
|
216
15
|
include TemplateMethods
|
217
16
|
include QueryMethods
|
@@ -231,14 +30,21 @@ module CodeNode
|
|
231
30
|
parent_path + [name]
|
232
31
|
end
|
233
32
|
@name = @path.last
|
234
|
-
@
|
235
|
-
@
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
33
|
+
@inverse_relation = {} # :rel => :inv_rel
|
34
|
+
@edge = {} # :rel => { 'node::path' => }
|
35
|
+
define_relation :parent, :children
|
36
|
+
define_relation :inherits_from, :inherited_by
|
37
|
+
define_relation :includes, :included_by
|
38
|
+
define_relation :extends, :extended_by
|
39
|
+
end
|
40
|
+
|
41
|
+
# @param rel [Symbol] the name of a relation
|
42
|
+
# @param inv [Symbol] the name of the inverse relation
|
43
|
+
def define_relation(rel, inv)
|
44
|
+
@inverse_relation[rel] = inv
|
45
|
+
@inverse_relation[inv] = rel
|
46
|
+
@edge[rel] = {}
|
47
|
+
@edge[inv] = {}
|
242
48
|
end
|
243
49
|
|
244
50
|
# @return [FixNum] order nodes by {#path}
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module CodeNode
|
2
|
+
module IR
|
3
|
+
class Node
|
4
|
+
|
5
|
+
# {Node} methods used during the graph building phase
|
6
|
+
# @api developer
|
7
|
+
module BuilderMethods
|
8
|
+
|
9
|
+
attr_reader :style
|
10
|
+
|
11
|
+
# Find a node contained in this node, or contained in this nodes {#parent}, recursively.
|
12
|
+
# @return [Node, nil]
|
13
|
+
def find(name)
|
14
|
+
path = (@path + [name].flatten).join '::'
|
15
|
+
children[path] || (parent && parent.find(name))
|
16
|
+
end
|
17
|
+
|
18
|
+
# Add other as a child of this node
|
19
|
+
# @param other [Node] another node
|
20
|
+
# @return [nil]
|
21
|
+
def contains(other)
|
22
|
+
add_edge :children, other
|
23
|
+
end
|
24
|
+
|
25
|
+
# Add other as the super class of this node
|
26
|
+
# @param other [Node] another node
|
27
|
+
# @return [nil]
|
28
|
+
def inherits_from(other)
|
29
|
+
add_edge :inherits_from, other
|
30
|
+
end
|
31
|
+
|
32
|
+
# Add other to this nodes includes set
|
33
|
+
# @param other [Node] another node
|
34
|
+
# @return [nil]
|
35
|
+
def includes(other)
|
36
|
+
add_edge :includes, other
|
37
|
+
end
|
38
|
+
|
39
|
+
# Add other to this nodes extends set
|
40
|
+
# @param other [Node] another node
|
41
|
+
# @return [nil]
|
42
|
+
def extends(other)
|
43
|
+
add_edge :extends, other
|
44
|
+
end
|
45
|
+
|
46
|
+
# Add an edge between this node and another with the given relation type
|
47
|
+
# @param rel [Symbol] the type of relation
|
48
|
+
# @param other [Node] another node
|
49
|
+
# @return [nil]
|
50
|
+
def add_edge(rel, other)
|
51
|
+
this = self
|
52
|
+
inv = @inverse_relation[rel]
|
53
|
+
@edge[rel][other.path] = other
|
54
|
+
other.instance_eval do
|
55
|
+
@edge[inv][this.path] = this
|
56
|
+
end
|
57
|
+
nil
|
58
|
+
end
|
59
|
+
|
60
|
+
# Mark this module node as a singleton
|
61
|
+
# @return [nil]
|
62
|
+
def mark_as_singleton
|
63
|
+
throw :NodeNotAModule unless module?
|
64
|
+
@singleton = true
|
65
|
+
end
|
66
|
+
|
67
|
+
# Remove any relations involving this node
|
68
|
+
def prune
|
69
|
+
this = self
|
70
|
+
@edge.each_pair do |rel, edges|
|
71
|
+
inv = @inverse_relation[rel]
|
72
|
+
edges.each_value do |other|
|
73
|
+
other.instance_eval do
|
74
|
+
@edge[inv].delete this.path
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module CodeNode
|
2
|
+
module IR
|
3
|
+
class Node
|
4
|
+
|
5
|
+
# {Node} methods which are useful for querying in matchers
|
6
|
+
module QueryMethods
|
7
|
+
|
8
|
+
# @return [Boolean] does this node represent a module?
|
9
|
+
def module?
|
10
|
+
@node_type == :module
|
11
|
+
end
|
12
|
+
|
13
|
+
# @return [Boolean] does this node represent a class?
|
14
|
+
def class?
|
15
|
+
@node_type == :class
|
16
|
+
end
|
17
|
+
|
18
|
+
# @return [String] fully qualified name of the node in the form <tt>'Foo::Bar::Car'</tt>. Not good as a graphviz identifier because of the colon (<tt>:</tt>) characters. Use {TemplateMethods#key} for graphviz identifiers instead.
|
19
|
+
def path
|
20
|
+
@path.join '::'
|
21
|
+
end
|
22
|
+
|
23
|
+
# @param path [String] a class or module path in the form <tt>Foo::Bar::Car</tt>
|
24
|
+
# @return [Boolean] does this node include a {#module?} node with the given {#path}?
|
25
|
+
def includes?(path)
|
26
|
+
includes.member? path
|
27
|
+
end
|
28
|
+
|
29
|
+
# @param path [String] a class or module path in the form <tt>Foo::Bar::Car</tt>
|
30
|
+
# @return [Boolean] does this node extend a {#module?} node with the given {#path}?
|
31
|
+
def extends?(path)
|
32
|
+
extends.member? path
|
33
|
+
end
|
34
|
+
|
35
|
+
# @param path [String] a class or module path in the form <tt>Foo::Bar::Car</tt>
|
36
|
+
# @return [Boolean] does this node inherit from (directly or indirectly) a {#class?} node with the given {#path}? Note that a node inherits from itself according to this method. Recursively checks the ancestry of the node.
|
37
|
+
def inherits_from?(path)
|
38
|
+
# TODO: need process all nodes first, marking for deletion on the first pass, because the superclass gets deleting and then the inherits from relation breaks down
|
39
|
+
self.path == path || !@edge[:inherits_from].empty? && @edge[:inherits_from].values.first.inherits_from?(path)
|
40
|
+
end
|
41
|
+
|
42
|
+
# @return [Boolean] whether or not this node represents a singleton module. A singleton module is one which contains an <tt>extend self</tt> statement.
|
43
|
+
def singleton?
|
44
|
+
@singleton
|
45
|
+
end
|
46
|
+
|
47
|
+
# @return [Boolean] whether or not this node is an island. An island is a node with no connections to other nodes.
|
48
|
+
def island?
|
49
|
+
@edge.values.all? &:empty?
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module CodeNode
|
2
|
+
module IR
|
3
|
+
class Node
|
4
|
+
|
5
|
+
# Node methods which are useful in templates
|
6
|
+
module TemplateMethods
|
7
|
+
|
8
|
+
# @return [String] the name of the node. Not necessarilly unique.
|
9
|
+
# @see {#path}
|
10
|
+
attr_reader :name
|
11
|
+
|
12
|
+
# @return [Node] node which contains this node
|
13
|
+
# @example
|
14
|
+
# module Foo # Foo is the parent
|
15
|
+
# module Bar # to Bar
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
def parent
|
19
|
+
@edge[:parent].values.first
|
20
|
+
end
|
21
|
+
|
22
|
+
# The child nodes of this node
|
23
|
+
# @return [Hash<String,Node>] a mapping from node {#path} names to nodes
|
24
|
+
# @example
|
25
|
+
# module Foo # Foo has children
|
26
|
+
# module Bar # Bar
|
27
|
+
# end # and
|
28
|
+
# class Car # Car
|
29
|
+
# end
|
30
|
+
# end
|
31
|
+
def children
|
32
|
+
@edge[:children]
|
33
|
+
end
|
34
|
+
|
35
|
+
# @return [Node,nil] the super class of this class. Will be +nil+ for modules.
|
36
|
+
def super_class_node
|
37
|
+
@edge[:inherits_from].values.first
|
38
|
+
end
|
39
|
+
|
40
|
+
# @return [Array<Node>] module nodes for which this node has an +include+ statement
|
41
|
+
def inclusions
|
42
|
+
@edge[:includes].values.sort
|
43
|
+
end
|
44
|
+
|
45
|
+
# @return [Array<Node>] module nodes for which this node has an +extend+ statement
|
46
|
+
def extensions
|
47
|
+
@edge[:extends].values.sort
|
48
|
+
end
|
49
|
+
|
50
|
+
# @return [String] fully qualified identifier for the node in the form <tt>Foo_Bar_Car</tt>. Ideal for graphviz identifiers.
|
51
|
+
def key
|
52
|
+
@path.join '_'
|
53
|
+
end
|
54
|
+
|
55
|
+
# @return [String] how the node will be labelled in the graph. Nodes without parents display their full {#path}, while nodes with parents only display their {#name}.
|
56
|
+
def label
|
57
|
+
parent.nil? ? path : name
|
58
|
+
end
|
59
|
+
|
60
|
+
# Stamp the accumulated GraphViz styles in a format suitable for inclusion in a <tt>.dot</tt> file
|
61
|
+
# @return [String] style in the form <tt>key1="value1" key2="value2"</tt>...
|
62
|
+
def stamp_styles
|
63
|
+
x = []
|
64
|
+
style.each_pair do |key, value|
|
65
|
+
x << "#{key}=\"#{value}\""
|
66
|
+
end
|
67
|
+
x.join ' '
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -23,39 +23,48 @@ module CodeNode
|
|
23
23
|
def walk(s = nil)
|
24
24
|
s ||= @root
|
25
25
|
if [:module, :class].member?(s[0])
|
26
|
-
|
27
|
-
if find_relations? && s[0] == :class && !s[2].nil?
|
28
|
-
super_node = @graph.node_for :class, s[2], :not_sure_if_nested => true
|
29
|
-
node.inherits_from super_node unless super_node.nil?
|
30
|
-
end
|
31
|
-
rest = s[0] == :module ? s.slice(2..-1) : s.slice(3..-1)
|
32
|
-
rest.each do |c|
|
33
|
-
walk(c) if c.class == Sexp
|
34
|
-
end
|
35
|
-
end
|
36
|
-
unless @graph.scope.empty?
|
37
|
-
@graph.scope.last.contains node
|
38
|
-
end
|
26
|
+
add_node s
|
39
27
|
elsif find_relations? && s[0] == :call && s.length >= 4 && [:extend, :include].member?(s[2]) && !@graph.scope.empty?
|
40
|
-
s
|
41
|
-
node = @graph.node_for :module, mod_sexp, :not_sure_if_nested => true
|
42
|
-
unless node.nil?
|
43
|
-
if s[2] == :extend
|
44
|
-
@graph.scope.last.extends node
|
45
|
-
else
|
46
|
-
@graph.scope.last.includes node
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
28
|
+
add_relation s
|
50
29
|
else
|
51
|
-
s.slice(1..-1)
|
52
|
-
walk(c) if c.class == Sexp
|
53
|
-
end
|
30
|
+
walk_siblings s.slice(1..-1)
|
54
31
|
end
|
55
32
|
end
|
56
33
|
|
57
34
|
private
|
58
35
|
|
36
|
+
def walk_siblings(s)
|
37
|
+
s.each do |c|
|
38
|
+
walk(c) if c.class == Sexp
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def add_node(s)
|
43
|
+
node = @graph.node_for(s[0], s[1]) do |node|
|
44
|
+
if find_relations? && s[0] == :class && !s[2].nil?
|
45
|
+
super_node = @graph.node_for :class, s[2], :not_sure_if_nested => true
|
46
|
+
node.inherits_from super_node unless super_node.nil?
|
47
|
+
end
|
48
|
+
walk_siblings s.slice((s[0] == :module ? 2 : 3)..-1)
|
49
|
+
end
|
50
|
+
unless @graph.scope.empty?
|
51
|
+
@graph.scope.last.contains node
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def add_relation(s)
|
56
|
+
s.slice(3..-1).each do |mod_sexp|
|
57
|
+
node = @graph.node_for :module, mod_sexp, :not_sure_if_nested => true
|
58
|
+
unless node.nil?
|
59
|
+
if s[2] == :extend
|
60
|
+
@graph.scope.last.extends node
|
61
|
+
else
|
62
|
+
@graph.scope.last.includes node
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
59
68
|
# @return [Boolean] whether or not the walker should look for relations
|
60
69
|
def find_relations?
|
61
70
|
@mode == :find_relations
|
@@ -59,27 +59,19 @@ module CodeNode
|
|
59
59
|
end
|
60
60
|
|
61
61
|
def has_containment?(path1, path2)
|
62
|
-
|
63
|
-
key2 = path2.to_s.split('::').join '_'
|
64
|
-
@edges[:containment].member?([key1, key2])
|
62
|
+
has_relation? :containment, path1, path2
|
65
63
|
end
|
66
64
|
|
67
65
|
def has_inheritance?(path1, path2)
|
68
|
-
|
69
|
-
key2 = path2.to_s.split('::').join '_'
|
70
|
-
@edges[:inheritance].member?([key1, key2])
|
66
|
+
has_relation? :inheritance, path1, path2
|
71
67
|
end
|
72
68
|
|
73
69
|
def has_inclusion?(path1, path2)
|
74
|
-
|
75
|
-
key2 = path2.to_s.split('::').join '_'
|
76
|
-
@edges[:inclusion].member?([key1, key2])
|
70
|
+
has_relation? :inclusion, path1, path2
|
77
71
|
end
|
78
72
|
|
79
73
|
def has_extension?(path1, path2)
|
80
|
-
|
81
|
-
key2 = path2.to_s.split('::').join '_'
|
82
|
-
@edges[:extension].member?([key1, key2])
|
74
|
+
has_relation? :extension, path1, path2
|
83
75
|
end
|
84
76
|
|
85
77
|
private
|
@@ -108,6 +100,12 @@ module CodeNode
|
|
108
100
|
end
|
109
101
|
@i += 1
|
110
102
|
end
|
103
|
+
|
104
|
+
def has_relation?(relation, path1, path2)
|
105
|
+
key1 = path1.to_s.split('::').join '_'
|
106
|
+
key2 = path2.to_s.split('::').join '_'
|
107
|
+
@edges[relation].member?([key1, key2])
|
108
|
+
end
|
111
109
|
end
|
112
110
|
|
113
111
|
end
|
data/lib/code_node/version.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: code_node
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 29
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 0.1.
|
9
|
+
- 3
|
10
|
+
version: 0.1.3
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Kevin Tonon
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-12-
|
18
|
+
date: 2012-12-17 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: cog
|
@@ -117,7 +117,13 @@ files:
|
|
117
117
|
- lib/code_node/cog_tool.rb
|
118
118
|
- lib/code_node/dsl/graph_definer.rb
|
119
119
|
- lib/code_node/dsl.rb
|
120
|
+
- lib/code_node/graph_builder.rb
|
121
|
+
- lib/code_node/ir/graph/builder_methods.rb
|
122
|
+
- lib/code_node/ir/graph/template_methods.rb
|
120
123
|
- lib/code_node/ir/graph.rb
|
124
|
+
- lib/code_node/ir/node/builder_methods.rb
|
125
|
+
- lib/code_node/ir/node/query_methods.rb
|
126
|
+
- lib/code_node/ir/node/template_methods.rb
|
121
127
|
- lib/code_node/ir/node.rb
|
122
128
|
- lib/code_node/ir/node_matcher.rb
|
123
129
|
- lib/code_node/ir.rb
|