sexp-rewrite (abbreviated sexprw) is an Emacs package for doing pattern-based rewriting of sexp-structured code---ie, code in Lisp, Scheme, and primarily Racket.
Some examples of pattern-based rewriting are:
Turn a chain of
ifexpressions into a
Rewrite an application of
letrecinto a sequence of internal definitions.
The pattern language is simple enough that you can easily define your own rewriting rules.
Transformations preserve comments and are halfway decent at preserving formatting, with the occasional help of appropriate spacing annotations.
Try it out
sexp-rewrite.el and evaluate the buffer (
racket-rewrites.el and evaluate that buffer too.
Most of the rewrite rules (or "tactics") in
examples after them. Go to the example labeled "
example for if-to-cond..." and place the cursor at the first left
parenthesis---that is, at
(if (< x 10)....
if-to-cond tactic by entering
if expression gets rewritten to a
cond expression---but only
if; there are still
if expressions left in the
Now run the
cond-else-absorb-if tactic. There's a keybinding for
sexprw-execute-tactic that makes executing a specific tactic
C-c C-d x. Then type
cond-else-absorb-if at the prompt
(you can use tab completion to save typing).
if expression gets "absorbed" into the
cond. There's just one
if left, but it's inside a
let, so let's leave it alone for now.
What a bother to have to type in the tactic names. Fortunately, there's an even quicker way.
Undo twice (
C-/ C-/) to reset the example to the original form,
and make sure the cursor is back at the beginning of the example.
C-c C-d e (which runs the
command). The command automatically tries a bunch of tactics until one
works, and it reports the name of the tactic in the minibuffer.
Undo once (
C-/) to reset the example again. Now type
C-c C-d r e,
which repeatedly tries tactics (up to about a hundred times) until
they stop working. This time we get the first two
if expressions in
one shot, as well as the
if under the
let, which can be converted
Note that the rewrite that produced the
=> can be unsafe: the
else-branch expression was originally in the scope of the
variable, but after rewriting it is in a separate clause. That's
usually not a problem, because the variable is always known to be
false in the else, branch (unless it's mutated...) so there's no
reason to refer to it. But it's always a good idea to keep an eye on
the tactics applied to make sure they don't break your code.
The prefix for all sexp-rewrite commands is
The following keybindings invoke sexp-rewrite tactics:
<prefix> x: runs
sexprw-execute-tactic, which applies the given tactic
<prefix> e: runs
<prefix> r e: runs
sexprw-auto-expressionrepeatedly until no tactic applies
<prefix> d: runs
<prefix> r d: runs
sexprw-auto-definitionrepeatedly until no tactic applies
The following keybindings manipulate sexpagons:
<prefix> k: runs
<prefix> w: runs
<prefix> y: runs
<prefix> M-SPC: runs
The following other keybindings are also provided:
<prefix> s: runs
sexprw-search-pattern, which searches forward for a term matching the given sexprw pattern
<prefix> [: runs
sexprw-squarify, which converts parentheses to square brackets for all following terms at the given level
<prefix> (: runs
sexprw-roundify, which converts square brackets to parentheses for all following terms at the given level
REFERENCE.md for a description of the tactic language.
Known bugs and limitations
This library has a vague notion of sexp syntax. Notions like
improper lists are not supported, and
. is treated as an
atom. Reader abbreviations like
quote are not recognized.