123456789_123456789_123456789_123456789_123456789_

Class: Prism::Node

Overview

This represents a node in the tree. It is the parent class of all of the various node types.

Class Method Summary

  • .fields

    Returns a list of the fields that exist for this node class.

  • .type

    Similar to #type, this method returns a symbol that you can use for splitting on the type of the node without having to do a long === chain.

Instance Attribute Summary

Instance Method Summary

  • #accept(visitor)

    Accepts a visitor and calls back into the specialized visit function.

  • #breadth_first_search(&block)

    Returns the first node that matches the given block when visited in a depth-first search.

  • #child_nodes (also: #deconstruct)

    Returns an array of child nodes, including nils in the place of optional nodes that were not present.

  • #comment_targets

    Returns an array of child nodes and locations that could potentially have comments attached to them.

  • #compact_child_nodes

    Returns an array of child nodes, excluding any nils in the place of optional nodes that were not present.

  • #deconstruct

    Alias for #child_nodes.

  • #end_offset

    The end offset of the node in the source.

  • #inspect

    Returns a string representation of the node.

  • #location

    A Location instance that represents the location of this node in the source.

  • #pretty_print(q)

    Similar to inspect, but respects the current level of indentation given by the pretty print object.

  • #script_lines

    Alias for #source_lines.

  • #slice

    Slice the location of the node from the source.

  • #slice_lines

    Slice the location of the node from the source, starting at the beginning of the line that the location starts on, ending at the end of the line that the location ends on.

  • #source_lines (also: #script_lines)

    Returns all of the lines of the source code associated with this node.

  • #start_offset

    The start offset of the node in the source.

  • #to_dot

    Convert this node into a graphviz dot graph string.

  • #tunnel(line, column)

    Returns a list of nodes that are descendants of this node that contain the given line and column.

  • #type

    Sometimes you want to check an instance of a node against a list of classes to see what kind of behavior to perform.

  • #deprecated(*replacements) Internal use only
  • #newline_flag!(lines) Internal use only

Class Method Details

.fields

Returns a list of the fields that exist for this node class. Fields describe the structure of the node. This kind of reflection is useful for things like recursively visiting each node and field in the tree.

Raises:

  • (NoMethodError)
[ GitHub ]

  
# File 'lib/prism/node.rb', line 153

def self.fields
  # This method should only be called on subclasses of Node, not Node
  # itself.
  raise NoMethodError, "undefined method `fields' for #{inspect}" if self == Node

  Reflection.fields_for(self)
end

.type

Similar to #type, this method returns a symbol that you can use for splitting on the type of the node without having to do a long === chain. Note that like #type, it will still be slower than using == for a single class, but should be faster in a case statement or an array comparison.

Raises:

  • (NoMethodError)
[ GitHub ]

  
# File 'lib/prism/node.rb', line 218

def self.type
  raise NoMethodError, "undefined method `type' for #{inspect}"
end

Instance Attribute Details

#flags (readonly, protected)

An bitset of flags for this node. There are certain flags that are common for all nodes, and then some nodes have specific flags.

[ GitHub ]

  
# File 'lib/prism/node.rb', line 68

attr_reader :flags

#newline? ⇒ Boolean (readonly)

Returns true if the node has the newline flag set.

[ GitHub ]

  
# File 'lib/prism/node.rb', line 72

def newline?
  flags.anybits?(NodeFlags::NEWLINE)
end

#newline_flag? ⇒ Boolean (readonly)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/prism/parse_result/newlines.rb', line 66

def newline_flag? # :nodoc:
  !!defined?(@newline_flag)
end

#node_id (readonly)

A unique identifier for this node. This is used in a very specific use case where you want to keep around a reference to a node without having to keep around the syntax tree in memory. This unique identifier will be consistent across multiple parses of the same source code.

[ GitHub ]

  
# File 'lib/prism/node.rb', line 21

attr_reader :node_id

#source (readonly, private)

A pointer to the source that this node was created from.

[ GitHub ]

  
# File 'lib/prism/node.rb', line 14

attr_reader :source

#static_literal? ⇒ Boolean (readonly)

Returns true if the node has the static literal flag set.

[ GitHub ]

  
# File 'lib/prism/node.rb', line 77

def static_literal?
  flags.anybits?(NodeFlags::STATIC_LITERAL)
end

Instance Method Details

#accept(visitor)

Accepts a visitor and calls back into the specialized visit function.

Raises:

  • (NoMethodError)
[ GitHub ]

  
# File 'lib/prism/node.rb', line 169

def accept(visitor)
  raise NoMethodError, "undefined method `accept' for #{inspect}"
end

#breadth_first_search(&block)

Returns the first node that matches the given block when visited in a depth-first search. This is useful for finding a node that matches a particular condition.

node.breadth_first_search { |node| node.node_id == node_id }
[ GitHub ]

  
# File 'lib/prism/node.rb', line 139

def breadth_first_search(&block)
  queue = [self] #: Array[Prism::node]

  while (node = queue.shift)
    return node if yield node
    queue.concat(node.compact_child_nodes)
  end

  nil
end

#child_nodes Also known as: #deconstruct

Returns an array of child nodes, including nils in the place of optional nodes that were not present.

Raises:

  • (NoMethodError)
[ GitHub ]

  
# File 'lib/prism/node.rb', line 175

def child_nodes
  raise NoMethodError, "undefined method `child_nodes' for #{inspect}"
end

#comment_targets

Returns an array of child nodes and locations that could potentially have comments attached to them.

Raises:

  • (NoMethodError)
