This is an optional tutorial for those curious to see what we can do with slightly more sophisticated macros.
In stacker, we learned that when we implement a language in Racket, we’re essentially making a source-to-source compiler: a program for converting the source code of our new language into ordinary Racket code, which we can then run as an ordinary Racket program.
“Can we make a source-to-source compiler that runs in the opposite direction?” Yes. We can convert ordinary Racket code into code for another language. As a demonstration, we’ll make stackerizer, a language that converts certain Racket S-expressions into a stacker program.
Why would we want to do this? A great reason is to help us generate nontrivial test programs. We claimed that stacker can handle a program of any length, and perform any kind of addition and multiplication. But the only test program we used when writing it was this one:
It would be nice to have some longer test programs. But it would be boring to write out these test programs by hand. Calculating the expected result for each program would also be a chore.
The faster, more reliable approach would be to write an appropriate Racket S-expression. We can evaluate this S-expression in DrRacket to find the initial result. We can then use stackerizer to convert this S-expression to the stacker language (where we could check that it produced the same result).
Moreover, we’ll be able to implement this language with two short macros. Macros are not usually the tool of first resort. If we can get the job done with ordinary functions, that tends to be the better idea. But macros are great at rewriting code. In this case, that’s all that stackerizer does.
We can try a demo now with:
This will return our original stacker test program:
A slightly more challenging example:
In this case, stackerizer will insert extra + and * operators where necessary:
The curious can confirm that the S-expression used as input, and the stacker program returned as output, both evaluate to 6314.