ELISP Макрос, проходящий по символу вместо списка

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

Пожалуйста помоги!


person RamneekHanda    schedule 23.03.2013    source источник


Ответы (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, вы даже можете пойти еще дальше и определить, что макрос оценивается, если это символ, в противном случае он используется как есть. Это позволит вам получить свой пирог и съесть его, то есть иметь возможность передавать как имена переменных, так и списки литералов. Ценой этого будет снижение согласованности с обычными правилами вычисления — вы сможете передать список или переменную, но не произвольное выражение, такое как вызов функции, что неприятно удивит пользователей макроса. Из-за этого реализация оставлена ​​в качестве упражнения для читателя.

person user4815162342    schedule 23.03.2013
comment
хорошо .. это работает, но что-то в моей голове все еще не имеет смысла. Я понимаю, что вы делаете, но все же я понимаю, почему расширение не оценивает символ с ,@? - person RamneekHanda; 23.03.2013
comment
@RamneekHanda ,@ прекрасно оценивает символ command внутри макроса, расширяет его до efx-flycheck-c-command и пытается вставить в список. Это не удается, потому что efx-flycheck-c-command — это не список, а символ. Итак, вы хотите, чтобы command оценивалось и затем расширялось внутри списка, что и делает моя версия. - person user4815162342; 23.03.2013
comment
Чтобы понять это, было бы неплохо начать с более простого макроса и сначала сделать это без обратных кавычек. Всегда помните, что обратные кавычки — это просто синтаксический сахар для построения списка и никоим образом не являются специфическими или обязательными для макросов. - person user4815162342; 23.03.2013

Как правило, лучше использовать defun, чем defmacro, за исключением случаев, когда defun действительно неудобно/невозможно использовать. В вашем случае defun действительно имеет больше смысла. Единственным недостатком является то, что вам нужно заключать аргументы c и c-mode в кавычки.

person Stefan    schedule 23.03.2013