Class: Laser::Analysis::Sexp

Overview

Replaces the ParseTree Sexps by adding a few handy-dandy methods.

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from ModuleExtensions

attr_accessor_with_default, cattr_accessor, cattr_accessor_with_default, cattr_get_and_setter, cattr_reader, cattr_writer, opposite_method

Methods included from Laser::Analysis::SexpExtensions::TypeInference

#expr_type

Methods included from Laser::Analysis::SexpExtensions::SourceLocation

#backtrack_expecting!, #backtrack_searching, #forwardtrack_searching, #line_number, #source_begin, #source_end, #text_at

Methods included from Laser::Analysis::SexpExtensions::ConstantExtraction

#constant_value, #is_constant

Methods inherited from Array

#&, #*, #+, #-, #<<, #<=>, #==, [], #[], #[]=, #abbrev, #assoc, #at, #clear, #collect, #collect!, #combination, #compact, #compact!, #concat, #count, #cycle, #delete, #delete_at, #delete_if, #drop, #drop_while, #each, #each_index, #empty?, #eql?, #fetch, #fill, #find_index, #first, #flatten, #flatten!, #frozen?, #hash, #include?, #index, #insert, #inspect, #join, #keep_if, #last, #map, #map!, new, #pack, #permutation, #pop, #product, #push, #rassoc, #reject, #reject!, #repeated_combination, #repeated_permutation, #replace, #reverse, #reverse!, #reverse_each, #rindex, #rotate, #rotate!, #sample, #select, #select!, #shift, #shuffle, #shuffle!, #size, #slice, #slice!, #sort, #sort!, #sort_by!, #take, #take_while, #to_a, #to_ary, #to_s, #transpose, #uniq, #uniq!, #unshift, #values_at, #zip, #|

Constructor Details

#initialize(other, file_name = nil, file_source = nil) ⇒ Sexp

Initializes the Sexp with the contents of the array returned by Ripper.



16
17
18
19
20
21
22
23
24
# File 'lib/laser/analysis/sexp.rb', line 16

def initialize(other, file_name=nil, file_source=nil)
  @reachable = true
  @expr_type = nil
  @errors = []
  @file_name = file_name
  @file_source = file_source
  replace other
  replace_children!
end

Instance Attribute Details

#bindingObject

Returns the value of attribute binding



10
11
12
# File 'lib/laser/analysis/sexp.rb', line 10

def binding
  @binding
end

#errorsObject

Returns the value of attribute errors



10
11
12
# File 'lib/laser/analysis/sexp.rb', line 10

def errors
  @errors
end

#file_nameObject

Returns the value of attribute file_name



10
11
12
# File 'lib/laser/analysis/sexp.rb', line 10

def file_name
  @file_name
end

#file_sourceObject

Returns the value of attribute file_source



10
11
12
# File 'lib/laser/analysis/sexp.rb', line 10

def file_source
  @file_source
end

#reachableObject

Returns the value of attribute reachable



11
12
13
# File 'lib/laser/analysis/sexp.rb', line 11

def reachable
  @reachable
end

#scopeObject

Returns the value of attribute scope



10
11
12
# File 'lib/laser/analysis/sexp.rb', line 10

def scope
  @scope
end

Instance Method Details

#add_error(error) ⇒ Object



36
37
38
# File 'lib/laser/analysis/sexp.rb', line 36

def add_error(error)
  errors << error unless errors.include?(error)
end

#all_errorsObject

Returns all errors in this subtree, in DFS order. returns: [Error]



94
95
96
# File 'lib/laser/analysis/sexp.rb', line 94

def all_errors
  dfs_enumerator.map(&:errors).flatten
end

#all_subtreesObject



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/laser/analysis/sexp.rb', line 64

def all_subtrees
  to_visit = self.children.dup
  visited = Set.new
  while to_visit.any?
    todo = to_visit.shift
    next unless is_sexp?(todo)

    case todo[0]
    when Array
      to_visit.concat todo
    when ::Symbol
      to_visit.concat todo.children
      visited << todo
    end
  end
  visited
end

#childrenArray<Object>



27
28
29
# File 'lib/laser/analysis/sexp.rb', line 27

def children
  @children ||= ((Array === self[0] ? self : self[1..-1]) || [])
end

#deep_findObject

Same as #find for Enumerable, only recursively. Useful for “jumping” past useless parser nodes.



57
58
59
60
61
62
# File 'lib/laser/analysis/sexp.rb', line 57

def deep_find
  ([self] + all_subtrees.to_a).each do |node|
    return node if yield(node)
  end
  nil
end

#dfs {|_self| ... } ⇒ Object

Performs a DFS on the node, yielding each subnode (including the given node) in DFS order.

Yields:

  • (_self)

Yield Parameters:



100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/laser/analysis/sexp.rb', line 100

def dfs
  yield self
  self.children.each do |child|
    next unless is_sexp?(child)
    case child[0]
    when Array
      child.each { |x| x.dfs { |y| yield y}}
    when ::Symbol
      child.dfs { |y| yield y }
    end
  end
end

#dfs_enumeratorObject

Returns an enumerator that iterates over each subnode of this node in DFS order.



84
85
86
87
88
89
90
# File 'lib/laser/analysis/sexp.rb', line 84

def dfs_enumerator
  Enumerator.new do |g|
    dfs do |node|
      g.yield node
    end
  end
end

#expanded_identifierObject

Returns the text of the identifier, assuming this node identifies something.



126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/laser/analysis/sexp.rb', line 126

def expanded_identifier
  case type
  when :@ident, :@const, :@gvar, :@cvar, :@ivar, :@kw, :@op
    self[1]
  when :var_ref, :var_field, :const_ref, :symbol
    self[1].expanded_identifier
  when :top_const_ref, :top_const_field
    "::#{self[1].expanded_identifier}"
  when :const_path_ref, :const_path_field
    lhs, rhs = children
    "#{lhs.expanded_identifier}::#{rhs.expanded_identifier}"
  end
end

#find_type(type) ⇒ Object



51
52
53
# File 'lib/laser/analysis/sexp.rb', line 51

def find_type(type)
  deep_find { |node| node.type == type }
end

#is_method_call?Boolean



140
141
142
143
# File 'lib/laser/analysis/sexp.rb', line 140

def is_method_call?
  [:command, :method_add_arg, :method_add_block, :vcall, :var_ref, :call,
   :fcall, :command_call, :binary, :unary, :super, :zsuper, :aref].include?(type)
end

#is_sexp?(sexp) ⇒ Boolean

is the given object a sexp?



43
44
45
# File 'lib/laser/analysis/sexp.rb', line 43

def is_sexp?(sexp)
  Analysis::Sexp === sexp
end

#linesObject



47
48
49
# File 'lib/laser/analysis/sexp.rb', line 47

def lines
  @file_source.lines.to_a
end

#method_callObject

Returns the MethodCall wrapping up all the method call information about this node.

raises: TypeError return: MethodCall



150
151
152
153
154
155
156
# File 'lib/laser/analysis/sexp.rb', line 150

def method_call
  unless is_method_call?
    raise TypeError.new("Only method call nodes define #method_call. "+
                        "This node is of type #{type}.")
  end
  MethodCall.new(self)
end

#typeSymbol



32
33
34
# File 'lib/laser/analysis/sexp.rb', line 32

def type
  self[0]
end