All Projects → yujinakayama → astrolabe

yujinakayama / astrolabe

Licence: MIT license
An object-oriented Ruby AST extension for Parser

Programming Languages

ruby
36898 projects - #4 most used programming language

Gem Version Dependency Status Build Status Coverage Status Code Climate

Astrolabe

Astrolabe is an AST node library that provides an object-oriented way to handle AST by extending Parser's node class.

Installation

Add this line to your Gemfile:

gem 'astrolabe'

And then execute:

$ bundle install

Usage

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.

APIs

See these references for all the public APIs:

Node Type Predicate Methods

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

Access to Parent Node

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

AST Traversal

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| ... }

Projects using Astrolabe

Compatibility

Tested on MRI 2.2, 2.3, 2.4, 2.5, and JRuby 9000.

License

Copyright (c) 2014 Yuji Nakayama

See the LICENSE.txt for details.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].