class Prism::Translation::Ripper

This class provides a compatibility layer between prism and Ripper. It functions by parsing the entire tree first and then walking it and executing each of the Ripper callbacks as it goes. To use this class, you treat ‘Prism::Translation::Ripper` effectively as you would treat the `Ripper` class.

Note that this class will serve the most common use cases, but Ripper’s API is extensive and undocumented. It relies on reporting the state of the parser at any given time. We do our best to replicate that here, but because it is a different architecture it is not possible to perfectly replicate the behavior of Ripper.

The main known difference is that we may omit dispatching some events in some cases. This impacts the following events:

  • on_assign_error

  • on_comma

  • on_ignored_nl

  • on_ignored_sp

  • on_kw

  • on_label_end

  • on_lbrace

  • on_lbracket

  • on_lparen

  • on_nl

  • on_op

  • on_operator_ambiguous

  • on_rbrace

  • on_rbracket

  • on_rparen

  • on_semicolon

  • on_sp

  • on_symbeg

  • on_tstring_beg

  • on_tstring_end

Constants

BINARY_OPERATORS

A list of all of the Ruby binary operators.

EVENTS

This array contains name of all ripper events.

KEYWORDS

A list of all of the Ruby keywords.

PARSER_EVENTS

This array contains name of parser events.

PARSER_EVENT_TABLE

This contains a table of all of the parser events and their corresponding arity.

SCANNER_EVENTS

This array contains name of scanner events.

SCANNER_EVENT_TABLE

This contains a table of all of the scanner events and their corresponding arity.

Attributes

column[R]

The current column number of the parser.

filename[R]

The filename of the source being parsed.

lineno[R]

The current line number of the parser.

source[R]

The source that is being parsed.

Public Class Methods

lex(src, filename = "-", lineno = 1, raise_errors: false) click to toggle source

Tokenizes the Ruby program and returns an array of an array, which is formatted like [[lineno, column], type, token, state]. The filename argument is mostly ignored. By default, this method does not handle syntax errors in src, use the raise_errors keyword to raise a SyntaxError for an error in src.

require "ripper"
require "pp"

pp Ripper.lex("def m(a) nil end")
#=> [[[1,  0], :on_kw,     "def", FNAME    ],
     [[1,  3], :on_sp,     " ",   FNAME    ],
     [[1,  4], :on_ident,  "m",   ENDFN    ],
     [[1,  5], :on_lparen, "(",   BEG|LABEL],
     [[1,  6], :on_ident,  "a",   ARG      ],
     [[1,  7], :on_rparen, ")",   ENDFN    ],
     [[1,  8], :on_sp,     " ",   BEG      ],
     [[1,  9], :on_kw,     "nil", END      ],
     [[1, 12], :on_sp,     " ",   END      ],
     [[1, 13], :on_kw,     "end", END      ]]
# File prism/translation/ripper.rb, line 72
def self.lex(src, filename = "-", lineno = 1, raise_errors: false)
  result = Prism.lex_compat(src, filepath: filename, line: lineno)

  if result.failure? && raise_errors
    raise SyntaxError, result.errors.first.message
  else
    result.value
  end
end
new(source, filename = "(ripper)", lineno = 1) click to toggle source

Create a new Translation::Ripper object with the given source.

# File prism/translation/ripper.rb, line 444
def initialize(source, filename = "(ripper)", lineno = 1)
  @source = source
  @filename = filename
  @lineno = lineno
  @column = 0
  @result = nil
end
parse(src, filename = "(ripper)", lineno = 1) click to toggle source

Parses the given Ruby program read from src. src must be a String or an IO or a object with a gets method.

# File prism/translation/ripper.rb, line 46
def self.parse(src, filename = "(ripper)", lineno = 1)
  new(src, filename, lineno).parse
end
sexp(src, filename = "-", lineno = 1, raise_errors: false) click to toggle source

Parses src and create S-exp tree. Returns more readable tree rather than Ripper.sexp_raw. This method is mainly for developer use. The filename argument is mostly ignored. By default, this method does not handle syntax errors in src, returning nil in such cases. Use the raise_errors keyword to raise a SyntaxError for an error in src.

require "ripper"
require "pp"

pp Ripper.sexp("def m(a) nil end")
  #=> [:program,
       [[:def,
        [:@ident, "m", [1, 4]],
        [:paren, [:params, [[:@ident, "a", [1, 6]]], nil, nil, nil, nil, nil, nil]],
        [:bodystmt, [[:var_ref, [:@kw, "nil", [1, 9]]]], nil, nil, nil]]]]
# File prism/translation/ripper.rb, line 381
def self.sexp(src, filename = "-", lineno = 1, raise_errors: false)
  builder = SexpBuilderPP.new(src, filename, lineno)
  sexp = builder.parse
  if builder.error?
    if raise_errors
      raise SyntaxError, builder.error
    end
  else
    sexp
  end
end
sexp_raw(src, filename = "-", lineno = 1, raise_errors: false) click to toggle source

Parses src and create S-exp tree. This method is mainly for developer use. The filename argument is mostly ignored. By default, this method does not handle syntax errors in src, returning nil in such cases. Use the raise_errors keyword to raise a SyntaxError for an error in src.

require "ripper"
require "pp"

pp Ripper.sexp_raw("def m(a) nil end")
  #=> [:program,
       [:stmts_add,
        [:stmts_new],
        [:def,
         [:@ident, "m", [1, 4]],
         [:paren, [:params, [[:@ident, "a", [1, 6]]], nil, nil, nil]],
         [:bodystmt,
          [:stmts_add, [:stmts_new], [:var_ref, [:@kw, "nil", [1, 9]]]],
          nil,
          nil,
          nil]]]]
# File prism/translation/ripper.rb, line 416
def self.sexp_raw(src, filename = "-", lineno = 1, raise_errors: false)
  builder = SexpBuilder.new(src, filename, lineno)
  sexp = builder.parse
  if builder.error?
    if raise_errors
      raise SyntaxError, builder.error
    end
  else
    sexp
  end
end

Public Instance Methods

error?() click to toggle source

True if the parser encountered an error during parsing.

# File prism/translation/ripper.rb, line 457
def error?
  result.failure?
end
parse() click to toggle source

Parse the source and return the result.

# File prism/translation/ripper.rb, line 462
def parse
  result.comments.each do |comment|
    location = comment.location
    bounds(location)

    if comment.is_a?(InlineComment)
      on_comment(comment.slice)
    else
      offset = location.start_offset
      lines = comment.slice.lines

      lines.each_with_index do |line, index|
        bounds(location.copy(start_offset: offset))

        if index == 0
          on_embdoc_beg(line)
        elsif index == lines.size - 1
          on_embdoc_end(line)
        else
          on_embdoc(line)
        end

        offset += line.bytesize
      end
    end
  end

  result.magic_comments.each do |magic_comment|
    on_magic_comment(magic_comment.key, magic_comment.value)
  end

  unless result.data_loc.nil?
    on___end__(result.data_loc.slice.each_line.first)
  end

  result.warnings.each do |warning|
    bounds(warning.location)

    if warning.level == :default
      warning(warning.message)
    else
      case warning.type
      when :ambiguous_first_argument_plus
        on_arg_ambiguous("+")
      when :ambiguous_first_argument_minus
        on_arg_ambiguous("-")
      when :ambiguous_slash
        on_arg_ambiguous("/")
      else
        warn(warning.message)
      end
    end
  end

  if error?
    result.errors.each do |error|
      location = error.location
      bounds(location)

      case error.type
      when :alias_argument
        on_alias_error("can't make alias for the number variables", location.slice)
      when :argument_formal_class
        on_param_error("formal argument cannot be a class variable", location.slice)
      when :argument_format_constant
        on_param_error("formal argument cannot be a constant", location.slice)
      when :argument_formal_global
        on_param_error("formal argument cannot be a global variable", location.slice)
      when :argument_formal_ivar
        on_param_error("formal argument cannot be an instance variable", location.slice)
      when :class_name, :module_name
        on_class_name_error("class/module name must be CONSTANT", location.slice)
      else
        on_parse_error(error.message)
      end
    end

    nil
  else
    result.value.accept(self)
  end
end
visit_alias_global_variable_node(node) click to toggle source

alias $foo $bar ^^^^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 561
def visit_alias_global_variable_node(node)
  new_name = visit_alias_global_variable_node_value(node.new_name)
  old_name = visit_alias_global_variable_node_value(node.old_name)

  bounds(node.location)
  on_var_alias(new_name, old_name)
end
visit_alias_method_node(node) click to toggle source

alias foo bar ^^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 551
def visit_alias_method_node(node)
  new_name = visit(node.new_name)
  old_name = visit(node.old_name)

  bounds(node.location)
  on_alias(new_name, old_name)
end
visit_alternation_pattern_node(node) click to toggle source

foo => bar | baz

^^^^^^^^^
# File prism/translation/ripper.rb, line 585
def visit_alternation_pattern_node(node)
  left = visit_pattern_node(node.left)
  right = visit_pattern_node(node.right)

  bounds(node.location)
  on_binary(left, :|, right)
end
visit_and_node(node) click to toggle source

a and b ^^^^^^^

# File prism/translation/ripper.rb, line 605
def visit_and_node(node)
  left = visit(node.left)
  right = visit(node.right)

  bounds(node.location)
  on_binary(left, node.operator.to_sym, right)
end
visit_arguments_node(node) click to toggle source

foo(bar)

^^^
# File prism/translation/ripper.rb, line 796
def visit_arguments_node(node)
  arguments, _ = visit_call_node_arguments(node, nil, false)
  arguments
end
visit_array_node(node) click to toggle source

^^

# File prism/translation/ripper.rb, line 615
def visit_array_node(node)
  case (opening = node.opening)
  when /^%w/
    opening_loc = node.opening_loc
    bounds(opening_loc)
    on_qwords_beg(opening)

    elements = on_qwords_new
    previous = nil

    node.elements.each do |element|
      visit_words_sep(opening_loc, previous, element)

      bounds(element.location)
      elements = on_qwords_add(elements, on_tstring_content(element.content))

      previous = element
    end

    bounds(node.closing_loc)
    on_tstring_end(node.closing)
  when /^%i/
    opening_loc = node.opening_loc
    bounds(opening_loc)
    on_qsymbols_beg(opening)

    elements = on_qsymbols_new
    previous = nil

    node.elements.each do |element|
      visit_words_sep(opening_loc, previous, element)

      bounds(element.location)
      elements = on_qsymbols_add(elements, on_tstring_content(element.value))

      previous = element
    end

    bounds(node.closing_loc)
    on_tstring_end(node.closing)
  when /^%W/
    opening_loc = node.opening_loc
    bounds(opening_loc)
    on_words_beg(opening)

    elements = on_words_new
    previous = nil

    node.elements.each do |element|
      visit_words_sep(opening_loc, previous, element)

      bounds(element.location)
      elements =
        on_words_add(
          elements,
          if element.is_a?(StringNode)
            on_word_add(on_word_new, on_tstring_content(element.content))
          else
            element.parts.inject(on_word_new) do |word, part|
              word_part =
                if part.is_a?(StringNode)
                  bounds(part.location)
                  on_tstring_content(part.content)
                else
                  visit(part)
                end

              on_word_add(word, word_part)
            end
          end
        )

      previous = element
    end

    bounds(node.closing_loc)
    on_tstring_end(node.closing)
  when /^%I/
    opening_loc = node.opening_loc
    bounds(opening_loc)
    on_symbols_beg(opening)

    elements = on_symbols_new
    previous = nil

    node.elements.each do |element|
      visit_words_sep(opening_loc, previous, element)

      bounds(element.location)
      elements =
        on_symbols_add(
          elements,
          if element.is_a?(SymbolNode)
            on_word_add(on_word_new, on_tstring_content(element.value))
          else
            element.parts.inject(on_word_new) do |word, part|
              word_part =
                if part.is_a?(StringNode)
                  bounds(part.location)
                  on_tstring_content(part.content)
                else
                  visit(part)
                end

              on_word_add(word, word_part)
            end
          end
        )

      previous = element
    end

    bounds(node.closing_loc)
    on_tstring_end(node.closing)
  else
    bounds(node.opening_loc)
    on_lbracket(opening)

    elements = visit_arguments(node.elements) unless node.elements.empty?

    bounds(node.closing_loc)
    on_rbracket(node.closing)
  end

  bounds(node.location)
  on_array(elements)
end
visit_array_pattern_node(node) click to toggle source

foo => [bar]

^^^^^
# File prism/translation/ripper.rb, line 775
def visit_array_pattern_node(node)
  constant = visit(node.constant)
  requireds = visit_all(node.requireds) if node.requireds.any?
  rest =
    if (rest_node = node.rest).is_a?(SplatNode)
      if rest_node.expression.nil?
        bounds(rest_node.location)
        on_var_field(nil)
      else
        visit(rest_node.expression)
      end
    end

  posts = visit_all(node.posts) if node.posts.any?

  bounds(node.location)
  on_aryptn(constant, requireds, rest, posts)
end
visit_assoc_node(node) click to toggle source

{ a: 1 }

^^^^
# File prism/translation/ripper.rb, line 803
def visit_assoc_node(node)
  key = visit(node.key)
  value = visit(node.value)

  bounds(node.location)
  on_assoc_new(key, value)
end
visit_assoc_splat_node(node) click to toggle source

def foo(**); bar(**); end

^^

{ **foo }

^^^^^
# File prism/translation/ripper.rb, line 816
def visit_assoc_splat_node(node)
  value = visit(node.value)

  bounds(node.location)
  on_assoc_splat(value)
end
visit_back_reference_read_node(node) click to toggle source

$+ ^^

# File prism/translation/ripper.rb, line 825
def visit_back_reference_read_node(node)
  bounds(node.location)
  on_backref(node.slice)
end
visit_begin_node(node) click to toggle source

begin end ^^^^^^^^^

# File prism/translation/ripper.rb, line 832
def visit_begin_node(node)
  clauses = visit_begin_node_clauses(node.begin_keyword_loc, node, false)

  bounds(node.location)
  on_begin(clauses)
end
visit_block_argument_node(node) click to toggle source

foo(&bar)

^^^^
# File prism/translation/ripper.rb, line 896
def visit_block_argument_node(node)
  visit(node.expression)
end
visit_block_local_variable_node(node) click to toggle source

foo { |; bar| }

^^^
# File prism/translation/ripper.rb, line 902
def visit_block_local_variable_node(node)
  bounds(node.location)
  on_ident(node.name.to_s)
end
visit_block_node(node) click to toggle source

Visit a BlockNode.

# File prism/translation/ripper.rb, line 908
def visit_block_node(node)
  braces = node.opening == "{"
  parameters = visit(node.parameters)

  body =
    case node.body
    when nil
      bounds(node.location)
      stmts = on_stmts_add(on_stmts_new, on_void_stmt)

      bounds(node.location)
      braces ? stmts : on_bodystmt(stmts, nil, nil, nil)
    when StatementsNode
      stmts = node.body.body
      stmts.unshift(nil) if void_stmt?(node.parameters&.location || node.opening_loc, node.body.location, false)
      stmts = visit_statements_node_body(stmts)

      bounds(node.body.location)
      braces ? stmts : on_bodystmt(stmts, nil, nil, nil)
    when BeginNode
      visit_body_node(node.parameters&.location || node.opening_loc, node.body)
    else
      raise
    end

  if braces
    bounds(node.location)
    on_brace_block(parameters, body)
  else
    bounds(node.location)
    on_do_block(parameters, body)
  end
end
visit_block_parameter_node(node) click to toggle source

def foo(&bar); end

^^^^
# File prism/translation/ripper.rb, line 944
def visit_block_parameter_node(node)
  if node.name_loc.nil?
    bounds(node.location)
    on_blockarg(nil)
  else
    bounds(node.name_loc)
    name = visit_token(node.name.to_s)

    bounds(node.location)
    on_blockarg(name)
  end
end
visit_block_parameters_node(node) click to toggle source

A block’s parameters.

# File prism/translation/ripper.rb, line 958
def visit_block_parameters_node(node)
  parameters =
    if node.parameters.nil?
      on_params(nil, nil, nil, nil, nil, nil, nil)
    else
      visit(node.parameters)
    end

  locals =
    if node.locals.any?
      visit_all(node.locals)
    else
      false
    end

  bounds(node.location)
  on_block_var(parameters, locals)
end
visit_break_node(node) click to toggle source

break ^^^^^

break foo ^^^^^^^^^

# File prism/translation/ripper.rb, line 982
def visit_break_node(node)
  if node.arguments.nil?
    bounds(node.location)
    on_break(on_args_new)
  else
    arguments = visit(node.arguments)

    bounds(node.location)
    on_break(arguments)
  end
end
visit_call_and_write_node(node) click to toggle source

foo.bar &&= baz ^^^^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 1194
def visit_call_and_write_node(node)
  receiver = visit(node.receiver)

  bounds(node.call_operator_loc)
  call_operator = visit_token(node.call_operator)

  bounds(node.message_loc)
  message = visit_token(node.message)

  bounds(node.location)
  target = on_field(receiver, call_operator, message)

  bounds(node.operator_loc)
  operator = on_op("&&=")
  value = visit_write_value(node.value)

  bounds(node.location)
  on_opassign(target, operator, value)
end
visit_call_node(node) click to toggle source

foo ^^^

foo.bar ^^^^^^^

foo.bar() {} ^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 1002
def visit_call_node(node)
  if node.call_operator_loc.nil?
    case node.name
    when :[]
      receiver = visit(node.receiver)
      arguments, block = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))

      bounds(node.location)
      call = on_aref(receiver, arguments)

      if block.nil?
        call
      else
        bounds(node.location)
        on_method_add_block(call, block)
      end
    when :[]=
      receiver = visit(node.receiver)

      *arguments, last_argument = node.arguments.arguments
      arguments << node.block if !node.block.nil?

      arguments =
        if arguments.any?
          args = visit_arguments(arguments)

          if !node.block.nil?
            args
          else
            bounds(arguments.first.location)
            on_args_add_block(args, false)
          end
        end

      bounds(node.location)
      call = on_aref_field(receiver, arguments)
      value = visit_write_value(last_argument)

      bounds(last_argument.location)
      on_assign(call, value)
    when :-@, :+@, :~
      receiver = visit(node.receiver)

      bounds(node.location)
      on_unary(node.name, receiver)
    when :!
      receiver = visit(node.receiver)

      bounds(node.location)
      on_unary(node.message == "not" ? :not : :!, receiver)
    when *BINARY_OPERATORS
      receiver = visit(node.receiver)
      value = visit(node.arguments.arguments.first)

      bounds(node.location)
      on_binary(receiver, node.name, value)
    else
      bounds(node.message_loc)
      message = visit_token(node.message, false)

      if node.variable_call?
        on_vcall(message)
      else
        arguments, block = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc || node.location))
        call =
          if node.opening_loc.nil? && arguments&.any?
            bounds(node.location)
            on_command(message, arguments)
          elsif !node.opening_loc.nil?
            bounds(node.location)
            on_method_add_arg(on_fcall(message), on_arg_paren(arguments))
          else
            bounds(node.location)
            on_method_add_arg(on_fcall(message), on_args_new)
          end

        if block.nil?
          call
        else
          bounds(node.block.location)
          on_method_add_block(call, block)
        end
      end
    end
  else
    receiver = visit(node.receiver)

    bounds(node.call_operator_loc)
    call_operator = visit_token(node.call_operator)

    message =
      if node.message_loc.nil?
        :call
      else
        bounds(node.message_loc)
        visit_token(node.message, false)
      end

    if node.name.end_with?("=") && !node.message.end_with?("=") && !node.arguments.nil? && node.block.nil?
      value = visit_write_value(node.arguments.arguments.first)

      bounds(node.location)
      on_assign(on_field(receiver, call_operator, message), value)
    else
      arguments, block = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc || node.location))
      call =
        if node.opening_loc.nil?
          bounds(node.location)

          if node.arguments.nil? && !node.block.is_a?(BlockArgumentNode)
            on_call(receiver, call_operator, message)
          else
            on_command_call(receiver, call_operator, message, arguments)
          end
        else
          bounds(node.opening_loc)
          arguments = on_arg_paren(arguments)

          bounds(node.location)
          on_method_add_arg(on_call(receiver, call_operator, message), arguments)
        end

      if block.nil?
        call
      else
        bounds(node.block.location)
        on_method_add_block(call, block)
      end
    end
  end
end
visit_call_operator_write_node(node) click to toggle source

foo.bar += baz ^^^^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 1172
def visit_call_operator_write_node(node)
  receiver = visit(node.receiver)

  bounds(node.call_operator_loc)
  call_operator = visit_token(node.call_operator)

  bounds(node.message_loc)
  message = visit_token(node.message)

  bounds(node.location)
  target = on_field(receiver, call_operator, message)

  bounds(node.binary_operator_loc)
  operator = on_op("#{node.binary_operator}=")
  value = visit_write_value(node.value)

  bounds(node.location)
  on_opassign(target, operator, value)
end
visit_call_or_write_node(node) click to toggle source

foo.bar ||= baz ^^^^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 1216
def visit_call_or_write_node(node)
  receiver = visit(node.receiver)

  bounds(node.call_operator_loc)
  call_operator = visit_token(node.call_operator)

  bounds(node.message_loc)
  message = visit_token(node.message)

  bounds(node.location)
  target = on_field(receiver, call_operator, message)

  bounds(node.operator_loc)
  operator = on_op("||=")
  value = visit_write_value(node.value)

  bounds(node.location)
  on_opassign(target, operator, value)
end
visit_call_target_node(node) click to toggle source

foo.bar, = 1 ^^^^^^^

# File prism/translation/ripper.rb, line 1238
def visit_call_target_node(node)
  if node.call_operator == "::"
    receiver = visit(node.receiver)

    bounds(node.message_loc)
    message = visit_token(node.message)

    bounds(node.location)
    on_const_path_field(receiver, message)
  else
    receiver = visit(node.receiver)

    bounds(node.call_operator_loc)
    call_operator = visit_token(node.call_operator)

    bounds(node.message_loc)
    message = visit_token(node.message)

    bounds(node.location)
    on_field(receiver, call_operator, message)
  end
end
visit_capture_pattern_node(node) click to toggle source

foo => bar => baz

^^^^^^^^^^
# File prism/translation/ripper.rb, line 1263
def visit_capture_pattern_node(node)
  value = visit(node.value)
  target = visit(node.target)

  bounds(node.location)
  on_binary(value, :"=>", target)
end
visit_case_match_node(node) click to toggle source

case foo; in bar; end ^^^^^^^^^^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 1286
def visit_case_match_node(node)
  predicate = visit(node.predicate)
  clauses =
    node.conditions.reverse_each.inject(visit(node.consequent)) do |consequent, condition|
      on_in(*visit(condition), consequent)
    end

  bounds(node.location)
  on_case(predicate, clauses)
end
visit_case_node(node) click to toggle source

case foo; when bar; end ^^^^^^^^^^^^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 1273
def visit_case_node(node)
  predicate = visit(node.predicate)
  clauses =
    node.conditions.reverse_each.inject(visit(node.consequent)) do |consequent, condition|
      on_when(*visit(condition), consequent)
    end

  bounds(node.location)
  on_case(predicate, clauses)
end
visit_class_node(node) click to toggle source

class Foo; end ^^^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 1299
def visit_class_node(node)
  constant_path =
    if node.constant_path.is_a?(ConstantReadNode)
      bounds(node.constant_path.location)
      on_const_ref(on_const(node.constant_path.name.to_s))
    else
      visit(node.constant_path)
    end

  superclass = visit(node.superclass)
  bodystmt = visit_body_node(node.superclass&.location || node.constant_path.location, node.body, node.superclass.nil?)

  bounds(node.location)
  on_class(constant_path, superclass, bodystmt)
end
visit_class_variable_and_write_node(node) click to toggle source

@@foo &&= bar ^^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 1352
def visit_class_variable_and_write_node(node)
  bounds(node.name_loc)
  target = on_var_field(on_cvar(node.name.to_s))

  bounds(node.operator_loc)
  operator = on_op("&&=")
  value = visit_write_value(node.value)

  bounds(node.location)
  on_opassign(target, operator, value)
end
visit_class_variable_operator_write_node(node) click to toggle source

@@foo += bar ^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 1338
def visit_class_variable_operator_write_node(node)
  bounds(node.name_loc)
  target = on_var_field(on_cvar(node.name.to_s))

  bounds(node.binary_operator_loc)
  operator = on_op("#{node.binary_operator}=")
  value = visit_write_value(node.value)

  bounds(node.location)
  on_opassign(target, operator, value)
end
visit_class_variable_or_write_node(node) click to toggle source

@@foo ||= bar ^^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 1366
def visit_class_variable_or_write_node(node)
  bounds(node.name_loc)
  target = on_var_field(on_cvar(node.name.to_s))

  bounds(node.operator_loc)
  operator = on_op("||=")
  value = visit_write_value(node.value)

  bounds(node.location)
  on_opassign(target, operator, value)
end
visit_class_variable_read_node(node) click to toggle source

@@foo ^^^^^

# File prism/translation/ripper.rb, line 1317
def visit_class_variable_read_node(node)
  bounds(node.location)
  on_var_ref(on_cvar(node.slice))
end
visit_class_variable_target_node(node) click to toggle source

@@foo, = bar ^^^^^

# File prism/translation/ripper.rb, line 1380
def visit_class_variable_target_node(node)
  bounds(node.location)
  on_var_field(on_cvar(node.name.to_s))
end
visit_class_variable_write_node(node) click to toggle source

@@foo = 1 ^^^^^^^^^

@@foo, @@bar = 1 ^^^^^ ^^^^^

# File prism/translation/ripper.rb, line 1327
def visit_class_variable_write_node(node)
  bounds(node.name_loc)
  target = on_var_field(on_cvar(node.name.to_s))
  value = visit_write_value(node.value)

  bounds(node.location)
  on_assign(target, value)
end
visit_constant_and_write_node(node) click to toggle source

Foo &&= bar ^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 1422
def visit_constant_and_write_node(node)
  bounds(node.name_loc)
  target = on_var_field(on_const(node.name.to_s))

  bounds(node.operator_loc)
  operator = on_op("&&=")
  value = visit_write_value(node.value)

  bounds(node.location)
  on_opassign(target, operator, value)
end
visit_constant_operator_write_node(node) click to toggle source

Foo += bar ^^^^^^^^^^^

# File prism/translation/ripper.rb, line 1408
def visit_constant_operator_write_node(node)
  bounds(node.name_loc)
  target = on_var_field(on_const(node.name.to_s))

  bounds(node.binary_operator_loc)
  operator = on_op("#{node.binary_operator}=")
  value = visit_write_value(node.value)

  bounds(node.location)
  on_opassign(target, operator, value)
end
visit_constant_or_write_node(node) click to toggle source

Foo ||= bar ^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 1436
def visit_constant_or_write_node(node)
  bounds(node.name_loc)
  target = on_var_field(on_const(node.name.to_s))

  bounds(node.operator_loc)
  operator = on_op("||=")
  value = visit_write_value(node.value)

  bounds(node.location)
  on_opassign(target, operator, value)
end
visit_constant_path_and_write_node(node) click to toggle source

Foo::Bar &&= baz ^^^^^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 1523
def visit_constant_path_and_write_node(node)
  target = visit_constant_path_write_node_target(node.target)
  value = visit(node.value)

  bounds(node.operator_loc)
  operator = on_op("&&=")
  value = visit_write_value(node.value)

  bounds(node.location)
  on_opassign(target, operator, value)
end
visit_constant_path_node(node) click to toggle source

Foo::Bar ^^^^^^^^

# File prism/translation/ripper.rb, line 1457
def visit_constant_path_node(node)
  if node.parent.nil?
    bounds(node.name_loc)
    child = on_const(node.name.to_s)

    bounds(node.location)
    on_top_const_ref(child)
  else
    parent = visit(node.parent)

    bounds(node.name_loc)
    child = on_const(node.name.to_s)

    bounds(node.location)
    on_const_path_ref(parent, child)
  end
end
visit_constant_path_operator_write_node(node) click to toggle source

Foo::Bar += baz ^^^^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 1509
def visit_constant_path_operator_write_node(node)
  target = visit_constant_path_write_node_target(node.target)
  value = visit(node.value)

  bounds(node.binary_operator_loc)
  operator = on_op("#{node.binary_operator}=")
  value = visit_write_value(node.value)

  bounds(node.location)
  on_opassign(target, operator, value)
end
visit_constant_path_or_write_node(node) click to toggle source

Foo::Bar ||= baz ^^^^^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 1537
def visit_constant_path_or_write_node(node)
  target = visit_constant_path_write_node_target(node.target)
  value = visit(node.value)

  bounds(node.operator_loc)
  operator = on_op("||=")
  value = visit_write_value(node.value)

  bounds(node.location)
  on_opassign(target, operator, value)
end
visit_constant_path_target_node(node) click to toggle source

Foo::Bar, = baz ^^^^^^^^

# File prism/translation/ripper.rb, line 1551
def visit_constant_path_target_node(node)
  visit_constant_path_write_node_target(node)
end
visit_constant_path_write_node(node) click to toggle source

Foo::Bar = 1 ^^^^^^^^^^^^

Foo::Foo, Bar::Bar = 1 ^^^^^^^^ ^^^^^^^^

# File prism/translation/ripper.rb, line 1480
def visit_constant_path_write_node(node)
  target = visit_constant_path_write_node_target(node.target)
  value = visit_write_value(node.value)

  bounds(node.location)
  on_assign(target, value)
end
visit_constant_read_node(node) click to toggle source

Foo ^^^

# File prism/translation/ripper.rb, line 1387
def visit_constant_read_node(node)
  bounds(node.location)
  on_var_ref(on_const(node.name.to_s))
end
visit_constant_target_node(node) click to toggle source

Foo, = bar ^^^

# File prism/translation/ripper.rb, line 1450
def visit_constant_target_node(node)
  bounds(node.location)
  on_var_field(on_const(node.name.to_s))
end
visit_constant_write_node(node) click to toggle source

Foo = 1 ^^^^^^^

Foo, Bar = 1 ^^^ ^^^

# File prism/translation/ripper.rb, line 1397
def visit_constant_write_node(node)
  bounds(node.name_loc)
  target = on_var_field(on_const(node.name.to_s))
  value = visit_write_value(node.value)

  bounds(node.location)
  on_assign(target, value)
end
visit_def_node(node) click to toggle source

def foo; end ^^^^^^^^^^^^

def self.foo; end ^^^^^^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 1560
def visit_def_node(node)
  receiver = visit(node.receiver)
  operator =
    if !node.operator_loc.nil?
      bounds(node.operator_loc)
      visit_token(node.operator)
    end

  bounds(node.name_loc)
  name = visit_token(node.name_loc.slice)

  parameters =
    if node.parameters.nil?
      bounds(node.location)
      on_params(nil, nil, nil, nil, nil, nil, nil)
    else
      visit(node.parameters)
    end

  if !node.lparen_loc.nil?
    bounds(node.lparen_loc)
    parameters = on_paren(parameters)
  end

  bodystmt =
    if node.equal_loc.nil?
      visit_body_node(node.rparen_loc || node.end_keyword_loc, node.body)
    else
      body = visit(node.body.body.first)

      bounds(node.body.location)
      on_bodystmt(body, nil, nil, nil)
    end

  bounds(node.location)
  if receiver.nil?
    on_def(name, parameters, bodystmt)
  else
    on_defs(receiver, operator, name, parameters, bodystmt)
  end
end
visit_defined_node(node) click to toggle source

defined? a ^^^^^^^^^^

defined?(a) ^^^^^^^^^^^

# File prism/translation/ripper.rb, line 1607
def visit_defined_node(node)
  bounds(node.location)
  on_defined(visit(node.value))
end
visit_else_node(node) click to toggle source

if foo then bar else baz end

^^^^^^^^^^^^
# File prism/translation/ripper.rb, line 1614
def visit_else_node(node)
  statements =
    if node.statements.nil?
      [nil]
    else
      body = node.statements.body
      body.unshift(nil) if void_stmt?(node.else_keyword_loc, node.statements.body[0].location, false)
      body
    end

  bounds(node.location)
  on_else(visit_statements_node_body(statements))
end
visit_embedded_statements_node(node) click to toggle source

“foo #{bar}”

^^^^^^
# File prism/translation/ripper.rb, line 1630
def visit_embedded_statements_node(node)
  bounds(node.opening_loc)
  on_embexpr_beg(node.opening)

  statements =
    if node.statements.nil?
      bounds(node.location)
      on_stmts_add(on_stmts_new, on_void_stmt)
    else
      visit(node.statements)
    end

  bounds(node.closing_loc)
  on_embexpr_end(node.closing)

  bounds(node.location)
  on_string_embexpr(statements)
end
visit_embedded_variable_node(node) click to toggle source

“foo #@bar”

^^^^^
# File prism/translation/ripper.rb, line 1651
def visit_embedded_variable_node(node)
  bounds(node.operator_loc)
  on_embvar(node.operator)

  variable = visit(node.variable)

  bounds(node.location)
  on_string_dvar(variable)
end
visit_ensure_node(node) click to toggle source

Visit an EnsureNode node.

# File prism/translation/ripper.rb, line 1662
def visit_ensure_node(node)
  statements =
    if node.statements.nil?
      [nil]
    else
      body = node.statements.body
      body.unshift(nil) if void_stmt?(node.ensure_keyword_loc, body[0].location, false)
      body
    end

  statements = visit_statements_node_body(statements)

  bounds(node.location)
  on_ensure(statements)
end
visit_false_node(node) click to toggle source

false ^^^^^

# File prism/translation/ripper.rb, line 1680
def visit_false_node(node)
  bounds(node.location)
  on_var_ref(on_kw("false"))
end
visit_find_pattern_node(node) click to toggle source

foo => [*, bar, *]

^^^^^^^^^^^
# File prism/translation/ripper.rb, line 1687
def visit_find_pattern_node(node)
  constant = visit(node.constant)
  left =
    if node.left.expression.nil?
      bounds(node.left.location)
      on_var_field(nil)
    else
      visit(node.left.expression)
    end

  requireds = visit_all(node.requireds) if node.requireds.any?
  right =
    if node.right.expression.nil?
      bounds(node.right.location)
      on_var_field(nil)
    else
      visit(node.right.expression)
    end

  bounds(node.location)
  on_fndptn(constant, left, requireds, right)
end
visit_flip_flop_node(node) click to toggle source

if foo .. bar; end

^^^^^^^^^^
# File prism/translation/ripper.rb, line 1712
def visit_flip_flop_node(node)
  left = visit(node.left)
  right = visit(node.right)

  bounds(node.location)
  if node.exclude_end?
    on_dot3(left, right)
  else
    on_dot2(left, right)
  end
end
visit_float_node(node) click to toggle source

1.0 ^^^

# File prism/translation/ripper.rb, line 1726
def visit_float_node(node)
  visit_number_node(node) { |text| on_float(text) }
end
visit_for_node(node) click to toggle source

for foo in bar do end ^^^^^^^^^^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 1732
def visit_for_node(node)
  index = visit(node.index)
  collection = visit(node.collection)
  statements =
    if node.statements.nil?
      bounds(node.location)
      on_stmts_add(on_stmts_new, on_void_stmt)
    else
      visit(node.statements)
    end

  bounds(node.location)
  on_for(index, collection, statements)
end
visit_forwarding_arguments_node(node) click to toggle source

def foo(…); bar(…); end

^^^
# File prism/translation/ripper.rb, line 1749
def visit_forwarding_arguments_node(node)
  bounds(node.location)
  on_args_forward
end
visit_forwarding_parameter_node(node) click to toggle source

def foo(…); end

^^^
# File prism/translation/ripper.rb, line 1756
def visit_forwarding_parameter_node(node)
  bounds(node.location)
  on_args_forward
end
visit_forwarding_super_node(node) click to toggle source

super ^^^^^

super {} ^^^^^^^^

# File prism/translation/ripper.rb, line 1766
def visit_forwarding_super_node(node)
  if node.block.nil?
    bounds(node.location)
    on_zsuper
  else
    block = visit(node.block)

    bounds(node.location)
    on_method_add_block(on_zsuper, block)
  end
end
visit_global_variable_and_write_node(node) click to toggle source

$foo &&= bar ^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 1815
def visit_global_variable_and_write_node(node)
  bounds(node.name_loc)
  target = on_var_field(on_gvar(node.name.to_s))

  bounds(node.operator_loc)
  operator = on_op("&&=")
  value = visit_write_value(node.value)

  bounds(node.location)
  on_opassign(target, operator, value)
end
visit_global_variable_operator_write_node(node) click to toggle source

$foo += bar ^^^^^^^^^^^

# File prism/translation/ripper.rb, line 1801
def visit_global_variable_operator_write_node(node)
  bounds(node.name_loc)
  target = on_var_field(on_gvar(node.name.to_s))

  bounds(node.binary_operator_loc)
  operator = on_op("#{node.binary_operator}=")
  value = visit_write_value(node.value)

  bounds(node.location)
  on_opassign(target, operator, value)
end
visit_global_variable_or_write_node(node) click to toggle source

$foo ||= bar ^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 1829
def visit_global_variable_or_write_node(node)
  bounds(node.name_loc)
  target = on_var_field(on_gvar(node.name.to_s))

  bounds(node.operator_loc)
  operator = on_op("||=")
  value = visit_write_value(node.value)

  bounds(node.location)
  on_opassign(target, operator, value)
end
visit_global_variable_read_node(node) click to toggle source

$foo ^^^^

# File prism/translation/ripper.rb, line 1780
def visit_global_variable_read_node(node)
  bounds(node.location)
  on_var_ref(on_gvar(node.name.to_s))
end
visit_global_variable_target_node(node) click to toggle source

$foo, = bar ^^^^

# File prism/translation/ripper.rb, line 1843
def visit_global_variable_target_node(node)
  bounds(node.location)
  on_var_field(on_gvar(node.name.to_s))
end
visit_global_variable_write_node(node) click to toggle source

$foo = 1 ^^^^^^^^

$foo, $bar = 1 ^^^^ ^^^^

# File prism/translation/ripper.rb, line 1790
def visit_global_variable_write_node(node)
  bounds(node.name_loc)
  target = on_var_field(on_gvar(node.name.to_s))
  value = visit_write_value(node.value)

  bounds(node.location)
  on_assign(target, value)
end
visit_hash_node(node) click to toggle source

{} ^^

# File prism/translation/ripper.rb, line 1850
def visit_hash_node(node)
  elements =
    if node.elements.any?
      args = visit_all(node.elements)

      bounds(node.elements.first.location)
      on_assoclist_from_args(args)
    end

  bounds(node.location)
  on_hash(elements)
end
visit_hash_pattern_node(node) click to toggle source

foo => {}

^^
# File prism/translation/ripper.rb, line 1865
def visit_hash_pattern_node(node)
  constant = visit(node.constant)
  elements =
    if node.elements.any? || !node.rest.nil?
      node.elements.map do |element|
        [
          if (key = element.key).opening_loc.nil?
            visit(key)
          else
            bounds(key.value_loc)
            if (value = key.value).empty?
              on_string_content
            else
              on_string_add(on_string_content, on_tstring_content(value))
            end
          end,
          visit(element.value)
        ]
      end
    end

  rest =
    case node.rest
    when AssocSplatNode
      visit(node.rest.value)
    when NoKeywordsParameterNode
      bounds(node.rest.location)
      on_var_field(visit(node.rest))
    end

  bounds(node.location)
  on_hshptn(constant, elements, rest)
end
visit_if_node(node) click to toggle source

if foo then bar end ^^^^^^^^^^^^^^^^^^^

bar if foo ^^^^^^^^^^

foo ? bar : baz ^^^^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 1907
def visit_if_node(node)
  if node.then_keyword == "?"
    predicate = visit(node.predicate)
    truthy = visit(node.statements.body.first)
    falsy = visit(node.consequent.statements.body.first)

    bounds(node.location)
    on_ifop(predicate, truthy, falsy)
  elsif node.statements.nil? || (node.predicate.location.start_offset < node.statements.location.start_offset)
    predicate = visit(node.predicate)
    statements =
      if node.statements.nil?
        bounds(node.location)
        on_stmts_add(on_stmts_new, on_void_stmt)
      else
        visit(node.statements)
      end
    consequent = visit(node.consequent)

    bounds(node.location)
    if node.if_keyword == "if"
      on_if(predicate, statements, consequent)
    else
      on_elsif(predicate, statements, consequent)
    end
  else
    statements = visit(node.statements.body.first)
    predicate = visit(node.predicate)

    bounds(node.location)
    on_if_mod(predicate, statements)
  end
end
visit_imaginary_node(node) click to toggle source

1i ^^

# File prism/translation/ripper.rb, line 1943
def visit_imaginary_node(node)
  visit_number_node(node) { |text| on_imaginary(text) }
end
visit_implicit_node(node) click to toggle source

{ foo: }

^^^^
# File prism/translation/ripper.rb, line 1949
def visit_implicit_node(node)
end
visit_implicit_rest_node(node) click to toggle source

foo { |bar,| }

^
# File prism/translation/ripper.rb, line 1954
def visit_implicit_rest_node(node)
  bounds(node.location)
  on_excessed_comma
end
visit_in_node(node) click to toggle source

case foo; in bar; end ^^^^^^^^^^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 1961
def visit_in_node(node)
  # This is a special case where we're not going to call on_in directly
  # because we don't have access to the consequent. Instead, we'll return
  # the component parts and let the parent node handle it.
  pattern = visit_pattern_node(node.pattern)
  statements =
    if node.statements.nil?
      bounds(node.location)
      on_stmts_add(on_stmts_new, on_void_stmt)
    else
      visit(node.statements)
    end

  [pattern, statements]
end
visit_index_and_write_node(node) click to toggle source

foo &&= baz ^^^^^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 1996
def visit_index_and_write_node(node)
  receiver = visit(node.receiver)
  arguments, _ = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))

  bounds(node.location)
  target = on_aref_field(receiver, arguments)

  bounds(node.operator_loc)
  operator = on_op("&&=")
  value = visit_write_value(node.value)

  bounds(node.location)
  on_opassign(target, operator, value)
end
visit_index_operator_write_node(node) click to toggle source

foo += baz ^^^^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 1979
def visit_index_operator_write_node(node)
  receiver = visit(node.receiver)
  arguments, _ = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))

  bounds(node.location)
  target = on_aref_field(receiver, arguments)

  bounds(node.binary_operator_loc)
  operator = on_op("#{node.binary_operator}=")
  value = visit_write_value(node.value)

  bounds(node.location)
  on_opassign(target, operator, value)
end
visit_index_or_write_node(node) click to toggle source

foo ||= baz ^^^^^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 2013
def visit_index_or_write_node(node)
  receiver = visit(node.receiver)
  arguments, _ = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))

  bounds(node.location)
  target = on_aref_field(receiver, arguments)

  bounds(node.operator_loc)
  operator = on_op("||=")
  value = visit_write_value(node.value)

  bounds(node.location)
  on_opassign(target, operator, value)
end
visit_index_target_node(node) click to toggle source

foo, = 1 ^^^^^^^^

# File prism/translation/ripper.rb, line 2030
def visit_index_target_node(node)
  receiver = visit(node.receiver)
  arguments, _ = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))

  bounds(node.location)
  on_aref_field(receiver, arguments)
end
visit_instance_variable_and_write_node(node) click to toggle source

@foo &&= bar ^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 2072
def visit_instance_variable_and_write_node(node)
  bounds(node.name_loc)
  target = on_var_field(on_ivar(node.name.to_s))

  bounds(node.operator_loc)
  operator = on_op("&&=")
  value = visit_write_value(node.value)

  bounds(node.location)
  on_opassign(target, operator, value)
end
visit_instance_variable_operator_write_node(node) click to toggle source

@foo += bar ^^^^^^^^^^^

# File prism/translation/ripper.rb, line 2058
def visit_instance_variable_operator_write_node(node)
  bounds(node.name_loc)
  target = on_var_field(on_ivar(node.name.to_s))

  bounds(node.binary_operator_loc)
  operator = on_op("#{node.binary_operator}=")
  value = visit_write_value(node.value)

  bounds(node.location)
  on_opassign(target, operator, value)
end
visit_instance_variable_or_write_node(node) click to toggle source

@foo ||= bar ^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 2086
def visit_instance_variable_or_write_node(node)
  bounds(node.name_loc)
  target = on_var_field(on_ivar(node.name.to_s))

  bounds(node.operator_loc)
  operator = on_op("||=")
  value = visit_write_value(node.value)

  bounds(node.location)
  on_opassign(target, operator, value)
end
visit_instance_variable_read_node(node) click to toggle source

@foo ^^^^

# File prism/translation/ripper.rb, line 2040
def visit_instance_variable_read_node(node)
  bounds(node.location)
  on_var_ref(on_ivar(node.name.to_s))
end
visit_instance_variable_target_node(node) click to toggle source

@foo, = bar ^^^^

# File prism/translation/ripper.rb, line 2100
def visit_instance_variable_target_node(node)
  bounds(node.location)
  on_var_field(on_ivar(node.name.to_s))
end
visit_instance_variable_write_node(node) click to toggle source

@foo = 1 ^^^^^^^^

# File prism/translation/ripper.rb, line 2047
def visit_instance_variable_write_node(node)
  bounds(node.name_loc)
  target = on_var_field(on_ivar(node.name.to_s))
  value = visit_write_value(node.value)

  bounds(node.location)
  on_assign(target, value)
end
visit_integer_node(node) click to toggle source

1 ^

# File prism/translation/ripper.rb, line 2107
def visit_integer_node(node)
  visit_number_node(node) { |text| on_int(text) }
end
visit_interpolated_match_last_line_node(node) click to toggle source

if /foo #{bar}/ then end

^^^^^^^^^^^^
# File prism/translation/ripper.rb, line 2113
def visit_interpolated_match_last_line_node(node)
  bounds(node.opening_loc)
  on_regexp_beg(node.opening)

  bounds(node.parts.first.location)
  parts =
    node.parts.inject(on_regexp_new) do |content, part|
      on_regexp_add(content, visit_string_content(part))
    end

  bounds(node.closing_loc)
  closing = on_regexp_end(node.closing)

  bounds(node.location)
  on_regexp_literal(parts, closing)
end
visit_interpolated_regular_expression_node(node) click to toggle source

/foo #{bar}/ ^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 2132
def visit_interpolated_regular_expression_node(node)
  bounds(node.opening_loc)
  on_regexp_beg(node.opening)

  bounds(node.parts.first.location)
  parts =
    node.parts.inject(on_regexp_new) do |content, part|
      on_regexp_add(content, visit_string_content(part))
    end

  bounds(node.closing_loc)
  closing = on_regexp_end(node.closing)

  bounds(node.location)
  on_regexp_literal(parts, closing)
end
visit_interpolated_string_node(node) click to toggle source

“foo #{bar}” ^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 2151
def visit_interpolated_string_node(node)
  if node.opening&.start_with?("<<~")
    heredoc = visit_heredoc_string_node(node)

    bounds(node.location)
    on_string_literal(heredoc)
  elsif !node.heredoc? && node.parts.length > 1 && node.parts.any? { |part| (part.is_a?(StringNode) || part.is_a?(InterpolatedStringNode)) && !part.opening_loc.nil? }
    first, *rest = node.parts
    rest.inject(visit(first)) do |content, part|
      concat = visit(part)

      bounds(part.location)
      on_string_concat(content, concat)
    end
  else
    bounds(node.parts.first.location)
    parts =
      node.parts.inject(on_string_content) do |content, part|
        on_string_add(content, visit_string_content(part))
      end

    bounds(node.location)
    on_string_literal(parts)
  end
end
visit_interpolated_symbol_node(node) click to toggle source

:“foo #{bar}” ^^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 2179
def visit_interpolated_symbol_node(node)
  bounds(node.parts.first.location)
  parts =
    node.parts.inject(on_string_content) do |content, part|
      on_string_add(content, visit_string_content(part))
    end

  bounds(node.location)
  on_dyna_symbol(parts)
end
visit_interpolated_x_string_node(node) click to toggle source

‘foo #{bar}` ^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 2192
def visit_interpolated_x_string_node(node)
  if node.opening.start_with?("<<~")
    heredoc = visit_heredoc_x_string_node(node)

    bounds(node.location)
    on_xstring_literal(heredoc)
  else
    bounds(node.parts.first.location)
    parts =
      node.parts.inject(on_xstring_new) do |content, part|
        on_xstring_add(content, visit_string_content(part))
      end

    bounds(node.location)
    on_xstring_literal(parts)
  end
