Astrolabe is an AST node library that provides an object-oriented way to handle AST by extending Parser's node class.
Add this line to your Gemfile
:
gem 'astrolabe'
And then execute:
$ bundle install
You can generate an AST that consists of Astrolabe::Node
by using Astrolabe::Builder
along with Parser
:
require 'astrolabe/builder'
require 'parser/current'
source_buffer = Parser::Source::Buffer.new('(string)')
source_buffer.source = 'puts :foo'
ast_builder = Astrolabe::Builder.new
parser = Parser::CurrentRuby.new(ast_builder)
root_node = parser.parse(source_buffer)
root_node.class # => Astrolabe::Node
Astrolabe::Node
is a subclass of Parser::AST::Node
.
See these references for all the public APIs:
These would be useful especially when combined with Enumerable
methods (described below).
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?`
def method_taking_block?(node)
return unless node.parent.block_type?
node.parent.children.first.equal?(node)
end
block_node = parser.parse(buffer)
# (block
# (send
# (int 3) :times)
# (args
# (arg :i))
# (send nil :do_something))
send_node, args_node, body_node = *block_node
method_taking_block?(send_node) # => true
These methods bring the power of Enumerable
to AST.
Note that you may want to use Parser::AST::Processor
if you don't need to track context of AST.
# Iterate ancestor nodes in the order from parent to root.
node.each_ancestor { |ancestor_node| ... }
# This is different from `node.children.each { |child| ... }`
# which yields all children including non-node element.
node.each_child_node { |child_node| ... }
# These iteration methods can be chained by Enumerable methods.
# Find the first lvar node under the receiver node.
lvar_node = node.each_descendant.find(&:lvar_type?)
# Iterate the receiver node itself and the descendant nodes.
# This would be useful when you treat the receiver node as a root of tree
# and want to iterate all nodes in the tree.
ast.each_node { |node| ... }
# Yield only specific type nodes.
ast.each_node(:send) { |send_node| ... }
# This is equivalent to:
ast.each_node.select(&:send_type?).each { |send_node| ... }
# Yield only nodes matching any of the types.
ast.each_node(:send, :block) { |send_or_block_node| ... }
ast.each_node([:send, :block]) { |send_or_block_node| ... }
# These are equivalent to:
ast.each_node
.select { |node| [:send, :block].include?(node.type) }
.each { |send_or_block_node| ... }
Tested on MRI 2.2, 2.3, 2.4, 2.5, and JRuby 9000.
Copyright (c) 2014 Yuji Nakayama
See the LICENSE.txt for details.