Class: Astrolabe::Node

Inherits:
Parser::AST::Node
  • Object
show all
Defined in:
lib/astrolabe/node.rb

Overview

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.

Examples:

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?)

Instance Method Summary collapse

Constructor Details

#initialize(type, children = [], properties = {}) ⇒ Node

Returns a new instance of Node.


23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 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

Instance Method Details

#ancestorsArray<Node>

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

Returns:

  • (Array<Node>)

    an array of ancestor nodes


109
110
111
# File 'lib/astrolabe/node.rb', line 109

def ancestors
  each_ancestor.to_a
end

#child_nodesArray<Node>

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

Returns:

  • (Array<Node>)

    an array of child nodes


151
152
153
# File 'lib/astrolabe/node.rb', line 151

def child_nodes
  each_child_node.to_a
end

#descendantsArray<Node>

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

Returns:

  • (Array<Node>)

    an array of descendant nodes


191
192
193
# File 'lib/astrolabe/node.rb', line 191

def descendants
  each_descendant.to_a
end

#each_ancestorself, Enumerator #each_ancestor(type) ⇒ self, Enumerator #each_ancestor(type_a, type_b, ...) ⇒ self, Enumerator #each_ancestor(types) ⇒ self, Enumerator

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

Overloads:

  • #each_ancestorself, Enumerator

    Yield all nodes.

  • #each_ancestor(type) ⇒ self, Enumerator

    Yield only nodes matching the type.

    Parameters:

    • type (Symbol)

      a node type

  • #each_ancestor(type_a, type_b, ...) ⇒ self, Enumerator

    Yield only nodes matching any of the types.

    Parameters:

    • type_a (Symbol)

      a node type

    • type_b (Symbol)

      a node type

  • #each_ancestor(types) ⇒ self, Enumerator

    Yield only nodes matching any of types in the array.

    Parameters:

    • types (Array<Symbol>)

      an array containing node types

Yield Parameters:

  • node (Node)

    each ancestor node

Returns:

  • (self)

    if a block is given

  • (Enumerator)

    if no block is given


91
92
93
94
95
96
97
98
99
100
101
102
103
# 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_nodeself, Enumerator #each_child_node(type) ⇒ self, Enumerator #each_child_node(type_a, type_b, ...) ⇒ self, Enumerator #each_child_node(types) ⇒ self, Enumerator

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.

Overloads:

  • #each_child_nodeself, Enumerator

    Yield all nodes.

  • #each_child_node(type) ⇒ self, Enumerator

    Yield only nodes matching the type.

    Parameters:

    • type (Symbol)

      a node type

  • #each_child_node(type_a, type_b, ...) ⇒ self, Enumerator

    Yield only nodes matching any of the types.

    Parameters:

    • type_a (Symbol)

      a node type

    • type_b (Symbol)

      a node type

  • #each_child_node(types) ⇒ self, Enumerator

    Yield only nodes matching any of types in the array.

    Parameters:

    • types (Array<Symbol>)

      an array containing node types

Yield Parameters:

  • node (Node)

    each child node

Returns:

  • (self)

    if a block is given

  • (Enumerator)

    if no block is given


134
135
136
137
138
139
140
141
142
143
144
145
# 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_descendantself, Enumerator #each_descendant(type) ⇒ self, Enumerator #each_descendant(type_a, type_b, ...) ⇒ self, Enumerator #each_descendant(types) ⇒ self, Enumerator

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

Overloads:

  • #each_descendantself, Enumerator

    Yield all nodes.

  • #each_descendant(type) ⇒ self, Enumerator

    Yield only nodes matching the type.

    Parameters:

    • type (Symbol)

      a node type

  • #each_descendant(type_a, type_b, ...) ⇒ self, Enumerator

    Yield only nodes matching any of the types.

    Parameters:

    • type_a (Symbol)

      a node type

    • type_b (Symbol)

      a node type

  • #each_descendant(types) ⇒ self, Enumerator

    Yield only nodes matching any of types in the array.

    Parameters:

    • types (Array<Symbol>)

      an array containing node types

Yield Parameters:

  • node (Node)

    each descendant node

Returns:

  • (self)

    if a block is given

  • (Enumerator)

    if no block is given


173
174
175
176
177
178
179
180
181
182
183
184
185
# 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_nodeself, Enumerator #each_node(type) ⇒ self, Enumerator #each_node(type_a, type_b, ...) ⇒ self, Enumerator #each_node(types) ⇒ self, Enumerator

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.

Overloads:

  • #each_nodeself, Enumerator

    Yield all nodes.

  • #each_node(type) ⇒ self, Enumerator

    Yield only nodes matching the type.

    Parameters:

    • type (Symbol)

      a node type

  • #each_node(type_a, type_b, ...) ⇒ self, Enumerator

    Yield only nodes matching any of the types.

    Parameters:

    • type_a (Symbol)

      a node type

    • type_b (Symbol)

      a node type

  • #each_node(types) ⇒ self, Enumerator

    Yield only nodes matching any of types in the array.

    Parameters:

    • types (Array<Symbol>)

      an array containing node types

Yield Parameters:

  • node (Node)

    each node

Returns:

  • (self)

    if a block is given

  • (Enumerator)

    if no block is given


216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# 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

#parentNode?

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

Returns:

  • (Node, nil)

    the parent node or nil


49
50
51
# File 'lib/astrolabe/node.rb', line 49

def parent
  @mutable_attributes[:parent]
end

#root?Boolean

Returns whether the receiver is a root node or not.

Returns:

  • (Boolean)

    whether the receiver is a root node or not


62
63
64
# File 'lib/astrolabe/node.rb', line 62

def root?
  parent.nil?
end

#sibling_indexInteger

Returns the index of the receiver node in its siblings.

Returns:

  • (Integer)

    the index of the receiver node in its siblings


69
70
71
# File 'lib/astrolabe/node.rb', line 69

def sibling_index
  parent.children.index { |sibling| sibling.equal?(self) }
end