end
visit_it_parameters_node(node) click to toggle source

-> { it } ^^^^^^^^^

# File prism/translation/ripper.rb, line 2222
def visit_it_parameters_node(node)
end
visit_keyword_hash_node(node) click to toggle source

foo(bar: baz)

^^^^^^^^
# File prism/translation/ripper.rb, line 2227
def visit_keyword_hash_node(node)
  elements = visit_all(node.elements)

  bounds(node.location)
  on_bare_assoc_hash(elements)
end
visit_keyword_rest_parameter_node(node) click to toggle source

def foo(**bar); end

^^^^^

def foo(**); end

^^
# File prism/translation/ripper.rb, line 2239
def visit_keyword_rest_parameter_node(node)
  if node.name_loc.nil?
    bounds(node.location)
    on_kwrest_param(nil)
  else
    bounds(node.name_loc)
    name = on_ident(node.name.to_s)

    bounds(node.location)
    on_kwrest_param(name)
  end
end
visit_lambda_node(node) click to toggle source

-> {}

# File prism/translation/ripper.rb, line 2253
def visit_lambda_node(node)
  bounds(node.operator_loc)
  on_tlambda(node.operator)

  parameters =
    if node.parameters.is_a?(BlockParametersNode)
      # Ripper does not track block-locals within lambdas, so we skip
      # directly to the parameters here.
      params =
        if node.parameters.parameters.nil?
          bounds(node.location)
          on_params(nil, nil, nil, nil, nil, nil, nil)
        else
          visit(node.parameters.parameters)
        end

      if node.parameters.opening_loc.nil?
        params
      else
        bounds(node.parameters.opening_loc)
        on_paren(params)
      end
    else
      bounds(node.location)
      on_params(nil, nil, nil, nil, nil, nil, nil)
    end

  braces = node.opening == "{"
  if braces
    bounds(node.opening_loc)
    on_tlambeg(node.opening)
  end

  body =
    case node.body
    when nil
      bounds(node.location)
      stmts = on_stmts_add(on_stmts_new, on_void_stmt)

      bounds(node.location)
      braces ? stmts : on_bodystmt(stmts, nil, nil, nil)
    when StatementsNode
      stmts = node.body.body
      stmts.unshift(nil) if void_stmt?(node.parameters&.location || node.opening_loc, node.body.location, false)
      stmts = visit_statements_node_body(stmts)

      bounds(node.body.location)
      braces ? stmts : on_bodystmt(stmts, nil, nil, nil)
    when BeginNode
      visit_body_node(node.opening_loc, node.body)
    else
      raise
    end

  bounds(node.location)
  on_lambda(parameters, body)
