Or see the list of project sponsors.
This system is highly recommended if you are writing a code which modifies a *readtable*
because it allows to define and switch between readtables as you do with Lisp packages.
If you are not familiar with what *readtable*
is, then read this article:
https://lisper.in/reader-macros
but pay attention, that the article manipulates with *readtable*
instead of using named-readtables
. This is bad. Use named-readtables
instead.
First, let's see how to use named-readtables
to switch between read-tables. As an example, we'll see how to use cl-interpol
and rutils
readtables.
This is how you can lookup which tables are available:
POFTHEDAY> (ql:quickload '(:cl-interpol :rutils))
POFTHEDAY> (named-readtables:list-all-named-readtables)
(#<NAMED-READTABLE :COMMON-LISP {1000024B73}>
#<NAMED-READTABLE :CURRENT {1000025663}>
#<NAMED-READTABLE RUTILS.READTABLE:RUTILS-READTABLE {1004A960E3}>
#<NAMED-READTABLE RUTILS.READTABLE:STANDARD-READTABLE {1004A96133}>
#<NAMED-READTABLE :INTERPOL-SYNTAX {1001D19853}>)
Now let's see how does switching work:
;; First I'll switch to the interpol's syntax:
POFTHEDAY> (named-readtables:in-readtable :interpol-syntax)
POFTHEDAY> (let ((username "Bob"))
#?"Hello ${username}!")
"Hello Bob!"
;; Rutils readtable is not active, and we can't
;; use it's syntax for hashes:
POFTHEDAY> #h(:foo "bar")
; Debugger entered on #<SB-INT:SIMPLE-READER-ERROR
; "no dispatch function defined for ~S" {10068D4C63}>
;; We have to activate it first
POFTHEDAY> (named-readtables:in-readtable
rutils:rutils-readtable)
POFTHEDAY> #h(:foo "bar")
#<HASH-TABLE :TEST EQL :COUNT 1 {10068B9013}>
;; But now we are unable to use iterpol's syntax:
POFTHEDAY> (let ((username "Bob"))
#?"Hello ${username}!")
; Debugger entered on #<SB-INT:SIMPLE-READER-ERROR
; "no dispatch function defined for ~S" {1006AE93F3}>
But what if we want to use both readtables from cl-interpol
and from rutils
?
It is possible if we merge them together and create a new readtable:
POFTHEDAY> (named-readtables:defreadtable
:poftheday
(:merge
rutils:rutils-readtable
:interpol-syntax))
POFTHEDAY> (named-readtables:in-readtable
:poftheday)
POFTHEDAY> (let ((username "Bob"))
#h(:greeting #?"Hello ${username}!"))
#<HASH-TABLE :TEST EQL :COUNT 1 {1003054C23}>
POFTHEDAY> (rutils:print-ht *)
#{
:GREETING "Hello Bob!"
}
Now we'll define a literal syntax for lambda from rutils
as a separate named read-table:
POFTHEDAY> (defmacro trivial-positional-lambda (body)
`(lambda (&optional % %%)
(declare (ignorable %) (ignorable %%))
,body))
POFTHEDAY> (defun |^-reader| (stream char)
(declare (ignore char))
(let ((sexp (read stream t nil t)))
`(trivial-positional-lambda
,(if (and (listp sexp) (listp (car sexp)))
(cons 'progn sexp)
sexp))))
POFTHEDAY> (named-readtables:defreadtable
:lambda
(:merge :standard)
(:macro-char #\^ #'|^-reader|))
;; Now we can switch to the new readtable
;; and use new syntax for lambdas:
POFTHEDAY> (named-readtables:in-readtable :lambda)
POFTHEDAY> ^(+ % %%)
#<FUNCTION (LAMBDA (&OPTIONAL % %%)) {2252593B}>
POFTHEDAY> (funcall *
2
3)
5
Named readtables has yet another useful feature - it integrates with SLIME. When you have a (in-readtable)
call after you package definition, SLIME will know what readtable
to use when you hit Ctrl-C Ctrl-C
on defuns.
That is what in-readtable
expands to:
POFTHEDAY> (named-readtables:in-readtable :interpol-syntax)
;; It expands to:
(eval-when (:compile-toplevel
:load-toplevel
:execute)
(setf *readtable*
(named-readtables:ensure-readtable
':interpol-syntax))
(when (find-package :swank)
(named-readtables::%frob-swank-readtable-alist
*package*
*readtable*)))
This %frob-swank-readtable-alist
modifies swank:*readtable-alist*
to make it know what readtable should be used for the package. But a comment to this code says it is a KLUDGE
.
Interesting, how this will or should work in the LispWorks?