意味に応じて括弧の色を変える

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

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

こんな指摘がある。

<blockquote class="twitter-tweet" lang="en"><p>全くの初心者にschemeをやらせた経験では、文法を理解するのに苦労するみたいだった。引数リストの()とcondの条件節の()と関数適用の()……等々がみんな同じ見た目(だけど解釈がそれぞれ違う)だから何がどうなってるのかわからなくなる。</p>&mdash; むとう (@masa_edw) <a href="https://twitter.com/masa_edw/status/91372234115006464">July 14, 2011</a></blockquote> <script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet" lang="en"><p>関数定義するときの (define (func args...) ...) の(func ...)は関数呼び出しではない! なにそれ! (cond (c0 value0) (c1 value1) ...) の (c0 value0)は関数呼び出しではない!</p>&mdash; むとう (@masa_edw) <a href="https://twitter.com/masa_edw/status/91372881530990592">July 14, 2011</a></blockquote> <script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet" lang="en"><p>つまりどのカッコが構文の一部でどのカッコがただの式(評価されて値になる)かっていうことと、どの式がいつ評価されるのかっていうのが、見た目の違いがないのでわかりにいうということですね。defineの次は構文、condの各節も構文、ifの二つ目は式だけど評価されないときもある、等々。</p>&mdash; むとう (@masa_edw) <a href="https://twitter.com/masa_edw/status/91374177642221568">July 14, 2011</a></blockquote> <script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

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

# 実装戦略

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

括弧の意味を確定させるためにはプログラム中のマクロをすべて展開しなければいけない。 RnRS Scheme には [Common Lisp Hyperspec Sec. 3.1.2.1.2.1](http://www.lispworks.com/documentation/lw50/CLHS/Body/03_ababa.htm#clspecialops) のような special form の定義はないし、そもそも `macroexpand` が存在しないので、どうしても処理系依存にならざるをえない。今回は Gauche を対象にする。

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

括弧の種類をどこに保存するか。 `read` したソースコードをキーにした連想リストをグローバルに持ち `assq` で引くのがポータブルそうだが、あまりグローバル変数は作りたくない。必要なところまで引数で渡すのも面倒だ。 Gauche には（undocumented だが） [extended pair](http://practical-scheme.net/wiliki/wiliki.cgi?Gauche%3ACompactingPair) というものがある。 Gauche のソースコードは extended pair になっていて、そこにソースプログラムのファイル名や行番号などが入っている（[src/read.c](https://github.com/shirok/Gauche/blob/master/src/read.c) の `read_list` 関数などを参照）。表示部分でどうせ行番号が必要になるはずだから一緒に入れてしまった方がきっと便利だ。

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

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

```scheme:
(("/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](https://qiita-image-store.s3.amazonaws.com/0/2215/03fb3d3d-3f88-2028-13d5-329376ccfec3.png)

Gauche は 0.9.4 に[こんなパッチ](https://github.com/leque/Gauche/commit/b7a58224e87b6c4dc6bc64782e9a55f81d75d814)を当てた。

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

# 感想

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

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