end
visit_local_variable_and_write_node(node) click to toggle source

foo &&= bar ^^^^^^^^^^^

# File prism/translation/ripper.rb, line 2350
def visit_local_variable_and_write_node(node)
  bounds(node.name_loc)
  target = on_var_field(on_ident(node.name_loc.slice))

  bounds(node.operator_loc)
  operator = on_op("&&=")
  value = visit_write_value(node.value)

  bounds(node.location)
  on_opassign(target, operator, value)
end
visit_local_variable_operator_write_node(node) click to toggle source

foo += bar ^^^^^^^^^^

# File prism/translation/ripper.rb, line 2336
def visit_local_variable_operator_write_node(node)
  bounds(node.name_loc)
  target = on_var_field(on_ident(node.name_loc.slice))

  bounds(node.binary_operator_loc)
  operator = on_op("#{node.binary_operator}=")
  value = visit_write_value(node.value)

  bounds(node.location)
  on_opassign(target, operator, value)
end
visit_local_variable_or_write_node(node) click to toggle source

foo ||= bar ^^^^^^^^^^^

# File prism/translation/ripper.rb, line 2364
def visit_local_variable_or_write_node(node)
  bounds(node.name_loc)
  target = on_var_field(on_ident(node.name_loc.slice))

  bounds(node.operator_loc)
  operator = on_op("||=")
  value = visit_write_value(node.value)

  bounds(node.location)
  on_opassign(target, operator, value)
