Except obvious general differences, Nemerle is quite similar to MetaLua in its approach to macros. It allows equivalents of -{ … } and +{ … } blocks just like MetaLua does. Said equivalents are <[ ... ]> and $(…), although they don’t work exactly the same. <[ ... ]> is the quotation block, i.e. it generates an AST (Nemerle.Compiler.Parsetree.PExpr) from whatever its content is, while $(…) punches holes in <[ ... ]> allowing usage of parameters. But unlike MetaLua they can’t be used just everywhere. It’s gotta be inside a macro.
A macro is defined in a manner similar to function in C/C++ using the following syntax:
macro m() { // ... }
It returns PExpr by default and it has to be defined in a separate module, otherwise Nemerle’s compiler complains about an unbound symbol. The returned PExpr is composed back into main AST wherever the macro is called. So, a macro can be regarded as an equivalent to MetaLua’s meta-function call in an automatically generated top-level -{ … } block. In MetaLua one would do something like this:
-{ function someMacro() { -- do something at compile-time and return an AST } }
and then use it anywhere in the code like this:
-{ someMacro() }
to achieve the similar effect.
Thanks to powerful introspection capabilities Nemerle’s macros are capable of doing some really cool stuff. Virtually anything that can be done in runtime can also be done in compile-time. This also includes modifications to existing classes, e.g. using class decorating macros additional methods can be injected, possibly along with adding some extra interfaces. Although for this functionality to work, a decorating macro has to be applied to the source code of a class. A simple truth about Nemerle (and pretty much all the other macro languages) but one always to be remembered is that we’re manipulating ASTs. It goes like this: parse source code -> AST -> run macros on AST -> AST’ -> compile AST’. Nemerle’s homepage contains many examples of macros usage and explanations of their internals.
Nemerle also allows syntax extensions in a way quite similar to MetaLua:
macro for (init, cond, change, body) syntax ("for", "(", init, ";", cond, ";", change, ")", body)
The keyword syntax can be placed below macro’s header to define its usage syntax. The tokens parsed have to match order specified in the syntax declaration. Parameters are matched against type as well. The default type is an expression (PExpr) but different types can be specified using : operator like this:
macro test(a : int, b : int)
OK, this was supposed to be a really brief overview of Nemerle’s macro system and I have a feeling that everything that could be considered really brief has already been written 😉 Stay tuned for the next entry in Domain-specific languages cycle.