1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
|
RSpec::Support.require_rspec_support 'source/location'
module RSpec
module Support
class Source
# @private
# A wrapper for Ripper AST node which is generated with `Ripper.sexp`.
class Node
include Enumerable
attr_reader :sexp, :parent
def self.sexp?(array)
array.is_a?(Array) && array.first.is_a?(Symbol)
end
def initialize(ripper_sexp, parent=nil)
@sexp = ripper_sexp.freeze
@parent = parent
end
def type
sexp[0]
end
def args
@args ||= raw_args.map do |raw_arg|
if Node.sexp?(raw_arg)
Node.new(raw_arg, self)
elsif Location.location?(raw_arg)
Location.new(*raw_arg)
elsif raw_arg.is_a?(Array)
ExpressionSequenceNode.new(raw_arg, self)
else
raw_arg
end
end.freeze
end
def children
@children ||= args.select { |arg| arg.is_a?(Node) }.freeze
end
def location
@location ||= args.find { |arg| arg.is_a?(Location) }
end
# We use a loop here (instead of recursion) to prevent SystemStackError
def each
return to_enum(__method__) unless block_given?
node_queue = []
node_queue << self
while (current_node = node_queue.shift)
yield current_node
node_queue.concat(current_node.children)
end
end
def each_ancestor
return to_enum(__method__) unless block_given?
current_node = self
while (current_node = current_node.parent)
yield current_node
end
end
def inspect
"#<#{self.class} #{type}>"
end
private
def raw_args
sexp[1..-1] || []
end
end
# @private
# Basically `Ripper.sexp` generates arrays whose first element is a symbol (type of sexp),
# but it exceptionally generates typeless arrays for expression sequence:
#
# Ripper.sexp('foo; bar')
# => [
# :program,
# [ # Typeless array
# [:vcall, [:@ident, "foo", [1, 0]]],
# [:vcall, [:@ident, "bar", [1, 5]]]
# ]
# ]
#
# We wrap typeless arrays in this pseudo type node
# so that it can be handled in the same way as other type node.
class ExpressionSequenceNode < Node
def type
:_expression_sequence
end
private
def raw_args
sexp
end
end
end
end
end
|