end
visit_local_variable_read_node(node) click to toggle source

foo ^^^

# File prism/translation/ripper.rb, line 2313
def visit_local_variable_read_node(node)
  bounds(node.location)

  if node.name == :"0it"
    on_vcall(on_ident(node.slice))
  else
    on_var_ref(on_ident(node.slice))
  end
end
visit_local_variable_target_node(node) click to toggle source

foo, = bar ^^^

# File prism/translation/ripper.rb, line 2378
def visit_local_variable_target_node(node)
  bounds(node.location)
  on_var_field(on_ident(node.name.to_s))
end
visit_local_variable_write_node(node) click to toggle source

foo = 1 ^^^^^^^

# File prism/translation/ripper.rb, line 2325
def visit_local_variable_write_node(node)
  bounds(node.name_loc)
  target = on_var_field(on_ident(node.name_loc.slice))
  value = visit_write_value(node.value)

  bounds(node.location)
  on_assign(target, value)
end
visit_match_last_line_node(node) click to toggle source

if /foo/ then end

^^^^^
# File prism/translation/ripper.rb, line 2385
def visit_match_last_line_node(node)
  bounds(node.opening_loc)
  on_regexp_beg(node.opening)

  bounds(node.content_loc)
  tstring_content = on_tstring_content(node.content)

  bounds(node.closing_loc)
  closing = on_regexp_end(node.closing)

  on_regexp_literal(on_regexp_add(on_regexp_new, tstring_content), closing)
