coatl / rubymacros

RubyMacros is a lisp-like macro pre-processor for Ruby. More than just a purely textual substitution scheme, RubyMacros can manipulate and morph Ruby parse trees (in the form of RedParse Nodes) at parse time in just about any way you see fit.
http://rubyforge.org/projects/rubymacros
GNU Lesser General Public License v2.1
61 stars 2 forks source link

= RubyMacros

== DESCRIPTION: RubyMacros is a lisp-like macro pre-processor for Ruby. More than just a purely textual substitution scheme, RubyMacros can manipulate and morph Ruby parse trees (in the form of RedParse Nodes) at parse time in just about any way you see fit.

Macros are programmed in ruby itself. And since parse trees are represented in RedParse format, they're easier to use (programatically) and more object- oriented than other available ruby parsetree formats. (RedParse Node format is actually designed to be straightforward to use and to represent the structure of ruby source code very closely.)

== Benefits:

== Drawbacks: Although in theory already as powerful as lisp macros, the current implementation has a number of problems which added together make it merely a proof of concept or toy at this point:

== Requirements: RubyMacros requires RedParse.

== Install: gem install rubymacros

== Examples: macro simple(a,b) :(^a+^b) end def simple_user p simple(1,2) #prints 3 end

loop as a macro, should be a bit faster than the #loop method

macro loop(body) :(while true ^body end ) end

for more examples, see the examples/ directory

== New Syntax: I have invented 3 new syntactical constructions in order to allow reasonably easy to use macros. Macros themselves look just like methods except that 'macro' instead of 'def' is used to start the macro definition off. A form literal is an expression surrounded by ':(' and ')'. The form escape operator is '^'. '^' is a unary operator of fairly high precedence.

== Forms and Form Escapes: Forms are an essential adjunct to macros. Forms represent quoted source code, which has been parsed but not evaled yet. When a form literal is executed, it returns a RedParse::Node representing the parse tree for the enclosed source code. Within a form literal, a ^, used as a unary operator, will escape the expression it controls, so that instead of being part of the form's data, it is executed at the same time as the form literal, and the result of an escaped expression (which should be a Node) is interpolated into the form at that point. The whole effect is much like that of string interpolations (#{}) inside string literals.

== How Macros Work: Typically, macros return a single form literal, which contains form escape expressions within it which make use of the macro's parameters. However, macro bodies may contain anything at all; more complicated macros will likely not contain any forms. (Likewise, form literals may be used outside macros, but the utility of doing so may be minimal.)

At parse time (well, really at method definition time, but in effect it's much the same thing) method bodies are scanned for callsites which have the names of known macros. When such a call is found, it is expanded as follows. The parsetrees for the arguments to the callsite are passed as arguments to the macro. The macro is expected to return a parsetree, which replaces the macro callsite in the parsetree which contained it.

=== How to use the examples: All the example code in the example/ directory is split into 2 parts; the actual example file itself (say, example/foo.rb) and a wrapper around it (say, example/foo_wrap.rb) which requires 'macro' and then Macro.requires the example itself. To run the examples, you should run the _wrap file, not the example file itself. For example, to try out assert.rb, you would issue this command line: ruby example/assert_wrap.rb The example wrappers all expect to be run from the main rubymacros directory, not the example/ subdirectory.

I wish this 2-level scheme were not necessary, so that you could directly execute a file containing macros, with perhaps a -rmacro preceding it on the command line. However, it isn't possible to override how the interpreter treats it command line arguments. I would hope that files named on the command line (with or without a -r option) would be pulled in via Kernel#require, but this is not the case.

== Hacker's guide (well, section) If you modify the parse rules (that is, RedParse::MacroMixin#RULES) you will need to recompile them before they will work. To do that, run 'make parser'. This will, unfortunately, take a little while to run. Running 'make parser' will regenerate two files: lib/macro/ReduceWithsFor_RedParse_RedParseMacroMixin_RedParseWithMacros_1_8.rb lib/macro/ReduceWithsFor_RedParse_RedParseMacroMixin_RedParseWithMacros_1_9.rb which normally come pre-generated with rubymacros.

== Known Problems

== License: Copyright (C) 2008, 2016 Caleb Clausen

This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with this program. If not, see http://www.gnu.org/licenses/.