More than 1 year has passed since last update.

Scheme は比較的オーバーロードの少ない言語で、標準の手続きでオーバーロードが使われているのは数値計算とポートまわりくらいだろうか。

しかし、構文レベルで見ると非常に大きなオーバーロードがある。括弧だ。

こんな指摘がある。



意味によって括弧の見た目を変えると読みやすくなるのだろうか。試してみよう。

実装戦略

ではどのように実装するか。

括弧の意味を確定させるためにはプログラム中のマクロをすべて展開しなければいけない。 RnRS Scheme には Common Lisp Hyperspec Sec. 3.1.2.1.2.1 のような special form の定義はないし、そもそも macroexpand が存在しないので、どうしても処理系依存にならざるをえない。今回は Gauche を対象にする。

マクロ展開を真面目に追おうとすると、 macroxpand だけでは足りず、現在可視な束縛やモジュール、ローカルマクロも追わないといけない。他によい方法があるかもしれないが、とりあえずコンパイラに手を入れることにしよう。

括弧の種類をどこに保存するか。 read したソースコードをキーにした連想リストをグローバルに持ち assq で引くのがポータブルそうだが、あまりグローバル変数は作りたくない。必要なところまで引数で渡すのも面倒だ。 Gauche には(undocumented だが) extended pair というものがある。 Gauche のソースコードは extended pair になっていて、そこにソースプログラムのファイル名や行番号などが入っている(src/read.cread_list 関数などを参照)。表示部分でどうせ行番号が必要になるはずだから一緒に入れてしまった方がきっと便利だ。

src/compile.scmpass1 を見ると、リストの car を見ながら、順番にプログラムを中間形式に変換している。ここで渡ってきたリストに順番に属性をつければよさそうだ。入力のプログラムを意味もなく deep copy するようなお行儀悪いマクロがあると情報が途切れてしまうが、どうにもできないので無視する。

改造した pass1 を呼び出し、その後種別情報をS式で出力するプログラムを書く。下のように、ファイル名と行番号、リストの先頭のシンボル、種別(macro または procedure)くらいがあれば色付けするには十分だろう。

(("/usr/local/share/gauche-0.9/0.9.4/lib/gauche/dictionary.scm" 34 "define-module" macro)
("/usr/local/share/gauche-0.9/0.9.4/lib/gauche/dictionary.scm" 35 "use" macro)
("/usr/local/share/gauche-0.9/0.9.4/lib/gauche/dictionary.scm" 36 "export" macro)
("/usr/local/share/gauche-0.9/0.9.4/lib/gauche/dictionary.scm" 51 "select-module" macro)
("/usr/local/share/gauche-0.9/0.9.4/lib/gauche/dictionary.scm" 76 "define-macro" macro)
("/usr/local/share/gauche-0.9/0.9.4/lib/gauche/dictionary.scm" 77 "define" macro)
("/usr/local/share/gauche-0.9/0.9.4/lib/gauche/dictionary.scm" 78 "let" macro)
("/usr/local/share/gauche-0.9/0.9.4/lib/gauche/dictionary.scm" 78 "gensym" procedure)
("/usr/local/share/gauche-0.9/0.9.4/lib/gauche/dictionary.scm" 79 "gensym" procedure)
("/usr/local/share/gauche-0.9/0.9.4/lib/gauche/dictionary.scm" 80 "gensym" procedure)
...
)

表示は、これを Emacs で読み込んで適当に色をつける。

できたもの

ということで、こんなものができた。

https://github.com/leque/gauche-paren-type

ss.png

Gauche は 0.9.4 にこんなパッチを当てた。

Emacs 側では jit-lock で適当なタイミングで括弧の種類をS式で吐くプログラムを呼び出し色をつける。種別情報はマクロか手続きかだが、それ以外の部分にも薄い色をつけることにした。行単位でしか情報を持っていないのでたまにハイライトに失敗することがあるかもしれない(Gauche の内部的にはバイト単位のオフセットもあるが、それを Emacs の文字単位のオフセットに変換する方法がよくわからなかった)。

感想

あまり深く考えずにナイーヴに実装してみたがそれなりに動いている。

で、実際に色がついてプログラムが読みやすくなったか、ということだけれど、個人的にはよくわからない。S式を見るときは、普段は特に括弧は意識せず、必要になったときだけ括弧を見ているので、色がついて自己主張されるとそれはそれで鬱陶しい気がする。と言いつつも、 let-values のような括弧がちな構文や、 match のパターン部分のような、構文の括弧の中にときどき評価される式が混ざる場合は確かに読みやすくなる気がする。