end
visit_match_predicate_node(node) click to toggle source

foo in bar ^^^^^^^^^^

# File prism/translation/ripper.rb, line 2400
def visit_match_predicate_node(node)
  value = visit(node.value)
  pattern = on_in(visit_pattern_node(node.pattern), nil, nil)

  on_case(value, pattern)
end
visit_match_required_node(node) click to toggle source

foo => bar ^^^^^^^^^^

# File prism/translation/ripper.rb, line 2409
def visit_match_required_node(node)
  value = visit(node.value)
  pattern = on_in(visit_pattern_node(node.pattern), nil, nil)

  on_case(value, pattern)
end
visit_match_write_node(node) click to toggle source

/(?<foo>foo)/ =~ bar ^^^^^^^^^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 2418
def visit_match_write_node(node)
  visit(node.call)
end
visit_missing_node(node) click to toggle source

A node that is missing from the syntax tree. This is only used in the case of a syntax error.

# File prism/translation/ripper.rb, line 2424
def visit_missing_node(node)
  raise "Cannot visit missing nodes directly."
end
visit_module_node(node) click to toggle source

module Foo; end ^^^^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 2430
def visit_module_node(node)
  constant_path =
    if node.constant_path.is_a?(ConstantReadNode)
      bounds(node.constant_path.location)
      on_const_ref(on_const(node.constant_path.name.to_s))
    else
      visit(node.constant_path)
    end

  bodystmt = visit_body_node(node.constant_path.location, node.body, true)

  bounds(node.location)
  on_module(constant_path, bodystmt)
end
visit_multi_target_node(node) click to toggle source

(foo, bar), bar = qux ^^^^^^^^^^

