Backquote String Interpolation

350 views Asked by At

Is it possible to use lisp's macro to do string interpolation?

For instance, can I make a macro like this:

(defmacro test (a) `",a")

So that (test abc) returns "abc" as a string? I could probably cheat by quoting it and turning that quote into a string, but that doesn't work for arguments like "9:00" (without double quotes).

3

There are 3 answers

3
m-n On BEST ANSWER

Lisp macros operate on arguments that have already been read by lisp's reader. Because of that a macro can't change how it's arguments are read, and if you pass an argument that will signal an error when you try to read it, e.g. 9:00 in (test 9:00), the reader signals the error before the macro even starts to run.

The reader is also customizable. Not in a way that would let you read 9:00 as "9:00" in a straightforward manner, but for example you could write a short reader macro that read @9:00 as either "9:00" or as a date object.

Edit: Something like this:

(defvar *arguments-rt* (copy-readtable ()))

(defun timestring-reader (stream char &optional count)
  (declare (ignore char count))
  (with-output-to-string (s)
    (loop repeat 4 do (write-char (read-char stream) s))))

(set-macro-character #\@ #'timestring-reader () *arguments-rt*)

(let ((*readtable* *arguments-rt*))
  (let ((args "dinner @9:00"))
    (with-input-from-string (stream args)
      (loop for arg = (read stream () ())
            while arg collect arg))))

-> (DINNER "9:00")
0
Sylwester On

For string interpolation, see format. eg. (format nil "~a" a) does what you want. In Practical Common Lisp there are some format recipes

quasiquote and unquote only works for lists, not other sequences.

Macros are transformation of code.. It's not something that happens runtime. Eg. when you have code like (my-macro x) where x in runtime would be a string, the macro only sees the symbol. It can't know what type it is at expansion time.

3
zck On

Backquoting, whether in a macro or not, won't do this:

[1]> (defmacro test (a) `",a")
TEST
[2]> (test oh-no)
",a"

What you can do is use format, as follows:

[3]> (defun interp (name) (format nil "Hi, my name is ~A" name))
INTERP
[4]> (interp "Steve")
"Hi, my name is Steve"