class Rephrase

for use with IRB, see dev.to/okinawarb/how-can-i-use-rubyvm-abstractsyntaxtree-in-irb-m2k for use in Pry, see dev.to/okinawarb/using-rubyvm-abstractsyntaxtree-of-in-pry-4cm3

Constants

VERSION

Public Class Methods

pp_ast(node, level = 0) click to toggle source

Pretty-prints an AST. @param node [RubyVM::AbstractSyntaxTree::Node] AST node

# File lib/rephrase.rb, line 48
def self.pp_ast(node, level = 0)
  case node
  when RubyVM::AbstractSyntaxTree::Node
    puts "#{'  ' * level}#{node.type.inspect} (#{node.first_lineno})"
    node.children.each { |c| pp_ast(c, level + 1) }
  when Array
    puts "#{'  ' * level}["
    node.each { |c| pp_ast(c, level + 1) }
    puts "#{'  ' * level}]"
  else
    puts "#{'  ' * level}#{node.inspect}"
    return
  end
end
to_source(code) click to toggle source

Converts a block or method to its source code. @param code [Proc, BoundMethod, UnboundMethod] proc or method @return [String] converted source code

# File lib/rephrase.rb, line 41
def self.to_source(code)
  ast = RubyVM::AbstractSyntaxTree.of(code)
  new.convert({}, ast)
end

Public Instance Methods

convert(ctx, node) click to toggle source

Converts an AST node to a string containing its source code. @param ctx [Hash] context object @param node [RubyVM::AbstractSyntaxTree::Node] AST node @return [String] source code

# File lib/rephrase.rb, line 67
def convert(ctx, node)
  ctx[:buffer] ||= +''
  ctx[:indent] ||= 0
  method_name = :"on_#{node.type.downcase}"

  if !respond_to?(method_name)
    raise "Could not convert #{node.type} node to ruby"
  end

  send(method_name, ctx, node)
  ctx[:buffer]
end
emit(ctx, *entries) click to toggle source

Emits code into the context buffer. @param ctx [Hash] context object @param entries [Array<String, Symbol>] code entries

# File lib/rephrase.rb, line 398
def emit(ctx, *entries)
  entries.each do |e|
    case e
    when RubyVM::AbstractSyntaxTree::Node, FakeNode
      convert(ctx, e)
    when Array
      ctx[:buffer] << "\n"
      e.each { |e2| convert(ctx, e2) }
      ctx[:buffer] << "\n"
    when String
      ctx[:buffer] << e
    when nil
      # ignore
    else
      raise "Invalid entry #{e.inspect}"
    end
  end
end
on_and(ctx, node) click to toggle source

Converts a ‘:AND` AST node. @param ctx [Hash] context object @param node [RubyVM::AbstractSyntaxTree::Node] AST node

# File lib/rephrase.rb, line 312
def on_and(ctx, node)
  left, right = node.children
  emit(ctx, left, " && ", right)
end
on_attrasgn(ctx, node) click to toggle source

Converts a ‘:ATTRASGN` AST node. @param ctx [Hash] context object @param node [RubyVM::AbstractSyntaxTree::Node] AST node

# File lib/rephrase.rb, line 372
def on_attrasgn(ctx, node)
  left, op, args = node.children
  emit(ctx, left)
  case op
  when :[]=
    args_list = args.children[0..-2]
    subscript = FakeNode.list(args_list[0..-2])
    right = args_list.last
    emit(ctx, "[", subscript, "] = ", right)
  else
    raise "Unsupported ATTRASGN op #{op.inspect}"
  end
end
on_block(ctx, node) click to toggle source

Converts a ‘:BLOCK` AST node. @param ctx [Hash] context object @param node [RubyVM::AbstractSyntaxTree::Node] AST node

# File lib/rephrase.rb, line 94
def on_block(ctx, node)
  last_idx = node.children.size - 1
  node.children.each_with_index do |c, idx|
    emit(ctx, c)
    emit(ctx, "\n") if idx < last_idx
  end
end
on_call(ctx, node) click to toggle source

Converts a ‘:CALL` AST node. @param ctx [Hash] context object @param node [RubyVM::AbstractSyntaxTree::Node] AST node

# File lib/rephrase.rb, line 141
def on_call(ctx, node)
  receiver, method, args = node.children
  args = args && FakeNode.list(args.children)
  case method
  when :[]
    emit(ctx, receiver, "[", args, "]")
  else
    if args
      emit(ctx, receiver, ".", "#{method}(", args, ")")
    else
      emit(ctx, receiver, ".", "#{method}()")
    end
  end
end
on_colon2(ctx, node) click to toggle source

Converts a ‘:COLON2` AST node. @param ctx [Hash] context object @param node [RubyVM::AbstractSyntaxTree::Node] AST node

# File lib/rephrase.rb, line 133
def on_colon2(ctx, node)
  left, right = node.children
  emit(ctx, left, "::", right.to_s)
end
on_const(ctx, node) click to toggle source

Converts a ‘:CONST` AST node. @param ctx [Hash] context object @param node [RubyVM::AbstractSyntaxTree::Node] AST node