# File prism/translation/ripper.rb, line 2447
def visit_multi_target_node(node)
  bounds(node.location)
  targets = visit_multi_target_node_targets(node.lefts, node.rest, node.rights, true)

  if node.lparen_loc.nil?
    targets
  else
    bounds(node.lparen_loc)
    on_mlhs_paren(targets)
  end
end
visit_multi_write_node(node) click to toggle source

foo, bar = baz ^^^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 2501
def visit_multi_write_node(node)
  bounds(node.location)
  targets = visit_multi_target_node_targets(node.lefts, node.rest, node.rights, true)

  unless node.lparen_loc.nil?
    bounds(node.lparen_loc)
    targets = on_mlhs_paren(targets)
  end

  value = visit_write_value(node.value)

  bounds(node.location)
  on_massign(targets, value)
end
visit_next_node(node) click to toggle source

next ^^^^

next foo ^^^^^^^^

# File prism/translation/ripper.rb, line 2521
def visit_next_node(node)
  if node.arguments.nil?
    bounds(node.location)
    on_next(on_args_new)
  else
    arguments = visit(node.arguments)

    bounds(node.location)
    on_next(arguments)
  end
end
visit_nil_node(node) click to toggle source

nil ^^^

# File prism/translation/ripper.rb, line 2535
def visit_nil_node(node)
  bounds(node.location)
  on_var_ref(on_kw("nil"))
end
visit_no_keywords_parameter_node(node) click to toggle source

def foo(**nil); end

^^^^^
# File prism/translation/ripper.rb, line 2542
def visit_no_keywords_parameter_node(node)
  bounds(node.location)
  on_nokw_param(nil)

  :nil
end
visit_numbered_parameters_node(node) click to toggle source

-> { _1 + _2 } ^^^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 2551
def visit_numbered_parameters_node(node)
end
visit_numbered_reference_read_node(node) click to toggle source

$1 ^^

# File prism/translation/ripper.rb, line 2556
def visit_numbered_reference_read_node(node)
  bounds(node.location)
  on_backref(node.slice)
end
visit_optional_keyword_parameter_node(node) click to toggle source

def foo(bar: baz); end

^^^^^^^^
# File prism/translation/ripper.rb, line 2563
def visit_optional_keyword_parameter_node(node)
  bounds(node.name_loc)
  name = on_label("#{node.name}:")
  value = visit(node.value)

  [name, value]
end
visit_optional_parameter_node(node) click to toggle source

def foo(bar = 1); end

^^^^^^^
# File prism/translation/ripper.rb, line 2573
def visit_optional_parameter_node(node)
  bounds(node.name_loc)
  name = visit_token(node.name.to_s)
  value = visit(node.value)

  [name, value]
end
visit_or_node(node) click to toggle source

a or b ^^^^^^

# File prism/translation/ripper.rb, line 2583
def visit_or_node(node)
  left = visit(node.left)
  right = visit(node.right)

  bounds(node.location)
  on_binary(left, node.operator.to_sym, right)
end
visit_parameters_node(node) click to toggle source

def foo(bar, *baz); end

^^^^^^^^^
# File prism/translation/ripper.rb, line 2593
def visit_parameters_node(node)
  requireds = node.requireds.map { |required| required.is_a?(MultiTargetNode) ? visit_destructured_parameter_node(required) : visit(required) } if node.requireds.any?
  optionals = visit_all(node.optionals) if node.optionals.any?
  rest = visit(node.rest)
  posts = node.posts.map { |post| post.is_a?(MultiTargetNode) ? visit_destructured_parameter_node(post) : visit(post) } if node.posts.any?
  keywords = visit_all(node.keywords) if node.keywords.any?
  keyword_rest = visit(node.keyword_rest)
  block = visit(node.block)

  bounds(node.location)
  on_params(requireds, optionals, rest, posts, keywords, keyword_rest, block)
end
visit_parentheses_node(node) click to toggle source

() ^^

(1) ^^^

# File prism/translation/ripper.rb, line 2620
def visit_parentheses_node(node)
  body =
    if node.body.nil?
      on_stmts_add(on_stmts_new, on_void_stmt)
    else
      visit(node.body)
    end

  bounds(node.location)
  on_paren(body)
end
visit_pinned_expression_node(node) click to toggle source

foo => ^(bar)

^^^^^^
# File prism/translation/ripper.rb, line 2634
def visit_pinned_expression_node(node)
  expression = visit(node.expression)

  bounds(node.location)
  on_begin(expression)
end
visit_pinned_variable_node(node) click to toggle source

foo = 1 and bar => ^foo

^^^^
# File prism/translation/ripper.rb, line 2643
def visit_pinned_variable_node(node)
  visit(node.variable)
end
visit_post_execution_node(node) click to toggle source

END {} ^^^^^^

# File prism/translation/ripper.rb, line 2649
def visit_post_execution_node(node)
  statements =
    if node.statements.nil?
      bounds(node.location)
      on_stmts_add(on_stmts_new, on_void_stmt)
    else
      visit(node.statements)
    end

  bounds(node.location)
  on_END(statements)
end
visit_pre_execution_node(node) click to toggle source

BEGIN {} ^^^^^^^^

# File prism/translation/ripper.rb, line 2664
def visit_pre_execution_node(node)
  statements =
    if node.statements.nil?
      bounds(node.location)
      on_stmts_add(on_stmts_new, on_void_stmt)
    else
      visit(node.statements)
    end

  bounds(node.location)
  on_BEGIN(statements)
end
visit_program_node(node) click to toggle source

The top-level program node.

# File prism/translation/ripper.rb, line 2678
def visit_program_node(node)
  body = node.statements.body
  body << nil if body.empty?
  statements = visit_statements_node_body(body)

  bounds(node.location)
  on_program(statements)
end
visit_range_node(node) click to toggle source

0..5 ^^^^

# File prism/translation/ripper.rb, line 2689
def visit_range_node(node)
  left = visit(node.left)
  right = visit(node.right)

  bounds(node.location)
  if node.exclude_end?
    on_dot3(left, right)
  else
    on_dot2(left, right)
  end
end
visit_rational_node(node) click to toggle source

1r ^^

# File prism/translation/ripper.rb, line 2703
def visit_rational_node(node)
  visit_number_node(node) { |text| on_rational(text) }
end
visit_redo_node(node) click to toggle source

redo ^^^^

# File prism/translation/ripper.rb, line 2709
def visit_redo_node(node)
  bounds(node.location)
  on_redo
end
visit_regular_expression_node(node) click to toggle source

/foo/ ^^^^^

# File prism/translation/ripper.rb, line 2716
def visit_regular_expression_node(node)
  bounds(node.opening_loc)
  on_regexp_beg(node.opening)

  if node.content.empty?
    bounds(node.closing_loc)
    closing = on_regexp_end(node.closing)

    on_regexp_literal(on_regexp_new, closing)
  else
    bounds(node.content_loc)
    tstring_content = on_tstring_content(node.content)

    bounds(node.closing_loc)
    closing = on_regexp_end(node.closing)

    on_regexp_literal(on_regexp_add(on_regexp_new, tstring_content), closing)
  end
end
visit_required_keyword_parameter_node(node) click to toggle source

def foo(bar:); end

^^^^
# File prism/translation/ripper.rb, line 2738
def visit_required_keyword_parameter_node(node)
  bounds(node.name_loc)
  [on_label("#{node.name}:"), false]
end
visit_required_parameter_node(node) click to toggle source

def foo(bar); end

^^^
# File prism/translation/ripper.rb, line 2745
def visit_required_parameter_node(node)
  bounds(node.location)
  on_ident(node.name.to_s)
end
visit_rescue_modifier_node(node) click to toggle source

foo rescue bar ^^^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 2752
def visit_rescue_modifier_node(node)
  expression = visit_write_value(node.expression)
  rescue_expression = visit(node.rescue_expression)

  bounds(node.location)
  on_rescue_mod(expression, rescue_expression)
end
visit_rescue_node(node) click to toggle source

begin; rescue; end

^^^^^^^
# File prism/translation/ripper.rb, line 2762
def visit_rescue_node(node)
  exceptions =
    case node.exceptions.length
    when 0
      nil
    when 1
      if (exception = node.exceptions.first).is_a?(SplatNode)
        bounds(exception.location)
        on_mrhs_add_star(on_mrhs_new, visit(exception))
      else
        [visit(node.exceptions.first)]
      end
    else
      bounds(node.location)
      length = node.exceptions.length

      node.exceptions.each_with_index.inject(on_args_new) do |mrhs, (exception, index)|
        arg = visit(exception)

        bounds(exception.location)
        mrhs = on_mrhs_new_from_args(mrhs) if index == length - 1

        if exception.is_a?(SplatNode)
          if index == length - 1
            on_mrhs_add_star(mrhs, arg)
          else
            on_args_add_star(mrhs, arg)
          end
        else
          if index == length - 1
            on_mrhs_add(mrhs, arg)
          else
            on_args_add(mrhs, arg)
          end
        end
      end
    end

  reference = visit(node.reference)
  statements =
    if node.statements.nil?
      bounds(node.location)
      on_stmts_add(on_stmts_new, on_void_stmt)
    else
      visit(node.statements)
    end

  consequent = visit(node.consequent)

  bounds(node.location)
  on_rescue(exceptions, reference, statements, consequent)
end
visit_rest_parameter_node(node) click to toggle source

def foo(*bar); end

^^^^

def foo(*); end

^
# File prism/translation/ripper.rb, line 2820
def visit_rest_parameter_node(node)
  if node.name_loc.nil?
    bounds(node.location)
    on_rest_param(nil)
  else
    bounds(node.name_loc)
    on_rest_param(visit_token(node.name.to_s))
  end
end
visit_retry_node(node) click to toggle source

retry ^^^^^

# File prism/translation/ripper.rb, line 2832
def visit_retry_node(node)
  bounds(node.location)
  on_retry
end
visit_return_node(node) click to toggle source

return ^^^^^^

return 1 ^^^^^^^^

# File prism/translation/ripper.rb, line 2842
def visit_return_node(node)
  if node.arguments.nil?
    bounds(node.location)
    on_return0
  else
    arguments = visit(node.arguments)

    bounds(node.location)
    on_return(arguments)
  end
end
visit_self_node(node) click to toggle source

self ^^^^

# File prism/translation/ripper.rb, line 2856
def visit_self_node(node)
  bounds(node.location)
  on_var_ref(on_kw("self"))
end
visit_shareable_constant_node(node) click to toggle source

A shareable constant.

# File prism/translation/ripper.rb, line 2862
def visit_shareable_constant_node(node)
  visit(node.write)
end
visit_singleton_class_node(node) click to toggle source

class << self; end ^^^^^^^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 2868
def visit_singleton_class_node(node)
  expression = visit(node.expression)
  bodystmt = visit_body_node(node.body&.location || node.end_keyword_loc, node.body)

  bounds(node.location)
  on_sclass(expression, bodystmt)
end
visit_source_encoding_node(node) click to toggle source

__ENCODING__ ^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 2878
def visit_source_encoding_node(node)
  bounds(node.location)
  on_var_ref(on_kw("__ENCODING__"))
end
visit_source_file_node(node) click to toggle source

__FILE__ ^^^^^^^^

# File prism/translation/ripper.rb, line 2885
def visit_source_file_node(node)
  bounds(node.location)
  on_var_ref(on_kw("__FILE__"))
