これが私がやろうとしていることです:
(defun test-macrolet ()
(macrolet
((%test
(x)
(message "%%test is called")
`(message "x: %s" ,x))
(%aref (array place)
`(aref ,array ,place)))
;; it doesn't expand to (message "x: %s" 100)
(message "expanded: %s" (macroexpand-all '(%test 100)))
(%test 100)
(%aref (%test 100) 0)))
この関数を別のマクロの内部から呼び出しています。ローカルで定義する必要のあるすべての内部マクロを展開したいと思います。おそらく、あなたにもっと良いアイデアを与えるために、私はこれが欲しいです:
(iter (for i from 0 to 10)
(when (oddp i) (collect i)))
for
マクロとが定義されている環境で拡張しますがcollect
、これらのマクロをグローバルに定義したくありません(明らかに、グローバル名前空間に入れるのに最適な名前ではありません)。
macrolet
ローカルマクロを定義することで、ローカルで定義されたマクロを含むフォームを展開してから展開を返すことができますが、ローカルで定義されたマクロを認識しない(そして展開しない)ように動作macroexpand
することを望んでいました。macroexpand-all
さて、私が自分自身を明確にしていることを願っています...
編集
回答者が削除することを決めた回答のおかげで、私はEmacsLispで字句環境を取得する方法を探し始めました。私はこのサンプル関数を見つけました:
(defun byte-compile-make-lambda-lexenv (form)
"Return a new lexical environment for a lambda expression FORM."
;; See if this is a closure or not
(let ((args (byte-compile-arglist-vars (cadr form))))
(let ((lexenv nil))
;; Fill in the initial stack contents
(let ((stackpos 0))
;; Add entries for each argument
(dolist (arg args)
(push (cons arg stackpos) lexenv)
(setq stackpos (1+ stackpos)))
;; Return the new lexical environment
lexenv))))
bytecomp.elで。表面的には、環境が関数(私の場合はマクロ)の名前とその「スタック上の位置」で構成された単なるリストであることを意味します。2番目の部分はよくわかりませんが、実験してみます。見る...
編集2
OK、これはうまくいくようです:
(defun test-macrolet ()
(macrolet
((%test
(x)
(message "%%test is called")
`(message "x: %s" ,x))
(%aref (array place)
`(aref ,array ,place)))
(%test 100)
(%aref (%test 100) 0)
(message "expanded: %S"
(macroexpand
'(%test 100)
(cons `(,@(list (intern "%test")) .
(lambda (y)
(message "Called from environment '%s'" y)
`(message "Final expansion '%s'" ,y)))
macroexpand-all-environment)))))
(test-macrolet)
"expanded: (message \"Final expansion '%s'\" 100)"
ずさんなコードでごめんなさい。さらに詳しい情報:macroexpand-all-environment
コンパイラ/インタプリタが現在の環境を格納する変数です。環境オブジェクトには(expander-name . expander-function)
ペアが含まれています。ペアの車はマクロ展開のこのステップで検出されたシンボルであり、cdrは展開を処理する必要がある関数です。
編集3
macrolet
これは読者を非常に混乱させるように思われるので、コードを生成する前に、を含むフォームを返すのではなく、すべてを展開したい理由はここにあります。
トップレベルのフォームを処理している間、私はたくさんのオブジェクトを作成しています。それをAST(抽象構文木)と呼びますが、実際にはそうではなく、これから行うコードの複雑なモデルにすぎません。マクロから生成します。マクロが繰り返し展開されると、その後の展開でASTが失われます。さらに、次の展開ステップを適切に生成できず、コードに対して特定の操作を実行できなくなります。たとえば、一部のフォームは依存しています。それらの漂遊論理部分は、「外国の」表現のより深いところに存在します。多くの場合、コードを生成してからマクロ展開メカニズムに依存する方が本当に簡単です。したがって、たとえば、@6502に答える:
forフォームが本体の前で閉じられているとすると、(for ...)は何に拡張する必要がありますか?
通常の意味での拡張ではありません。フォームが解析され、その一部が特別なオブジェクトに抽出され、後で最終的なコードの生成に使用されます。例えば:
(iter (for var in '(1 2 3 4))
(when (oddp var) (collect (next var)))
最初に、2つの変数を登録するオブジェクトとvar
、結果が格納される自動生成された変数を作成します。それを呼び出しましょう--0
(これが、実際に名前を生成する方法だからです)。--0
内の2番目の式を解析した後でのみ、変数が必要であることがわかりますiter
。したがって、for
非常に早い段階let
で式に展開し、その最初の展開の後で初めて、変数をもう1つ追加する必要があることに気づきました。これでは遅すぎます。つまり、上記の展開は次のようになります。
(let (--0 var (--1 '(1 2 3 4)))
(while --1
(setq var (car --1) --1 (cdr --1))
(if (oddp var) (setq --0 (cons (cadr --l) --0))))
--0)
前の式を半分拡張してを生成する必要がある場合macrolet
、どの変数に収集する必要があるか、next
式を生成する方法などがわかりません。
また、collectが単なるローカル関数ではなくマクロである必要があるのはなぜですか?
どちらの要件もありません。適切なコードを生成できるように、マクロ展開プロセス中に通知する必要があります。技術的には、何にも拡張されません(たとえば、コード分析によって到達不能コードが作成されることが示された場合、拡張中に無視される可能性もあります)。
編集4
これは本当に長くなっています、ごめんなさい。拡張プロセス全体で保持しなければならないスキーマと拡張の状態に加えて、別の理由があります。次の2つの例を考えてみましょう。
(iter (for (key . value) in some-hash-table)
(message "key: %s => value: %s" key value))
このコードはループを生成するの(while ...)
は非効率的であるため、ループを生成することは期待されていません(maphash ...)
。ここで使用することをお勧めします。でも:
(iter (for (key . value) in some-hash-table)
(for (key-2 . value-2) in some-other-hash-table)
(message "key: %s => value: %s" (list key key-2) (list value value-2)))
(maphash ...)
2番目のハッシュテーブル(または最初のハッシュテーブルのいずれか小さい方)のキーを収集するための追加のフォームを生成する必要があり、以前に生成された可能性のある最初の式の前のセクションにかなりのコードを入力する必要があります。したがって、ここでマクロレットを生成した場合、すでに生成されているコードを生成する必要がある状況で立ち往生します。つまり、式が古くなっているため、戻って再解析する必要があります(私は幸運で、どういうわけかまだそこに到達することができます)。