module Simplabs module Excellent module Parsing # For most nodes the Excellent processor processes, it will create the corresponding context that contains meta information of the processed # node. This is the base class for all these contexts. # # === Example # # For a method like the following: # # module Shop # class Basket # def buy_product(product) # other_method # end # end # end # # four context will be generated: # # ModuleContext # name: 'Shop' # full_name: 'Shop' # parent: nil # ClassContext # name: 'Basket' # full_name: 'Shop::Basket' # parent: ModuleContext # MethodContext # name: 'buy_product' # full_name: 'Shop::Basket#buy_product' # parent: ClassContext # parameters: [:product] # CallContext (other_method) # name: nil # full_name: nil # parent: MethodContext # method: :other_method # # === Custom Processors # # The Excelent processor will also invoke custom processor methods on the contexts if they are defined. To process call nodes in the context for # example, you could simply define a +process_call+ method in the context that will be invoked with each call Sexp (S-expression, see # http://en.wikipedia.org/wiki/S_expression) that is processed by the Excellent processor. # # def process_call(exp) # super # do_something() # end # # Custom processor methods must always call +super+ since there might be several processor methods defined in several modules that are in the # included in the context and all of these have to be invoked. Also processor methods must not modify the passed Sexp since other processor # methods also need the complete Sexp. If you have to modify the Sexp in a processor method, deep clone it: # # exp = exp.deep_clone # class SexpContext # The parent context attr_reader :parent # The name of the code fragment the context is bound to (e.g. 'User' for a +class+) attr_reader :name # The file the code fragment was read from attr_reader :file # The line the code fragment is located at attr_reader :line # Initializes a SexpContext. # # Always call +super+ in inherited custom contexts! # # === Parameters # # * exp - The Sexp (S-expression, see http://en.wikipedia.org/wiki/S_expression) the context is created for. # * parent - The parent context. def initialize(exp, parent = nil) @exp = exp @parent = parent @file = exp.file @line = exp.line @full_name = nil end # Gets the full name of the code fragment the context is bound to. For a method +name+ might be '+add_product+' while +full_name+ might be # 'Basket#add_product'. def full_name return @full_name if @full_name return @name if !@parent "#{@parent.full_name}::#{@name}" end def method_missing(method, *args) #:nodoc: return if method.to_s =~ /^process_[a-zA-Z0-9_]+$/ super end private def count_lines(node = @exp, line_numbers = []) count = 0 line_numbers << node.line node.children.each { |child| count += count_lines(child, line_numbers) } line_numbers.uniq.length end def has_assignment?(exp = @exp[1]) found_assignment = false found_assignment = found_assignment || exp.node_type == :lasgn exp.children.each { |child| found_assignment = found_assignment || has_assignment?(child) } found_assignment end def resolve_colon(exp) scope = if exp[1].node_type == :colon2 resolve_colon(exp[1]) elsif exp[1].node_type == :const exp[1][1] end "#{scope.to_s}::#{exp[2].to_s}" end end end end end