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
аргументы.