【问题标题】:ELISP Macro passing by symbol instead of listELISP 宏通过符号而不是列表传递
【发布时间】:2013-03-24 04:32:23
【问题描述】:
(defmacro flycheck-define-clike-checker (name command modes)
    `(flycheck-declare-checker ,(intern (format "flycheck-checker-%s" name))
       ,(format "A %s checker using %s" name (car command))
       :command '(,@command source-inplace)
       :error-patterns
       '(("^\\(?1:.*\\):\\(?2:[0-9]+\\):\\(?3:[0-9]+\\): error: \\(?4:.*\\)$"
          error)
         ("^\\(?1:.*\\):\\(?2:[0-9]+\\):\\(?3:[0-9]+\\): warning: \\(?4:.*\\)$"
          warning))
       :modes ',modes))

  (flycheck-define-clike-checker c
                                 ("gcc" "-fsyntax-only" "-Wall" "-Wextra")
                                 c-mode)

以上是我从https://github.com/jedrz/.emacs.d/blob/master/setup-flycheck.el获取的代码

除了为 flycheck 定义一个检查器(可以在 https://github.com/lunaryorn/flycheck987654322@ 找到)之外,它并没有做任何事情。

我的问题是微不足道的,我已经花了一天的时间,我更加困惑。

第二部分代码使用定义的宏调用flycheck注册一个编译器

(flycheck-define-clike-checker c
                                     ("gcc" "-fsyntax-only" "-Wall" "-Wextra")
                                     c-mode)

以上代码完美运行。

但由于我希望我的编译器有一些动态包含等,我有一个变量定义为

(defvar efx-flycheck-c-command '("gcc" "-fsyntax-only" "-Wall" "-Wextra"))

当我将它传递给宏时,像

(flycheck-define-clike-checker c
                                         efx-flycheck-c-command
                                         c-mode)

我收到一个编译错误

Debugger entered--Lisp error: (wrong-type-argument sequencep efx-flycheck-c-command)
  append(efx-flycheck-c-command (source-inplace))
  (list (quote quote) (append command (quote (source-inplace))))
  (list (quote flycheck-declare-checker) (intern (format "flycheck-checker-%s" name)) (format "A %s checker" name) (quote :command) (list (quote quote) (app$
  (\` (flycheck-declare-checker (\, (intern (format "flycheck-checker-%s" name))) (\, (format "A %s checker" name)) :command (quote ((\,@ command) source-in$
  (lambda (name command modes) (\` (flycheck-declare-checker (\, (intern (format "flycheck-checker-%s" name))) (\, (format "A %s checker" name)) :command (q$
  (flycheck-define-clike-checker c efx-flycheck-c-command c-mode)
  eval((flycheck-define-clike-checker c efx-flycheck-c-command c-mode) nil)
  eval-last-sexp-1(nil)
  eval-last-sexp(nil)
  call-interactively(eval-last-sexp nil nil)

我想我对宏在 elisp 中的扩展方式感到困惑。

请帮忙!

【问题讨论】:

    标签: emacs elisp flymake


    【解决方案1】:

    作为一般规则,您最好使用defun 而不是defmacro,除非defun 确实不方便/无法使用。在您的情况下,defun 确实更有意义。唯一的缺点是您需要引用 cc-mode 参数。

    【讨论】:

      【解决方案2】:

      您需要决定是希望command 参数已评估还是未评估。未评估的参数允许您键入列表而不引用它们,即("gcc" "-Wall") 而不是'("gcc" "-Wall"),代价是无法将变量作为参数传递。评估参数使您能够向宏提供变量(或实际上是任意表达式),但必须引用简单列表。

      通常,要评估反引号中的宏参数,您只需使用 , 运算符。但是,您已经在使用 ,@ 运算符,并且提到了两次 command,因此最好使用 eval 明确评估它:

      (defmacro flycheck-define-clike-checker (name command modes)
        (let ((command (eval command)))
          `(flycheck-declare-checker ,(intern (format "flycheck-checker-%s" name))
             ,(format "A %s checker using %s" name (car command))
             :command '(,@command source-inplace)
             :error-patterns
             '(("^\\(?1:.*\\):\\(?2:[0-9]+\\):\\(?3:[0-9]+\\): error: \\(?4:.*\\)$"
                error)
               ("^\\(?1:.*\\):\\(?2:[0-9]+\\):\\(?3:[0-9]+\\): warning: \\(?4:.*\\)$"
                warning))
             :modes ',modes)))
      

      借助defmacro 的强大功能,您甚至可以更进一步,定义宏将评估如果它是一个符号,否则按原样使用。这将允许您拥有蛋糕并吃掉它,即能够同时传递变量名和文字列表。这样做的代价是降低了与正常评估规则的一致性——你可以传递一个列表或一个变量,但不能传递一个任意的表达式,比如函数调用,这会让宏的用户感到不快。因此,该实现留给读者作为练习。

      【讨论】:

      • 好的..这行得通,但我脑子里的东西仍然没有意义。我确实了解您在做什么,但我仍然可以看到为什么扩展没有使用 ,@ 评估符号?
      • @RamneekHanda ,@ 正在评估 宏内command 符号,将其扩展为 efx-flycheck-c-command 并尝试将其拼接到列表中。这失败了,因为efx-flycheck-c-command 不是一个列表,而是一个符号。因此,您希望对 command 进行评估并然后在列表中展开,这就是我的版本所做的。
      • 要理解这一点,最好先从一个更简单的宏开始,然后先不加反引号。永远记住,反引号只是列表构造的语法糖,绝不是特定于宏或由宏所要求的。
      猜你喜欢
      • 1970-01-01
      • 2018-11-28
      • 1970-01-01
      • 1970-01-01
      • 2019-07-25
      • 2015-08-13
      • 2019-12-12
      • 2013-07-26
      • 2012-01-29
      相关资源
      最近更新 更多