bm_sexp.rb 9.1 KB
Newer Older
1 2 3 4 5
#Sexp changes from ruby_parser
#and some changes for caching hash value and tracking 'original' line number
#of a Sexp.
class Sexp
  attr_reader :paren
6
  ASSIGNMENT_BOOL = [:gasgn, :iasgn, :lasgn, :cvdecl, :cdecl, :or, :and]
7

J
Justin Collins 已提交
8
  def method_missing name, *args
J
Justin Collins 已提交
9 10 11 12 13
    #Brakeman does not use this functionality,
    #so overriding it to raise a NoMethodError.
    #
    #The original functionality calls find_node and optionally
    #deletes the node if found.
J
Justin Collins 已提交
14 15 16
    raise NoMethodError.new("No method '#{name}' for Sexp", name, args)
  end

17 18 19 20 21
  def paren
    @paren ||= false
  end

  def value
22
    raise WrongSexpError, "Sexp#value called on multi-item Sexp", caller[1..-1] if size > 2
23 24 25
    last
  end

26 27 28 29
  def second
    self[1]
  end

30 31 32 33
  def to_sym
    self.value.to_sym
  end

34 35 36 37
  def node_type= type
    self[0] = type
  end

38
  def resbody delete = false
J
Justin Collins 已提交
39 40 41 42 43
    #RubyParser relies on method_missing for this, but since we don't want to use
    #method_missing, here's a real method.
    find_node :resbody, delete
  end

44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
  alias :node_type :sexp_type
  alias :values :sexp_body # TODO: retire

  alias :old_push :<<
  alias :old_line :line
  alias :old_line_set :line=
  alias :old_file_set :file=
  alias :old_comments_set :comments=
  alias :old_compact :compact
  alias :old_fara :find_and_replace_all
  alias :old_find_node :find_node

  def original_line line = nil
    if line
      @my_hash_value = nil
      @original_line = line
60
      self
61
    else
62
      @original_line ||= nil
63 64 65 66 67 68 69 70 71 72 73
    end
  end

  def hash
    #There still seems to be some instances in which the hash of the
    #Sexp changes, but I have not found what method call is doing it.
    #Of course, Sexp is subclasses from Array, so who knows what might
    #be going on.
    @my_hash_value ||= super
  end

74 75 76
  def line num = nil
    @my_hash_value = nil if num
    old_line(num)
77 78 79 80
  end

  def line= *args
    @my_hash_value = nil
J
Justin Collins 已提交
81
    old_line_set(*args)
82 83 84 85
  end

  def file= *args
    @my_hash_value = nil
J
Justin Collins 已提交
86
    old_file_set(*args)
87 88 89 90 91 92 93 94 95
  end

  def compact
    @my_hash_value = nil
    old_compact
  end

  def find_and_replace_all *args
    @my_hash_value = nil
J
Justin Collins 已提交
96
    old_fara(*args)
97 98 99 100
  end

  def find_node *args
    @my_hash_value = nil
J
Justin Collins 已提交
101
    old_find_node(*args)
102 103 104 105 106 107 108 109 110
  end

  def paren= arg
    @my_hash_value = nil
    @paren = arg
  end

  def comments= *args
    @my_hash_value = nil
J
Justin Collins 已提交
111
    old_comments_set(*args)
112
  end
J
Justin Collins 已提交
113

114 115
  #Iterates over the Sexps in an Sexp, skipping values that are not
  #an Sexp.
J
Justin Collins 已提交
116 117 118 119 120
  def each_sexp
    self.each do |e|
      yield e if Sexp === e
    end
  end
121

122 123
  #Raise a WrongSexpError if the nodes type does not match one of the expected
  #types.
124 125
  def expect *types
    unless types.include? self.node_type
126
      raise WrongSexpError, "Expected #{types.join ' or '} but given #{self.inspect}", caller[1..-1]
127 128 129
    end
  end

130
  #Returns target of a method call:
131 132 133
  #
  #s(:call, s(:call, nil, :x, s(:arglist)), :y, s(:arglist, s(:lit, 1)))
  #         ^-----------target-----------^
J
Justin Collins 已提交
134
  def target
135
    expect :call, :attrasgn
J
Justin Collins 已提交
136 137 138
    self[1]
  end

139
  #Sets the target of a method call:
J
Justin Collins 已提交
140 141 142 143 144
  def target= exp
    expect :call, :attrasgn
    self[1] = exp
  end

