sb-walkerのrpushの例がよく分からない — #:g1

Posted 2011-12-03 00:33:00 GMT

SBCLのsb-walker (walk.lisp)はコードウォーカーで、

;;; Here are some examples of the kinds of things you should be able
;;; to do with your implementation of the macroexpansion environment
;;; hacking mechanism.
;;;
;;; WITH-LEXICAL-MACROS is kind of like MACROLET, but it only takes
;;; names of the macros and actual macroexpansion functions to use to
;;; macroexpand them. The win about that is that for macros which want
;;; to wrap several MACROLETs around their body, they can do this but
;;; have the macroexpansion functions be compiled. See the WITH-RPUSH
;;; example.
;;;
;;; If the implementation had a special way of communicating the
;;; augmented environment back to the evaluator that would be totally
;;; great. It would mean that we could just augment the environment
;;; then pass control back to the implementations own compiler or
;;; interpreter. We wouldn't have to call the actual walker. That
;;; would make this much faster. Since the principal client of this is
;;; defmethod it would make compiling defmethods faster and that would
;;; certainly be a win.

(defmacro with-lexical-macros (macros &body body &environment old-env)
  (with-augmented-environment (new-env old-env :macros macros)
    (walk-form (cons 'progn body) :environment new-env)))

(defun expand-rpush (form env) (declare (ignore env)) `(push ,(caddr form) ,(cadr form)))

(defmacro with-rpush (&body body) `(with-lexical-macros ,(list (list 'rpush #'expand-rpush)) ,@body))

のような例が付いてくるのですが、これが何を示したいのかが、ちょっと分かりません。 「コードウォーカーを使うとこんなことができるよー」ということなんだと思うのですが、そもそもsb-walker:walk-formでは動かなかったり謎でした。
そのままで動かないのは、元になったPortable CommonLoops (PCL)のコードウォーカーの例をそのまま載せているからだということが最近分かったので、sbclのものを使うようにしてみたのですが、それでもそのままでは動かず、macroexpand-allでマクロを展開してやらないと思ったようには動きません。
(defmacro with-lexical-macros (macros &body body &environment old-env)
  (sb-walker::with-augmented-environment (new-env old-env :macros macros)
    (sb-walker:walk-form (cons 'progn body) new-env)))

(defmacro with-rpush (&body body &environment env) (sb-cltl2:macroexpand-all `(with-lexical-macros ((rpush ,#'expand-rpush)) ,@body) env) )

(let ((ans '())) (with-rpush (rpush ans 'foo) )) ;=> (FOO)

とりあえず、眺めてみての解釈ですが、
  1. 環境にあらたな構文を導入するのに with-augmented-environment を利用した with-lexical-macros を定義。
  2. with-lexical-macros の利用例として、with-rpushを定義。展開の関数としてexpand-rpushを定義してあり、先の with-augmented-environment に拡張した分を与えて新しい環境とする
  3. with-rpushの肝としては、大域では、rpushというマクロは定義されていないのにwith-rpushで囲まれた範囲ではrpushの展開が機能する。
というところでしょうか。
既存の構文と比較するとmacroletと大体似た感じのことを提供するものです。
with-lexical-macrosの定義も少し微妙なので、
(defmacro with-lexical-macros (macros &body body &environment old-env)
  (sb-walker::with-augmented-environment (new-env
                                          old-env
                                          :macros macros)
    (sb-cltl2:macroexpand-all
     (sb-walker:walk-form (cons 'progn body) new-env)
     new-env)))

(setf (symbol-function 'expand-push) (macro-function 'push))

(let ((ans '() )) (with-lexical-macros ((rpush expand-rpush) (mypush expand-push) ) (rpush ans 8) (mypush 9 ans))) ;=> (9 8)

という構成にすれば若干の制限は増しますが、分かりやすいといえば分かりやすいです。
なんにしろ、本来macroexpand-allを与える必要が必要ないものに思われるので、詳しい方がいれば是非教えて頂きたいところなのです。

comments powered by Disqus