gpt4 book ai didi

emacs - 在宏环境中强制扩展宏

转载 作者:太空宇宙 更新时间:2023-11-03 18:54:53 26 4
gpt4 key购买 nike

我要做的是:

(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)))

在定义了宏 forcollect的环境中展开,但我不希望全局定义这些宏(显然,不是放入全局名称空间的最佳名称)。
我希望通过使用 macrolet定义本地宏,可以展开包含本地定义的宏的窗体,然后返回展开,但是 macroexpandmacroexpand-all的行为好像它们不识别本地定义的宏(并且不展开它们)。
希望我能说清楚。。。
编辑
由于回答者决定删除,我开始寻找在Emacs Lisp中获得词汇环境的方法。我发现这个示例函数:
(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
好吧,这看起来是可行的:
(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是编译器/解释器存储当前环境的变量environment对象包含 (expander-name . expander-function)对,其中对的car是在宏扩展的这一步遇到的符号,cdr是处理扩展的函数。
编辑3
由于这对读者来说似乎非常混乱,所以我不想返回包含 macrolet的表单,而是在生成代码之前展开所有内容。
在处理顶级表单时,我创建了一堆对象,好吧,我称之为AST(抽象语法树),但事实并非如此,它只是一些复杂的代码模型,我将从宏中生成如果宏被重复扩展,AST将在随后的扩展中丢失,此外,我将无法正确生成下一步的扩展,也将无法对代码进行某些操作,例如,某些依赖于其杂散逻辑部分的表单将在“foreign”表达式的更深处出现在很多情况下,生成代码然后依赖宏扩展机制对我来说确实更容易例如,回答@6502:
既然for表单在主体之前是关闭的,那么(for…)应该扩展到什么?
它不是在这个词的常规意义上扩展的表单被解析,其中的部分被提取到一个特殊的对象中,稍后将用于生成最终代码例如:
(iter (for var in '(1 2 3 4))
(when (oddp var) (collect (next var)))

将首先创建一个对象,它注册两个变量 var和一个自动生成的变量,结果存储在其中,我们将其命名为 --0(因为这就是我生成名称的方式)只有在分析 --0中的第二个表达式之后,我才能发现我需要 iter变量所以,如果我很早就将 for扩展成了一些 let表达式,并且只有在第一次扩展之后,我才发现我需要再添加一个变量-那就太晚了也就是说,上面的扩展应该是这样的:
(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
真的很长时间了,对不起除了在整个扩展过程中必须保留的模式和扩展状态之外,还有另一个原因考虑以下两个例子:
(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 ...)表达式前面的部分所以,如果我在这里生成了一个宏,我会陷入这样一种境地,我必须生成已经生成的代码,也就是说,我已经有了一个陈旧的表达式,我需要回去重新分析(如果我幸运的话,我仍然可以到达那里)。

最佳答案

我还是不知道你想干什么但是,您编写的代码运行在 不是在宏扩展时间,而是在运行时,这就是为什么%TeXT不存在于该点(与第二个 macroexpand-all相反,它应该是宏展开的)。
我认为你想要的是:

(defmacro iter (&rest elems)
`(macrolet ((for (..) ...)
(collect (..) ...))
,@elems))

如果正如您所说,您确实需要在 (%test 100)for宏的各种扩展过程中保持一些本地状态,那么您应该将 collect全部删除,并使用类似的命令;
(defmacro iter (&rest elems)
(let (..some.local.state..)
(let ((body (macroexpand-all `(progn elems)
`((for . ,(lambda (..) ...))
(loop . ,(lambda (..) ...))
,@macroexpand-all-environment))))
...)))

它只是复制了 macrolet在内部所做的事情。

关于emacs - 在宏环境中强制扩展宏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13531362/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com