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 'otherfile.boo'

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.

Sometimes, large company is no better than several independent companies.
I always get this thought when looking at ASP.NET AJAX property accessors.

For people not familiar with the matter:
Microsoft ASP.NET AJAX requires developer to use property accessors to encapsulate javascript fields.
This would be a great idea, if only Microsoft IE supported javascript getters and setters.
Mozilla supports them, and Opera will in 9.50, but IE does not (they are not standard).
Also, IE does not have any hacky way to achieve the same effect (except in VBScript, which seems to be limited).

So in ASP.NET AJAX each property requires two functions named get_propertyName and set_propertyName.

Fortunately, javascript is very powerful in class member manipulation.
So I do not have to write things like this:

MyControl.prototype = {
    get_text : function() {
        return this._text;
    },

    set_text : function(value) {
        this._text = value;
    }
}

And I do not have to wait for auto properties as it is the case for C#.

Instead I just made an helper object named Auto and do things this way:

MyControl.prototype = {…}
Auto.properties(MyControl.prototype, [
    'text',
    'value',
    …
]);

Code for this helper can be very simple.
(in actual project I have additional complexity like Auto support for INotifyPropertyChanged).

var Auto = {
    property : function(prototype, name) {
        var getter = function() { return this['_' + name]; };
        prototype['get_' + name] = prototype['get_' + name] || getter;

        var setter = function(value) { this['_' + name] = value; };
        prototype['set_' + name] = prototype['set_' + name] || setter;
    },

    properties : function(prototype, names) {
        names.forEach(function(name) { 
            Auto.property(prototype, name);
        });
    }
}

This also requires forEach method on Array, but that one is pretty obvious.

I like to show this code when I hear about Javascript being assembly language and whatever-to-javascript compilation.