(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!
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.
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.
Related
I'm trying to create something similar to with-eval-after-load except that the body evaluates after all features have been provided. Additionally, the feature list must be provided at runtime.
For example, I want something like
(setq feature-list '(a b))
(something feature-list (message "a and b both provided"))
where this performs functionality equivalent to
(with-eval-after-load 'a
(with-eval-after-load 'b
(message "a and b both provided")))
Providing the list at runtime seems to be the tricky part. Without that requirement I could write a macro:
(defmacro eval-after-load-all (features body)
(if (null features)
body
`(with-eval-after-load (quote ,(car features))
(eval-after-load-all ,(cdr features) ,body))))
and pass the list with:
(eval-after-load-all (a b) (message "a and b both provided"))
But passing it feature-list will cause it to use the literal characters "feature-list".
I've tried defining a recursive function:
(defun eval-after-load-all (features body)
(if (null features)
body
(with-eval-after-load (car features)
(eval-after-load-all (cdr features) body))))
But when I evaluate
(eval-after-load-all feature-list (message "a and b both provided"))
(provide 'a)
;; (provide 'b)
It triggers an error at the (provide 'a) call complaining about void-variable body in the recursive call step (i.e. last expression in the function). This scope confuses me. Why is body void here?
I also tried to wrap the macro in a function so that I could pass it the evaluated arguments:
(defun macro-wrapper (features body)
(eval-after-load-all features body))
but this complains at function definition that features is not a list: wrong-type-argument listp features.
You may not use the symbol features as an argument since that is (I cite the doc of features):
A list of symbols which are the features of the executing Emacs.
Used by featurep and require, and altered by provide.
The following code for eval-after-load-all works as expected. It is derived from your recursive function definition.
I added the evaluation of the form as function or as expression with funcall or eval, respectively, I used the backquote for the lambda, and I introduced the quoting for the list and the expression in the generated lambda expression.
(defun eval-after-load-all (my-features form)
"Run FORM after all MY-FEATURES are loaded.
See `eval-after-load' for the possible formats of FORM."
(if (null my-features)
(if (functionp form)
(funcall form)
(eval form))
(eval-after-load (car my-features)
`(lambda ()
(eval-after-load-all
(quote ,(cdr my-features))
(quote ,form))))))
I'm having a problem passing a list to macro, where the list will be used to generate function name. For example, the code below causes an error.
(defmacro gen (str-lst)
`(defun ,(intern (string-upcase (car str-lst))) () (print "foo")))
(gen '("foo" "bar"))
The resulting error was:
*** - DEFUN/DEFMACRO: QUOTE is a special operator and may not be redefined. The following restarts are available: ABORT :R1
Abort main loop
How should I modify my code, and what is wrong with my code?
The thing makes me even more confused is that the code below, about which answer exits here, works fine.
(defmacro easy-one (str-lst)
`(mapc #'(lambda (str) (print str)) ,str-lst))
(easy-one '("foo" "bar"))
Don't quote the list. Macros don't evaluate their arguments, so you don't need to quote them to prevent them from being evaluated, like you do for ordinary functions.
(gen ("foo" "bar"))
When you quote it, you're executing
(get (quote ("foo" "bar")))
The value of str-list is the list (quote ("foo" "bar")), so (car str-list) is the symbol QUOTE. As a result, the macro expands to
(defun quote () (print "foo"))
That's why you get an error complaining that you're trying to redefine the built-in QUOTE.
The difference in your second example is that you're just substituting the parameter into the expansion, you're not using its value in the expansion code. So it expands to
(mapc #'(lambda (str) (print str)) '("foo" "bar")))
Here the list will be used when the expansion runs, not while the macro is being expanded. It needs to be quoted there, to prevent it from being evaluated as a function call.
You should use macroexpand to see how your macros are being expanded when debugging.
I'm attempting to make a macro that will take an input stream and do something different depending on the contents of the first line read, and then read further input. I'm having trouble just having a macro that will take an input stream and read some values from it.
A contrived example:
(defmacro read-and-print (&optional in)
`(print
,(if (string= (read-line in) "greet")
`(concatenate 'string "hello" (read-line ,in))
`(read-line ,in))))
(with-input-from-string (in "greet
bob") (read-and-print in))
but even that is producing the following error
There is no applicable method for the generic function
#<STANDARD-GENERIC-FUNCTION SB-GRAY:STREAM-READ-LINE (1)>
when called with arguments
(IN).
[Condition of type SB-INT:COMPILED-PROGRAM-ERROR]
The thing that's really baffling me is even changing the function to take a string for the first line isn't working:
(defmacro read-and-print (command &optional in)
`(print
,(if (string= command "greet")
`(concatenate 'string "hello " (read-line ,in))
`(read-line ,in))))
(with-input-from-string (in "greet
bob")
(read-and-print (read-string in) in))
This gives me
The value
(READ-LINE IN)
is not of type
(OR (VECTOR CHARACTER) (VECTOR NIL) BASE-STRING SYMBOL CHARACTER)
when binding SB-IMPL::STRING1
[Condition of type SB-INT:COMPILED-PROGRAM-ERROR]
While this executes completely fine:
(with-input-from-string (in "greet
bob")
(read-and-print "greet" in))
Is there something special about the with-input-from-string macro that I'm missing? I suspect I'm missing something very obvious about macros, but googling has gotten me nowhere.
What you asked
Macros are a code generation tool.
They do not evaluate their arguments.
Your with-input-from-string example works because strings are self-evaluating. If you quite the string literal, you will get an error.
What you should have asked
You do not need a macro here. Use a function instead.
When deciding between a function and a macro, you need to ask yourself:
Am I defining a new syntax?
Does the code generate more code?
Unless you understand the questions and answer yes, you should be using functions.
See also How does Lisp let you redefine the language itself?
Your macro:
(defmacro read-and-print (&optional in)
`(print
,(if (string= (read-line in) "greet")
`(concatenate 'string "hello" (read-line ,in))
`(read-line ,in))))
But ,(if ... in ...) makes no sense, since the value of in typically isn't a stream at macro expansion time, but code (a symbol, an expression, ...). Since your code is not executing and the macro sees the source (and not the values of something which has not been executed yet) you can't do that. You can't also not usefully repair that: It's just the wrong approach. Use a function instead.
Use a function:
CL-USER 17 > (defun read-and-print (&optional in)
(if (string= (read-line in) "greet")
(concatenate 'string "hello" (read-line in))
(read-line in)))
READ-AND-PRINT
CL-USER 18 > (with-input-from-string (in "greet
foo
bar")
(read-and-print in))
"hellofoo"
Using a macro
You still can write it as a macro, but then you need to generate the code so that it runs at runtime, and not at macro-expansion time:
(defmacro read-and-print (&optional in)
`(print
(if (string= (read-line ,in) "greet")
(concatenate 'string "hello" (read-line ,in))
(read-line ,in))))
Note: one would actually might want to handle that in is not evaluated multiple times.
This macro would give you the advantage that the code is inlined.
This macro would give you the disadvantage that the code is inlined.
The function above gives you the advantage that the code typically is not inlined.
The function above gives you the advantage that the code can optionally be inlined, by telling the compiler to do so with an inline declaration.
I am trying to make a macro that creates a function that takes S-expresions and evaluates them inside the lexical context of the fixture. Here is the macro I wrote:
(defmacro def-fixture (name bindings)
"Return a function that takes the form to execute but is wrapped between a let of the bindings"
`(defun ,(intern (symbol-name name)) (body)
(let (,bindings)
(unwind-protect
(progn
body)))))
But when I run it it appears to be executing outside the lexical context I provided
(def-fixture test-fixture '(zxvf 1))
(test-fixture '(= zxvf 1))
let: Symbol's value as variable is void: zxvf
Btw, I have enabled the variable lexical binding. Any ideas as to what is my mistake?
This has nothing to do with lexical scoping. Your macro call expands to:
(defun test-fixture (body)
(let ((quote (zxvf 1)))
(unwind-protect (progn body))))
Which is of course not what you intended. I do not believe that (test-fixture '(= zxvf 1)) signals the error you cite (i.e. variable is void). Instead, the call signals (void-function zxvf) because it tries to evaluate (zxvf 1). The (= zxvf 1) expression is never evaluated, since it's quoted.
You might like to try something more like:
(defmacro def-fixture (name bindings)
"Return a macro that takes the form to execute but is wrapped between a let of the bindings"
`(defmacro ,name (body)
`(let (,',bindings)
(unwind-protect
(progn
,body)))))
and then use it as in:
(def-fixture test-fixture (zxvf 1))
(test-fixture (= zxvf 1))
The following note is in the emacs manual:
Also, the code in the body of a defun or defmacro cannot refer to
surrounding lexical variables.
It might be what your problem is.
Also I do not know if you need to quote the second argument of def-fixture. I used the macrostep package to inspect the resulting macro and the result seems better without the quote.
I am trying to generate functions using a macro:
(defmacro make-my-emacs-command-region (cmd name)
(list 'defun (intern (format "my-emacs-command-%s-%s" cmd name))
'(&optional arg)
(list 'interactive "p")
(list (intern (format "mark-%s" name)) 'arg)
(list (intern (format "my-emacs-command-%s-region" cmd))
'(region-beginning) '(region-end))))
generator:
(mapcar (lambda (a) (make-my-emacs-command-region a buffer))
'(foo bar))
But I get:
my-emacs-command-a-buffer
What am I doing wrong? How can I force to pass value of a?
A major point of lisp macros is that the arguments are not evaluated. Read up on the macro pages in the manual, specifically the expansion of macros. The macroexpand function would be of use in debugging the problem. Also, backquote might help you write the body of the macro a little more succinctly.
My elisp is a bit rusty, but until someone comes up with the actual explanation: I could get your examples to work a bit more as expected my replacing cmd with (eval cmd) (possibly same with name) in the macro definition body.
Hope this helps.