WhitespaceをRubyに埋め込む
Whitespaceというプログラミング言語があります。 この言語はスペースとタブと改行文字だけで構成された言語です。
たとえばHello, world!
を出力するプログラムは次のようになります。
このWhitespaceをRubyで実装してみました。Akazaという名前1のgemです。
使い方
まずはgem installします。
$ gem install akaza
Akaza.eval
メソッドにWhitespaceのコードを渡すと、解釈して実行します。
なおこのプログラムではRubyのパターンマッチ構文2を使用しているため、Rubyのtrunkが必要です。3
単にプログラムを実行しているだけで面白味に欠けますね。単なるWhitespaceのインタプリタなら実装がいくらでもあるでしょう。
そこでAkazaにはインターフェイスをもう1つ用意しています。こちらのインターフェイスはかっこいいです。
かっこいい使い方
Akaza.eval
メソッドを使う方法ではプログラムをStringリテラルとして定義していました。
これはあまりかっこよくありませんね。
そこでWhitespaceの言語仕様を少し思い出してみましょう。Whitespaceの構成文字はスペース、タブ、改行文字の3種類だけでしたね。 この3種類の文字しか使わないことには明確なメリットがあります。それは「Rubyのコードに直接Whitespaceのコードを埋め込んでもSyntax Errorにならない」ことです。
Rubyでは空白文字はだいたい無視されるので、少し注意すればほとんどの場所にWhitespcaeのコードを埋め込むことができます。
そこでwhitespace
という修飾子を作ってみました。whitespace
修飾子をつけてメソッドを定義すると、そのメソッドの中身がWhitespaceのコードとして解釈されます。
Akaza::Annotation
をextend
したクラスの中で、whitespace
修飾子をつけてsum
メソッドを定義しています。
そして、なにやらsum
メソッド本文には不穏な空白がありますね。この空白は2つの数値の合計を出力するWhitespaceのプログラムです。
このsum
メソッドを実行すると、ちゃんと数値の合計が出力されます。
whitespace
修飾子を使うと、このようにメソッド定義に直接Whitespaceのプログラムを書くことができます!とてもかっこいいですね!
ちなみに、whitespaceでは空白文字以外はコメントとして無視されるので、RubyのSyntax Errorがでない限りは、空白を含まない任意の文字列をコメントとして書くことができます。 保守性にも優れていますね。
もう1つの使い方
whitespace
修飾子は便利ですが、メソッドの引数がIOに限定されてしまうのは少々使いづらいです。先ほどのsum
メソッドは数値を2つ受け取って1つ返すようなメソッドにしたほうが自然ですね。
そのような需要を満たすため、whitespace
修飾子では任意の位置にWhitespaceのプログラムを埋め込むプレースホルダを用意しています。
メソッド中にAkaza::Body
と記述しておくと、これがWhitespaceのプログラムに置き換えられます。
そしてプレースホルダなしの場合とは異なり、メソッド中に記述したコードはRubyのコードとして実行されます。
次にsum
メソッドをプレースホルダを使って書き直したコードを紹介します。Whitespaceのコードは全く同じですが、中にRubyのコードも含まれています。
Akaza::Bodyより前にinput
とoutput
変数にIOを代入しておくと、それがWhitespaceのIOとして使われます。
先ほどよりも使いやすいインターフェイスになっていますね。
内部実装
WhitespaceのVMはパターンマッチを使って実装しています。
https://github.com/pocke/akaza/blob/6856397674cddd03c49deb4bfdb8345fa5b5a618/lib/akaza/vm.rb
また、whitespace
修飾子はRubyVM::AbstractSyntaxTree
を使ってコードを書き換えることで実現しています。
https://github.com/pocke/akaza/blob/6856397674cddd03c49deb4bfdb8345fa5b5a618/lib/akaza/annotation.rb
RubyVM::AbstractSyntaxTree
については別の記事でより詳しく解説しているので、そちらを参照ください。
まとめ
Akaza gemによってWhitespaceをRubyに直接埋め込む方法を紹介しました。 Whitespaceは一番自然にRubyプログラムに埋め込むことができる言語でしょう。 たとえばPythonやJavaScriptなどの言語をRubyに直接埋め込もうとしたら、構文がぶつかってうまくいきません。
「Rubyに飽きて他の言語を使いたいけど、Rubyプログラムと自然に連携したい」、そんな時にAkaza gemとWhitespaceを使ってみてはいかがでしょうか。
参考文献
WhitespaceのVM、パーサの実装の際には、主に次の文献を参考にしました。 Whitespaceのオリジナルの紹介ページが死んでいる(?)ため、言語仕様を探すのに苦労しました。つらい…
- http://susisu.github.io/wspace/ws.pdf
- VMを実装する上でとても参考になりました。ですが空白文字がそのまま書かれているため、パーサを実装するにはこれだけだと厳しいです。
- Rubyist のための他言語探訪 【第 14 回】 Whitespace
- 構文がテーブルにまとまっているので、パーサを実装する際にこれを見ながら実装していました。