言語ゲーム

とあるエンジニアが嘘ばかり書く日記

Twitter: @propella

cola/flash syntax-rules のようなものを実装

行き詰まったので記録を書く。

前回 http://d.hatena.ne.jp/propella/20100414/p1 なんとかワークスペースの制作まで辿り着いたが、いよいよ自己記述コンパイラの開発という所で問題にぶちあたった。というのも、Flash はその仕組み上バイトコードの読み込みは非同期なので、eval したい時はイベントハンドラで読み終わりを待つ必要がある。これではマクロ定義が難しくなってしまう。

cola で使っているマクロは、いわゆる伝統的マクロという物で、コンパイラに食わせる前のコードを一旦 lisp プログラムで変換する仕組みだ。つまりマクロ定義時に eval が必要。しかし同じソースコードにマクロ定義とマクロの利用の両方を続けて書きたい場合、マクロの利用はイベントハンドラの中に書かないといけない。これが何度も連なると丁度継続渡しスタイルになるが、何十個もあるマクロをこれで書くのは技巧的すぎる。

そこで、マクロ定義時に eval を使わない方式として、Scheme の syntax-rules を真似する事にした。syntax-rules というのは、左側にパターンを書いて右側にテンプレートを書くだけで簡単なプログラム変換が出来るという物だ。二つのリストを比べて新しいリストを作るだけなので実装も簡単そう。マクロ中にどんな処理でも書ける伝統的マクロと比べると、リスト構築以外出来ない syntax-rules は貧弱だが、表記もとても分かりやすい。

syntax-rules の他の特徴として、ローカル変数との衝突を避ける仕組みと、... 記法を使って繰り返しパターンを書く仕組みがあるのだが、実装が面倒なので省略した。特に、... の存在意義が全然分からなかったのだが、しばらくして ... が無いと (a b c ..) を (a a b b c c ..) に 変換するようなプログラムを書けないのに気がついた。一見 ... が無くても

(define-syntax double
  (syntax-rules ()
    ((double x . xs) (x x . (double . xs)))))

のような定義でいけそうだけど、(x x . (double . xs)) は (x x double . xs) に評価されてそれ以上展開されない。なんて奥が深い!というか、リストとプログラムが同じという lisp の特徴は便利そうで美しく無いなー。

その他の問題として、syntax-rules ではマクロ中に動的にシンボル名を作る事が出来ないので、相当問題が多い事が分かった。例えば自作オブジェクト指向風ライブラリで、コンストラクタ名が klass ならクラス名は <klass> に決めうちみたいな事が出来ない。Flash ではグローバル名はコンパイル時に決定する決まりなので、関数を使っても回避しようが無いのだ。シンボル名生成だけ syntax-rules でも出来るよう特別扱いしようかとも思ったが、あまりにも醜すぎる。。。そこで今の所の解決策。

  • とりあえず伝統的マクロ復活。実は Flash では使えないが生の Tamarin VM は同期コード読み込みをサポートしている。しばらくは web 上で自己生成コンパイラ等という大それた事は忘れる。
  • インタプリタの制作。Flash 用にはマクロ定義のための lisp インタプリタを用意する。マクロ用だから多少遅くとも構わないし、lisp インタプリタの自己定義は簡単だと聞いた事がある。

こんな近況です。