Macros, Syntax Extensions and Domain-Specific Languages

I’d like to write a series of articles on the topic of macros in modern programming languages, language syntax extensions and domain-specific languages. After discussing some of the existing projects like Common Lisp, Scheme, Nemerle and MetaLUA I would like to present you with my solution for C++ syntax extending macro preprocessor and its initial (i.e. one evening 😉 ) implementation. First of all, I don’t know about you but I’ve heard at least a dozen of times that macros are bad for code readability and maintainability and shouldn’t be used at all. This is obviously not true, if you take for example Qt’s foreach macro. Thanks to it instead of:

for (QVector<int>::iterator it = vec.begin(); it != vec.end(); it++) ...

you could write:

foreach(it, vec) ...

I don’t know about maintainability (Qt is 15+ years old AFAIK, so it probably isn’t so bad 😉 ) but I certainly can’t see how this could hurt readability!

You might have heard of languages like Common Lisp and Scheme which are the two most popular dialects of Lisp. Scheme offers only a minimalistic core and powerful syntax extension mechanisms while Common Lisp has more standardized syntax and similar extension capabilities. The problem with both of them being? I bet you’ve guessed it all right – they’re both dialects of Lisp 😉 and as such they’re more popular in research environments than in commercial practice. Anyway, the example below creates a new syntax which behaves like ‘if’ with many expressions in the true branch and no expressions in the false branch.

(define-syntax when
  (syntax-rules ()
    ((when pred exp exps ...)
      (if pred (begin exp exps ...)))))

Now, Nemerle is a .NET Framework language developed at Wroclaw University (Poland). As such it has much broader application because it has access to all the .NET assemblies. It provides a powerful macros and syntax extension mechanisms which are pretty impressive especially when you look at them for the first time. A sample Nemerle macro usage is shown below:

ExecuteReaderLoop (
  "SELECT firstname, lastname FROM employee WHERE firstname = $myparm",
    System.Console.WriteLine ("Name: {0} {1}", firstname, lastname)

In this code, thanks to ExecuteReaderLoop macro, the compiler is actually aware (or it seems to be aware, which is pretty much the same thing in most situations 😉 ) of the semantics of SQL syntax used in the first parameter. Normally you would have to write it using much more verbosity, involving interfaces like IDbCommand, IDataParameterCollection and IDataReader – it would take twice the number of lines of code. I will discuss this particular example in more detail later, for now I would just like to show an example of another macro and its source code:

macro print_date (at_compile_time)
  match (at_compile_time) {
    | <[ true ]> => MyModule.print_compilation_time ()
    | _ => <[ WriteLine (DateTime.Now.ToString ()) ]>

The above macro either prints the date of its evaluation at compile time or the current date at runtime depending on the at_compile_time parameter.

MetaLUA is an extension to the LUA scripting language which lets you construct macros from parts of Abstract Syntax Tree (AST). To allow this MetaLUA introduces concepts of quoting and splicing, which is actually very similar to what Nemerle does. An example of MetaLUA macro and its usage follows:

-{ block:
   mlp.lexer:add{ "let", "in" }

     "let",, "=", mlp.expr, "in", mlp.expr,
     builder = let_in_builder }

   local function let_in_builder (x)
     local variable, value, expr = unpack (x)
     return +{
       function (-{variable})
         return -{expr}
       end (-{value}) }

a, b, c = 1, 1, -2
roots = let sqrt_delta = (b^2-4*a*c)^0.5 in
        { (sqrt_delta-b)/(2*a), (-sqrt_delta-b)/(2*a) }

It computes roots of a square equation using ‘let’ syntax, which is nothing more than declaring a function taking expression on the left side of ‘in’ keyword as variable named sqrt_delta and returning the right-hand side of ‘in’ keyword evaluated in the scope with sqrt_delta defined as I wrote earlier. It becomes really intuitive as soon as you start writing your own macros and syntax extensions.

Well, I think this is enough for a starter. I just wanted to familiarize you a bit with the solutions above before I start discussing their applications, pros and cons and before I state why I decided to write similar (probably not as neat but also quite powerful) macro/syntax ext. system for C++.

Also I would like to mention the issues with most macro systems, which are:
– syntax (it has to be nice enough to be practical in agile development)
– type-safety (it’s a whole new quality in the world of macros, type-aware macros are available in Nemerle)
– hygiene (look it up on Wikipedia, all mentioned solutions have it).

This is definitely enough for a starter 😉

Leave a Reply

Your email address will not be published. Required fields are marked *