class Astrolabe::Node

‘Astrolabe::Node` is a subclass of `Parser::AST::Node`. It provides an access to parent node and an object-oriented way to handle AST with the power of `Enumerable`.

Though not described in the auto-generated API documentation, it has predicate methods for every node type. These methods would be useful especially when combined with ‘Enumerable` methods.

@example

node.send_type?    # Equivalent to: `node.type == :send`
node.op_asgn_type? # Equivalent to: `node.type == :op_asgn`

# Non-word characters (other than a-zA-Z0-9_) in type names are omitted.
node.defined_type? # Equivalent to: `node.type == :defined?`

# Find the first lvar node under the receiver node.
lvar_node = node.each_descendant.find(&:lvar_type?)

Public Class Methods

new(type, children = [], properties = {}) click to toggle source

@see rubydoc.info/gems/ast/AST/Node:initialize

Calls superclass method
# File lib/astrolabe/node.rb, line 23
def initialize(type, children = [], properties = {})
  @mutable_attributes = {}

  # ::AST::Node#initialize freezes itself.
  super

  # #parent= would be invoked multiple times for a node because there are pending nodes while
  # constructing AST and they are replaced later.
  # For example, `lvar` and `send` type nodes are initially created as an `ident` type node and
  # fixed to each type later.
  # So, the #parent attribute needs to be mutable.
  each_child_node do |child_node|
    child_node.parent = self
  end
end

Public Instance Methods

ancestors() click to toggle source

Returns an array of ancestor nodes. This is a shorthand for ‘node.each_ancestor.to_a`.

@return [Array<Node>] an array of ancestor nodes

# File lib/astrolabe/node.rb, line 109
def ancestors
  each_ancestor.to_a
end
child_nodes() click to toggle source

Returns an array of child nodes. This is a shorthand for ‘node.each_child_node.to_a`.

@return [Array<Node>] an array of child nodes

# File lib/astrolabe/node.rb, line 151
def child_nodes
  each_child_node.to_a
end
descendants() click to toggle source

Returns an array of descendant nodes. This is a shorthand for ‘node.each_descendant.to_a`.

@return [Array<Node>] an array of descendant nodes

# File lib/astrolabe/node.rb, line 191
def descendants
  each_descendant.to_a
end
each_ancestor(*types, &block) click to toggle source

Calls the given block for each ancestor node in the order from parent to root. If no block is given, an ‘Enumerator` is returned.

@overload each_ancestor

Yield all nodes.

@overload each_ancestor(type)

Yield only nodes matching the type.
@param [Symbol] type a node type

@overload each_ancestor(type_a, type_b, …)

Yield only nodes matching any of the types.
@param [Symbol] type_a a node type
@param [Symbol] type_b a node type

@overload each_ancestor(types)

Yield only nodes matching any of types in the array.
@param [Array<Symbol>] types an array containing node types

@yieldparam [Node] node each ancestor node @return [self] if a block is given @return [Enumerator] if no block is given

# File lib/astrolabe/node.rb, line 91
def each_ancestor(*types, &block)
  return to_enum(__method__, *types) unless block_given?

  types.flatten!

  if types.empty?
    visit_ancestors(&block)
  else
    visit_ancestors_with_types(types, &block)
  end

  self
end
each_child_node(*types) { |child| ... } click to toggle source

Calls the given block for each child node. If no block is given, an ‘Enumerator` is returned.

Note that this is different from ‘node.children.each { |child| … }` which yields all children including non-node element.

@overload each_child_node

Yield all nodes.

@overload each_child_node(type)

Yield only nodes matching the type.
@param [Symbol] type a node type

@overload each_child_node(type_a, type_b, …)

Yield only nodes matching any of the types.
@param [Symbol] type_a a node type
@param [Symbol] type_b a node type

@overload each_child_node(types)

Yield only nodes matching any of types in the array.
@param [Array<Symbol>] types an array containing node types

@yieldparam [Node] node each child node @return [self] if a block is given @return [Enumerator] if no block is given

# File lib/astrolabe/node.rb, line 134
def each_child_node(*types)
  return to_enum(__method__, *types) unless block_given?

  types.flatten!

  children.each do |child|
    next unless child.is_a?(Node)
    yield child if types.empty? || types.include?(child.type)
  end

  self