end
visit_source_line_node(node) click to toggle source

__LINE__ ^^^^^^^^

# File prism/translation/ripper.rb, line 2892
def visit_source_line_node(node)
  bounds(node.location)
  on_var_ref(on_kw("__LINE__"))
end
visit_splat_node(node) click to toggle source

foo(*bar)

^^^^

def foo((bar, *baz)); end

^^^^

def foo(*); bar(*); end

^
# File prism/translation/ripper.rb, line 2905
def visit_splat_node(node)
  visit(node.expression)
end
visit_statements_node(node) click to toggle source

A list of statements.

# File prism/translation/ripper.rb, line 2910
def visit_statements_node(node)
  bounds(node.location)
  visit_statements_node_body(node.body)
end
visit_string_node(node) click to toggle source

“foo” ^^^^^

# File prism/translation/ripper.rb, line 2927
def visit_string_node(node)
  if (content = node.content).empty?
    bounds(node.location)
    on_string_literal(on_string_content)
  elsif (opening = node.opening) == "?"
    bounds(node.location)
    on_CHAR("?#{node.content}")
  elsif opening.start_with?("<<~")
    heredoc = visit_heredoc_string_node(node.to_interpolated)

    bounds(node.location)
    on_string_literal(heredoc)
  else
    bounds(node.content_loc)
    tstring_content = on_tstring_content(content)

    bounds(node.location)
    on_string_literal(on_string_add(on_string_content, tstring_content))
  end
end
visit_super_node(node) click to toggle source

super(foo) ^^^^^^^^^^

# File prism/translation/ripper.rb, line 3059
def visit_super_node(node)
  arguments, block = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.rparen_loc || node.location))

  if !node.lparen_loc.nil?
    bounds(node.lparen_loc)
    arguments = on_arg_paren(arguments)
  end

  bounds(node.location)
  call = on_super(arguments)

  if block.nil?
    call
  else
    bounds(node.block.location)
    on_method_add_block(call, block)
  end
end
visit_symbol_node(node) click to toggle source

:foo ^^^^

