1
(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)

Above is the code that i took from https://github.com/jedrz/.emacs.d/blob/master/setup-flycheck.el

It doesn't do anything much apart from defining a checker for flycheck which can be found https://github.com/lunaryorn/flycheck

My problem is trivial and i have already spent a day on it and i am more confused.

The second part of the code uses the defined macro to call flycheck to register a compiler

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

Above code works perfectly.

But since i wanted my compiler to have some dynamic includes et all, I have a variable defined as

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

when i pass that to the macro like

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

i receive a compilation error

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)

I guess i confused in how the macro expands in elisp.

Please help!

RamneekHanda
  • 171
  • 5

2 Answers2

2

As a general rule, you're better off using a defun than a defmacro, except when defun is really inconvenient/impossible to use. In your case, a defun indeed makes more sense. The only downside is that you need to quote the c and c-mode arguments.

Stefan
  • 27,908
  • 4
  • 53
  • 82
1

You need to decide whether you want the command argument to be evaluated or unevaluated. An unevaluated argument allows you to type lists without quoting them, i.e. ("gcc" "-Wall") instead of '("gcc" "-Wall"), at the cost of not being able to pass a variable as the argument. An evaluated argument enables you to provide variables (or indeed arbitrary expressions) to the macro, at the cost of having to quote simple lists.

Normally, to evaluate the macro argument in backticks, you'd just use the , operator. However, you're already using the ,@ operator, and you're mentioning command twice, so it's better to explicitly evaluate it using 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)))

With the power of defmacro at your disposal, you can even go one step further and define that the macro is evaluates if it is a symbol, otherwise it's used as-is. This would allow you to have your cake and eat it, i.e. be able to pass both variable names and literal lists. The cost of this would be reduced consistency with normal evaluation rules—you would be able to pass a list or a variable, but not an arbitrary expression such as a function call, which would unpleasantly surprise the users of the macro. Because of that the implementation is left as an exercise for the reader.

user4815162342
  • 141,790
  • 18
  • 296
  • 355
  • ok.. this works but something in my head still doesn't make sense. I do understand what you are doing but still I can see why the expansion isn't evaluating the symbol with `,@`? – RamneekHanda Mar 23 '13 at 13:18
  • @RamneekHanda `,@` is evaluating the `command` symbol **inside the macro** just fine, expanding it to `efx-flycheck-c-command` and attempting to splice it into the list. This fails because `efx-flycheck-c-command` is not a list, but a symbol. So you want `command` to be evaluated and *then* expanded inside the list, which is what my version does. – user4815162342 Mar 23 '13 at 14:25
  • 1
    To understand this, it might be a good idea to start off with a simpler macro, and do it without the backquotes first. Always remember that backquotes are just syntactic sugar for list construction and are in no way specific to or required by macros. – user4815162342 Mar 23 '13 at 14:26