macroscommon-lispreader-macro

Read input into string in Lisp reader macro


I am trying to make a reader macro that would convert @this into "this". This is what I currently have:

(defun string-reader (stream char)
   (declare (ignore char))
   (format nil "\"~a\"" (read-line stream t nil t))   
)    
(set-macro-character #\@ #'string-reader )

The problem is that this requires that I put a newline after ever @this. I've also tried it with (read), but that just returns the variable test, which has not been set. I can't just hard-code the number of characters after the @ symbol, because I don't know how many there would be. Is there any way to fix this?

Edit: is the only way to do this to loop over read-char and peek-char, reading until I get to #),#\space, or #\Newline?


Solution

  • You can try to use read and then look at what it returns:

    (defun string-reader (stream char)
       (declare (ignore char))
       (let ((this (let ((*readtable* (copy-readtable)))
                     (setf (readtable-case *readtable*) :preserve)
                     (read stream t nil t))))
         (etypecase this
           (string this)
           (symbol (symbol-name this)))))
    
    (set-macro-character #\@ #'string-reader)
    

    Above would allow @This and @"This", but not @333.

    This version just reads a string until whitespace:

    (defun read-as-string-until-whitespace (stream)
      (with-output-to-string (out-stream)
        (loop for next = (peek-char nil stream t nil t)
              until (member next '(#\space #\newline #\tab))
              do (write-char (read-char stream t nil t) out-stream))))
    
    (defun string-reader (stream char)
       (declare (ignore char))
       (read-as-string-until-whitespace stream))
    
    (set-macro-character #\@ #'string-reader)
    

    Example:

    CL-USER 21 > @this
    "this"
    
    CL-USER 22 > @42
    "42"
    
    CL-USER 23 > @FooBar
    "FooBar"