[slime-devel] Broken readtables
I have a file which defines a readtable so I can type things like #v(1 2). (Don't ask. :-) When I hit M-. to find any definition in this file, emacs croaks and asks if I want to enter recursive edit. Saying yes yields a SLDB buffer like this: Subcharacter #\v not defined for dispatch char #\#. [Condition of type CONDITIONS:SIMPLE-READER-ERROR] Restarts: 0: [ABORT] Abort handling SLIME request. 1: [ABORT] Quit process. Now, I grovelled a bit in the sources and found: (defvar *readtable-alist* '() "An alist mapping package names to readtables.") I tried to use this thusly: Try to add what I think is reasonable to this alist CL-USER> (push (cons "GRAPHICS" *readtable*) swank::*readtable-alist*) (("GRAPHICS" . #<READTABLE 202E1B84>)) Verify that it finds the right package CL-USER> (swank::guess-buffer-readtable "graphics") #<READTABLE 202E1B84> Make sure the correct table is in place: CL-USER> (get-dispatch-macro-character #\# #\v (swank::guess-buffer-readtable "graphics")) #<function GRAPHICS::VECTOR-READER 237CF9D2> (looks good). However, the next M-. failed in exactly the same way. Tracing GUESS-BUFFER-READTABLE, I found it was in fact calling it with argument ":graphics", which I thought sub-optimal, but, no matter: CL-USER> (push (cons ":graphics" *readtable*) swank::*readtable-alist*) ((":graphics" . #<READTABLE 202E1B84>) ("GRAPHICS" . #<READTABLE 202E1B84>)) Now, surely it will work, right? Nope. Any clues? Cheers, --ap
Alain.Picard@memetrics.com writes:
I have a file which defines a readtable so I can type things like #v(1 2). (Don't ask. :-)
When I hit M-. to find any definition in this file, emacs croaks and asks if I want to enter recursive edit. Saying yes yields a SLDB buffer like this:
The `with-standard-io-syntax' in dspec-stream-position looks suspicious to me (swank-lispworks.lisp). How about if you remove it? -Luke
Luke Gorrie writes:
Alain.Picard@memetrics.com writes:
I have a file which defines a readtable so I can type things like #v(1 2). (Don't ask. :-)
When I hit M-. to find any definition in this file, emacs croaks and asks if I want to enter recursive edit. Saying yes yields a SLDB buffer like this:
The `with-standard-io-syntax' in dspec-stream-position looks suspicious to me (swank-lispworks.lisp). How about if you remove it?
If you remove it, it works. In fact, I think there are TWO bugs in that function; I think that *read-eval* needs to be set to T. I propose: (defun dspec-stream-position (stream dspec) (let ((*read-eval* t)) (loop (let* ((pos (file-position stream)) (form (read stream nil '#1=#:eof))) (when (eq form '#1#) (return nil)) (labels ((check-dspec (form) (when (consp form) (let ((operator (car form))) (case operator ((progn) (mapcar #'check-dspec (cdr form))) ((eval-when locally macrolet symbol-macrolet) (mapcar #'check-dspec (cddr form))) ((in-package) (let ((package (find-package (second form)))) (when package (setq *package* package)))) (otherwise (let ((form-dspec (dspec:parse-form-dspec form))) (when (dspec:dspec-equal dspec form-dspec) (return pos))))))))) (check-dspec form)))))) in swank-lispworks.lisp. Thanks!
Alain.Picard@memetrics.com writes:
If you remove it, it works. In fact, I think there are TWO bugs in that function; I think that *read-eval* needs to be set to T. I propose:
Committed, though I don't really understand this dspec mechanism. I suspect we should also be making a dynamic binding of *package* to avoid side-effecting the global value while looking up a location. Right? -Luke
On Thu, 17 Jun 2004 18:56:58 +1000, Alain.Picard@memetrics.com said:
ap> Luke Gorrie writes:
Alain.Picard@memetrics.com writes:
I have a file which defines a readtable so I can type things like #v(1 2). (Don't ask. :-)
When I hit M-. to find any definition in this file, emacs croaks and asks if I want to enter recursive edit. Saying yes yields a SLDB buffer like this:
The `with-standard-io-syntax' in dspec-stream-position looks suspicious to me (swank-lispworks.lisp). How about if you remove it?
ap> If you remove it, it works. In fact, I think there are TWO bugs ap> in that function; I think that *read-eval* needs to be set to T.
It was bound it to NIL to prevent unexpected evaluation -- it depends on how dangerous you want M-. to be :-) #.(call-system "rm -rf /") ap> I propose: ap> (defun dspec-stream-position (stream dspec) ap> (let ((*read-eval* t)) ap> (loop (let* ((pos (file-position stream)) ap> (form (read stream nil '#1=#:eof))) ap> (when (eq form '#1#) ap> (return nil)) ap> (labels ((check-dspec (form) ap> (when (consp form) ap> (let ((operator (car form))) ap> (case operator ap> ((progn) ap> (mapcar #'check-dspec ap> (cdr form))) ap> ((eval-when locally macrolet symbol-macrolet) ap> (mapcar #'check-dspec ap> (cddr form))) ap> ((in-package) ap> (let ((package (find-package (second form)))) ap> (when package ap> (setq *package* package)))) ap> (otherwise ap> (let ((form-dspec (dspec:parse-form-dspec form))) ap> (when (dspec:dspec-equal dspec form-dspec) ap> (return pos))))))))) ap> (check-dspec form)))))) ap> in swank-lispworks.lisp. I made it use WITH-STANDARD-IO-SYNTAX originally to get all the standard bindings, in particular you must also bind *PACKAGE* because the function sets it. There is also *READ-BASE*, *READ-DEFAULT-FLOAT-FORMAT* and *READ-SUPPRESS* to consider. What binds *READTABLE* when you do M-. anyway? It doesn't seem to be bound for me when DSPEC-STREAM-POSITION is reached via FIND-DEFINITIONS-FOR-EMACS. __Martin
Martin Simmons <martin@xanalys.com> writes:
I made it use WITH-STANDARD-IO-SYNTAX originally to get all the standard bindings, in particular you must also bind *PACKAGE* because the function sets it. There is also *READ-BASE*, *READ-DEFAULT-FLOAT-FORMAT* and *READ-SUPPRESS* to consider.
True, plus any other implementation-defined reader setting (I don't know if LispWorks has any). I particularly hadn't realised that with-standard-io-syntax was binding *package*. I hacked it to use a `with-fairly-standard-io-syntax' macro. This does with-standard-io-syntax but then rebinds *package* and *readtable* as they were before executing the body. Sound reasonable?
What binds *READTABLE* when you do M-. anyway? It doesn't seem to be bound for me when DSPEC-STREAM-POSITION is reached via FIND-DEFINITIONS-FOR-EMACS.
[Just read the *readtable-alist* code for the first time.] Nobody sets it for definition-finding. That's reasonable in swank.lisp since it doesn't know what package the definition will be in. Perhaps ideal would be for dspec-stream-position to set the right readtable (from *readtable-alist*) when it sees IN-PACKAGE. I'm not sure if the other backends can/should do this too. We'd have to move *readtable-alist* into swank-backend.lisp. But I suspect the main issue right now is to make sure that a custom-hacked readtable that one installs globally as *READTABLE* will be used by `M-.'. That's how I do my readtable'ery, at least. P.S., Helmut, I think I was sleeping when *readtable-alist* when in. Could you post a snippet of how it's supposed to be used? Cheers, Luke
Luke Gorrie <luke@bluetail.com> writes:
That's reasonable in swank.lisp since it doesn't know what package the definition will be in.
er, yes it does, from the symbol. I wonder if it's worth choosing *readtable* based on the package of the symbol we're looking up.
On Thu, 17 Jun 2004 13:47:25 +0200, Luke Gorrie <luke@bluetail.com> said:
Luke> Martin Simmons <martin@xanalys.com> writes:
I made it use WITH-STANDARD-IO-SYNTAX originally to get all the standard bindings, in particular you must also bind *PACKAGE* because the function sets it. There is also *READ-BASE*, *READ-DEFAULT-FLOAT-FORMAT* and *READ-SUPPRESS* to consider.
Luke> True, plus any other implementation-defined reader setting (I don't Luke> know if LispWorks has any). I particularly hadn't realised that Luke> with-standard-io-syntax was binding *package*. Luke> I hacked it to use a `with-fairly-standard-io-syntax' macro. This does Luke> with-standard-io-syntax but then rebinds *package* and *readtable* as Luke> they were before executing the body. Sound reasonable? Yes, sound good.
What binds *READTABLE* when you do M-. anyway? It doesn't seem to be bound for me when DSPEC-STREAM-POSITION is reached via FIND-DEFINITIONS-FOR-EMACS.
Luke> [Just read the *readtable-alist* code for the first time.] Luke> Nobody sets it for definition-finding. That's reasonable in swank.lisp Luke> since it doesn't know what package the definition will be in. Perhaps Luke> ideal would be for dspec-stream-position to set the right readtable Luke> (from *readtable-alist*) when it sees IN-PACKAGE. I'm not sure if the Luke> other backends can/should do this too. We'd have to move Luke> *readtable-alist* into swank-backend.lisp. Luke> But I suspect the main issue right now is to make sure that a Luke> custom-hacked readtable that one installs globally as *READTABLE* will Luke> be used by `M-.'. That's how I do my readtable'ery, at least. It looks like using SWANK::WITH-BUFFER-SYNTAX somewhere might be the right thing, since *BUFFER-READTABLE* and *BUFFER-PACKAGE* are already bound. __Martin
Luke Gorrie <luke@bluetail.com> writes:
Nobody sets it for definition-finding. That's reasonable in swank.lisp since it doesn't know what package the definition will be in. Perhaps ideal would be for dspec-stream-position to set the right readtable (from *readtable-alist*) when it sees IN-PACKAGE. I'm not sure if the other backends can/should do this too. We'd have to move *readtable-alist* into swank-backend.lisp.
For CMUCL/SBCL, at least, we don't look at the IN-PACKAGE forms. *read-suppress* is T most of the time and we only look at the "shape" of the expression.
But I suspect the main issue right now is to make sure that a custom-hacked readtable that one installs globally as *READTABLE* will be used by `M-.'. That's how I do my readtable'ery, at least.
Yeah, and it's probably what 99% of the users do. People who use multiple readtables deserve to loose :-)
P.S., Helmut, I think I was sleeping when *readtable-alist* when in. Could you post a snippet of how it's supposed to be used?
It went in with the other SBCL reader stuff. Say you have a package foo and readtable *bar* then you could use it like: (push (cons (package-name :foo) *bar*) swank::*readtable-alist*) swank::eval-for-emacs uses this to bind *buffer-readtable*; the current readtable will be used if there's no entry in the *readtable-alist*. `with-buffer-syntax' can then be used to actually bind *readtable* to *buffer-readtable*. I replaced most uses of (let ((*package* *buffer-package*)) ...) with the idiom: (defslimefun <foo> (...) (with-buffer-syntax () ...)) All this is currently only used for SBCL and I don't know if *readtable-alist* is very useful for users. I assumed it is more convenient to associate readtables with packages than to use Emacs-style file variables, ala -*- readtable: ... -*-. OTOH, file variables would be handy when find-definitions opens an arbitrary file; we could just look at the first line. Helmut.
Helmut Eller writes:
Luke Gorrie <luke@bluetail.com> writes:
But I suspect the main issue right now is to make sure that a custom-hacked readtable that one installs globally as *READTABLE* will be used by `M-.'. That's how I do my readtable'ery, at least.
Yeah, and it's probably what 99% of the users do. People who use multiple readtables deserve to loose :-)
I'm currently working on Objective-C integration with CLOS, and the code implementing the integration uses a normal readtable, while ObjC-using code needs a different readtable (so far the only change is to use :invert read-case to support camelCase ObjC method names). The specter of having to constantly switch readtables has made me think about adding the ability to associate a readtable with an Emacs buffer. All this is to say that I think there are reasonable situations in which this might come up.
All this is currently only used for SBCL and I don't know if *readtable-alist* is very useful for users. I assumed it is more convenient to associate readtables with packages than to use Emacs-style file variables, ala -*- readtable: ... -*-. OTOH, file variables would be handy when find-definitions opens an arbitrary file; we could just look at the first line.
I think the file variables method is the way to go, maybe in combination with grovelling for a (setf *readtable* ...) form at the top of the file, so the common case gets handled automatically.
participants (5)
-
Alain.Picard@memetrics.com
-
Helmut Eller
-
Luke Gorrie
-
Martin Simmons
-
Thomas F. Burdick