自分用のリーダーマクロやcl-annot等のライブラリを使う際に、SLIMEとの連携で多少つまづいたのでメモ。
リーダーマクロを含むコードをSLIMEで評価する
例えばcl-annotを使うとすると、ファイルの書き出しはこんな風になるでしょう1。
;; 必要なら
(eval-when (:compile-toplevel :load-toplevel :execute)
(ql:quickload :cl-annot))
(defpackage #:annot-test
(:use :cl))
(in-package #:annot-test)
(annot:enable-annot-syntax)
@export
(defun add (x y)
(+ x y))
(annot:enable-annot-syntax)
以降はアノテーションが有効になりますが、SLIME上では@export (defun...
の部分をC-c C-c slime-compile-defun
すると
The variable @EXPORT is unbound.
[Condition of type UNBOUND-VARIABLE]
というエラーがでます(環境にもよるかもしれません)。これはEmacs側で(require 'slime-annot)
としておいても解決しません。
SLIMEと正しく連携できるようにするためにはcl-syntaxを使うと良いようです。cl-annotはcl-syntaxの中の1モジュール(cl-syntax-annot
)として利用できます。
(eval-when (:compile-toplevel :load-toplevel :execute)
(ql:quickload :cl-syntax-annot))
(defpackage #:annot-test
(:use :cl))
(in-package #:annot-test)
(syntax:use-syntax :annot)
@export
(defun add (x y)
(+ x y))
(annot:enable-annot-syntax)
が(syntax:use-syntax :annot)
に置き換わっただけです。今度はC-c C-c
で問題なく@export (defun...
全体が評価されるようになりました。
リーダーマクロを含むコードをSLIMEで展開する
しかし、問題はまだあります。例えばannot:defannotation
で新しいアノテーションを導入する時など、リーダーマクロの展開がどうなっているかSLIME上で確かめながら書きたいことがあります。通常のケースではC-c C-m slime-expand-1
でマクロ展開を表示して、同時にリーダーマクロの展開も確認すれば十分なわけですが、この場合は@export...
の位置でC-c C-m
しても正しく展開されません2。
これは実践的には、トップレベルフォームをマクロ展開するコマンドがあれば解決になっていると思われます。~/.emacs.d/init.elなどに、以下を定義しましょう(あるいはslime.elを書き換えても良い)。
(defun slime-eval-macroexpand-toplevel (expander)
(let ((string (slime-defun-at-point)))
(setq slime-eval-macroexpand-expression `(,expander ,string))
(slime-eval-async slime-eval-macroexpand-expression
#'slime-initialize-macroexpansion-buffer)))
(defun slime-expand-1-toplevel (&optional repeatedly)
"Display the macro expansion of the current toplevel form.
See `slime-expand-1'."
(interactive "P")
(slime-eval-macroexpand-toplevel
(if repeatedly
'swank:swank-expand
'swank:swank-expand-1)))
(defun my-slime-mode-hooks ()
(define-key slime-mode-map "\C-\M-m" #'slime-expand-1-toplevel))
(add-hook 'slime-mode-hook 'my-slime-mode-hooks)
先ほどの@export (defun...
の中でC-M-m slime-expand-1-toplevel
を実行すると、別バッファに
(PROGN (EXPORT 'ADD) (DEFUN ADD (X Y) (+ X Y)))
と展開されるようになりました。リーダーマクロに絡んだケース以外でも役には立つコマンドかと思います。
-
defpackageと分散されたエクスポート宣言の相性の問題については、ひとまず考えないことにします。 ↩
-
cl-syntaxの導入によって(slime-defun-at-point)は正しく動作するようになりますが、(slime-sexp-at-point)は依然としてうまく動かないのが原因のようです。 ↩