Starbucks-fueled Developer

Tuesday, June 26, 2007

DSL Support in Brail, Part I

Ayende mentioned on his blog about the work I've done to incorporate support for DSLs in Brail views within MonoRail. While he did a great job giving an overview of how to make use of the functionality, I figured I'd dive into the actual implementation and give some details of how this is achieved and where this can be taken -- at least my "vision".

The Beginning

First, none of this is even possible without mentioning what the Boo project team has done and giving a bit of a background - to my understanding - of what's at the core. With the 0.7.8 release of Boo, the team enabled the ability to invoke methods of an object via Boo's macro syntax. What's that mean; and what's a macro? C# developers have been using -- no, well, ok, pun intended -- since C# was introduced. In C#, using() is or can be thought of as a macro as when the C# compiler finds a using() block, it expands it into a try/catch/finally block calling dispose on the instance referenced in the using statement. Here's an example; this code:

using(SqlConnection c = new SqlConnection())
{
// do stuff
}

is rewritten/transformed by the compiler to:

SqlConnection c = null;
try
{
c= new SqlConnection()
}
finally
{
if(c != null)
{
c.Dispose()
}
}

which is why the using statement only accepts types that implement IDisposable, as it ensures Dispose() is always called. So, basically, macros are shorthand for developers.

Back to Boo. The 0.7.8 release allows developers to invoke methods using the macro syntax. So if I had a method called text that took a string as a parameter:

def text(value as string):
print value

in Boo 0.7.8, I can call text like this:

text "hello world"

Well, that's all well and good, isn't it? Nothing really "magical" there; how does this enable DSLs in Brail?

The Awakening

One of the major advantages that Boo offers developers is the ability to extend the language in numerous ways and macros is just one, "simple" option developers have at their disposal. The only caviaet is that your macros, obviously, need to produce valid, compilable code. So, from the example above, when the compiler parses:

text "hello world"

it rewrites/transforms the source to:

text("hello world")

So, how, or rather what, does a MonoRail view in Brail that's something like this:

<?brail
dsl Html:
html:
head:
title:
text "Untitled page"
end
end
body:
p:
text "Hello World"
end
end
end
end
?>

Give you the output you expect?

The Implementation

As you might guess, "dsl" is a macro that takes one parameter: the language you intend to use. So what does "dsl Html" generate? This, in Boo:

dsl = Castle.MonoRail.Views.Brail.DslProvider(self)
dsl.Register(Castle.MonoRail.Views.Brail.HtmlExtension(OutputStream))

or, in C#:

DslProvider dsl = new Castle.MonoRail.Views.Brail.DslProvider(this);
dsl.Register(new Castle.MonoRail.Views.Brail.HtmlExtension(OutputStream));

Okay, that might be a lot to swallow. "dsl Html" starts off by creating a new instance of a DslProvider object. The DslProvider object acts as a registry of languages to use. In this case, the second line tells the DslProvider that we're specifically interested in working with the HtmlExtension.

What's up with "this" and "OutputStream"? Here's where we get into the real nitty gritty, and for that, I leave you to Part II in this series. Hope this is as interesting for you as it is for me!!

2 Comments:

  • hi, are you somehow handling attributes?
    something like

    div:
    a {"href":"http://bla.com"}:
    text "click here"
    end
    end

    By Blogger Jan, at Monday, May 26, 2008 8:43:00 PM  

  • I've been out of touch with this code for a while, however, my guess would be that you're correct. Attributes are accepted using a Key/Value pair syntax like you described.

    Thanks for the comment!!

    By Blogger Harris, at Thursday, May 29, 2008 12:52:00 PM  

Post a Comment

<< Home