# File lib/rephrase.rb, line 126
def on_const(ctx, node)
  emit(ctx, node.children.first.to_s)
end
on_dasgn_curr(ctx, node) click to toggle source

Converts a ‘:DASGN_CURR` AST node. @param ctx [Hash] context object @param node [RubyVM::AbstractSyntaxTree::Node] AST node

# File lib/rephrase.rb, line 187
def on_dasgn_curr(ctx, node)
  left, right = node.children
  emit(ctx, left.to_s, " = ", right)
end
on_dstr(ctx, node) click to toggle source

Converts a ‘:DSTR` AST node. @param ctx [Hash] context object @param node [RubyVM::AbstractSyntaxTree::Node] AST node

# File lib/rephrase.rb, line 284
def on_dstr(ctx, node)
  prefix, evstr1, rest = node.children
  emit(ctx, "\"", prefix.inspect[1..-2], evstr1)
  if rest
    rest.children.compact.each do |n|
      case n.type
      when :STR
        emit(ctx, n.children.first.inspect[1..-2])
      when :EVSTR
        emit(ctx, n)
      else
        raise "Unexpected node #{n.type.inspect} encountered in DSTR"
      end
    end
  end
  emit(ctx, "\"")
end
on_dvar(ctx, node) click to toggle source

Converts a ‘:DVAR` AST node. @param ctx [Hash] context object @param node [RubyVM::AbstractSyntaxTree::Node] AST node

# File lib/rephrase.rb, line 263
def on_dvar(ctx, node)
  emit(ctx, node.children.first.to_s)
end
on_evstr(ctx, node) click to toggle source

Converts a ‘:EVSTR` AST node. @param ctx [Hash] context object @param node [RubyVM::AbstractSyntaxTree::Node] AST node

# File lib/rephrase.rb, line 305
def on_evstr(ctx, node)
  emit(ctx, "\#{", node.children.first, "}")
end
on_false(ctx, node) click to toggle source

Converts a ‘:FALSE` AST node. @param ctx [Hash] context object @param node [RubyVM::AbstractSyntaxTree::Node] AST node

# File lib/rephrase.rb, line 256
def on_false(ctx, node)
  emit(ctx, "false")
end
on_fcall(ctx, node) click to toggle source

Converts a ‘:FCALL` AST node. @param ctx [Hash] context object @param node [RubyVM::AbstractSyntaxTree::Node] AST node

# File lib/rephrase.rb, line 159
def on_fcall(ctx, node)
  method, args = node.children
  args = args && FakeNode.list(args.children)
  emit(ctx, "#{method}(", args, ")")
end
on_hash(ctx, node) click to toggle source

Converts a ‘:HASH` AST node. @param ctx [Hash] context object @param node [RubyVM::AbstractSyntaxTree::Node] AST node

# File lib/rephrase.rb, line 354
def on_hash(ctx, node)
  list = node.children.first
  idx = 0
  emit(ctx, "{")
  while true
    k, v = list.children[idx, 2]
    break unless k

    emit(ctx, ", ") if idx > 0
    idx += 2
    emit(ctx, k, " => ", v)
  end
  emit(ctx, "}")
end
on_iasgn(ctx, node) click to toggle source

Converts a ‘:IASGN` AST node. @param ctx [Hash] context object @param node [RubyVM::AbstractSyntaxTree::Node] AST node

# File lib/rephrase.rb, line 195
def on_iasgn(ctx, node)
  left, right = node.children
  emit(ctx, left.to_s, " = ", right)
end
on_if(ctx, node) click to toggle source

Converts a ‘:IF AST` node. @param ctx [Hash] context object @param node [RubyVM::AbstractSyntaxTree::Node] AST node

# File lib/rephrase.rb, line 203
def on_if(ctx, node)
  cond, branch1, branch2 = node.children
  if branch2
    emit(ctx, "if ", cond, "\n", branch1, "\nelse\n", branch2, "\nend")
  else
    emit(ctx, "if ", cond, "\n", branch1, "\nend")
  end
end
on_iter(ctx, node) click to toggle source

Converts a ‘:ITER` AST node. @param ctx [Hash] context object @param node [RubyVM::AbstractSyntaxTree::Node] AST node

# File lib/rephrase.rb, line 105
def on_iter(ctx, node)
  call, scope = node.children
  emit(ctx, call)
  emit(ctx, FakeNode.iter_scope(scope.children))
end
on_iter_scope(ctx, node) click to toggle source

Converts a ‘:ITER_SCOPE` AST (fake) node. @param ctx [Hash] context object @param node [Rephrase::FakeNode] AST node

# File lib/rephrase.rb, line 114
def on_iter_scope(ctx, node)
  args, arg_spec, body = node.children
  emit(ctx, " do")
  emit(ctx, " |", args.map(&:to_s).join(", "), "|") unless args.empty?
  emit(ctx, "\n")
  emit(ctx, body)
  emit(ctx, "\nend")
end
on_ivar(ctx, node) click to toggle source

Converts a ‘:IVAR` AST node. @param ctx [Hash] context object @param node [RubyVM::AbstractSyntaxTree::Node] AST node

