Macros for Ruby
gem install macros
Any source file that contains macro definitions, or that requires macro
expansion, should be loaded with
require 'macros' Macros.require 'my_macros' Macros.require 'my_code_using_macros'
Macros look like normal method definitions, but they are defined inside a
Macros do ; end block.
# my_macros.rb Macros do # Replace the +output+ method with the +puts+ method def with_output(ast) treemap(ast) do |node| if smatch? node, s(:send, nil, :output) s(:send, nil, :puts, *node.children.drop(2)) else node end end end end
Now this code
# my_code_using_macros.rb with_output do output "foo" output "bar" end
Will be transformed into
puts "foo" puts "bar"
AST stands for Abstract Syntax Tree, a way of representing the structure of code as data.
The macro will receive an instance of
Parser::AST::Node, and must return an
Node consists of a
type and zero or more
children. You can use
Macros.unparse to experiment.
s(:send, s(:send, nil, :obj), :do_thing, s(:send, nil, :x), s(:send, nil, :y))
Inside the macro definition the following convenience functions are available:
Construct an AST node of given type, with specific children.
Is the given object an AST node?
Enumerable#map, but performs a full tree walk, passing any
AST::Node to the block.
Returns an array of any node in the tree that satisfies the predicate
spec is an Array of symbols, integers, and arrays. It is used a bit like XPath
or CSS locators.
node = s(:def, :a_name, s(:args, s(:arg, :x), s(:arg, :y))) sfind(node, [:def, 1, :args, [:arg, 0]]) # => [:x, y]
Checks if the node matches the "pattern"
node = s(:def, :a_name, s(:args, s(:arg, :x), s(:arg, :y))) smatch?(node, s(:def, :a_name)) # => true
Is this a joke?
Well, it works, but it's a toy. Working with Ruby syntax trees is pretty awkward, and macros can easily lead to a mess. You have been warned!
© Arne Brasseur 2015
Mozilla Public License Version 2.0. See LICENSE file.