#:g1: 構文チェッカーとしてのコンパイラマクロ

Posted 2021-05-06 02:32:02 GMT

コンパイラマクロがコンパイル時に展開されることを利用して、非推奨APIの警告を出すというアイデアがあったりしますが、スタイルチェッカー的なものを動かすタイミングとしては丁度良く、また、コンパイラマクロは意味論を変えないことが前提なので親和性も高いと思います。

ということで、コンパイラマクロが構文チェッカーとして使えないか考えてみたいと思います。

定義

まず、letの別名のmyというものを定義します。

(defmacro my ((&rest binds) &body body)
  `(let (,@binds) ,@body))

これに対し、

というのをコンパイラマクロで追加してみます。

(define-compiler-macro my (&whole whole (&rest binds) &body body)
  (declare (ignore body))
  (dolist (b binds)
    (check-type b (cons symbol (cons T null))))
  (assert (subsetp binds (remove-duplicates binds :key #'car :from-end T))
          nil
         "Variable name duplicated in bind spec: ~S"
         binds)
  whole)

試してみる

束縛部の形式チェック

(defun foo ()
  (my ((x 3) y)
    (list x y)))
;⏏ Error: The value y of b inside (define-compiler-macro my) is not of type (cons symbol (cons t null)).

束縛部の変数重複チェック

(defun foo ()
  (my ((x 3) (x 4))
    (list x)))
;⏏ Error: Variable name duplicated in bind spec: ((x 3) (x 9)).

まとめ

チェッカーの使い勝手的には元のコードはいじらずに後からチェッカーを追加したりしたいところですが、define-compiler-macro定義は一つだけなので、元のコードをいじらないわけにはいきません。

compiler-macro-function関数をラップするようなインターフェイスを作成して、adviceの真似事をしてみれば可能な気もします。

何にしろコンパイラマクロを定義する場所は一つしかないので、複数のフックが共存できるような仕組みが必要ですね。

関連


HTML generated by 3bmd in LispWorks 7.0.0

comments powered by Disqus