在Emacs中rx宏和急切的宏扩展失败

时间:2013-09-08 11:42:56

标签: emacs macros elisp

(defun my-blah (str)
  (rx (+ (eval (substring str 1)))))

对该代码的评估将以下消息放入消息缓冲区:

Eager macro-expansion failure: (void-variable str)

这是什么意思,需要担心什么?

该功能似乎工作正常。另一方面,对以下代码的评估不会产生这样的消息:

(defvar my-str "oink")
(rx (+ (eval (substring my-str 1))))

附录:在不生成此类消息的情况下定义函数的替代方法

(defun my-nah (str)
  (rx-to-string `(+ ,(substring str 1))))

(defun my-llama (str)
  (eval `(rx (+ ,(substring str 1)))))

2 个答案:

答案 0 :(得分:2)

rx是一个宏,这意味着它可以在实际评估代码之前随时进行扩展。通常,这意味着它将在编译期间进行扩展。编译在str已知之前发生,因此rx的扩展不能取决于str的值。

如果你在没有编译的情况下测试它,那么宏扩展发生的时间很晚(在评估之前/期间),所以问题是隐藏的(如果使用lexical-binding,问题将会重新出现,因为eval将无法访问定义str的词汇上下文。

Eager macroexpansion(24.4中的新增功能)用于在加载非编译文件时扩展宏,这提供了类似于编译代码的宏扩展行为。为了向后兼容,当在急剧的宏扩展期间发生错误时,Emacs会延迟回扩展宏(在发出警告之后)。

答案 1 :(得分:0)

如果您想轻松测试rx表达式,请使用:

(defun test-rx (test-file test-reg)

  (if (get-buffer (setq test-buffer "RX"))
      (kill-buffer test-buffer))

  (with-temp-buffer
    (insert-file-contents-literally test-file)
    (goto-char (point-min))

    (while
        (re-search-forward test-reg nil t)
      (when (match-string 0)
        (let ((one (match-string 1))
              (two (match-string 2)))
          (if one
              (progn
                (pop-to-buffer test-buffer)
                (goto-char (point-max))
                (insert (format "Found %s and possibly %s" one two)))))))))

(defun test-rx-now (file)
  "plop"
  (interactive
   (let (file)
     (setq file (read-file-name "File to search: "))
     (list file)))

  (test-rx file
           (rx
            "$(function"
            (*? anything)
            (or "'" "\"")
            (group
             (* (not (any "'" "\"")))
             ".php"
             )
            (or "'" "\"")
            (*? anything)
            "})"
            )))