Includes in Boo

December 13th, 2009

This post assumes you know what Boo is (a rather useful .NET language).
I wanted to write a more descriptive post on Boo and other scripting approaches, however this post is more specific.

Ok, so if you know what’s Boo is about, you probably know that there is no official way to include one boo file into another. There are several approaches that achieve similar results, like Ayende’s import file from. However, they are all lacking when using Boo as a DSL or a scripting tool.

They are based on pre-compilation of referenced file, which has a side-effect of creating unnecessary assembly and losing access to global variables from the original file. Now, if I actually wanted to import global variables (for example, as a configuration in a build-file DSL), I was out of luck.

Fortunately, Boo has a rather useful macro system, which allows classes named ‘macros’ to affect the parsed representation of the program. So the question of include is just a question of building a macro that injects parse tree of referenced file into referencing one. I have just finished a basic include implementation, which can be found here:

  1. IncludeSupportStep.cs
  2. IIncludeCompiler.cs

(note that Phantom project itself is 1. awesome, more on that in future posts and 2. not written by me)

IncludeMacro is the macro, and the IncludeSupportStep is used to inject IIncludeCompiler implementation into the list of services provided by CompilerContext so it will be available to the macro. Now, what is IIncludeCompiler? It is a wrapper for a basic Boo compiler which should have only Parse steps in the pipeline + any steps provided by the DSL factory (except ImplicitBaseClassCompilerStep).

Having IncludeSupportStep means that this functionality is not directly available through boish/booi/etc, which is a pity, but is probably not hard to fix.

The syntax for the include is pretty simple:
include ''

I have chosen ‘include’ instead of import to make the difference obvious and to simplify the syntax for use in DSLs. Not sure if it is the best choice, though.

It is interesting that in comparison with Ruby/etc, this is a purely static include, so wrapping it in any control flow blocks achieves nothing.