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

Он не делает ничего особенного, кроме определения средства проверки на наличие мух, которое можно найти по 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 ответа

Решение

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

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

Другие вопросы по тегам