The Reader and Reader Macros

24.1 The Lisp Reader

read converts text into Lisp objects; the evaluator never sees text. The reader is controlled by the readtable and variables like *read-base*, *read-default-float-format*, *read-eval*, and *package*.

` x `, ,x, ,@x | Backquote (function f) #+sbcl form, #-windows form Feature conditionals (*features*) #1=(a. #1#) Circular structure labels #xFF #b101 #o17 #36rZ #S(point:x 1)`

(read-from-string "(+ 1 2)")              ; (+ 1 2), 7
(let ((*read-base* 16)) (read-from-string "FF"))   ; 255
(let ((*read-eval* nil)) (read-from-string "#.(+ 1 2)"))   ; error: protects against read-time eval

24.2 Defining Reader Macros

Modify a copy of the readtable ((copy-readtable)) and use the named-readtables library so custom syntax doesn't leak into other code. Reader macros are powerful but can hurt readability and tooling.

;; #[1 2 3] reads as (list 1 2 3)
(defun read-list-literal (stream subchar arg)
  (declare (ignore subchar arg))
  `(list ,@(read-delimited-list #\] stream t)))

(set-dispatch-macro-character #\# #\[ #'read-list-literal)
(set-macro-character #\] (get-macro-character #\)))

#[1 (+ 1 1) 3]                          ; (1 2 3)

;; JSON-like {"key" value} syntax building a hash table
(defun read-hash-literal (stream char)
  (declare (ignore char))
  (let ((items (read-delimited-list #\} stream t)))
    `(let ((h (make-hash-table :test 'equal)))
       ,@(loop for (k v) on items by #'cddr collect `(setf (gethash ,k h) ,v))
       h)))
(set-macro-character #\{ #'read-hash-literal)
(set-macro-character #\} (get-macro-character #\)))