[This page is adapted from Interlisp Reference Manual*.]
Suppose we are editing the following incorrect definition
of append (user input shown in WHITE
):
[LAMBDA (X) Y (COND ((NUL X) Z) (T (CONS (CAR) (APPEND (CDR X Y]
We call the editor via the function editf:
_EDITF(APPEND)
edit
*
The editor responds by typing edit
followed
by *, which is the editor's prompt character, i.e.,
it signifies that the editor is ready to accept commands.
At any given moment, the editor's attention is centered on
some substructure of the expression being edited. This
substructure is called the current expression, and it is
what te user sees when he gives the editor the
command P
, for print. Initially, the current
expression is the top level one, i.e., the entire expression
being edited. Thus:
*P
(LAMBDA (X) Y (COND & &))
*
Note that the editor prints the current expression as though
printlevel were set to (2 . 20), i.e., sublists of sublists are
printed as &, tails of long lists preinted as --. The
command ?
wil print the current expression as though
printlevel were 1000.
*?
(LAMBDA (X) Y (COND ((NUL X) Z) (T (CONS (CAR) (APPEND (CDR X Y))))))
*
and the command PP
will prettyprint the current
expression.
A positive integer is interpreted by the editor as a command to descend into the correpondingly numbered element of the current expression. Thus:
*2 *P (X) *
A negative integer has a similar effect, but counting begins from the end of the current expression and proceeds backward, i.e., -1 refers to the last element in the current expression, -2 the next to the last, etc. For either positive or negative integer, if there is no such element, an error occurs, the editor types the faulty command followed by a ?, and then another *. The current expression is never changed when a command causes an error. Thus:
*P (X) *2 2 ? *1 *P X *
A phrase of the form "the current expresseion is changed" or "the current expression becomes" refers to a shift in the editor's attention, not to a modification of the structure being edited.
When the user changes the current expression by descending
into it, the old current expression is not lost. Instead, the
editor actually operates by maintaining a chain of
expressions leading to the current one. The current expression is
simply the last link in the chain. Descending adds the indicated
subexpression onto the end of the chain, thereby making it be the
current expression. The command 0
is used to ascend
the chain; it removes the last link of the chain, thereby making
the previous link be the current expression. Thus:
*P X *0 P (X) *0 -1 P (COND (& Z) (T &)) *
Note the use of several commands on a single line in the previous output. The editor operates in a line buffered mode, the same as evalqt. Thus no command is actually seen by the editor, or executed, until the line is terminated, either by a carriage return, or a matching right parenthesis. The user can thus use control-A and control-Q for line-editing edit commands, the same as he does for inputs the evalqt.
In our editing session, we will make the following corrections to append: delete Y from where it appears, add Y to the end of the argument list, change NUL to NULL, change Z to Y, add Z after CAR, and insert a right parenthesis following CDR X.
First we will delete Y. By now we have forgotten where we are
in the function definition, but we want to be at the "top" so we
use the command ^
, which ascends through the entire chain
of expressions to the top level expression, which then becomes
the current expression, i.e., ^
removes all links
except the first one.
*^ P
(LAMBDA (X) Y (COND & &))
*
Note that if we are already at the top, ^
has no
effect, i.e., it is a no-op. However, 0
would
generate an error. In other words, ^
means "go to
the top," while 0
means "ascend one link."
The basic structure modification commands in the editor are:
(n) | n ≥ 1 deletes the corresponding element from the current expression. |
(n e1 … em) | n,m ≥ 1 replaces the nth element in the current expression with e1 … em. |
(-n e1 … em) | n,m ≥ 1 inserts e1 … em before the nth element in the current expression. |
Thus:
*P (LAMBDA (X) Y (COND & &)) *(3) *(2 (X Y)) *P (LAMBDA (X Y) (COND & &)) *
All structure modification done by the editor is destructive, i.e., the edotor uses rplaca and rplacd to physically change the structure it was given.
Note that all three of the above commands perform their
operation with respect to the nth element from the front of the
current expression; the sign Of n is used to specify whether
the operation is replacement or insertion. Thus, there is no way to
specify deletion or replacement of the nth element from the end of the
current expression, or insertion before the nth element from
the end without counting out that element's position from the front of
the list. Similarly, because we cannot specify insertion after a
particular clement, we cannot attach something at the end of the
current expression using the above commands. Instead, we use the
command N
(for nconc). Thus we could have
performed the above changes instead by:
*P (LAMBDA (X) Y (COND & &)) *(3) *2 (N Y) *P (X Y) *^ P *(LAMBDA (X Y) (COND & &)) *
Now we are ready to change NUL
to NULL
. Rather than specify the sequence of descent
commands necessary to reach NUL
, and then replace it
with NULL
, e.g., 3 2 1 (1 NULL)
, we will
use F
, the find command, to find NUL
:
*P (LAMBDA (X Y) (COND & &)) *F NUL *P (NUL X) *(1 NULL) *0 P ((NULL X) Z) *
Note that F
is special in that it corresponds to
two inputs. In other words, F
says to the editor, "treat
your next command as an expression to be searched for." The search is
carried out in printout order in the current expression. If the target
expression is not found there, F
automatically ascends
and searches those portions of the higher expressions that would
appear after (in a printout) the current expression. If the search is
successful, the new current expression will be the structure where the
expression was found, and the chain will be the same as one resulting
from the appropriate sequence of ascent and descent commands. If the
search is not successful, an error occurs, and neither the current
expression nor the chain is changed:
*P ((NULL X) Z) *F COND P COND ? *P ((NULL X) Z) *
Here the search failed to find a cond following the current expression, although of course a cond does appear earlier in the structure. This last example illustrates another facet of the error recovery mechanism: to avoid further confusion when an error occurs, all commands on the line beyond the one which caused the error (and all commands that may have been typed ahead while the editor was computing) are forgotten.
We could also have used the R
command
(for replace) to change NUL
to NULL
. A command of the form (R e1
e2)
will replace all occurrences of e1 in
the current expression by e2. There must be at least one
such occurrence or the R
command will generate an
error. Let us use the R
command to change
all Z
's (even though there is only one) in append
to Y
:
*^ (R Z Y) *F Z Z ? *PP [LAMBDA (X Y) (COND ((NULL X) Y) (T (CONS (CAR) (APPEND (CDR X Y] *
The next task is to change (CAR)
to (CAR
X)
. We could do this by (R (CAR) (CAR X))
,
or by:
*F CAR *(N X) *P (CAR X) *
The expression we now want to change is the next expression
after the current expression, i.e., we are currently looking
at (CAR X)
in (CONS (CAR X) (APPEND (CDR X
Y)))
. We could get to the append expression by
typing 0
and then 3 or -1, or we can use the
command NX
. which does both operations:
*P (CAR X) *NX P (APPEND (CDR X Y)) *
Finally, to change (APPEND (CDR X Y))
to (APPEND (CDR X) Y)
, we could perform (2 (CDR X)
Y)
, or (2 (CDR X))
and (N Y)
, or 2
and (3)
. deleting the Y
, and then 0 (N
Y)
. However, if Y
were a complex expression, we
would not want to have to retype it. Instead, we could use a command
which effectively inserts and/or removes left and right
parentheses. There are six of these commands: BI, BO, LI, LO,
RI,
and RO
. for both in, both out, left in, left out,
right in, and right out. Of course, we will always have the same number of left parentheses as
right parentheses, because the parentheses are just a notational guide to structure that is provided
by our print program. Thus, left in, left out, right in, and right out actually do not insert or
remove just one parenthesis, but this is very suggestive of what actually happens.
In this case, we would like a right parenthesis to appear
following X
in (CDR X Y)
. Therefore, we use
the command (RI 2 2)
, which means insert a right
parentheses after the second element in the second clement (of the
current expression):
*P (APPEND (CDR X Y)) *(RI 2 2) *P (APPEND (CDR X) Y) *
We have now finished our editing, and can exit from the editor,
to test append, or we could test it while still inside of
the editor, by using the E
command:
*E APPEND((A B) (C D E))
(A B C D E)
*
The E
command causes the next input to be given
to evalqt. If there is another input following it, as in the
above example, the first will be applied (apply) to the
second. Otherwise, the input is evaluated (eval).
We prettyprint append, and leave the editor.
*PP [LAMBDA (X Y) (COND ((NULL X) Y) (T (CONS (CAR X) (APPEND (CDR X) Y] *OK APPEND _
* Warren Teitelman. "Section 9 The Interlisp Editor," in Interlisp Reference Manual. (1978). pp. 9.1-9.7. [Online]. Available: https://www.softwarepreservation.org/projects/LISP/interlisp/Interlisp-Oct_1978.pdf. Accessed: October 6, 2024.