end
each_descendant(*types, &block) click to toggle source

Calls the given block for each descendant node with depth first order. If no block is given, an ‘Enumerator` is returned.

@overload each_descendant

Yield all nodes.

@overload each_descendant(type)

Yield only nodes matching the type.
@param [Symbol] type a node type

@overload each_descendant(type_a, type_b, …)

Yield only nodes matching any of the types.
@param [Symbol] type_a a node type
@param [Symbol] type_b a node type

@overload each_descendant(types)

Yield only nodes matching any of types in the array.
@param [Array<Symbol>] types an array containing node types

@yieldparam [Node] node each descendant node @return [self] if a block is given @return [Enumerator] if no block is given

# File lib/astrolabe/node.rb, line 173
def each_descendant(*types, &block)
  return to_enum(__method__, *types) unless block_given?

  types.flatten!

  if types.empty?
    visit_descendants(&block)
  else
    visit_descendants_with_types(types, &block)
  end

  self
end
each_node(*types) { |self| ... } click to toggle source

Calls the given block for the receiver and each descendant node with depth first order. If no block is given, an ‘Enumerator` is returned.

This method would be useful when you treat the receiver node as a root of tree and want to iterate all nodes in the tree.

@overload each_node

Yield all nodes.

@overload each_node(type)

Yield only nodes matching the type.
@param [Symbol] type a node type

@overload each_node(type_a, type_b, …)

Yield only nodes matching any of the types.
@param [Symbol] type_a a node type
@param [Symbol] type_b a node type

@overload each_node(types)

Yield only nodes matching any of types in the array.
@param [Array<Symbol>] types an array containing node types

@yieldparam [Node] node each node @return [self] if a block is given @return [Enumerator] if no block is given

# File lib/astrolabe/node.rb, line 216
def each_node(*types, &block)
  return to_enum(__method__, *types) unless block_given?

  types.flatten!

  yield self if types.empty? || types.include?(type)

  if types.empty?
    visit_descendants(&block)
  else
    visit_descendants_with_types(types, &block)
  end

  self
end
parent() click to toggle source

Returns the parent node, or ‘nil` if the receiver is a root node.

@return [Node, nil] the parent node or ‘nil`

# File lib/astrolabe/node.rb, line 49
def parent
  @mutable_attributes[:parent]
end
root?() click to toggle source

Returns whether the receiver is a root node or not.

@return [Boolean] whether the receiver is a root node or not

# File lib/astrolabe/node.rb, line 62
def root?
  parent.nil?
end
sibling_index() click to toggle source

Returns the index of the receiver node in its siblings.

@return [Integer] the index of the receiver node in its siblings

# File lib/astrolabe/node.rb, line 69
def sibling_index
  parent.children.index { |sibling| sibling.equal?(self) }
end

Protected Instance Methods

parent=(node) click to toggle source
# File lib/astrolabe/node.rb, line 53
def parent=(node)
  @mutable_attributes[:parent] = node
end
visit_descendants() { |child| ... } click to toggle source
# File lib/astrolabe/node.rb, line 234
def visit_descendants(&block)
  children.each do |child|
    next unless child.is_a?(Node)
    yield child
    child.visit_descendants(&block)
  end
end
visit_descendants_with_types(types) { |child| ... } click to toggle source
# File lib/astrolabe/node.rb, line 242
def visit_descendants_with_types(types, &block)
  children.each do |child|
    next unless child.is_a?(Node)
    yield child if types.include?(child.type)
    child.visit_descendants_with_types(types, &block)
  end
end

Private Instance Methods

visit_ancestors() { |current_node| ... } click to toggle source
# File lib/astrolabe/node.rb, line 252
def visit_ancestors
  last_node = self

  while (current_node = last_node.parent)
    yield current_node
    last_node = current_node
  end
end
visit_ancestors_with_types(types) { |current_node| ... } click to toggle source
# File lib/astrolabe/node.rb, line 261
def visit_ancestors_with_types(types)
  last_node = self

  while (current_node = last_node.parent)
    yield current_node if types.include?(current_node.type)
    last_node = current_node
  end
end