Class: Laser::Analysis::Sexp

Inherits:
Array
  • Object
show all
Extended by:
ModuleExtensions
Includes:
Laser::Analysis::SexpExtensions::ConstantExtraction, Laser::Analysis::SexpExtensions::SourceLocation, Laser::Analysis::SexpExtensions::TypeInference
Defined in:
lib/laser/analysis/sexp.rb

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

- (Sexp) initialize(other, file_name = nil, file_source = nil)

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

Parameters:

  • other (Array<Object>)

    the other



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

- (Object) binding

Returns the value of attribute binding



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

def binding
  @binding
end

- (Object) errors

Returns the value of attribute errors



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

def errors
  @errors
end

- (Object) file_name

Returns the value of attribute file_name



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

def file_name
  @file_name
end

- (Object) file_source

Returns the value of attribute file_source



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

def file_source
  @file_source
end

- (Object) reachable

Returns the value of attribute reachable



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

def reachable
  @reachable
end

- (Object) scope

Returns the value of attribute scope



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

def scope
  @scope
end

Instance Method Details

- (Object) add_error(error)



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

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

- (Object) all_errors

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

- (Object) all_subtrees



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

- (Array<Object>) children

Returns the children of the node.

Returns:

  • (Array<Object>)

    the children of the node.



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

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

- (Object) deep_find

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

- (Object) dfs {|_self| ... }

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

- (Object) dfs_enumerator

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

- (Object) expanded_identifier

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

- (Object) find_type(type)



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

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

- (Boolean) is_method_call?

Returns:

  • (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

- (Boolean) is_sexp?(sexp)

is the given object a sexp?

Returns:

  • (Boolean)

    Boolean



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

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

- (Object) lines



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

def lines
  @file_source.lines.to_a
end

- (Object) method_call

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

- (Symbol) type

Returns the type of the node.

Returns:

  • (Symbol)

    the type of the node.



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

def type
  self[0]
end