NOTE: In this post I use the term “S-expression” and “Sexp” quite a lot. If you’re not quite sure what they mean, check out Sexp for Rubyists.
I very often use Air Castle Driven Development. So when I see something like this:
I just write down what I want it to look like:
Then I know I have a useful API and I can start implement it. Okay, the examples above aren’t quite equal, so let’s have a look at what I’m really trying to solve.
Matching complex Sexp in Parkaby
Parkaby is my little experiment to create a super-duper-feaky-fast Markaby replacement by parsing the source and “compiling” it. Ultimately, a template like this:
Should be compiled into:
In Parkaby, this is accomplished in two steps: A processor which figues out what should be considered HTML-tags and what should be considered regular method calls, and a generator which compiles it back into Ruby.
An interesting aspect of Parkaby is that the processor is actually quite complex. For instance, it has to figure out that
div.post.clearfix.main!(:style => "display:none") should be compiled to
class="post clearfix" id="main" style="display:none"></div>.
My origianl approach was to use SexpProcessor:
Every time it finds a method call, it checks if it’s a text-code or a HTML-tag (and then turns it into a parkaby-sexp), or it just leaves it alone. The code ended up quite messy, and it’s before I even tried to implement CSS-proxies (
tag.klass.klass.klass.id!): Parkaby before SexpBuilder.
Adam Samderson to the rescue!
Ka-poof! Adam Sanderson writes SexpPath, a simple DSL for matching Sexp.
Let’s say we want to match
text "Hello" and
self << "Hello":
Woah, isn’t that powerful? The vertical-bar means “or”,
wild matches everything, and the percent sign captures the value. Exactly what I want!
gem install sexp_builder
Well, SexpPath alone wasn’t enough to solve my problem - I had to introduce a new library. SexpBuilder is a more complete solution to easily match and replace complex Sexp. Very much like a parser, you define matchers and rules, and by combining them with a rewriter it was breeze to implement CSS-proxy.