From: budden
Subject: local readtables
Date: 
Message-ID: <cb14c563-a334-4486-9b6b-1f61e55dd90d@j35g2000yqh.googlegroups.com>
Hi list!
  I'm searching the way to work with readtables locally. I know
many people there would curse me, but it is not my opinion,
but a matter of fact that sexp syntax was not the intended
syntax for lisp. In fact, lisp as a language stopped its
development half-way. It is not likely that lisp will change. So
we need workarounds if we want to be efficient compared to
other languages. Local readtables is one of them.
  Dramatically, CL standard provides no means to make
readtables local and this removes scalability from using of
readtables. You can use your own readtable in your project,
but there is still commonly admitted way way to use many
readtables. I have started now to test a very trivial kludge
to asdf:

(defclass around-load-op-mixin () ()
  (:documentation "
  Mixin to be able to do some actions around load-op"))

;;; !!! Redefining asdf class!
(defclass cl-source-file (around-load-op-mixin source-file) ())

(defmethod perform :around
    ((o operation) (c around-load-op-mixin))
  (let ((*readtable* (copy-readtable nil)))
    (call-next-method)
    ))

So, readtable is bound to a fresh copy of standard readtable
around any asdf operation on cl-source file. It looks like it
works, but it is too early to say. I could (and I think I will)
make my new class instead. E.g.

(defclass f (around-load-op-mixin cl-source-file) ())

So, I can do any changes to a readtable in any file and
I won't harm subsequent files. If I want that subsequent files
were affected by readtable change, I have to add one form
to any such file. E.g. I can write file readtable.lisp which
defines new readtable and stores it to global variable.
Any file which wants to use that readtable should have
a form like
(eval-when (:compile-toplevel :load-toplevel :execute)
  (setf *readtable* (copy-readtable <my-variable>)))

Second thing we need is a way to read a form with
*readtable* bound. There is another rather trivial
fix. CL fails to supply an input stream to a form being
eval-read, so it should be fixed.

(defvar *read-eval-stream*
  "Something's wrong with #.? "
  "Bound to current input stream in the scope of #. ")

(defun sbcl-sharp-dot (stream sub-char numarg)
  "code borrowed from sbcl"
  (declare (ignore sub-char numarg))
  (let ((token (read stream t nil t))
        (*read-eval-stream* stream))
    (unless *read-suppress*
      (unless *read-eval*
        (error
          "can't read #. from ~S while *READ-EVAL* is NIL"
          stream)))
    (prog1
        (eval token)
      ))
  )

(set-dispatch-macro-char #\# #\. #'sbcl-sharp-dot)

So I can write (with the help of "named-readtables"):

(defreadtable foo (:merge)
  (:macro-char #\$
    (lambda (&rest ignore)
        (declare (ignore ignore))
        (print "Ypa!"))))

(defmacro &wrt ()
   (let1 *readtable*
       (find-readtable (read *read-eval-stream*))
     (read *read-eval-stream* t nil t)))

(read-from-string "#.(&wrt foo) $")
"Ypa!"
"Ypa!"
14

What is posted here is a solution already, provided that you
download semi-portable named-readtable by
Tobias C. Rittweiler.

I think more reasonable would be to bind some macro-characters
instead of binding readtable itself. Something like
#.(&with-macro-char #\$ 'some-function) $

But I didn't implement it.

What I ask for is a prior art.
I didn't see anything alike, but I can't say I have read all CL
sources
available to the public :)
From: budden
Subject: Re: local readtables
Date: 
Message-ID: <c5254e1d-79f7-4a9f-8ee6-f7ae7afe9978@z9g2000yqi.googlegroups.com>
oops :)
*readtable* is bound by compile-file and load.
So load-op mixin is unnecessary. But, at least, this is a way to
establish some other guards and bindings that might be necessary.