Skip to content

Instantly share code, notes, and snippets.

@ochaochaocha3
Last active June 24, 2017 16:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ochaochaocha3/0db14de62b57b4a318798faf497914b1 to your computer and use it in GitHub Desktop.
Save ochaochaocha3/0db14de62b57b4a318798faf497914b1 to your computer and use it in GitHub Desktop.
指定したキーワードからDodontoFServer.rb内のコールグラフを作るスクリプト
IDENTIFIER_PATTERN = /[A-Za-z_][A-Za-z0-9_]*/
FUNC_PATTERN = /\Adef\s*((?:DodontoFServer|self)\.)?(#{IDENTIFIER_PATTERN})/o
class DodontoFMethod < Struct.new(:name, :decl_line, :lines, :class_method)
def to_s
"<#{self.class_method ? 'class_method' : 'method'} #{self.name}>"
end
end
dodontof_methods = []
current_method = nil
File.open('DodontoFServer.rb') do |f|
n_line = 0
loop do
line = f.gets
n_line += 1
break if line.start_with?('class DodontoFServer')
end
end_level = 0
state = :class
state_proc = {
class: lambda { |line|
m = line.lstrip.match(FUNC_PATTERN)
next :class unless m
method_name = m[2]
class_method = !!m[1]
current_method = DodontoFMethod.new(method_name, n_line, [], class_method)
dodontof_methods << current_method
end_level = 0
next :method
},
method: lambda { |line|
if end_level == 0 && /\A\s*end\s*/ === line
next :class
end
current_method.lines << line.rstrip
case line
when /\A\s*end\b/
end_level -= 1
next :class if end_level < 0
#syn match rubyControl "\<\%(case\|begin\|do\|for\|if\|unless\|while\|until\|else\|elsif\|ensure\|then\|when\|end\)\>[?!]\@!"
when /\A\s*(?:if|unless|case|while|until)\b/, /\b(?:begin|do)\b/
end_level += 1
end
next :method
}
}
loop do
line = f.gets
n_line += 1
break if /\Aend\s*$/ === line
state = state_proc[state][line]
end
end
class CallTree < Struct.new(:query, :parents, :visited_methods, :methods, :line_nums)
def walk!
corresponding_method = methods.find { |method| method.name == query }
self.visited_methods << corresponding_method if corresponding_method
targets = self.methods - self.visited_methods
containing_methods = targets.reduce([]) { |acc, method|
line_nums = method.lines.each.with_index(1).reduce([]) { |acc_line_nums, (line, i)|
line.include?(self.query) ? (acc_line_nums + [method.decl_line + i]) : acc_line_nums
}
line_nums.length > 0 ? (acc + [[method, line_nums]]) : acc
}
containing_methods.each do |method, line_nums|
parent_tree = CallTree.new(method.name, [], self.visited_methods, targets, line_nums)
self.parents << parent_tree
parent_tree.walk!
end
end
def print_tree
def print_inner(call_tree, level)
print(' ' * level)
print('<- ') if level > 0
print(call_tree.query)
unless call_tree.line_nums.empty?
line_nums_str = call_tree.line_nums.map { |n| "L#{n}" }.join(', ')
print(" (#{line_nums_str})")
end
puts
call_tree.parents.each do |parent|
print_inner(parent, level + 1)
end
end
print_inner(self, 0)
end
end
ARGV.each_with_index do |keyword, i|
puts if i > 0
puts("[#{keyword}]")
leaf = CallTree.new(keyword, [], [], dodontof_methods, [])
leaf.walk!
leaf.print_tree
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment