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.")
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.
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.