高度すぎるコードジェネレータ WLang

投稿者 nanki 2010-10-10 00:14:00 GMT

みなさんはコードジェネレータを使った事があるでしょうか。私はあります。

SQLにHTML, JavaScript, はてはPHPまで、Rubyでコードジェネレータを使う機会は増すばかりです。

さて、Rubyでコードジェネレーションと言えばERbを使うことが多いですが、それで充分なのでしょうか。 他の選択肢はどうでしょうか。

今日は、wlangというコードジェネレータを紹介します。

template engine?

次のサンプルコードを見てください。 とてもシンプルなHTMLテンプレートエンジンのように見えます。

require 'wlang'

puts WLang::instantiate(<<EOS, {:name => "O'Reilly & Animals", :animals => %w(Koala Rhino Giraffe)}, "wlang/xhtml")
<h1>${name}</h1>
<div id="{name}">
  <img alt='{name}' src="http://....."/>
  <ul>
    *{animals as animal}{${animal}}{, }
  </ul>
</div>
EOS

このプログラムの出力結果はこうなります。

<h1>O'Reilly &amp; Animals</h1>
<div id="O'Reilly & Animals">
  <img alt='O\'Reilly & Animals' src="http://....."/>
  Koala, Rhino, Giraffe
</div>

コンテキストに合わせて正しく、'&がエスケープされているのがわかります。 また、記法にはあまり馴染みがありませんが、配列の展開もうまく動いています。

wlang >> template engine

次にwlangの仕組みを見ていきます。 wlangでは、wlangタグを使って、テンプレートを記述します。 wlangタグは次のような形式です。

記号{.....}{.....}{.....}

後ろの{}はblockと呼ばれ、任意の個数持つ事ができます。 記号はたとえば $, *などです。

そう、実は先のテンプレートで出現した、${...}, "{...}, '{...}, *{...}{...}{...} は全て、wlangタグだったのでした。

(ちなみに、”{name}” の末尾の”はwlangタグとは関係なく、ただの文字列です。 これは <div id="{name}'>が文法エラーを出さずに動作する事で確認できます。このセンス嫌いじゃない)

なにやらあやしいかおりがしてきました。

wlang = programming language?

次に、WLang::instantiateの第三引数は”wlang/xhtml”となっていますが、WLangではこれをdialect(方言、なまりの意味)と呼んでおり、当然変えられます。

require 'wlang'

sql = "SELECT * FROM books WHERE name='{name}';"
values = {:name => "O'Reilly"}

puts WLang::instantiate(sql, values, "wlang/sql")
puts WLang::instantiate(sql, values, "wlang/sql/sybase")

# => SELECT * FROM books WHERE name = 'O\'Reilly';
# => SELECT * FROM books WHERE name = 'O''Reilly';

このように、dialect毎に'{...}タグの動作が変わるわけです。

dialectは複数のRuleSetとEncoderSetで定義されています。 RuleSetはタグの機能をオーバライドしたり新たなタグを定義します。 EncoderSetはdialect固有のencoding、つまり文字列の変換方法を定義していて、 ^{..encoding..}{...}というタグで呼び出す事ができます。

以下は大文字に変換するencodingの例です。

# instantiateとか省略します
^{plain-text/upcase}{wlang}
=> WLANG

これが例えばwlang/xhtml dialect であれば wlang/xhtml/main-encoding, wlang/xhtml/single-quoting, wlang/xhtml/double-quoting などが定義され、それぞれ違うエスケープがされるようになっています。

先のwlang/sqlwlang/sql/sybaseの結果の違いも、ここで決まっています。

勘のいい方ならお気づきだと思いますが、${...}, '{...}, "{...}はそれぞれdialectのmain-encoding, single-quoting, double-quotingを呼び出すためのショートカットになっています。

polyglot!

この様な拡張可能かつ再利用可能な変換規則を用意することで、Rubyの文字列を埋め込んだJavaScript、を埋め込んだxhtmlを埋め込んだRubyスクリプト、などといった複雑なコード生成を行う場合でも、 エスケープに悩まされることなく気楽にコード生成を行う事ができるwlangかっこいい!

でも、encodingの実装にバグがあるっぽくて、まだそれは無理っぽい。

参考:

vimwikiすごい

投稿者 nanki 2010-10-05 03:13:00 GMT

紹介しようと思ったのですが、こちらにて詳細にまとめられていたので参照してください。

すごいポイントはいくつかありますが、特にテーブル編集中に自動でセルサイズが再計算されるのが感動です。ただ、pre中でも再レイアウトされるのは残念。

なお、現在のバージョンはいくつか相違点がありました。 * tableのsyntaxがちょっと違う * Vim7.3のconcealに対応している

関係ないけど、作者のMaxim KimさんはVim好きそうな名前ですね。

参考: