Vernacular::AST
Extends Vernacular to support rewriting the AST.
Installation
Add this line to your application's Gemfile:
gem 'vernacular-ast'And then execute:
$ bundle
Or install it yourself as:
$ gem install vernacular-ast
Usage
For general usage information, see the README for the Vernacular gem.
Modifiers::ASTModifier
AST modifiers are somewhat more difficult to configure. A basic knowledge of the parser gem is required. First, extend the Parser to understand the additional syntax that you're trying to add. Second, extend the Builder with information about how to build s-expressions with your extra information. Finally, extend the Rewriter with code that will modify your extended AST by rewriting into a valid Ruby AST. An example is below:
Vernacular::ASTModifier.new do |modifier|
# Extend the parser to support and equal sign and a class path following the
# declaration of a functions arguments to represent its return type.
modifier.extend_parser(:f_arglist, 'f_arglist tEQL cpath', <<~PARSE)
result = @builder.type_check_arglist(*val)
PARSE
# Extend the builder by adding a `type_check_arglist` function that will build
# a new node type and place it at the end of the argument list.
modifier.extend_builder(:type_check_arglist) do |arglist, equal, cpath|
arglist << n(:type_check_arglist, [equal, cpath], nil)
end
# Extend the rewriter by adding an `on_def` callback, which will be called
# whenever a `def` node is added to the AST. Then, loop through and find any
# `type_check_arglist` nodes, and remove them. Finally, insert the
# appropriate raises around the execution of the function to mirror the type
# checking.
modifier.build_rewriter do
def on_def(node)
type_check_node = node.children[1].children.last
return super if !type_check_node || type_check_node.type != :type_check_arglist
remove(type_check_node.children[0][1])
remove(type_check_node.children[1].loc.expression)
type = build_constant(type_check_node.children[1])
@source_rewriter.transaction do
insert_before(node.children[2].loc.expression, "result = begin\n")
insert_after(node.children[2].loc.expression,
"\nend\nraise \"Invalid return value, expected #{type}, " <<
"got \#{result.class.name}\" unless result.is_a?(#{type})\nresult")
end
super
end
private
def build_constant(node, suffix = nil)
child_node, name = node.children
new_name = suffix ? "#{name}::#{suffix}" : name
child_node ? build_constant(child_node, new_name) : new_name
end
end
endDevelopment
After checking out the repo, run bin/setup to install dependencies. Then, run rake test to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/kddeisz/vernacular-ast.
License
The gem is available as open source under the terms of the MIT License.