macroexpand と environment object

ちょっと諦め気味なので、メモだけ残しておく。昨日からメモりすぎだけど。

こんなマクロを書いた。

(defmacro merge-env (&environment env &optional outer)
  (if outer `(macroexpand '(merge-env) ,outer)
    env))

merge-env が呼ばれた環境の environment-object を返す。outer に environment-object が渡されたら

    • 呼ばれた環境
    • 渡された environment-object に保存されてる環境

を混ぜる。つまり

;;; A: local macro 'foo' を含む environment
(macrolet ((foo ...))
  (setq env-foo (merge-env)))
=> #<environment-object xxxxx>

;;; B: local macro 'foo' と 'bar' を含む environment
(macrolet ((bar ...))
  (setq env-bar (merge-env env-foo)))
=> #<environment-object yyyyy>

;;; env-bar には env-foo も含まれるので、A の local-macro 'foo' を
;;; 展開できる
(macroexpand '(foo) env-bar)
=> ...

;;; もちろん B の local-macro 'bar' も展開できる
(macroexpand '(bar) env-bar)
=> ...

狙いとしては、env-bar が

(macrolet ((foo ...))
  (macrolet ((bar ...))
    ...))

という環境と同等になって欲しかったんだけど

(macrolet ((foo () :outer))
  (setq outer (merge-env)))
=> #<environment-object 56693724>

(macroexpand '(foo) outer)
=> :outer


(macrolet ((foo () :middle))
  (setq middle* (merge-env)))
=> #<environment-object 56693700>

(macroexpand '(foo) middle*)
=> :middle

(macrolet ((foo () :inner))
  (setq inner* (merge-env middle)))
=> #<environment-object 56693664>

;;; これで inner* の environment-object はこうなってて欲しい
(macrolet ((foo () :outer))
  (macrolet ((foo () :middle))
    (macrolet ((foo () :inner))
      (foo))))
=> :inner

;;; (?_?)
(macroexpand '(foo) inner*)
=> :middle