PUBLIC OBJECT

Using m4 as a macro processor for Java, the nice way

For reasons I won't disclose, I've come to need a macro processor to generate a bunch of .java files. Somebody at work is using m4 for a similar task, so I decided to try it. Unfortunately, using macro processors with Java brings a huge problem - you typically cannot use an IDE like IntelliJ to edit the raw files. For example, this trivial macro program cannot be parsed by IntelliJ:

public class HelloNTimes {
    public static void main(String[] args) {
        forloop(`i', 1, HELLO_COUNT, `System.out.println("Hello i");
        ')
    }
}`</pre>
After running it through m4 (first adding the standard [m4 forloop](http://www.gnu.org/software/m4/manual/html_node/Loops.html)), the output is compilable:
<pre>`public class HelloNTimes {
    public static void main(String[] args) {
        System.out.println("Hello 1");
        System.out.println("Hello 2");
        System.out.println("Hello 3");
        System.out.println("Hello 4");
    }
}`</pre>

But I'd like to have my cake and eat it too. I'd like to be able to edit a .java file that is both well-formed and contains macros! To make this possible, I need alternate code for every piece of macro code that I write. This alternate code serves many purposes:
<li>it allows me to reference symbols defined only by macros
</li><li>it gives me something to verify the generated code against

Including the alternate code, here's what the HelloNTimes app looks like:
<pre>`public class HelloNTimes {
    public static void main(String[] args) {
        /* BEGIN_M4_MACRO        forloop(`i', 1, HELLO_COUNT, `System.out.println("Hello i");        ')        END_M4_MACRO */        // BEGIN_M4_ALTERNATE
        System.out.println("Hello 1");
        System.out.println("Hello 2");
        // END_M4_ALTERNATE
    }
}`</pre>
As you can see, the 'alternate' section tells you what the macro could generate. Of course, if the parameters passed to the macro change (in this case _HELLO_COUNT_), the alternate will not equal what's generated. Due to the careful use of commenting, `/*` and `*/`, this code will compile fine! That means I can edit this file just fine in my IDE.

Then I add m4 definitions, to remove my markers when the file is processed:
<pre>`define(`BEGIN_M4_MACRO', ` BEGIN M4 MACRO GENERATED CODE *'`/')
define(`END_M4_MACRO', `/'`* END M4 MACRO GENERATED CODE ')
define(`BEGIN_M4_ALTERNATE', `BEGIN M4 ALTERNATE CODE
/'`* ')
define(`END_M4_ALTERNATE', `END ALTERNATE CODE *'`/')`</pre>

Finally I can see what the whole works generates. Note that it keeps the alternate code inside a comment, so I can verify that it's generating what I expect:
<pre>`public class HelloNTimes {
    public static void main(String[] args) {
        /*  BEGIN M4 MACRO GENERATED CODE */
        System.out.println("Hello 1");
        System.out.println("Hello 2");
        System.out.println("Hello 3");
        System.out.println("Hello 4");

        /* END M4 MACRO GENERATED CODE  */         // BEGIN M4 ALTERNATE CODE/*         System.out.println("Hello 1");        System.out.println("Hello 2");        // END ALTERNATE CODE */
    }
}

Note that the generated code has 4 hellos, wheras the alternate has only 2. This is because I'm running m4 with a parameter, -DHELLO_COUNT=4.

Preprocessor code is inconvenient, but when I can edit in my IDE, it loses a lot of its pain! If you're interested, please look at the example HelloNTimes.jh source file, or my real-world usage in NodeN.