ELISP宏通过符号而不是列表传递

时间:2013-03-23 09:19:19

标签: emacs elisp flymake

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

我的问题很简单,我已经花了一天时间而且我更加困惑。

代码的第二部分使用定义的宏来调用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中的扩展感到困惑。

请帮忙!

2 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分:1)

您需要决定是否要让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的强大功能,你甚至可以更进一步,定义宏是评估如果它是一个符号,否则它按原样使用。这将允许你吃蛋糕并吃掉它,即能够传递变量名和文字列表。这样做的成本会降低与正常评估规则的一致性 - 您可以传递一个列表或变量,但不能传递一个任意表达式,例如函数调用,这会让宏用户感到不愉快。因此,实施仍然是读者的练习。