# File lib/rephrase.rb, line 270
def on_ivar(ctx, node)
  emit(ctx, node.children.first.to_s)
end
on_list(ctx, node) click to toggle source

Converts a ‘:LIST` AST node. @param ctx [Hash] context object @param node [RubyVM::AbstractSyntaxTree::Node] AST node

# File lib/rephrase.rb, line 328
def on_list(ctx, node)
  items = node.children[0..-2]
  last_idx = items.size - 1
  emit(ctx, "[")
  items.each_with_index do |c, idx|
    emit(ctx, c)
    emit(ctx, ", ") if idx < last_idx
  end
  emit(ctx, "]")
end
on_list_embedded(ctx, node) click to toggle source

Converts a ‘:LIST_EMBEDDED` AST (fake) node. @param ctx [Hash] context object @param node [Rephrase::FakeNode] AST node

# File lib/rephrase.rb, line 342
def on_list_embedded(ctx, node)
  items = node.children.compact
  last_idx = items.size - 1
  items.each_with_index do |c, idx|
    emit(ctx, c)
    emit(ctx, ", ") if idx < last_idx
  end
end
on_lit(ctx, node) click to toggle source

Converts a ‘:LIT` AST node. @param ctx [Hash] context object @param node [RubyVM::AbstractSyntaxTree::Node] AST node

# File lib/rephrase.rb, line 235
def on_lit(ctx, node)
  emit(ctx, node.children.first.inspect)
end
on_nil(ctx, node) click to toggle source

Converts a ‘:NIL` AST node. @param ctx [Hash] context object @param node [RubyVM::AbstractSyntaxTree::Node] AST node

# File lib/rephrase.rb, line 242
def on_nil(ctx, node)
  emit(ctx, "nil")
end
on_opcall(ctx, node) click to toggle source

Converts a ‘:OPCALL` AST node. @param ctx [Hash] context object @param node [RubyVM::AbstractSyntaxTree::Node] AST node

# File lib/rephrase.rb, line 175
def on_opcall(ctx, node)
  left, op, right = node.children
  if op == :!
    emit(ctx, "!(", left, ")")
  else
    emit(ctx, left, " #{op} ", right.children.first)
  end
end
on_or(ctx, node) click to toggle source

Converts a ‘:OR` AST node. @param ctx [Hash] context object @param node [RubyVM::AbstractSyntaxTree::Node] AST node

# File lib/rephrase.rb, line 320
def on_or(ctx, node)
  left, right = node.children
  emit(ctx, left, " || ", right)
end
on_rescue(ctx, node) click to toggle source

Converts a ‘:RESCUE` AST node. @param ctx [Hash] context object @param node [RubyVM::AbstractSyntaxTree::Node] AST node

# File lib/rephrase.rb, line 389
def on_rescue(ctx, node)
  code, rescue_body = node.children
  emit(ctx, code)
  emit(ctx, " rescue ", rescue_body.children[1])
end
on_scope(ctx, node) click to toggle source

Converts a ‘:SCOPE` AST node. @param ctx [Hash] context object @param node [RubyVM::AbstractSyntaxTree::Node] AST node

# File lib/rephrase.rb, line 83
def on_scope(ctx, node)
  body = node.children.last
  # if body.type != :BLOCK
  #   body = FakeNode.new(:BLOCK, [body])
  # end
  emit(ctx, "proc do", [body], "end")
end
on_str(ctx, node) click to toggle source

Converts a ‘:STR` AST node. @param ctx [Hash] context object @param node [RubyVM::AbstractSyntaxTree::Node] AST node

# File lib/rephrase.rb, line 277
def on_str(ctx, node)
  emit(ctx, node.children.first.inspect)
end
on_true(ctx, node) click to toggle source

Converts a ‘:TRUE` AST node. @param ctx [Hash] context object @param node [RubyVM::AbstractSyntaxTree::Node] AST node

# File lib/rephrase.rb, line 249
def on_true(ctx, node)
  emit(ctx, "true")
end
on_unless(ctx, node) click to toggle source

Converts a ‘:UNLESS` AST node. @param ctx [Hash] context object @param node [RubyVM::AbstractSyntaxTree::Node] AST node

# File lib/rephrase.rb, line 215
def on_unless(ctx, node)
  cond, branch1, branch2 = node.children
  if branch2
    emit(ctx, "unless ", cond, "\n", branch1, "\nelse\n", branch2, "\nend")
  else
    emit(ctx, "unless ", cond, "\n", branch1, "\nend")
  end
end
on_vcall(ctx, node) click to toggle source

Converts a ‘:VCALL` AST node. @param ctx [Hash] context object @param node [RubyVM::AbstractSyntaxTree::Node] AST node

# File lib/rephrase.rb, line 168
def on_vcall(ctx, node)
  emit(ctx, node.children.first.to_s, "()")
end
on_while(ctx, node) click to toggle source

Converts a ‘:WHILE` AST node. @param ctx [Hash] context object @param node [RubyVM::AbstractSyntaxTree::Node] AST node

# File lib/rephrase.rb, line 227
def on_while(ctx, node)
  cond, body = node.children
  emit(ctx, "while ", cond, "\n", body, "\nend")
end