🔖

Lem Advent Calendar 2023 - Lisp Mode - code walker編

2023/12/13に公開

これは Lem Advent Calendar の記事です。

以前、Lisp Advent CalendarでCode walkerの記事を書きました。
そこでいくつかのcode walkerについて見ていきましたが、既存のcode walkerでは出来ないことがあります。

例えば次のコードの中の変数xをハイライトしたいとします。
ここではカーソルを|、ハイライトを[]で表しています。

(defun foo ([|x])
  (let (([x] x))
    x))
(defun foo (x)
  (let ((x [|x]))
    [x]))

メジャーな言語では上記のようなスコープを考慮した変数のハイライトを出来ますが、Common Lispではいくつか課題があり実現できていません。

課題1: 既存のCode walkerがソースコード内の位置情報を扱わない

既存のコードウォーカーのいくつかを確認しましたが、式に対応する位置を保持しないのでエディタからその位置を知る術が存在しませんでした。

課題2: ユーザーが定義したマクロの扱い

ご存知の通り、Lispはマクロを使って構文を拡張できます。
強力な機能なのですが、今回のケースの場合はとても厄介です。

例えばalexandriaでもマクロが定義されていて以下のように書くことが出来ます。

(alexandria:with-gensyms (a b)
  (list a b))
(alxandria:destructuring-case x
  ((:add a b) (+ a b))
  ((:sub a b) (- a b)))

変数のハイライトをしたい場合、コードウォーカーの実装側でこのマクロについて特別な対応する必要があります。
ただ、あるマクロについて対応してもユーザーは新しい意味を持った構造のマクロをいくらでも定義できてしまうので、コードウォーカーは全てのマクロについて完全に対応することが出来ません。
加えて、一般的なコードウォーカーではマクロを展開した式を扱いますが、マクロ展開前後で位置を保持する術が無いのが困りどころです。

micors/walker

色々課題はありますが、とにかく既存のコードウォーカーの実装では物足りなかったので、microsにコードウォーカーのモジュールを作っています。
https://github.com/lem-project/micros/tree/main/contrib/walker

これによって、まだ限定的ではありますがLemでCommon Lispの変数のハイライトが出来るようになりました。

位置情報

エディタと統合して変数をハイライトする場合は式に対応する位置情報が必要になります。
ソースコードの位置情報は行と桁で表すのが一般的な表現だと思いますが、lispの場合はreadした後のデータに、コードに対応する位置情報がありません。
そのためmicros/walkerではリスト単位で要素を辿った回数をパスとしています。

(a b (c) d)

例えば上記の式でcの位置は内側のリストの中の0個目に存在し、その内側のリストは外側のリストの2個目に位置します。
なのでcの位置は(0 2)と表します。

(defun foo ()
  (let ((x 10))
    ...))

上記の例だとletの中のx(0 0 1 3)になります。

マクロ

マクロを展開すると展開前の変数などの位置情報を展開後に保てない問題が残っています。
この問題についてはいくつかの方法で対応方法が考えられます。

  • 出来るだけ多くのマクロをコードウォーカー側でカバーする
    少なくともCommon Lisp標準のパッケージにあるマクロは全て対応したいです。
    また、alexandria等のデファクトスタンダードなライブラリもカバーできればなお良いです。
  • lambda-listやマクロ名から推測する
    例えばwith-やdefine-で始まっているマクロはどれも似たスタイルになっているので、そこから決められるかもしれません。
    現状では、(with-* (var ...) ...)の形式は決め打ちで対応しています。
    加えて&bodyの後の引数は暗黙のprognと見做して処理しています。
  • マクロ定義時に形式を宣言する方法を用意する
    lemにはwith-pointが存在しますが、このマクロの変数の扱いはletと同じです。
    このようなマクロ定義に対する宣言方法を提供するのも良いかもしれません。

おわりに

今日はlemとmicrosに実験的にコードウォーカーを導入して、変数をハイライトする機能を付けている話でした。
まだ先は長いです。

Discussion