seattlerb / ruby_parser

ruby_parser is a ruby parser written in pure ruby. It outputs s-expressions which can be manipulated and converted back to ruby via the ruby2ruby gem.
http://www.zenspider.com/projects/ruby_parser.html
476 stars 100 forks source link

Ruby 3: Extracting a sub part of Sexp returns an array instead of a Sexp #313

Closed swiknaba closed 3 years ago

swiknaba commented 3 years ago

Parsing the following simple example:

class Hello
  def hihi
    'alfabet'.gsub('r', 'b'.gsub('a', 'z'))
  end
end

returns the following Sexp:

s(:call, s(:str, "alfabet"), :gsub, s(:str, "r"), s(:call, s(:str, "b"), :gsub, s(:str, "a"), s(:str, "z")))

let's assign this a variable, e.g. call_element.

Now let's extract a sub-tree:

call_element[3..-1]

Result in Ruby 2.7.2:

s(:str, "r"), s(:call, s(:str, "b"), :gsub, s(:str, "a"), s(:str, "z"))

Result in Ruby 3.0.0:

[s(:str, "r"), s(:call, s(:str, "b"), :gsub, s(:str, "a"), s(:str, "z"))]

is this an expected change, i.e. do I now need to check, if a sub-tree is an array or a Sexp? I would expect call_element[3..-1] to always return a Sexp if possible.

Find more details here: https://github.com/DamirSvrtan/fasterer/pull/85

presidentbeef commented 3 years ago

This is due to a change in Ruby: https://bugs.ruby-lang.org/issues/6087#note-18

Sexp is a subclass of Array, so it is affected.

However, I found that Sexp#sexp_body is a good replacement.

swiknaba commented 3 years ago

Using Sexp#sexp_body() instead of Sexp#[] indeed solves the problem, since it yields the same result as Sexp#[] in Ruby < 3. Thanks!