13.7.1 Readtables
The dispatch table in Delimiters and Dispatch
corresponds to the default readtable. By creating a new
readtable and installing it via the current-readtable
parameter, the reader’s behavior can be extended.
A readtable is consulted at specific times by the reader:
when looking for the start of a datum;
when determining how to parse a datum that starts with
#;
when looking for a delimiter to terminate a symbol or number;
when looking for an opener (such as (), closer (such
as )), or . after the first character parsed as
a sequence for a pair, list, vector, or hash table; or
when looking for an opener after #‹n› in a
vector of specified length ‹n›.
The readtable is ignored at other times. In particular, after parsing
a character that is mapped to the default behavior of ;, the
readtable is ignored until the comment’s terminating newline is
discovered. Similarly, the readtable does not affect string parsing
until a closing double-quote is found. Meanwhile, if a character is
mapped to the default behavior of (, then it starts sequence
that is closed by any character that is mapped to a closing parenthesis
). An apparent exception is that the default parsing of
| quotes a symbol until a matching character is found, but
the parser is simply using the character that started the quote; it
does not consult the readtable.
For many contexts, #f identifies the default readtable. In
particular, #f is the initial value for the
current-readtable parameter, which causes the reader to
behave as described in The Reader.
Returns #t if v is a readtable, #f
otherwise.
Creates a new readtable that is like
readtable (which can be
#f), except that the reader’s behavior is modified for each
key according to the given
mode and
action. The
...+ for
make-readtable applies
to all three of
key,
mode, and
action; in
other words, the total number of arguments to
make-readtable
must be
1
modulo
3
.
The possible combinations for key, mode, and
action are as follows:
char 'terminating-macro proc — causes
char to be parsed as a delimiter, and an
unquoted/uncommented char in the input string triggers a
call to the reader macro proc; the activity of
proc is described further below. Conceptually, characters
like ;, (, and ) are mapped to
terminating reader macros in the default readtable.
char 'non-terminating-macro proc — like
the 'terminating-macro variant, but char is not
treated as a delimiter, so it can be used in the middle of an
identifier or number. Conceptually, # is mapped to a
non-terminating macro in the default readtable.
char 'dispatch-macro proc — like the
'non-terminating-macro variant, but for char only
when it follows a # (or, more precisely, when the character
follows one that has been mapped to the behavior of #
in the default readtable).
char like-char readtable — causes
char to be parsed in the same way that like-char
is parsed in readtable, where readtable can be
#f to indicate the default readtable. Mapping a character to
the same actions as | in the default reader means that the
character starts quoting for symbols, and the same character
terminates the quote; in contrast, mapping a character to the same
action as a " means that the character starts a string, but
the string is still terminated with a closing ". Finally,
mapping a character to an action in the default readtable means that
the character’s behavior is sensitive to parameters that affect the
original character; for example, mapping a character to the same
action as a curly brace { in the default readtable means
that the character is disallowed when the
read-curly-brace-as-paren parameter is set to #f.
#f 'non-terminating-macro proc —
replaces the macro used to parse characters with no specific mapping:
i.e., the characters (other than # or |) that can
start a symbol or number with the default readtable.
If multiple 'dispatch-macro mappings are provided for a
single char, all but the last one are ignored. Similarly, if
multiple non-'dispatch-macro mappings are provided for a
single char, all but the last one are ignored.
A reader macro proc must accept six arguments, and it can
optionally accept two arguments. The first two arguments are always
the character that triggered the reader macro and the input port for
reading. When the reader macro is triggered by read-syntax
(or read-syntax/recursive), the procedure is passed four
additional arguments that represent a source location for
already-consumed character(s): the source name, a line number or
#f, a column number or #f, and a position or
#f. When the reader macro is triggered by read (or
read/recursive), the procedure is passed only two arguments
if it accepts two arguments, otherwise it is passed six arguments
where the last four are all #f. See Reader-Extension Procedures
for information on the procedure’s results.
A reader macro normally reads characters from the given input port to
produce a value to be used as the “reader macro-expansion” of the
consumed characters. The reader macro might produce a special-comment
value (see Special Comments) to cause the consumed
character to be treated as whitespace, and it might use
read/recursive or read-syntax/recursive.
Produces information about the mappings in readtable for
char. The result is three values:
either a character (mapping to the same behavior as the
character in the default readtable), 'terminating-macro, or
'non-terminating-macro; this result reports the main (i.e.,
non-'dispatch-macro) mapping for char. When the result
is a character, then char is mapped to the same behavior as the
returned character in the default readtable.
either #f or a reader-macro procedure; the result is a
procedure when the first result is 'terminating-macro or
'non-terminating-macro.
either #f or a reader-macro procedure; the result is a
procedure when the character has a 'dispatch-macro mapping in
readtable to override the default dispatch behavior.
Note that reader-macro procedures for the default readtable are not
directly accessible. To invoke default behaviors, use
read/recursive or read-syntax/recursive with a
character and the #f readtable.
Examples:
; Provides raise-read-error and raise-read-eof-error |
> (require syntax/readerr) |
|
|
|
|
|
> (define (parse port read-one src) | ; First, check for empty tuple | (skip-whitespace port) | (if (eq? #\> (peek-char port)) | null | (let ([elem (read-one)]) | (if (special-comment? elem) | ; Found a comment, so look for > again | (parse port read-one src) | ; Non-empty tuple: | (cons elem | (parse-nonempty port read-one src)))))) |
|
|
> (define (parse-nonempty port read-one src) | ; Need a comma or closer | (skip-whitespace port) | (case (peek-char port) | [(#\>) (read-char port) | ; Done | null] | [(#\,) (read-char port) | ; Read next element and recur | (cons (skip-comments read-one port src) | (parse-nonempty port read-one src))] | [else | ; Either a comment or an error; grab location (in case | ; of error) and read recursively to detect comments | (let-values ([(l c p) (port-next-location port)] | [(v) (read-one)]) | (cond | [(special-comment? v) | ; It was a comment, so try again | (parse-nonempty port read-one src)] | [else | ; Wasn't a comment, comma, or closer; error | ((if (eof-object? v) | raise-read-eof-error | raise-read-error) | "expected `,' or `>'" src l c p 1)]))])) |
|
|
> (define (make-delims-table) | ; Table to use for recursive reads to disallow delimiters | ; (except those in sub-expressions) | (letrec ([misplaced-delimiter | (case-lambda | [(ch port) (misplaced-delimiter ch port #f #f #f #f)] | [(ch port src line col pos) | (raise-read-error | (format "misplaced `~a' in tuple" ch) | src line col pos 1)])]) | (make-readtable (current-readtable) | #\, 'terminating-macro misplaced-delimiter | #\> 'terminating-macro misplaced-delimiter))) |
|
|
> (define (wrap l) | `(make-tuple (list ,@l))) |
|
|
|
|
|
|
|
'(make-tuple (list 1 2 "a")) |
|
'(make-tuple (list 1 2 "a")) |
|
|
|
'(make-tuple (list 1 2 "a")) |