# File prism/translation/ripper.rb, line 3080
def visit_symbol_node(node)
  if (opening = node.opening)&.match?(/^%s|['"]:?$/)
    bounds(node.value_loc)
    content = on_string_content

    if !(value = node.value).empty?
      content = on_string_add(content, on_tstring_content(value))
    end

    on_dyna_symbol(content)
  elsif (closing = node.closing) == ":"
    bounds(node.location)
    on_label("#{node.value}:")
  elsif opening.nil? && node.closing_loc.nil?
    bounds(node.value_loc)
    on_symbol_literal(visit_token(node.value))
  else
    bounds(node.value_loc)
    on_symbol_literal(on_symbol(visit_token(node.value)))
  end
end
visit_true_node(node) click to toggle source

true ^^^^

# File prism/translation/ripper.rb, line 3104
def visit_true_node(node)
  bounds(node.location)
  on_var_ref(on_kw("true"))
end
visit_undef_node(node) click to toggle source

undef foo ^^^^^^^^^

# File prism/translation/ripper.rb, line 3111
def visit_undef_node(node)
  names = visit_all(node.names)

  bounds(node.location)
  on_undef(names)
end
visit_unless_node(node) click to toggle source

unless foo; bar end ^^^^^^^^^^^^^^^^^^^

bar unless foo ^^^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 3123
def visit_unless_node(node)
  if node.statements.nil? || (node.predicate.location.start_offset < node.statements.location.start_offset)
    predicate = visit(node.predicate)
    statements =
      if node.statements.nil?
        bounds(node.location)
        on_stmts_add(on_stmts_new, on_void_stmt)
      else
        visit(node.statements)
      end
    consequent = visit(node.consequent)

    bounds(node.location)
    on_unless(predicate, statements, consequent)
  else
    statements = visit(node.statements.body.first)
    predicate = visit(node.predicate)

    bounds(node.location)
    on_unless_mod(predicate, statements)
  end
end
visit_until_node(node) click to toggle source

until foo; bar end ^^^^^^^^^^^^^^^^^

bar until foo ^^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 3151
def visit_until_node(node)
  if node.statements.nil? || (node.predicate.location.start_offset < node.statements.location.start_offset)
    predicate = visit(node.predicate)
    statements =
      if node.statements.nil?
        bounds(node.location)
        on_stmts_add(on_stmts_new, on_void_stmt)
      else
        visit(node.statements)
      end

    bounds(node.location)
    on_until(predicate, statements)
  else
    statements = visit(node.statements.body.first)
    predicate = visit(node.predicate)

    bounds(node.location)
    on_until_mod(predicate, statements)
  end
end
visit_when_node(node) click to toggle source

case foo; when bar; end

^^^^^^^^^^^^^
# File prism/translation/ripper.rb, line 3175
def visit_when_node(node)
  # This is a special case where we're not going to call on_when directly
  # because we don't have access to the consequent. Instead, we'll return
  # the component parts and let the parent node handle it.
  conditions = visit_arguments(node.conditions)
  statements =
    if node.statements.nil?
      bounds(node.location)
      on_stmts_add(on_stmts_new, on_void_stmt)
    else
      visit(node.statements)
    end

  [conditions, statements]
end
visit_while_node(node) click to toggle source

while foo; bar end ^^^^^^^^^^^^^^^^^^

bar while foo ^^^^^^^^^^^^^

# File prism/translation/ripper.rb, line 3196
def visit_while_node(node)
  if node.statements.nil? || (node.predicate.location.start_offset < node.statements.location.start_offset)
    predicate = visit(node.predicate)
    statements =
      if node.statements.nil?
        bounds(node.location)
        on_stmts_add(on_stmts_new, on_void_stmt)
      else
        visit(node.statements)
      end

    bounds(node.location)
    on_while(predicate, statements)
  else
    statements = visit(node.statements.body.first)
    predicate = visit(node.predicate)

    bounds(node.location)
    on_while_mod(predicate, statements)
  end
end
visit_x_string_node(node) click to toggle source

‘foo` ^^^^^

# File prism/translation/ripper.rb, line 3220
def visit_x_string_node(node)
  if node.unescaped.empty?
    bounds(node.location)
    on_xstring_literal(on_xstring_new)
  elsif node.opening.start_with?("<<~")
    heredoc = visit_heredoc_x_string_node(node.to_interpolated)

    bounds(node.location)
    on_xstring_literal(heredoc)
  else
    bounds(node.content_loc)
    content = on_tstring_content(node.content)

    bounds(node.location)
    on_xstring_literal(on_xstring_add(on_xstring_new, content))
  end
end
visit_yield_node(node) click to toggle source

yield ^^^^^

yield 1 ^^^^^^^

# File prism/translation/ripper.rb, line 3243
def visit_yield_node(node)
  if node.arguments.nil? && node.lparen_loc.nil?
    bounds(node.location)
    on_yield0
  else
    arguments =
      if node.arguments.nil?
        bounds(node.location)
        on_args_new
      else
        visit(node.arguments)
      end

    unless node.lparen_loc.nil?
      bounds(node.lparen_loc)
      arguments = on_paren(arguments)
    end

    bounds(node.location)
    on_yield(arguments)
  end
end

Private Instance Methods

bounds(location) click to toggle source

This method is responsible for updating lineno and column information to reflect the current node.

This method could be drastically improved with some caching on the start of every line, but for now it’s good enough.

# File prism/translation/ripper.rb, line 3377
def bounds(location)
  @lineno = location.start_line
  @column = location.start_column
end
command?(node) click to toggle source

Returns true if the given node is a command node.

# File prism/translation/ripper.rb, line 1163
        def command?(node)
  node.is_a?(CallNode) &&
    node.opening_loc.nil? &&
    (!node.arguments.nil? || node.block.is_a?(BlockArgumentNode)) &&
    !BINARY_OPERATORS.include?(node.name)
end
compile_error(msg) click to toggle source

This method is called when the parser found syntax error.

# File prism/translation/ripper.rb, line 3415
def compile_error(msg)
end
dedent_string(string, width) click to toggle source

This method is provided by the Ripper C extension. It is called when a string needs to be dedented because of a tilde heredoc. It is expected that it will modify the string in place and return the number of bytes that were removed.

# File prism/translation/ripper.rb, line 3430
def dedent_string(string, width)
  whitespace = 0
  cursor = 0

  while cursor < string.length && string[cursor].match?(/\s/) && whitespace < width
    if string[cursor] == "\t"
      whitespace = ((whitespace / 8 + 1) * 8)
      break if whitespace > width
    else
      whitespace += 1
    end

    cursor += 1
  end

  string.replace(string[cursor..])
  cursor
end
result() click to toggle source

Lazily initialize the parse result.

# File prism/translation/ripper.rb, line 3269
def result
  @result ||=
    begin
      scopes = RUBY_VERSION >= "3.3.0" ? [] : [[]]
      Prism.parse(source, scopes: scopes)
    end
end
trailing_comma?(left, right) click to toggle source

Returns true if there is a comma between the two locations.

# File prism/translation/ripper.rb, line 3282
def trailing_comma?(left, right)
  source.byteslice(left.end_offset...right.start_offset).include?(",")
end
visit_alias_global_variable_node_value(node) click to toggle source

Visit one side of an alias global variable node.

# File prism/translation/ripper.rb, line 570
        def visit_alias_global_variable_node_value(node)
  bounds(node.location)

  case node
  when BackReferenceReadNode
    on_backref(node.slice)
  when GlobalVariableReadNode
    on_gvar(node.name.to_s)
  else
    raise
  end
end
visit_arguments(elements) click to toggle source

Visit a list of elements, like the elements of an array or arguments.

# File prism/translation/ripper.rb, line 756
        def visit_arguments(elements)
  bounds(elements.first.location)
  elements.inject(on_args_new) do |args, element|
    arg = visit(element)
    bounds(element.location)

    case element
    when BlockArgumentNode
      on_args_add_block(args, arg)
    when SplatNode
      on_args_add_star(args, arg)
    else
      on_args_add(args, arg)
    end
  end
end
visit_begin_node_clauses(location, node, allow_newline) click to toggle source

Visit the clauses of a begin node to form an on_bodystmt call.

# File prism/translation/ripper.rb, line 840
        def visit_begin_node_clauses(location, node, allow_newline)
  statements =
    if node.statements.nil?
      on_stmts_add(on_stmts_new, on_void_stmt)
    else
      body = node.statements.body
      body.unshift(nil) if void_stmt?(location, node.statements.body[0].location, allow_newline)

      bounds(node.statements.location)
      visit_statements_node_body(body)
    end

  rescue_clause = visit(node.rescue_clause)
  else_clause =
    unless (else_clause_node = node.else_clause).nil?
      else_statements =
        if else_clause_node.statements.nil?
          [nil]
        else
          body = else_clause_node.statements.body
          body.unshift(nil) if void_stmt?(else_clause_node.else_keyword_loc, else_clause_node.statements.body[0].location, allow_newline)
          body
        end

      bounds(else_clause_node.location)
      visit_statements_node_body(else_statements)
    end
  ensure_clause = visit(node.ensure_clause)

  bounds(node.location)
  on_bodystmt(statements, rescue_clause, else_clause, ensure_clause)
end
visit_body_node(location, node, allow_newline = false) click to toggle source

Visit the body of a structure that can have either a set of statements or statements wrapped in rescue/else/ensure.

# File prism/translation/ripper.rb, line 875
        def visit_body_node(location, node, allow_newline = false)
  case node
  when nil
    bounds(location)
    on_bodystmt(visit_statements_node_body([nil]), nil, nil, nil)
  when StatementsNode
    body = [*node.body]
    body.unshift(nil) if void_stmt?(location, body[0].location, allow_newline)
    stmts = visit_statements_node_body(body)

    bounds(node.body.first.location)
    on_bodystmt(stmts, nil, nil, nil)
  when BeginNode
    visit_begin_node_clauses(location, node, allow_newline)
  else
    raise
  end
end
visit_call_node_arguments(arguments_node, block_node, trailing_comma) click to toggle source

Visit the arguments and block of a call node and return the arguments and block as they should be used.

# File prism/translation/ripper.rb, line 1136
        def visit_call_node_arguments(arguments_node, block_node, trailing_comma)
  arguments = arguments_node&.arguments || []
  block = block_node

  if block.is_a?(BlockArgumentNode)
    arguments << block
    block = nil
  end

  [
    if arguments.length == 1 && arguments.first.is_a?(ForwardingArgumentsNode)
      visit(arguments.first)
    elsif arguments.any?
      args = visit_arguments(arguments)

      if block_node.is_a?(BlockArgumentNode) || arguments.last.is_a?(ForwardingArgumentsNode) || command?(arguments.last) || trailing_comma
        args
      else
        bounds(arguments.first.location)
        on_args_add_block(args, false)
      end
    end,
    visit(block)
  ]
end
visit_constant_path_write_node_target(node) click to toggle source

Visit a constant path that is part of a write node.

# File prism/translation/ripper.rb, line 1489
        def visit_constant_path_write_node_target(node)
  if node.parent.nil?
    bounds(node.name_loc)
    child = on_const(node.name.to_s)

    bounds(node.location)
    on_top_const_field(child)
  else
    parent = visit(node.parent)

    bounds(node.name_loc)
    child = on_const(node.name.to_s)

    bounds(node.location)
    on_const_path_field(parent, child)
  end
end
visit_destructured_parameter_node(node) click to toggle source

Visit a destructured positional parameter node.

# File prism/translation/ripper.rb, line 2607
        def visit_destructured_parameter_node(node)
  bounds(node.location)
  targets = visit_multi_target_node_targets(node.lefts, node.rest, node.rights, false)

  bounds(node.lparen_loc)
  on_mlhs_paren(targets)
end
visit_heredoc_node(parts, base) { |result, on_tstring_content(map(&:content).join)| ... } click to toggle source

Visit a string that is expressed using a <<~ heredoc.

# File prism/translation/ripper.rb, line 2978
        def visit_heredoc_node(parts, base)
  common_whitespace = visit_heredoc_node_whitespace(parts)

  if common_whitespace == 0
    bounds(parts.first.location)

    string = []
    result = base

    parts.each do |part|
      if part.is_a?(StringNode)
        if string.empty?
          string = [part]
        else
          string << part
        end
      else
        unless string.empty?
          bounds(string[0].location)
          result = yield result, on_tstring_content(string.map(&:content).join)
          string = []
        end

        result = yield result, visit(part)
      end
    end

    unless string.empty?
      bounds(string[0].location)
      result = yield result, on_tstring_content(string.map(&:content).join)
    end

    result
  else
    bounds(parts.first.location)
    result =
      parts.inject(base) do |string_content, part|
        yield string_content, visit_string_content(part)
      end

    bounds(parts.first.location)
    on_heredoc_dedent(result, common_whitespace)
  end
end
visit_heredoc_node_whitespace(parts) click to toggle source

Ripper gives back the escaped string content but strips out the common leading whitespace. Prism gives back the unescaped string content and a location for the escaped string content. Unfortunately these don’t work well together, so here we need to re-derive the common leading whitespace.

# File prism/translation/ripper.rb, line 2953
        def visit_heredoc_node_whitespace(parts)
  common_whitespace = nil
  dedent_next = true

  parts.each do |part|
    if part.is_a?(StringNode)
      if dedent_next && !(content = part.content).chomp.empty?
        common_whitespace = [
          common_whitespace || Float::INFINITY,
          content[/\A\s*/].each_char.inject(0) do |part_whitespace, char|
            char == "\t" ? ((part_whitespace / 8 + 1) * 8) : (part_whitespace + 1)
          end
        ].min
      end

      dedent_next = true
    else
      dedent_next = false
    end
  end

  common_whitespace || 0
end
visit_heredoc_string_node(node) click to toggle source

Visit a heredoc node that is representing a string.

# File prism/translation/ripper.rb, line 3024
        def visit_heredoc_string_node(node)
  bounds(node.opening_loc)
  on_heredoc_beg(node.opening)

  bounds(node.location)
  result =
    visit_heredoc_node(node.parts, on_string_content) do |parts, part|
      on_string_add(parts, part)
    end

  bounds(node.closing_loc)
  on_heredoc_end(node.closing)

  result
end
visit_heredoc_x_string_node(node) click to toggle source

Visit a heredoc node that is representing an xstring.

# File prism/translation/ripper.rb, line 3041
        def visit_heredoc_x_string_node(node)
  bounds(node.opening_loc)
  on_heredoc_beg(node.opening)

  bounds(node.location)
  result =
    visit_heredoc_node(node.parts, on_xstring_new) do |parts, part|
      on_xstring_add(parts, part)
    end

  bounds(node.closing_loc)
  on_heredoc_end(node.closing)

  result
end
visit_multi_target_node_targets(lefts, rest, rights, skippable) click to toggle source

Visit the targets of a multi-target node.

# File prism/translation/ripper.rb, line 2460
        def visit_multi_target_node_targets(lefts, rest, rights, skippable)
  if skippable && lefts.length == 1 && lefts.first.is_a?(MultiTargetNode) && rest.nil? && rights.empty?
    return visit(lefts.first)
  end

  mlhs = on_mlhs_new

  lefts.each do |left|
    bounds(left.location)
    mlhs = on_mlhs_add(mlhs, visit(left))
  end

  case rest
  when nil
    # do nothing
  when ImplicitRestNode
    # these do not get put into the generated tree
    bounds(rest.location)
    on_excessed_comma
  else
    bounds(rest.location)
    mlhs = on_mlhs_add_star(mlhs, visit(rest))
  end

  if rights.any?
    bounds(rights.first.location)
    post = on_mlhs_new

    rights.each do |right|
      bounds(right.location)
      post = on_mlhs_add(post, visit(right))
    end

    mlhs = on_mlhs_add_post(mlhs, post)
  end

  mlhs
end
visit_number_node(node) { |slice| ... } click to toggle source

Visit a node that represents a number. We need to explicitly handle the unary - operator.

# File prism/translation/ripper.rb, line 3321
def visit_number_node(node)
  slice = node.slice
  location = node.location

  if slice[0] == "-"
    bounds(location.copy(start_offset: location.start_offset + 1))
    value = yield slice[1..-1]

    bounds(node.location)
    on_unary(:-@, value)
  else
    bounds(location)
    yield slice
  end
end
visit_pattern_node(node) click to toggle source

Visit a pattern within a pattern match. This is used to bypass the parenthesis node that can be used to wrap patterns.

# File prism/translation/ripper.rb, line 595
        def visit_pattern_node(node)
  if node.is_a?(ParenthesesNode)
    visit(node.body)
  else
    visit(node)
  end
end
visit_statements_node_body(body) click to toggle source

Visit the list of statements of a statements node. We support nil statements in the list. This would normally not be allowed by the structure of the prism parse tree, but we manually add them here so that we can mirror Ripper’s void stmt.

# File prism/translation/ripper.rb, line 2919
        def visit_statements_node_body(body)
  body.inject(on_stmts_new) do |stmts, stmt|
    on_stmts_add(stmts, stmt.nil? ? on_void_stmt : visit(stmt))
  end
end
visit_string_content(part) click to toggle source

Visit an individual part of a string-like node.

# File prism/translation/ripper.rb, line 2211
        def visit_string_content(part)
  if part.is_a?(StringNode)
    bounds(part.content_loc)
    on_tstring_content(part.content)
  else
    visit(part)
  end
end
visit_token(token, allow_keywords = true) click to toggle source

Visit the string content of a particular node. This method is used to split into the various token types.

# File prism/translation/ripper.rb, line 3294
def visit_token(token, allow_keywords = true)
  case token
  when "."
    on_period(token)
  when "`"
    on_backtick(token)
  when *(allow_keywords ? KEYWORDS : [])
    on_kw(token)
  when /^_/
    on_ident(token)
  when /^[[:upper:]]\w*$/
    on_const(token)
  when /^@@/
    on_cvar(token)
  when /^@/
    on_ivar(token)
  when /^\$/
    on_gvar(token)
  when /^[[:punct:]]/
    on_op(token)
  else
    on_ident(token)
  end
end
visit_words_sep(opening_loc, previous, current) click to toggle source

Dispatch a words_sep event that contains the space between the elements of list literals.

# File prism/translation/ripper.rb, line 745
        def visit_words_sep(opening_loc, previous, current)
  end_offset = (previous.nil? ? opening_loc : previous.location).end_offset
  start_offset = current.location.start_offset

  if end_offset != start_offset
    bounds(current.location.copy(start_offset: end_offset))
    on_words_sep(source.byteslice(end_offset...start_offset))
  end
end
visit_write_value(node) click to toggle source

Visit a node that represents a write value. This is used to handle the special case of an implicit array that is generated without brackets.

# File prism/translation/ripper.rb, line 3339
def visit_write_value(node)
  if node.is_a?(ArrayNode) && node.opening_loc.nil?
    elements = node.elements
    length = elements.length

    bounds(elements.first.location)
    elements.each_with_index.inject((elements.first.is_a?(SplatNode) && length == 1) ? on_mrhs_new : on_args_new) do |args, (element, index)|
      arg = visit(element)
      bounds(element.location)

      if index == length - 1
        if element.is_a?(SplatNode)
          mrhs = index == 0 ? args : on_mrhs_new_from_args(args)
          on_mrhs_add_star(mrhs, arg)
        else
          on_mrhs_add(on_mrhs_new_from_args(args), arg)
        end
      else
        case element
        when BlockArgumentNode
          on_args_add_block(args, arg)
        when SplatNode
          on_args_add_star(args, arg)
        else
          on_args_add(args, arg)
        end
      end
    end
  else
    visit(node)
  end
end
void_stmt?(left, right, allow_newline) click to toggle source

Returns true if there is a semicolon between the two locations.

# File prism/translation/ripper.rb, line 3287
def void_stmt?(left, right, allow_newline)
  pattern = allow_newline ? /[;\n]/ : /;/
  source.byteslice(left.end_offset...right.start_offset).match?(pattern)
end
warn(fmt, *args) click to toggle source

This method is called when weak warning is produced by the parser. fmt and args is printf style.

# File prism/translation/ripper.rb, line 3406
def warn(fmt, *args)
end
warning(fmt, *args) click to toggle source

This method is called when strong warning is produced by the parser. fmt and args is printf style.

# File prism/translation/ripper.rb, line 3411
def warning(fmt, *args)
end