145
  #Returns method of a method call:
146 147 148
  #
  #s(:call, s(:call, nil, :x, s(:arglist)), :y, s(:arglist, s(:lit, 1)))
  #                        ^- method
J
Justin Collins 已提交
149
  def method
150 151 152 153 154 155 156 157
    expect :call, :attrasgn, :super, :zsuper

    case self.node_type
    when :call, :attrasgn
      self[2]
    when :super, :zsuper
      :super
    end
J
Justin Collins 已提交
158 159
  end

160
  #Sets the arglist in a method call.
161
  def arglist= exp
162
    expect :call, :attrasgn
163 164 165 166 167 168 169
    self[3] = exp
    #RP 3 TODO
  end

  #Returns arglist for method call. This differs from Sexp#args, as Sexp#args
  #does not return a 'real' Sexp (it does not have a node type) but
  #Sexp#arglist returns a s(:arglist, ...)
170 171 172
  #
  #    s(:call, s(:call, nil, :x, s(:arglist)), :y, s(:arglist, s(:lit, 1), s(:lit, 2)))
  #                                                 ^------------ arglist ------------^
173
  def arglist
174 175 176 177 178 179 180 181 182 183 184 185
    expect :call, :attrasgn, :super, :zsuper

    case self.node_type
    when :call, :attrasgn
      self[3]
    when :super, :zsuper
      if self[1]
        Sexp.new(:arglist).concat self[1..-1]
      else
        Sexp.new(:arglist)
      end
    end
186 187 188 189 190

    #For new ruby_parser
    #Sexp.new(:arglist, *self[3..-1])
  end

191
  #Returns arguments of a method call. This will be an 'untyped' Sexp.
192
  #
193 194
  #    s(:call, s(:call, nil, :x, s(:arglist)), :y, s(:arglist, s(:lit, 1), s(:lit, 2)))
  #                                                             ^--------args--------^
195
  def args
196
    expect :call, :attrasgn, :super, :zsuper
197 198 199 200 201 202 203
    #For new ruby_parser
    #if self[3]
    #  self[3..-1]
    #else
    #  []
    #end

204 205 206 207 208 209 210 211 212 213 214 215 216 217
    case self.node_type
    when :call, :attrasgn
      #For old ruby_parser
      if self[3]
        self[3][1..-1]
      else
        []
      end
    when :super, :zsuper
      if self[1]
        self[1..-1]
      else
        []
      end
J
Justin Collins 已提交
218 219
    end
  end
220

221
  #Returns first argument of a method call.
222 223 224 225 226 227 228
  def first_arg
    expect :call, :attrasgn
    if self[3]
      self[3][1]
    end
  end

229
  #Sets first argument of a method call.
230 231 232 233 234 235 236
  def first_arg= exp
    expect :call, :attrasgn
    if self[3]
      self[3][1] = exp
    end
  end

237
  #Returns second argument of a method call.
238 239 240 241 242 243 244
  def second_arg
    expect :call, :attrasgn
    if self[3]
      self[3][2]
    end
  end

245
  #Sets second argument of a method call.
246 247 248 249 250 251
  def second_arg= exp
    expect :call, :attrasgn
    if self[3]
      self[3][2] = exp
    end
  end
J
Justin Collins 已提交
252

253 254 255 256 257 258
  #Returns condition of an if expression:
  #
  #    s(:if,
  #     s(:lvar, :condition), <-- condition
  #     s(:lvar, :then_val),
  #     s(:lvar, :else_val)))
J
Justin Collins 已提交
259
  def condition
260
    expect :if
J
Justin Collins 已提交
261 262 263
    self[1]
  end

264 265 266 267 268 269
  #Returns 'then' clause of an if expression:
  #
  #    s(:if,
  #     s(:lvar, :condition),
  #     s(:lvar, :then_val), <-- then clause
  #     s(:lvar, :else_val)))
J
Justin Collins 已提交
270
  def then_clause
271
    expect :if
J
Justin Collins 已提交
272 273 274
    self[2]
  end

275 276 277 278 279 280 281
  #Returns 'else' clause of an if expression:
  #
  #    s(:if,
  #     s(:lvar, :condition),
  #     s(:lvar, :then_val),
  #     s(:lvar, :else_val)))
  #     ^---else caluse---^
J
Justin Collins 已提交
282
  def else_clause
283
    expect :if
