Lisp implementation hax

Here are some bits of Lisp code which might help you if you are using a specific implementation, or an interface from Emacs to Lisp.

LispWorks

Defining new `colon commands'

LispWorks, like other Lisp systems, has a top level `listener' in which, as well as Lisp forms being evaluated, certain `commands' can be issued. In common with Franz' Allegro CL, it distinguishes these commands by looking for lines that begin with a keyword: :a will cause the debugger to select the `abort' restart for instance.

Unlike Allegro, there's no documented way of defining such commands. However after poking around (and asking people) it turns out that it is possible to intervene in the system in such a way as to define new commands. This is fairly grotty code which relies on LW internal functionality, and I'm hoping that a more documented interface will appear at some point. It doesn't let you get at the various `contexts' - such as `in the debugger', `in the listener' that LW has, but just provides the commands in all contexts.

The code supports a fairly simple interface.The function that implements a new command has a lambda list of (cmd &rest args), where cmd is the command keyword - so the same function can handle many commands. The function is responsible for destructuring the remaining arguments itself & should check argument count and types. Here is an example function (from the code) which deals with compiling and loading files.

(defvar *file-command-default-files '())

(defun file-command (cmd &rest args)
  (let ((files (if args
                   (setf *file-command-default-files args)
                 *file-command-default-files)))
    (case cmd
      ((:ld)
       (dolist (f files (values))
         (load f)))
      ((:cf)
       (dolist (f files (values))
         (compile-file f)))
      ((:cl)
       (dolist (f files (values))
         (let ((cp (compile-file-pathname f))
               (sp (merge-pathnames f #.(make-pathname :type "lisp"))))
           (when (or (not (probe-file cp))
                     (< (file-write-date cp) (file-write-date sp)))
             (compile-file f))
           (load cp))))
      (otherwise
       (warn "Unknown file command ~S" cmd)
       (values)))))

Once the function is defined, then the macro declare-extra-lw-commands is used to tell the system which commands correspond to this function, and to provide documentation for them:

(declare-extra-lw-commands
  (:ld file-command "Load a file")
  (:cf file-command "Compile a file")
  (:cl file-command "Compile a file and load it"))

Here, each form in the body of the macro should be a triple of command (a keyword), function-name and documentation string. It is safe to evaluate the same macro multiple times: things get redefined suitably.

Once all this is done, then the new commands will be available. There is a slight subtelty in that they are in general only available the next time a top-level loop is started, although in practice they often become available immediately (because *extra-lw-commands* gets modified destructively, and if there are no commands provided by LW itself then this destructive modification is seen by the top-loop).

The way the arguments to commands get read is slightly different to ACL - they are read by the normal Lisp reader, so if you want to pass a string to quote it:

CL-USER 8 > :cl "~/work/lw/tdb/tdb.lisp"
; Loading fasl file D:\home\tfb\work\lw\tdb\tdb.fsl
    

Downloading the file, contact information

Copyright lw-commands.lisp is copyright 2002 by me, Tim Bradshaw, and may be used for any purpose whatsoever by anyone. It has no warranty whatsoever. I would appreciate acknowledgement if you use it in anger, and I would also very much appreciate any feedback or bug fixes.
Here is the current version.
Please mail me with any bugs or comments.