[ GitHub ]

  
# File 'lib/prism/node.rb', line 189

def comment_targets
  raise NoMethodError, "undefined method `comment_targets' for #{inspect}"
end

#compact_child_nodes

Returns an array of child nodes, excluding any nils in the place of optional nodes that were not present.

Raises:

  • (NoMethodError)
[ GitHub ]

  
# File 'lib/prism/node.rb', line 183

def compact_child_nodes
  raise NoMethodError, "undefined method `compact_child_nodes' for #{inspect}"
end

#deconstruct

Alias for #child_nodes.

[ GitHub ]

  
# File 'lib/prism/node.rb', line 179

alias deconstruct child_nodes

#deprecated(*replacements)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/prism/node_ext.rb', line 10

def deprecated(*replacements) # :nodoc:
  location = caller_locations(1, 1)
  location = location[0].label if location
  suggest = replacements.map { |replacement| "#{self.class}##{replacement}" }

  warn(<<~MSG, uplevel: 1, category: :deprecated)
    [deprecation]: #{self.class}##{location} is deprecated and will be \
    removed in the next major version. Use #{suggest.join("/")} instead.
    #{(caller(1, 3) || []).join("\n")}
  MSG
end

#end_offset

The end offset of the node in the source. This method is effectively a delegate method to the location object.

[ GitHub ]

  
# File 'lib/prism/node.rb', line 40

def end_offset
  location = @location
  location.is_a?(Location) ? location.end_offset : ((location >> 32) + (location & 0xFFFFFFFF))
end

#inspect

Returns a string representation of the node.

Raises:

  • (NoMethodError)
[ GitHub ]

  
# File 'lib/prism/node.rb', line 194

def inspect
  raise NoMethodError, "undefined method `inspect' for #{inspect}"
end

#location

A Location instance that represents the location of this node in the source.

[ GitHub ]

  
# File 'lib/prism/node.rb', line 25

def location
  location = @location
  return location if location.is_a?(Location)
  @location = Location.new(source, location >> 32, location & 0xFFFFFFFF)
end

#newline_flag!(lines)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/prism/parse_result/newlines.rb', line 70

def newline_flag!(lines) # :nodoc:
  line = location.start_line
  unless lines[line]
    lines[line] = true
    @newline_flag = true
  end
end

#pretty_print(q)

Similar to inspect, but respects the current level of indentation given by the pretty print object.

[ GitHub ]

  
# File 'lib/prism/node.rb', line 83

def pretty_print(q)
  q.seplist(inspect.chomp.each_line, -> { q.breakable }) do |line|
    q.text(line.chomp)
  end
  q.current_group.break
end

#script_lines

Alias for #source_lines.

[ GitHub ]

  
# File 'lib/prism/node.rb', line 52

alias script_lines source_lines

#slice

Slice the location of the node from the source.

[ GitHub ]

  
# File 'lib/prism/node.rb', line 55

def slice
  location.slice
end

#slice_lines

Slice the location of the node from the source, starting at the beginning of the line that the location starts on, ending at the end of the line that the location ends on.

[ GitHub ]

  
# File 'lib/prism/node.rb', line 62

def slice_lines
  location.slice_lines
end

#source_lines Also known as: #script_lines

Returns all of the lines of the source code associated with this node.

[ GitHub ]

  
# File 'lib/prism/node.rb', line 46

def source_lines
  location.source_lines
end

#start_offset

The start offset of the node in the source. This method is effectively a delegate method to the location object.

[ GitHub ]

  
# File 'lib/prism/node.rb', line 33

def start_offset
  location = @location
  location.is_a?(Location) ? location.start_offset : location >> 32
end

#to_dot

Convert this node into a graphviz dot graph string.

[ GitHub ]

  
# File 'lib/prism/node.rb', line 91

def to_dot
  # @type self: node
  DotVisitor.new.tap { |visitor| accept(visitor) }.to_dot
end

#tunnel(line, column)

Returns a list of nodes that are descendants of this node that contain the given line and column. This is useful for locating a node that is selected based on the line and column of the source code.

Important to note is that the column given to this method should be in bytes, as opposed to characters or code units.

[ GitHub ]

  
# File 'lib/prism/node.rb', line 102

def tunnel(line, column)
  queue = [self] #: Array[Prism::node]
  result = []

  while (node = queue.shift)
    result << node

    node.compact_child_nodes.each do |child_node|
      child_location = child_node.location

      start_line = child_location.start_line
      end_line = child_location.end_line

      if start_line == end_line
        if line == start_line && column >= child_location.start_column && column < child_location.end_column
          queue << child_node
          break
        end
      elsif (line == start_line && column >= child_location.start_column) || (line == end_line && column < child_location.end_column)
        queue << child_node
        break
      elsif line > start_line && line < end_line
        queue << child_node
        break
      end
    end
  end

  result
end

#type

Sometimes you want to check an instance of a node against a list of classes to see what kind of behavior to perform. Usually this is done by calling [cls1, cls2].include?(node.class) or putting the node into a case statement and doing case node; when cls1; when cls2; end. Both of these approaches are relatively slow because of the constant lookups, method calls, and/or array allocations.

Instead, you can call #type, which will return to you a symbol that you can use for comparison. This is faster than the other approaches because it uses a single integer comparison, but also because if you're on CRuby you can take advantage of the fact that case statements with all symbol keys will use a jump table.

Raises:

  • (NoMethodError)
[ GitHub ]

  
# File 'lib/prism/node.rb', line 210

def type
  raise NoMethodError, "undefined method `type' for #{inspect}"
end