0
votes

I'm creating a domain-specific language. One of the macro calls looks something like this:

(my-macro foo (bar "baz" qux) yay)

With the way I've written my-macro, the second argument here is supposed to be a list of strings inside a pair of parentheses. So my-macro works as long as bar and qux evaluate to strings. Let's just say they evaluate eponymously to "bar" and "qux" and my-macro treats the second argument as "bar baz qux".

But now I've realized that I really want "baz" to retain its quotes; I want my-macro to treat the second argument as "bar \"baz\" qux". Is this possible? Inside my-macro, can I tell when I can just use a string as-is vs when I need to add back the double quotes?

Thanks in advance!

3

3 Answers

1
votes

Your macro is passed the symbol FOO, the list (BAR "baz" QUX), and the symbol YAY as arguments. If you want to see if any of the elements in the second argument are a string, use something like stringp in the macro definition to test the argument.

Your macro completely controls how evaluation works in the resulting macroexpansion. It sees all its arguments un-evaluated. If you want to know something about the un-evaluated code passed as arguments, just look at it.

0
votes

To treat the second argument as a string "bar \"baz\" qux" you can simply write it to a string, as in:

(defmacro my-macro (arg1 (&rest arg2) arg3)
  (list arg1 (write-to-string arg2 :case :downcase) arg3)) 

(macroexpand '(my-macro foo (bar "baz" qux) yay))

=> (FOO "(bar \"baz\" qux)" YAY)

0
votes

A macro can test the type of its arguments at time of expansion. For example, this macro will create different code depending on whether the arg is a string or not:

(defmacro my-macro (arg) 
  (if (stringp arg)
     (format nil "\"~a\"" arg)
     `(format nil "~a" ,arg)))

Test:

(my-macro "baz")

=> "\"baz\""

(let ((evals-to-a-string "a string"))
  (my-macro evals-to-a-string))

=> "a string"

Note that if the argument isn't a string, the macro expands to a form:

(macroexpand '(my-macro evals-to-a-string))

=> (format nil "~a" evals-to-a-string)