Class: Dependencytree::TreeInterpreter
- Inherits:
-
Object
- Object
- Dependencytree::TreeInterpreter
- Defined in:
- lib/dependencytree/treeinterpreter.rb
Overview
Interprets AST trees from the Ruby parser and maintains a list of seen classes, modules, constants and methods.
Instance Attribute Summary collapse
-
#classes_and_modules ⇒ Object
readonly
Returns the value of attribute classes_and_modules.
Instance Method Summary collapse
-
#_casgn(node) ⇒ Object
Handle a def expression.
-
#_class(node) ⇒ Object
Handle a class expression.
-
#_const(node) ⇒ Object
Handle a const expression.
-
#_def(node) ⇒ Object
Handle a def expression.
-
#_handle_class_module_common(type, name, node) ⇒ Object
Handle the common parts of a module or class definition.
-
#_module(node) ⇒ Object
Handle a module expression.
-
#_resolve(full_name) ⇒ Object
Finds a class or module by its full name.
-
#flatten_const_tree(node) ⇒ Object
Make an array of strings out of a encapsulated tree of :const expressions.
-
#initialize ⇒ TreeInterpreter
constructor
A new instance of TreeInterpreter.
-
#top_of_stack ⇒ Object
Gets the top of stack class/module or Kernel if nothing set.
-
#visit(path, tree) ⇒ Object
Visits all children of the AST tree.
-
#visit_children(children) ⇒ Object
Visit all children of a node.
-
#visit_node(node) ⇒ Object
Visit a AST node and do the appropriate actions.
Constructor Details
#initialize ⇒ TreeInterpreter
Returns a new instance of TreeInterpreter.
11 12 13 14 15 16 17 18 19 20 21 22 |
# File 'lib/dependencytree/treeinterpreter.rb', line 11 def initialize # path will be the file system path of the source file @path = nil # context_stack is the stack of modules/classes loaded (namespacing) @context_stack = [] # this is a flat list of all classes / modules seen @classes_and_modules = [] # force adding the Kernel module to the list of classes _handle_class_module_common(:module, "Kernel", nil) @kernel = _resolve("Kernel") end |
Instance Attribute Details
#classes_and_modules ⇒ Object (readonly)
Returns the value of attribute classes_and_modules.
9 10 11 |
# File 'lib/dependencytree/treeinterpreter.rb', line 9 def classes_and_modules @classes_and_modules end |
Instance Method Details
#_casgn(node) ⇒ Object
Handle a def expression.
139 140 141 142 143 144 145 146 147 |
# File 'lib/dependencytree/treeinterpreter.rb', line 139 def _casgn(node) raise ArgumentError, "Children count for casgn is != 3 (#{node.children.length})" if node.children.length != 3 LOG.debug("casgn #{node.children[1]}") top_of_stack.add_constant(node.children[1]) visit_children(node.children[1..-1]) end |
#_class(node) ⇒ Object
Handle a class expression.
80 81 82 83 84 85 86 87 |
# File 'lib/dependencytree/treeinterpreter.rb', line 80 def _class(node) raise ArgumentError, "Children count for class is != 3 (#{node.children.length})" if node.children.length != 3 raise ArgumentError, "First class child needs to be a const (#{node.children[0].type} #{node.children[0].type})" if node.children[0].type != :const LOG.debug("class #{node.children[0].children[1]}") current_class_name = node.children[0].children[1] _handle_class_module_common(:class, current_class_name, node) end |
#_const(node) ⇒ Object
Handle a const expression.
54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/dependencytree/treeinterpreter.rb', line 54 def _const(node) LOG.debug("const") raise ArgumentError, "type needs to be const (#{node.type})" if node.type != :const raise ArgumentError, "Children count needs to be 2 (#{node.children.length})" if node.children.length != 2 reference = flatten_const_tree(node) LOG.debug("Reference to #{reference.to_s}") top_of_stack.add_reference(reference) end |
#_def(node) ⇒ Object
Handle a def expression.
127 128 129 130 131 132 133 134 135 |
# File 'lib/dependencytree/treeinterpreter.rb', line 127 def _def(node) raise ArgumentError, "Children count for def is != 3 (#{node.children.length})" if node.children.length != 3 LOG.debug("def #{node.children[0]}") top_of_stack.add_method(node.children[0]) visit_children(node.children[1..-1]) end |
#_handle_class_module_common(type, name, node) ⇒ Object
Handle the common parts of a module or class definition. Will try to resolve the instance or create it if not found.
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
# File 'lib/dependencytree/treeinterpreter.rb', line 93 def _handle_class_module_common(type, name, node) full_name = name parent = nil if ! @context_stack.empty? parent = @context_stack[-1] full_name = parent.full_name.to_s + "::" + name.to_s end LOG.debug("Full name is #{full_name}") resolved = _resolve(full_name) if ! resolved.nil? # found an existing module/class with the full_name model = resolved else # found no existing module/class with the full_name model = ClassModel.new(type, @path, name) if parent model.set_parent(parent) end @classes_and_modules << model end if resolved.nil? LOG.debug("Created new ClassModel for #{model.full_name}") end @context_stack << model # recurse over the contents of the module visit_children(node.children[1..-1]) if node @context_stack.pop end |
#_module(node) ⇒ Object
Handle a module expression.
68 69 70 71 72 73 74 75 76 |
# File 'lib/dependencytree/treeinterpreter.rb', line 68 def _module(node) raise ArgumentError, "Children count for module is != 2 (#{node.children.length})" if node.children.length != 2 raise ArgumentError, "First module child needs to be a const (#{node.children[0].type} #{node.children[0].type})" if node.children[0].type != :const LOG.debug("module #{node.children[0].children[1]}") current_module_name = node.children[0].children[1] _handle_class_module_common(:module, current_module_name, node) end |
#_resolve(full_name) ⇒ Object
Finds a class or module by its full name.
48 49 50 |
# File 'lib/dependencytree/treeinterpreter.rb', line 48 def _resolve(full_name) @classes_and_modules.find { |clazz| clazz.full_name.to_s == full_name.to_s } end |
#flatten_const_tree(node) ⇒ Object
Make an array of strings out of a encapsulated tree of :const expressions.
35 36 37 38 39 40 41 42 43 44 |
# File 'lib/dependencytree/treeinterpreter.rb', line 35 def flatten_const_tree(node) raise ArgumentError, "type needs to be const (#{node.type})" if node.type != :const raise ArgumentError, "Children count needs to be 2 (#{node.children.length})" if node.children.length != 2 result = node.children[1..1] if node.children[0] && node.children[0].type == :const result = flatten_const_tree(node.children[0]) + result end result end |
#top_of_stack ⇒ Object
Gets the top of stack class/module or Kernel if nothing set.
25 26 27 28 29 30 31 |
# File 'lib/dependencytree/treeinterpreter.rb', line 25 def top_of_stack if @context_stack.empty? @kernel else @context_stack[-1] end end |
#visit(path, tree) ⇒ Object
Visits all children of the AST tree.
180 181 182 183 184 185 186 187 188 189 190 191 |
# File 'lib/dependencytree/treeinterpreter.rb', line 180 def visit(path, tree) begin LOG.debug("Visiting path #{path}") @path = path visit_node(tree) @path = nil rescue Exception => e LOG.error("Error in path #{path}") puts "Error in path #{path}" raise e end end |
#visit_children(children) ⇒ Object
Visit all children of a node. Will call #visit_node on each node child.
170 171 172 173 174 175 |
# File 'lib/dependencytree/treeinterpreter.rb', line 170 def visit_children(children) return if ! children children.each do |child| visit_node(child) if child.respond_to?(:children) end end |
#visit_node(node) ⇒ Object
Visit a AST node and do the appropriate actions.
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/dependencytree/treeinterpreter.rb', line 151 def visit_node(node) case node.type when :const _const(node) when :class _class(node) when :module _module(node) when :def _def(node) when :casgn _casgn(node) else visit_children(node.children) end end |