J
Justin Collins 已提交
284 285 286
    self[3]
  end

287
  #Method call associated with a block:
288
  #
289 290 291 292
  #    s(:iter,
  #     s(:call, nil, :x, s(:arglist)), <- block_call
  #      s(:lasgn, :y),
  #       s(:block, s(:lvar, :y), s(:call, nil, :z, s(:arglist))))
293 294 295 296 297
  def block_call
    expect :iter, :call_with_block
    self[1]
  end

298 299
  #Returns block of a call with a block.
  #Could be a single expression or a block:
300
  #
301 302 303 304 305
  #    s(:iter,
  #     s(:call, nil, :x, s(:arglist)),
  #      s(:lasgn, :y),
  #       s(:block, s(:lvar, :y), s(:call, nil, :z, s(:arglist))))
  #       ^-------------------- block --------------------------^
306
  def block
J
Justin Collins 已提交
307 308 309 310 311 312 313 314
    expect :iter, :call_with_block, :scope

    case self.node_type
    when :iter, :call_with_block
      self[3]
    when :scope
      self[1]
    end
J
Justin Collins 已提交
315
  end
316

317 318
  #Returns parameters for a block
  #
319 320 321 322
  #    s(:iter,
  #     s(:call, nil, :x, s(:arglist)),
  #      s(:lasgn, :y), <- block_args
  #       s(:call, nil, :p, s(:arglist, s(:lvar, :y))))
323 324 325 326 327
  def block_args
    expect :iter, :call_with_block
    self[2]
  end

328
  #Returns the left hand side of assignment or boolean:
329
  #
330 331
  #    s(:lasgn, :x, s(:lit, 1))
  #               ^--lhs
332
  def lhs
333
    expect *ASSIGNMENT_BOOL
334 335 336
    self[1]
  end

337 338
  #Sets the left hand side of assignment or boolean.
  def lhs= exp
339
    expect *ASSIGNMENT_BOOL
340
    self[1] = exp
341 342
  end

343
  #Returns right side (value) of assignment or boolean:
344
  #
345 346
  #    s(:lasgn, :x, s(:lit, 1))
  #                  ^--rhs---^
347
  def rhs
348 349 350 351
    expect *ASSIGNMENT_BOOL
    self[2]
  end

352 353 354 355 356 357 358
  #Sets the right hand side of assignment or boolean.
  def rhs= exp
    expect *ASSIGNMENT_BOOL
    self[2] = exp
  end

  #Returns name of method being defined in a method definition.
359
  def method_name
360 361 362 363 364 365 366 367 368 369
    expect :defn, :defs, :methdef, :selfdef

    case self.node_type
    when :defn, :methdef
      self[1]
    when :defs, :selfdef
      self[2]
    end
  end

370
  #Sets body
371 372
  def body= exp
    expect :defn, :defs, :methdef, :selfdef, :class, :module
373

374 375 376 377 378 379 380 381 382 383
    case self.node_type
    when :defn, :methdef, :class
      self[3] = exp
    when :defs, :selfdef
      self[4] = exp
    when :module
      self[2] = exp
    end
  end

384
  #Returns body of a method definition, class, or module.
385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
  def body
    expect :defn, :defs, :methdef, :selfdef, :class, :module

    case self.node_type
    when :defn, :methdef, :class
      self[3]
    when :defs, :selfdef
      self[4]
    when :module
      self[2]
    end
  end

  def render_type
    expect :render
    self[1]
  end

  def class_name
    expect :class, :module
    self[1]
  end

J
Justin Collins 已提交
408 409
  alias module_name class_name

410 411
  def parent_name
    expect :class
412 413
    self[2]
  end
414 415 416 417 418 419
end

#Invalidate hash cache if the Sexp changes
[:[]=, :clear, :collect!, :compact!, :concat, :delete, :delete_at,
  :delete_if, :drop, :drop_while, :fill, :flatten!, :replace, :insert,
  :keep_if, :map!, :pop, :push, :reject!, :replace, :reverse!, :rotate!,
420
  :select!, :shift, :shuffle!, :slice!, :sort!, :sort_by!, :transpose,
421 422 423 424 425 426 427 428 429 430
  :uniq!, :unshift].each do |method|

  Sexp.class_eval <<-RUBY
    def #{method} *args
      @my_hash_value = nil
      super
    end
    RUBY
end

431
class WrongSexpError < RuntimeError; end