tfeb-lisp-implementation-hax

LispWorks hacks

tools

Tools for LW.

LW command line tools

lw-comands provides some extra commands for the LW toplevel. These are the commands I use: they may or may not be useful for other people. The documentation below is rough: a lot of the commands have slightly unclear semantics which I can’t always remember.

This module needs both ASDF and require-module. It makes some attempt to make sure ASDF is loaded but it just assumes require-module is there. I might remove this dependency as I don’t actually use the :require command very often any more.

Package commands. These let you change package and maintain a stack of packages so you can push or pop packages.

File commands. These commands let you compile & load files, remembering a notion of the current file.

Doing things to systems. These commands will let you compile and load systems. There is a mild assumption that system declarations live in files called sysdcl.lisp if not loaded otherwise. For things like ASDF system declarations you can make symlinks.

Inspecting. :gi will run the GUI inspector on either its arguments, or with no arguments either * or / if more than one value was returned by the last thing in the REPL.

Background. :& will run a form in a new process.

Modules. :require just calls require-module. This is the only place there is a dependency on require-module.

Directory commands. These let you change directory and also support directory aliases, autoloaded from files.

Directory aliases. These get loaded from files with names like .directory-aliases when they exist: when this file is loaded it will attempt to load such a file from your home directory, and whenever you change directory with one of these commands it will look for such a file in the new directory and load it if found. The files just contain lots of two-element lists of (<alias> <translation>), where <alias> is a symbol, and in practice a keyword is usually best. Here’s part of the one that lives in my home directory:

(:src "src/lisp/")
(:play "play/lisp/")
(:work "work/lisp/")

The system makes attempts to read these files safely using with-standard-io-syntax and turning off *read-eval*. But it’s only as safe as the reader: caveat emptor.

Given these aliases, all the directory commands accept arguments which can be put together from aliases and directory fragments: :src "modules" for instance means ‘make a pathname by translating :src and then merge it with a pathname whose diretory is “modules”. One implication of this is that aliases should translate to directory names, not the file names of directories. This means in practice that their translations should (if they’re not other aliases) end with a / on Unixoid systems.

Aliases are translated recursively, and there is loop detection (in fact loops are detected when loading aliases and those are rejected).

The translations of aliases are made with respect to the directory they were loaded from, so relative pathnames work fine.

The nice thing about autoloading aliases is that you can have a bunch of them which sit at the top of some source directory and then let you navigate around it, but only spring into existence at the point you change directory to it.

So, here are the commands.

Prompt commands. These manipulate named prompts, stashed in the *prompts* variable which is an alist of the form (<name> . <prompt-string>). See the LW documentation for the syntax of prompt strings. There are some predefined prompts in *prompts* which I find useful:

:prompt then allows you to select a prompt, either by name, directly as a string or by some special things:

With no arguments, :prompt will print what the current state is.

There is no stack of prompts, and this whole mechanism is not really sorted out, but it’s enough for me.

Module, package, &c. lw-commands lives in :org.tfeb.lw.lw-commands, provides :org.tfeb.lw.lw-commands and pushes :org.tfeb.lw.lw-commands onto *features*.

Exports:

Implementation note. lw-commands currently uses an ancient, undocumented hack to define new toplevel commands to LW, based on grovelling around in the implementation in 2001-2002. system:define-top-loop-command is now how you are meant to do this, but it does not (yet) have a way of adding documentation strings, so :? is less useful. That’s why I’m still using this ancient hack.

modules

Small useful things for LW.

Advice

recording-advice teaches defadvice how to record what things you have advised. *recording-advice* controls whether advice is recorded and map-recorded-advice calls a function on the dspec and name of each bit of recorded advice. The function is allowed to remove or add more advice.

replayable-advice allows you to to ‘replay’, or reapply, advice. *replayable-advice* controls whether advice is noted for replaying, and map-replayable-advice calls a function for each bit of replayable advice with three arguments: the dspec, the name, and a function of no arguments which, if called, will replay the advice. Finally forget-replayable-advice, if called with a dspec and name will forget the replayability of that bit of advice, if it has any.

Both of these work by themselves advising the internal function to which defadvice expands and are thus very fragile.

Stack control

allowing-stack-extensions is a macro which allows you to control how and whether LW will extend the stack, by invoking the appropriate restart. For instance

(allowing-stack-extensions (:limit 100000)
  ...)

will allow the stack to be extended while it is smaller than 100000. The default value of the limit is *stack-limit* which in turn defaults to the current stack length at load time. There are options to say ‘always extend’, ‘never extend’ and ‘use *stack-limit* dynamically to decide’.

You will need metatronic macros to use this.