String parsing with the LOOP facility

As part of a read macro I needed to parse strings. Lisp strings don’t have escapes except for double quotes (as they are string delimiters) and the backslash (which is the escape character).

Here’s what I came up with, a really fine example of Algol style code with three nested conditionals:

(defun read-lisp-string (stream)
  "Parse a Lisp string. Expects stream to point to the
  first character after the leading double quote."
  (coerce
    (loop for char = (read-char stream)
          with nbackslashes = 0
          with eos = nil
 
          if (char= char #\\)
            do (setq nbackslashes (+ nbackslashes 1))
            and if (and (evenp nbackslashes)
                              (not (equal nbackslashes 0)))
                collect char
                end
          else
            if (char/= char #\")
                do (setq nbackslashes 0)
                and collect char
            else
                if (oddp nbackslashes)
                    collect char
                else
                    do (setq eos t)
 
          until eos)
    'simple-string))

The most important thing I learned: although in all places you can read that there’s no accepted standard for the LOOP facility, the respective section of the spec is quite helpful in determining whether a certain clause is allowed at some place.

Comments

  1. Zach Beane
    December 13th, 2007 | 5:18 pm

    LOOP’s fun, but I think this particular task would look better with a simpler variation:

    (defun read-lisp-string (input)
    (with-output-to-string (output)
    (loop
    (let ((char (read-char input)))
    (case char
    (#\\
    (setf char (read-char input)))
    (#\”
    (return)))
    (write-char char output)))))

    (Hope the formatting survives…)

  2. December 13th, 2007 | 8:17 pm

    Great, thanks!

Leave a reply