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

标签 emacs macros lisp elisp

我要做的是:

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

相关文章:

javascript - 如何编写与 GNU Emacs 一起使用的 Node.js REPL?

regex - 用于将 PCRE 正则表达式转换为 emacs 正则表达式的 Elisp 机制

VBA 宏运行速度极慢

c++ - 错误 "not declared in the scope"但有接口(interface)

shell - 在 emacs 中使用 ansi-term 时出现换行符问题

macros - 为什么 Julia 中的字符串宏使用...?

scheme - 压平一次程序

macros - Common Lisp (SBCL) 中的素数宏

lisp - Scheme (Lazy Racket) 自然数的无限列表

emacs - Emacs启动后如何触发反显模式?