Chapter 3 - Essential Lisp in Twelve Lessons

Lesson 12 - Essential Reader Macros

The reader turns characters into data

We saw in Lesson 11 that the Lisp reader gathers constituent characters into symbols and numbers, and that macro characters control the reader to handle lists, strings, quoted forms, and comments. In all of these cases, the reader turns characters into data. (For reasons that will become clear shortly, a comment is just "no data.")

Standard reader macros handle built-in data types

So far, we've seen just the standard syntax for Lisp. This is implemented by the reader, and controlled by the readtable. The reader works by processing characters according to information stored in the readtable.

User programs can define reader macros

Lisp exposes the readtable through the *readtable* variable, and provides several functions to manipulate entries in readtables. You can use these to alter the behavior of the Lisp reader. In the following example, we change the syntax so we can write quoted (i.e. unevaluated) lists using [ and ].

;This is wrong:
? (1 2 3 4 5 6)
->| Error: 1 is not a function

;Should have done this, instead:
? '(1 2 3 4 5 6)
-> (1 2 3 4 5 6)

;Define new syntax so we can write something like
; [1 2 3 4 5 6]
;instead of
; '(1 2 3 4 5 6)
? (defun open-bracket-macro-character (stream char)
`',(read-delimited-list #\] stream t))
-> OPEN-BRACKET-MACRO-CHARACTER

? (set-macro-character #\[ #'open-bracket-macro-character)
-> T

? (set-macro-character #\] (get-macro-character #\)))
-> T

;Now try it:
? [1 2 3 4 5 6]
-> (1 2 3 4 5 6)

First we tried to evaluate (1 2 3 4 5 6) -- this is wrong because 1 is not a function. What we really meant to do was to quote the list. But if we're going to do this often, we'd like a more convenient syntax. In particular we'd like [...] to behave like '(...).

To make this work, we have to define a specialized list reader macro function that doesn't evaluate its arguments. We'll arrange for the function to be called when the reader encounters a [ character; the function will return the list when it encounters a ] character. Every reader macro function gets called with two arguments: the input stream and the character that caused the macro to be invoked.

Our reader macro is very simple because Lisp has a function designed to read delimited lists. READ-DELIMITED-LIST expects one argument -- the character which will terminate the list being read. The other two arguments are optional -- the input stream and a flag which is normally set to T when used in reader macro functions. READ-DELIMITED-LIST reads objects from the input stream until it encounters the terminating character, then returns all of the objects in a list. By itself, this does everything we need except for suppressing evaluation.

QUOTE (or ') suppresses evaluation, as we saw in Lesson 3. But we can't use '(READ-DELIMITED-LIST ...); that would suppress evaluation of the form we need to evaluate to get the form we need to quote... Instead, we use ` (see Lesson 8) to selectively require evaluation within a quoted form.

Our definition of OPEN-BRACKET-MACRO-CHARACTER uses

`',form

to evaluate form and return the result, quoted.

Lisp reserves six characters for the programmer:

 [ ] { } ! ?
You can define any or all of these as macro characters without interfering with Lisp. However, you should watch out for conflicts if you share code with other programmers.


Contents | Cover
Chapter 2 | Chapter 3, Introduction | Chapter 3, Lesson 11 | Chapter 3, Lesson 12 | Chapter 4 |

Copyright © 1995-2001, David B. Lamkins
All Rights Reserved Worldwide

This book may not be reproduced without the written consent of its author. Online distribution is restricted to the author's site.