Monday, 17 December 2007

Python decorators in Lisp, Part 3

In this last post, I showed a way to implement Python's decorator syntax in Lisp which actually seemed to work for more than just myself!

What I did not show is how you can use this in regular source files. As mentioned previously, one way to add new syntax into Lisp is to tell the reader (via *readtable*) to call a reader macro when it encounters a particular pair of characters. So the answer to using this syntax in regular source files is to locally enable it by rebinding *readtable*. As always, it helps to write out what you would like to do:


(enable-py-decorator-syntax)

#@my-decorator
(defun my-function (x) ... )

#@my-decorator
(defun my-other-function (x) ... )

#@my-decorator
(defun yaf[yet-another-function] (x) ... )

(disable-py-decorator-syntax)

Quite simply, enable-py-decorator-syntax copies the current readtable and sets the dispatch function. It also assigns the original readtable to a variable so it can be reset. Conversely, disable-py-decorator-syntax does the opposite: it sets the current readtable to the original readtable and sets the auxiliary variable to nil. Without further ado, the code for these functions:

(defparameter *original-readtable* nil)

(defmacro enable-py-decorator-syntax ()
"Turns on the decorator syntax"
'(eval-when (:compile-toplevel :load-toplevel :execute)
(%enable-py-decorator-syntax)))

(defun %enable-py-decorator-syntax ()
(unless *original-readtable*
(setf *original-readtable* *readtable*
*readtable* (copy-readtable))
(set-dispatch-macro-character #\# #\@
#'|#@-reader|
*readtable*)))

(defmacro disable-py-decorator-syntax ()
"Turns off the decorator syntax"
'(eval-when (:compile-toplevel :load-toplevel :execute)
(%disable-py-decorator-syntax)))

(defun %disable-py-decorator-syntax ()
(when *original-readtable*
(setf *readtable* *original-readtable*
*original-readtable* nil)))

Thanks to popeix for submitting the original post to reddit (mod it up!) The code for enabling and disabling the syntax was based on syntax.lisp.

2 comments:

Robert said...

For what it's worth, Tobias Rittweiler and I spent some time developing named readtables (this is a facility that's offered in Allegro CL, but isn't ANSI) that make it easier to specify readtables in mode lines (you can't really do this unless readtables are named things). See http://www.cliki.net/Editor%20Hints

Having that might make your python decorators easier to use.

I don't believe that Tobias has yet gotten around to making SLIME pay attention to readtables in its mode lines (I still use ELI, which does), but it should be a close-to-trivial mod.

Sohail Somani said...

That would make it ultimately useful. So how does this work? You define the readtable in some Common Lisp file and ensure that file is loaded in Slime while you're editing some other file which has the appropriate modeline?

I wonder how common it really is to define your own readtable. When I mentioned it in #lisp, people were praying for me and throwing holy water.