Trailing-Edge
-
PDP-10 Archives
-
decuslib10-04
-
43,50322/edit.doc
There are 19 other files named edit.doc in the archive. Click here to see a list.
The LISP Editor
Contents
2 CURRENT EXPRESSION, P, &, PP, EDIT CHAIN, 0, ^,
5 (n), (n e1, ..., em), (-n e1, ..., em), N, F, R, NX, RI,
10 UNDO, BK, BF, \, \P, &, --, @ (AT-SIGN),
13 UP, B, A, :, DELETE, MBD, XTR, UP, ..., n, -n,
18 0, !0, ^, NX, BK, (NX n), (BK n), !NX, (NTH n),
22 PATTERN MATCH, &, *ANY*, --, ==, ...,
24 SEARCH ALGORITHM, MAXLEVEL, UNFIND, F, (F pat n),
27 (F pat T), (F pat N), (F pat), FS, F=, ORF, BF, (BF pat T),
30 LOCATION SPECIFICATION, IF, ##, $, LC, LCL, SECOND, THIRD,
32 (_ pat), BELOW, NEX, (NTH $), .., MARK, _, __, \, UNFIND,
37 \P, S, (n), (n e1, ..., em), (-n e1, ..., em), N,
41 B, A, :, DELETE, INSERT, REPLACE, DELETE, ##, UPFINDFLG,
46 XTR, EXTRACT, MBD, EMBED, MOVE, BI, BO, LI, LO, RI, RO,
57 THRU, TO, R, SW, P, ?, E, I, ##, COMS, COMSQ,
66 IF, LP, LPQ, ORR, MACROS, M, BIND, USERMACROS,
71 NIL, TTY:, OK, STOP, SAVE, REPACK, MAKEFN,
76 UNDO, TEST, ??, !UNDO, UNBLOCK, EDITDEFAULT, EDITL,
81 EDITF, EDITE, EDITV, EDITP, EDITFNS, EDIT4E,
84 EDITFPAT, EDITFINDP
The LISP editor allows rapid, convenient modification
of list structures. Most often it is used to edit function
definitions, (often while the function itself is running)
via the function EDITF, e.g., (EDITF FOO). However, the
editor can also be used to edit the value of a variable, via
EDITV, to edit special properties of an atom, via EDITP, or
to edit an arbitrary expression, via EDITE. It is an
important feature which allows good on-line interaction in
the UCI LISP system.
This chapter begins with a lengthy introduction
intended for the new user. The reference portion begins on
page 15.
2 . 1
Introduction
Let us introduce some of the basic editor commands, and
give a flavor for the editor's language structure by guiding
the reader through a hypothetical editing session. Suppose
we are editing the following incorrect definition of APPEND
(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 ready character, i.e., it signifies that the
editor is ready to accept commands. (In other words, all
lines beginning with # were typed by the user, the rest by
the editor.)
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 the 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,
using PRINTLEV, to a depth of 2, i.e., sublists of sublists
are printed as &. The command ? Will print the current
expression as though PRINTLEV was given a depth of 100.
#?
(LAMBDA (X) Y (COND ((NUL X) Z) (T (CONS (CAR) (APPEND (CDR
X Y))))))
#
and the command PP (for PrettyPrint) will GRINDEF the
current expression.
2 . 2
A positive integer is interpreted by the editor as a
command to descend into the correspondingly 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
expression, -2 the next to the last, etc. For either
positive integer or negative integer, if there is no such
element, an error occurs. 'Editor errors' are not the same
as 'LISP errors' , i.e., they never cause breaks or even go
through the error machinery but are direct calls to ERR
indicating that a command is in some way faulty. What
happens next depends on the context in which the command was
being executed. For example, there are conditional commands
which branch on errors. In most situations, though, an
error will cause the editor to type the faulty command
followed by a ? And wait for more input. In this case, 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 expression 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
2 . 3
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. Thus no command is actually seen by the editor, or
executed until the line is terminated, either by a carriage
return, or an escape (alt-mode).
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, (These two operations could
be thought of as one operation, i.e., move Y from its
current position to a new position, and in fact there is a
MOVE command in the editor. However, for the purposes of
this introduction, we will confine ourselves to the simpler
edit commands.) change NUL to NULL, change Z to Y, add X
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 NOP. However, 0 would generate an
error. In other words, ^ means "go to the top," while 0
means "ascend one link."
2 . 4
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 editor 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
element, 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:
2 . 5
#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
NULL, and then replace it with NULL, i.e., 3 2 1 (1 NULL),
we will use F, the find command, to find NULL:
#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, (If the search is for an atom, e.g., F
NUL, the current expression will be the structure containing
the atom. If the search is for a list, e.g., F (NUL X), the
current expression will be the list itself.) 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: (F is never a NOP,
i.e., if successful, the current expression after the search
will never be the same as the current expression before the
search. Thus F EXPR repeated without intervening commands
that change the edit chain can be used to find successive
instances of EXPR.)
2 . 6
#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 occurrances 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
2 . 7
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 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. (Herein lies one of the
principal advantages of a LISP oriented editor over a text
editor: unbalanced parentheses errors are not possible.)
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 element (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 (QUOTE (A B)) (QUOTE (C D E)))
(A B C D E)
2 . 8
The E command causes the next input to be given to
EVAL.
We GRINDEF APPEND, and leave the editor.
#PP
(LAMBDA(X Y)
(COND ((NULL X) Y)
(T (CONS (CAR X) (APPEND (CDR X) Y)))))
#OK
APPEND
*
2 . 9
Commands for the New User
This manual is intended primarily as a reference
manual, and the remainder of this chapter is organized and
presented accordingly. While the commands introduced in the
previous scenario constitute a complete set, i.e., the user
could perform any and all editing operations using just
those commands, there are many situations in which knowing
the right command(s) can save the user considerable effort.
We include here as part of the introduction a list of those
commands which are not only frequently applicable but also
easy to use. They are not presented in any particular
order, and are all discussed in detail in the reference
portion of the chapter.
UNDO
Undoes the last modification to the
structure being edited, e.g., if the
user deletes the wrong element, UNDO
will restore it. The availability
of UNDO should give the user
confidence to experiment with any
and all editing commands, no matter
how complex, because he can always
reverse the effect of the command.
BK
Like NX, except makes the expression
immediately before the current
expression become current.
BF
Backwards Find. Like F, except
searches backwards, i.e., in inverse
print order.
\
Restores the current expression to
the expression before the last "big
jump", e.g., a find command, an ^,
or another \. For example, if the
user types F COND, and then F CAR, \
would take him back to the COND.
Another \ would take him back to the
CAR.
2 . 10
\P
Like \ except it restores the edit
chain to its state as of the last
print, either by P, ?, or PP. If
the edit chain has not been changed
since the last print, \P restores it
to its state as of the printing
before that one, i.e., two chains
are always saved.
Thus if the user types P followed by 3 2 1 P, \P will
take him back to the first P, i.e., would be equivalent to 0
0 0. Another \P would then take him back to the second P,
i.e., he can use \P to flip back and forth between two
current expressions.
&,--
The search expression given to the F
or BF command need not be a literal
S-expression. Instead, it can be a
pattern. The symbol & can be used
anywhere within this pattern to
match with any single element of a
list, and -- can be used to match
with any segment of a list. Thus,
in the incorrect definition of
APPEND used earlier, F (NUL &) could
have been used to find (NUL X), and
F (CDR --) or F (CDR & &), but not F
(CDR &), to find (CDR X Y).
Note that & and -- can be nested arbitrarily deeply in
the pattern. For example, if there are many places where
the varaible X is set, F SETQ may not find the desired
expression, nor may F (SETQ X &). It may be necessary to use
F (SETQ X (LIST --)). However, the usual technique in such a
case is to pick out a unique atom which occurs prior to the
desired expression and perform two F commands. This "homing
in" process seems to be more convenient than ultra-precise
specification of the pattern.
2 . 11
@ (at-sign)
Any atom ending in @ (at-sign) in a
pattern will match with the first
atom or string that contains the
same initial characters. For
example, F VER@ will find
VERYLONGATOM. @ can be nested inside
of the pattern, e.g., F (SETQ VER@
(CONS --)).
If the search is successful, the
editor will print = followed by the
atom which matched with the @-atom,
e.g.,
#F (SETQ VER@ &)
=VERYLONGATOM
#
Frequently the user will want to replace the entire
current expression or insert something before it. In order
to do this using a command of the form (n e1,...,em) or (-n
e1,...,em), the user must be above the current expression.
In other words, he would have to perform a 0 followed by a
command with the appropriate number. However, if he has
reached the current expression via an F command, he may not
know what that number is. In this case, the user would like
a command whose effect would be to modify the edit chain so
that the current expression became the first element in a
new, higher current expression. Then he could perform the
desired operation via (1 e1,...,em) or (-1 e1,...,em). UP
is provided for this purpose.
2 . 12
UP
After UP operates, the old current
expression is the first element of
the new current expression. Note
that if the current expression
happens to be the first element in
the next higher expression, then UP
is exactly the same as 0.
Otherwise, UP modifies the edit
chain so that the new current
expression is a tail (Throughout
this chapter 'tail' means 'proper
tail') of the next higher
expression:
#F APPEND
(APPEND (CDR X) Y)
#UP P
... (APPEND & Y))
#0 P
(CONS (CAR X) (APPEND & Y))
#
The ... is used by the editor to
indicate that the current expression
is a tail of the next higher
expression as opposed to being an
element (i.e., a member) of the next
higher expression. Note: if the
current expression is already a
tail, UP has no effect.
(B e1,...,em)
Inserts e1,...,em before the current
expression, i.e., does an UP and
then a -1.
(A e1,...,em)
Inserts e1,...,em after the current
expression, i.e., does an UP and
then either a (-2 e1,...,em) or an
(N e1,...,em), if the current
expression is the last one in the
next higher expression.
2 . 13
(: e1,...,em)
Replaces current expression by
e1,...,em, i.e., does an UP and then
a (1 e1,...,em).
DELETE
Deletes current expression, i.e.,
equivalent to (:).
Earlier, we introduced the RI command in the APPEND
example. The rest of the commands in this family:, BI, RO,
LI, LO, and RO, perform similar functions and are useful in
certain situations. In addition, the commands MBD and XTR
can be used to combine the effects of several commands of
the BI-BO family. MBD is used to embed the current
expression in a larger expression. For example, if the
current expression is (PRINT bigexpression), and the user
wants to replace it by (COND (FLG (PRINT bigexpression))),
he can acomplish this by (LI 1), (-1 FLG), (LI 1), and (-1
COND), or by a single MBD command.
XTR is used to extract an expression from the current
expression. For example, extracting the PRINT expression
from the above COND could be accomplished by (1), (LO 1),
and (LO 1) or by a single XTR command. The new user is
encouraged to include XTR and MBD in his repertoire as soon
as he is familiar with the more basic commands.
2 . 14
Attention Changing Commands
Commands to the editor fall into three classes:
commands that change the current expression (i.e., change
the edit chain) thereby "shifting the editor's attention,"
commands that modify the structure being edited, and
miscellaneous commands, e.g., exiting from the editor,
printing, evaluating expressions.
within the context of commands that shift the editor's
attention, we can distinguish among (1) those commands whose
operation depends only on the structure of the edit chain,
e.g., 0, UP, NX; (2) those which depend on the contents of
the structure, i.e., commands that search; and (3) those
commands which simply restore the edit chain to some
previous state, e.g., \, \P. (1) and (2) can also be
thought of as local, small steps versus open ended, big
jumps. Commands of type (1) are discussed on pp.
2.15-2.21; type (2) on pp. 2.22-2.35; and type (3) on pp.
2.36-2.37.
2 . 15
Local Attention-Changing Commands
UP
(1) If a P command would cause the
editor to type ... before typing
the current expression, i.e., the
current expression is a tail of the
next higher expression, UP has no
effect; otherwise
(2) UP modifies the edit chain so
that the old current expression
(i.e., the one at the time UP was
called) is the first element in the
new current expression. (If the
current expression is the first
element in the next higher
expression UP simply does a 0.
Otherwise UP adds the corresponding
tail to the edit chain.
Examples: The current expression in each case is (COND
((NULL X) (RETURN Y))).
1. #1 P
COND
#UP P
(COND (& &))
2. #-1 P
((NULL X) (RETURN Y))
#UP P
... ((NULL X) (RETURN Y)))
#UP P
... ((NULL X) (RETURN Y)))
3. #F NULL P
(NULL X)
#UP P
((NULL X) (RETURN Y))
#UP P
... ((NULL X) (RETURN Y)))
The execution of UP is straightforward, except in those
cases where the current expression appears more than once in
the next higher expression. For example, if the current
expression is (A NIL B NIL C NIL) and the user performs 4
followed by UP, the current expression should then be ...
NIL C NIL.) UP can determine which tail is the correct one
2 . 16
because the commands that descend save the last tail on an
internal editor variable, LASTAIL. Thus after the 4 command
is executed, LASTAIL is (NIL C NIL). When UP is called, it
first determines if the current expression is a tail of the
next higher expression. If it is, UP is finished.
Otherwise, UP computes
(MEMB current-expression next-higher-expression) to obtain a
tail beginning with the current expression. (The current
expression should always be either a tail or an element of
the next higher expression. If it is neither, for example
the user has directly (and incorrectly) manipulated the edit
chain, UP generates an error.) If there are no other
instances of the current-expression in the next higher
expression, this tail is the correct one. Otherwise UP uses
LASTAIL to select the correct tail. (Occasionally the user
can get the edit chain into a state where LASTAIL cannot
resolve the ambiguity, for example if there were two
non-atomic structures in the same expression that were EQ,
and the user descended more than one level into one of them
and then tried to come back out using UP. In this case, UP
selects the first tail and prints LOCATION UNCERTAIN to warn
the user. Of course, we could have solved this problem
completely in our implementation by saving at each descent
both elements and tails. However, this would be a costly
solution to a situation that arises infrequently, and when
it does, has no detrimental effects. The LASTAIL solution
is cheap and resolves 99% of the ambiguities.
n (n>0)
Adds the nth element of the current
expression to the front of the edit
chain, thereby making it be the new
current expression. Sets LASTAIL
for use by UP. Generates an error
if the current expression is not a
list that contains at least n
elements.
-n (n>0)
Adds the nth element from the end of
the current expression to the front
of the edit chain, thereby making it
be the new current expression. Sets
LASTAIL for use by UP. Generates an
error if the current expression is
not a list that contains at least n
elements.
2 . 17
0
Sets edit chain to CDR of edit
chain, thereby making the next
higher expression be the new correct
expression. Generates an error if
there is no higher expression, i.e.,
CDR of edit chain is NIL.
Note that 0 usually corresponds to going back to the next
higher left parenthesis, but not always. For example, if
the current expression is (A B C D E F G), and the user
performs
# UP P
... C D E F G)
#3 UP P
... E F G)
#0 P
... C D E F G)
If the intention is to go back to the next higher left
parenthesis, regardless of any intervening tails, the
command !0 can be used. (!0 is pronounced bang-zero.)
!0
Does repeated 0's until it reaches a
point where the current expression
is not a tail of the next higher
expression, i.e., always goes back
to the next higher left parenthesis.
^
Sets edit chain to LAST of edit
chain, thereby making the top level
expression be the current
expression. Never generates an
error.
2 . 18
NX
Effectively does an UP followed by a
2, (Both NX and BK operate by
performing a !0 followed by an
appropriate number, i.e. There
won't be an extra tail above the new
current expression, as there would
be if NX operated by performing an
UP followed by a 2.) thereby making
the current expression be the next
expression. Generates an error if
the current expression is the last
one in a list. (However, !NX
described below will handle this
case.)
BK
Makes the current expression be the
previous expression in the next
higher expression. Generates an
error if the current expression is
the first expression in a list.
For example, if the current expression is (COND ((NULL X)
(RETURN Y)))
#F RETURN P
(RETURN Y)
#BK P
(NULL X)
(NX n) n>0
Equivalent to n NX commands, except
if an error occurs, the edit chain
is not changed.
(BK n) n>0
Equivalent to n BK commands, except
if an error occurs, the edit chain
is not changed.
Note: (NX -n) is equivalent to (BK n), and vice versa.
2 . 19
!NX
Makes current expression be the next
expression at a higher level, i.e.,
goes through any number of right
parentheses to get to the next
expression.
For example:
#PP
(PROG (UF)
(SETQ UF L)
LP (COND ((NULL (SETQ L (CDR L))) (ERR NIL))
((NULL (CDR (MEMQ# (CAR L) (CADR L))))
(GO LP)))
(EDITCOM (QUOTE NX))
(SETQ UNFIND UF)
(RETURN L))
#F CDR P
(CDR L)
#NX
NX ?
#!NX P
(ERR NIL)
#NX P
((NULL &) (GO LP))
#!NX P
(EDITCOM (QUOTE NX))
#
!NX operates by doing 0's until it reaches a stage
where the current expression is not the last expression in
the next higher expression, and then does a NX. Thus !NX
always goes through at least one unmatched right
parenthesis, and the new current expression is always on a
different level, i.e., !NX and NX always produce different
results. For example using the previous current expression:
2 . 20
#F CAR P
(CAR L)
#!NX P
(GO LP)
#\P P
(CAR L)
#NX P
(CADR L)
#
(NTH n) n>0
Equivalent to n followed by UP,
i.e., causes the list starting with
the nth element of the current
expression. ((NTH 1) is a NOP.)
Causes an error if current
expression does not have at least n
elements.
A generalized form of NTH using location specifications is
described on page 2.34.
2 . 21
Commands That Search
All of the editor commands that search use the same
pattern matching routine. (This routine is available to the
user directly, and is described later in this chapter in the
section on "Editor Functions.") We will therefore begin our
discussion of searching by describing the pattern match
mechanism. A pattern PAT matches with X if
1. PAT is EQ to X.
2. PAT is &.
3. PAT is a number and EQUAL to X.
4. If (CAR pat) is the atom *ANY*, (CDR pat) is a
list of patterns, and PAT matches X if and only
if one of the patterns on (CDR pat) matches X.
5. If PAT is a literal atom or string, and (NTHCHAR
pat -1) is @, then PAT matches with any literal
atom or string which has the same initial
characters as PAT, e.g. VER@ matches with
VERYLONGATOM, as well as "VERYLONGSTRING".
6. If (CAR pat) is the atom --, PAT matches X if
A. (CDR pat)=NIL, i.e. PAT=(--),
e.g., (A --) matches (A) (A B C) and
(A . B)
In other words, -- can match any tail of
a list.
B. (CDR pat) matches with some tail of X,
e.g. (A -- (&)) will match with (A B
C (D)), but not (A B C D), or (A B C
(D) E). However, note that (A -- (&)
--) will match with (A B C (D) E).
In other words, -- will match any
interior segment of a list.
7. If (CAR pat) is the atom ==, PAT matches X if
and only if (CDR pat) is EQ to X. (This pattern
is for use by programs that call the editor as a
subroutine, since any non-atomic expression in a
command type in by the user obviously cannot be
EQ to existing structure.)
8. Otherwise if X is a list, PAT matches X if (CAR
pat) matches (CAR x), and (CDR pat) matches (CDR
x).
When searching, the pattern matching routine is called
only to match with elements in the structure, unless the
pattern begins with :::, in which case CDR of the pattern is
matched against tails in the structure. (In this case, the
tail does not have to be a proper tail, e.g. (::: A --)
2 . 22
will match with the element (A B C) as well as with CDR of
(X A B C), since (A B C) is a tail of (A B C).) Thus if the
current expressiion is (A B C (B C)),
#F (B --)
#P
(B C)
#0 F (::: B --)
#P
... B C (B C))
#F (::: B --)
#P
(B C)
#
2 . 23
Search Algorithm
Searching begins with the current expression and
proceeds in print order. Searching usually means find the
next instance of this pattern, and consequently a match is
not attempted that would leave the edit chain unchanged.
(However, there is a version of the find command which can
succeed and leave the current expression unchanged.) At each
step, the pattern is matched against the next element in the
expression currently being searched, unless the pattern
begins with ::: in which case it is matched against the
corresponding tail of the expression. (EQ pattern
tail-of-expression)=T also indicates a successful match, so
that a search for FOO will find the FOO in (FIE . FOO).
The only exception to this occurs when PATTERN=NIL, e.g., F
NIL. In this case, the pattern will not match with a null
tail (since most lists end in NIL) but will match with a NIL
element.
If the match is not successful, the search operation is
recursive first in the CAR direction and then in the CDR
direction, i.e., if the element under examination is a list,
the search descends into that list before attempting to
match with other elements (or tails) at the same level.
(There is also a version of the find command which only
attempts matches at the top level of the current expression,
i.e., does not descend into elements, or ascend to higher
expressions.)
However, at no point is the total recursive depth of
the search (sum of number of CARs and CDRs descended into)
allowed to exceed the value of the variable MAXLEVEL. At
that point, the search of that element or tail is abandoned,
exactly as though the element or tail had been completely
searched without finding a match, and the search continues
with the next element or tail for which the recursive depth
is below MAXLEVEL. This feature is designed to enable the
user to search circular list structures (by setting MAXLEVEL
small), as well as protecting him from accidentally
encountering a circular list structure in the course of
normal editing. MAXLEVEL is initially set to 300. If a
successful match is not found in the current expression, the
search automatically ascends to the next higher expression,
and continues searching there on the next expression after
the expression it just finished searching. If there is
none, it ascends again, etc. This process continues until
the entire edit chain has been searched, at which point the
search fails, and an error is generated. If the search
2 . 24
fails the edit chain is not changed (nor are any CONSes
performed.)
If the search is successful, i.e., an expression is
found that the pattern matches, the edit chain is set to the
value it would have had had the user reached that expression
via a sequence of integer commands.
If the expression that matched was a list, it will be
the final link in the edit chain, i.e., the new current
expression. If the expression that matched is not a list,
e.g., is an atom, the current expression will be the tail
beginning with that atom, (Except for situations where match
is with Y in (X . Y), Y atomic and not NIL. In this case,
the current expression will be (X . Y).) i.e., that atom
will be the first element in the new current expression. In
other words, the search effectively does an UP. (Unless
UPFINDFLG=NIL (initially set to T). For discussion, see
page 2.45).
2 . 25
Search Commands
All of the commands below set LASTAIL for use by UP,
set UNFIND for use by \ (p. 2.36), And do not change the
edit chain or perform any CONSes if they are unsuccessful or
aborted.
F pattern
i.e., two commands: the F informs
the editor that the next command is
to be interpreted as a pattern.
This is the most common and useful
form of the find command. If
successful, the edit chain always
changes, i.e., F pattern means find
the next instance of PATTERN.
If (MEMB pattern current-expression)
is true, F does not proceed with a
full recursive search.
If the value of the MEMB is NIL, F
invokes the search algorithm
described earlier.
Thus if the current expression were (PROG NIL LP (COND
(--(GO LP1))) ... LP1 ...), F LP1 would find the prog
label, not the LP1 inside of the GO expression, even though
the latter appears first (in print order) in the current
expression. Note that 1 (making the atom PROG be the
current expression), followed by F LP1 would find the first
LP1.
(F pattern N)
Same as F pattern, i.e., finds the
next instance of pattern, except the
MEMB check of F pattern is not
performed.
2 . 26
(F pattern T)
Similar to F pattern, except may
succeed without changing edit chain,
and does not perform the MEMB check.
Thus if the current expression is (COND ..), F COND
will look for the next COND, but (F COND T) will 'stay
here'.
(F pattern n) n>0
Finds the nth place that pattern
matches. Equivalent to (F pattern
T) followed by (F pattern N)
repeated n-1 times. Each time
PATTERN successfully matches, n is
decremented by 1, and the search
continues, until n reaches 0. Note
that the pattern does not have to
match with n identical expressions;
it just has to match N times. Thus
if the current expression is (FOO1
FOO2 FOO3), (F F00@ 3) will find
FOO3.
If the pattern does not match
successfully N times, an error is
generated and the edit chain is
unchanged (even if the PATTERN
matched n-1 times).
(F pattern) or
(F pattern NIL)
Only matches with elements at the
top level of the current expression,
i.e., the search will not descend
into the current expression, nor
will it go outside of the current
expression. May succeed without
changing edit chain.
For example, if the current expression is
(PROG NIL (SETQ X (COND & &)) (COND &) ...)
F (COND --) will find the COND inside the SETQ, whereas (F
(COND --)) will find the top level COND, i.e., the second
one.
2 . 27
(FS pattern[1] ... pattern[n])
Equivalent to F pattern[1] followed
by F pattern[2] ... followed by F
pattern n, so that if F pattern m
fails, edit chain is left at place
pattern m-1 matched.
(F= expression x)
Equivalent to (F (== . Expression)
x), i.e., searches for a structure
EQ to expression, see p. 2.22.
(ORF pattern[1] ... pattern[n])
Equivalent to (F (*ANY* pattern[1]
... pattern[n]) N), i.e., searches
for an expression that is matched by
either pattern[1] or ...
pattern[n]. See p. 2.22.
BF pattern
Backwards Find. Searches in reverse
print order, beginning with
expression immediately before the
current expression (unless the
current expression is the top level
expression, in which case BF
searches the entire expression, in
reverse order.)
BF uses the same pattern match
routine as F, and MAXLEVEL and
UPFINDFLG have the same effect, but
the searching begins at the end of
each list, and descends into each
element before attempting to match
that element. If unsuccessful, the
search continues with the next
previous element, etc., until the
front of the list is reached, at
which point BF ascends and backs up,
etc.
For example, if the current expression is
(PROG NIL (SETQ X (SETQ Y (LIST Z))) (COND ((SETQ W --) --)) --)
F LIST followed by BF SETQ will leave the current
expression as (SETQ Y (LIST Z)), as will F COND followed by
BF SETQ
2 . 28
(BF pattern T)
Search always includes current
expression, i.e., starts at end of
current expression and works
backward, then ascends and backs up,
etc.
Thus in the previous example, where F COND followed by
BF SETQ found (SETQ Y (LIST Z)), F COND followed by (BF SETQ
T) would find the (SETQ W --) expression.
(BF pattern) Same as BF pattern.
(BF pattern NIL)
2 . 29
Location Specification
Many of the more sophisticated commands described later
in this chapter use a more general method of specifying
position called a LOCATION SPECIFICATION. A LOCATION
SPECIFICATION is a list of edit commands that are executed
in the normal fashion with two exceptions. First, all
commands not recognized by the editor are interpreted as
though they had been preceded by F. (Normally such commands
would cause errors.) For example, the location specification
(COND 2 3) specifies the 3rd element in the first clause of
the next COND. (Note that the user could always write (F
COND 2 3) for (COND 2 3) if he were not sure whether or not
COND was the name of an atomic command.)
Secondly, if an error occurs while evaluating one of
the commands in the location specification, and the edit
chain had been changed, i.e., was not the same as it was at
the beginning of that execution of the location
specification, the location operation will continue. In
other words, the location operation keeps going unless it
reaches a state where it detects that it is 'looping', at
which point it gives up. Thus, if (COND 2 3) is being
located, and the first clause of the next COND contained
only two elements, the execution of the command 3 would
cause an error. The search would then continue by looking
for the next COND. However, if a point were reached where
there were no further CONDs, then the first command, COND,
would cause the error; the edit chain would not have been
changed, and so the entire location operation would fail,
and cause an error.
The IF command and the ## function provide a way of
using in location specifications arbitrary predicates
applied to elements in the current expression. IF and ##
will be described in detail later in the chapter, along with
examples ilustrating their use in location specifications.
Throughout this chapter, the meta-symbol $ is used to
denote a location specification. Thus $ is a list of
commands interpreted as described above. $ Can also be
atomic, in which case it is interpreted as (LIST $).
2 . 30
(LC . $)
Provides a way of explicitly
invoking the location operation,
e.g. (LC COND 2 3) will perform the
search described above.
(LCL . $)
Same as LC except search is confined
to current expression, i.e., the
edit chain is rebound during the
search so it looks as if the editor
were called on just the current
expression. For example, to find a
COND containing a RETURN, one might
use the location specification (COND
(LCL RETURN) \) where the \ would
reverse the effects of the LCL
command, and make the final current
expression be the COND.
(SECOND . $)
Same as (LC . $) Followed by
another (LC . $) Except that if
the first succeeds and second fails,
no change is made to the edit chain.
(THIRD . $)
Similar to second.
2 . 31
(_ pattern)
Ascends the edit chain looking for a
link which matches PATTERN. in other
words, it keeps doing 0's until it
gets to a specified point. If
PATTERN is atomic, it is matched
with the first element of each link,
otherwise with the entire link. (If
pattern is of the form (IF
expression), EXPRESSION is evaluated
at each link, and if its value is
NIL, or the evaluation causes an
error, the ascent continues.)
For example:
#PP
(PROG NIL
(COND ((NULL (SETQ L (CDR L)))
(COND (FLG (RETURN L))))
((NULL (CDR (MEMB (CAR L (CADR L)))))
(GO LP))))
#F CADR
#(_ COND)
#P
(COND (& &) (& &))
#
Note that this command differs from BF in that it does
not search inside of each link, it simply ascends. Thus in
the above example, F CADR followed by BF COND would find
(COND (FLG (RETURN L))), not the higher COND.
If no match is found, an error is
generated and the edit chain is
unchanged.
(BELOW com x)
Ascends the edit chain looking for a
link specified by COM, and stops x
links below that, i.e. BELOW keeps
doing 0's until it gets to a
specified point, and then backs off
N 0's. (X is evaluated, e.g.,
(BELOW com (*PLUS X Y)))
2 . 32
(BELOW com)
Same as (BELOW com 1)
For example, (BELOW COND) will cause the COND clause
containing the current expression to become the new current
expression. Thus if the current expression is as shown
above, F CADR followed by (BELOW COND) will make the new
expression be ([NULL (CDR (FMEMB (CAR L) CADR L] (GO LP)),
and is therefore equivalent to 0 0 0 0.
BELOW operates by evaluating X and
then executing COM, or (_ com) if
COM is not a recognized edit
command, and measuring the length of
the edit chain at that point. If
that length is M and the length of
the current edit chain is N, then
BELOW ascends n-m-y links where Y is
the value of X. Generates an error
if COM causes an error, i.e., it
can't find the higher link, or if
n-m-y is negative.
The BELOW command is useful for locating a substructure
by specifying something it contains. For example, suppose
the user is editing a list of lists, and wants to find a
sublist that contains a FOO (at any depth). He simply
executes F FOO (BELOW \).
(NEX x)
Same as (BELOW x) followed by NX.
For example, if the user is deep inside of a SELECTQ clause,
he can advance to the next clause with (NEX SELECTQ).
NEX
Same as (NEX _).
The atomic form of NEX is useful if the user will be
performing repeated executions of (NEX x). By simply
MARKing (see p. 2.36) The chain corresponding to X, he can
use NEX to step through the sublists.
2 . 33
(NTH $)
Generalized NTH command.
Effectively performs (LCL . $),
Followed by (BELOW \), followed by
UP.
In other words, NTH locates $, using a search restricted to
the current expression, and then backs up to the current
level, where the new current expression is the tail whose
first element contains, however deeply, the expression that
was the terminus of the location operation. For example:
#P
(PROG (& &) LP (COND & &) (EDITCOM &) (SETQ UNFIND UF) (RETURN L))
#(NTH UF)
#P
... (SETQ UNFIND UF) (RETURN L))
#
If the search is unsuccessful, NTH
generates an error and the edit
chain is not changed.
Note that (NTH n) is just a special case of (NTH $), and in
fact, no special check is made for $ a number; both commands
are executed identically.
(pattern :: . $)
E.g., (COND :: RETURN). Finds a
COND that contains a RETURN, at any
depth. Equivalent to (F pattern N),
(LCL . $) followed by (_ pattern).
For example, if the current expression is (PROG NIL
(COND ((NULL L) (COND (FLG (RETURN L))))) --), then (COND ::
RETURN) will make (COND (FLG (RETURN L))) be the current
expression. Note that it is the innermost COND that is
found, because this is the first COND encountered when
ascending from the RETURN. In other words, (pattern :: $)
is not equivalent to (F pattern N), followed by (LCL . $)
followed by \.
Note that $ is a location specification, not just a
pattern. Thus (RETURN :: COND 2 3) can be used to find the
RETURN which contains a COND whose first clause contains (at
least) three elements. Note also that since $ permits any
edit command, the user can write commands of the form (COND
:: (RETURN :: COND)), which will locate the first COND that
2 . 34
contains a RETURN that contains a COND.
2 . 35
Commands That Save and Restore the Edit Chain
Three facilities are available for saving the current
edit chain and later retrieving it. The commands are MARK,
which marks the current chain for future reference, _, (An
atomic command; do not confuse with the list command (_
pattern).) which returns to the last mark without destroying
it, and __, which returns to the last mark and also erases
it.
MARK
Adds the current edit chain to the
front of the list MARKLIST.
_
Makes the new edit chain be (CAR
MARKLIST). Generates an error if
MARKLIST is NIL, i.e., no MARKS have
been performed, or all have been
erased.
__
Similar to _ but also erases the
MARK, i.e., performs (SETQ MARKLST
(CDR MARKLST)).
If the user did not prepare in advance for returning to
a particular edit chain, he may still be able to return to
that chain with a single command by using \ or \P.
\
Makes the edit chain be the value of
UNFIND. Generates an error if
UNFIND=NIL.
UNFIND is set to the current edit chain by each command
that makes a "big jump", i.e., a command that usually
performs more than a single ascent or descent, namely ^, _,
__, !NX, all commands that involve a search, e.g., F, LC,
::, BELOW, et al and \ and \P themselves. (Except that
UNFIND is not reset when the current edit chain is the top
level expression, since this could always be returned to via
the ^ command.)
For example, if the user types F COND, and then F CAR,
\ would take him back to the COND. Another \ would take him
back to the CAR, etc.
2 . 36
\P
Restores the edit chain to its state
as of the last print operation,
i.e., P, ?, or PP. If the edit
chain has not changed since the last
printing, \P restores it to its
state as of the printing before that
one, i.e., two chains are always
saved.
For example, if the user types P followed by 3 2 1 P,
\P will return to the first P, i.e., would be equivalent to
0 0 0. (Note that if the user had typed P followed by F
COND, he could use either \ or \P to return to the P, i.e.,
the action of \ and \P are independent.) another \P would
then take him back to the second P, i.e., the user could use
\P to flip back and forth between the two edit chains.
(S var . $)
Sets var (using SETQ) to the current
expression after performing (LC .
$). Edit chain is not changed.
Thus (S FOO) will set FOO to the current expression, (S
FOO -1 1) will set FOO to the first element in the last
element of the current expression.
2 . 37
Commands That Modify Structure
The basic structure modifications 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
n element in the current expression.
(N e1 ... em)
m>1 attaches e1 ... em at the end
of the current expression.
As mentioned earlier:
All structure modificaton done by the editor is destructive,
i.e., the editor uses RPLACA and RPLACD to physically
change the structure it was given.
However, all structure modification is undoable, see
UNDO p. 2.76.
All of the above commands generate errors if the
current expression is not a list, or in the case of the
first three commands, if the list contains fewer than n
elements. In addition, the command (1), i.e., delete the
first element, will cause an error if there is only one
element, since deleting the first element must be done by
replacing it with the second element, and then deleting the
second element. Or, to look at it another way, deleting the
first element when there is only one element would require
changing a list to an atom (i.e. to NIL) which cannot be
done. (However, the command DELETE will work even if there
is only one element in the current expression, since it will
ascend to a point where it can do the deletion.)
2 . 38
Implementation of Structure Modification Commands
Note: Since all commands that insert, replace, delete or
attach structure use the same low level editor functions,
the remarks made here are valid for all structure changing
commands.
For all replacement, insertion, and attaching at the
end of a list, unless the command was typed in directly to
the editor, copies of the corresponding structure are used,
because of the possibility that the exact same command,
(i.e. same list structure) might be used again. (Some
editor commands take as arguments a list of edit commands,
e.g. (LP F FOO (1 (CAR FOO))). In this case, the command
(1 (CAR FOO)) is not considered to have been "typed in" even
though the LP command itself may have been typed in.
Similarly, commands originating from macros, or commands
given to the editor as arguments to EDITF, EDITV, et al,
e.g. (EDITF FOO F COND (N --)) are not considered typed
in.) Thus if the program constructs the command (1 (A B C))
via (LIST 1 FOO), and gives this command to the editor, the
(A B C) used for the replacement will NOT be EQ to FOO.
(The user can circumvent this by using the I command, which
computes the structure to be used. In the above example,
the form of the command would be (I 1 FOO), which would
replace the first element with the value of FOO itself. See
p. 2.63)
The rest of this section is included for applications
wherein the editor is used to modify a data structure, and
pointers into that data structure are stored elsewhere. In
these cases, the actual mechanics of structure modification
must be known in order to predict the effect that various
commands may have on these outside pointers. For example,
if the value of FOO is CDR of the current expression, what
will the commands (2), (3), (2 X Y Z), (-2 X Y Z), etc., do
to FOO?
Deletion of the first element in the current expression
is performed by replacing it with the second element and
deleting the second element by patching around it. Deletion
of any other element is done by patching around it, i.e.,
the previous tail is altered. Thus if FOO is EQ to the
current expression which is (A B C D), and FIE is CDR of
FOO, after executing the command (1), FOO will be (B C D)
(which is EQUAL but not EQ to FIE). However, under the same
initial conditions, after executing (2) FIE will be
unchanged, i.e., FIE will still be (B C D) even though the
2 . 39
current expression and FOO are now (A C D). (A general
solution of the problem just isn't possible, as it would
require being able to make two lists EQ to each other that
were originally different. Thus if FIE is CDR of the
current expression, and FUM is CDDR of the current
expression, performing(2) would have to make FIE be EQ to
FUM if all subsequent operations were to update both FIE and
FUM correctly. Think about it.)
Both replacement and insertion are accomplished by
smashing both CAR and CDR of the corresponding tail. Thus,
if FOO were EQ to the current expression, (A B C D), after
(1 X Y Z), FOO would be (X Y Z B C D). Similarly, if FOO
were EQ to the current expression, (A B C D), then after (-1
X Y Z), FOO would be (X Y Z A B C D).
The N command is accomplished by smashing the last CDR
of the current expression a la NCONC. Thus, if FOO were EQ
to any tail of the current expression, after executing an N
command, the corresponding expressions would also appear at
the end of FOO.
In summary, the only situation in which an edit
operation will not change an external pointer occurs when
the external pointer is to a proper tail of the data
structure, i.e., to CDR of some node in the structure, and
the operation is deletion. If all external pointers are to
elements of the structure, i.e., to CAR of some node, or if
only insertions, replacements, or attachments are performed,
the edit operation will always have the same effect on an
external pointer as it does on the current expression.
2 . 40
The A,B,: Commands
In the (n), (n e1 ... em), and (-n e1 ... em)
commands, the sign of the integer is used to indicate the
operation. As a result, there is no direct way to express
insertion after a particular element, (hence the necessity
for a separate N command). Similarly, the user cannot
specify deletion or replacement of the NTH element from the
end of a list without first converting n to the
corresponding positive integer. Accordingly, we have:
(B e1 ... em)
Inserts e1 ... em before the
current expression. Equivalent to
UP followed by (-1 e1 ... em).
For example, to insert FOO before the last element in
the current expression, perform -1 and then (B FOO).
(A e1 ... em)
Inserts e1 ... em after the current
expression. Equivalent to UP
followed by (-2 e1 ... em) or (N e1
... em) or (N e1 ... em) whichever
is appropriate.
(: e1 ... em)
Replaces the current expression by
e1 ... em. Equivalent to UP
followed by (1 e1 ... em).
DELETE or (:)
Deletes the current expression, or
if the current expression is a tail,
deletes its first element.
DELETE first tries to delete the current expression by
performing an UP and then a (1). This works in most cases.
However, if after performing UP, the new current expression
contains only one element, the command (1) will not work.
Therefore DELETE starts over and performs a BK, followed by
UP, followed by (2). For example, if the current expression
is (COND ((MEMB X Y)) (T Y)), and the user performs -1, and
then DELETE, the BK-UP-(2) method is used, and the new
current expression will be ... ((MEMB X Y)))
However, if the next higher expression contains only
one element, BK will not work. So in this case, DELETE
performs UP, followed by (: NIL), i.e., it REPLACES the
2 . 41
higher expression by NIL. For example, if the current
expression is (COND ((MEMB X Y)) (T Y)) and the user
performs F MEMB and then DELETE, the new current expression
will be ... NIL (T Y)) and the original expression would
now be (COND NIL (T Y)). The rationale behind this is that
deleting (MEMB X Y) from ((MEMB X Y)) changes a list of one
element to a list of no elements, i.e., () or NIL. Note
that 2 followed by DELETE would DELETE ((MEMB X Y)) NOT
replace it by NIL.
If the current expression is a tail, then B, A, and :
will work exactly the same as though the current expression
were the first element in that tail. Thus if the current
expression were ... (PRINT Y) (PRINT Z)), (B (PRINT X))
would insert (PRINT X) before (PRINT Y), leaving the current
expression ...(PRINT X) (PRINT Y) (PRINT Z)).
2 . 42
The following forms of the A, B, and : commands
incorporate a location specification:
(INSERT e1 ... em BEFORE . $)
Similar to (LC. $) followed by (B
e1 ... em).
#P
(PROG (W Y X) (SELECTQ ATM & NIL) (OR & &) (PRIN1 &))
#(INSERT LABEL BEFORE PRIN1)
#P
(PROG (W Y X) (SELECTQ ATM & NIL) (OR & &) LABEL (PRIN1 &))
#
Current edit chain is not changed,
but UNFIND is set to the edit chain
after the B was performed, i.e., \
will make the edit chain be that
chain where the insertion was
performed.
(INSERT e1 ... em AFTER . $)
Similar to INSERT BEFORE except uses
A instead of B.
(INSERT e1 ... em FOR . $)
Similar to INSERT BEFORE except uses
: for B.
(REPLACE $ WITH e1 ... em)
Here $ is the segment of the command
between REPLACE and WITH. Same as
(INSERT e1 ... em FOR . $). (BY
can be used for WITH.)
Example: (REPLACE COND -1 WITH (T (RETURN L)))
(CHANGE $ TO e1 ... em)
Same as REPLACE WITH
(DELETE . $)
Does a (LC . $) followed by
DELETE. Current edit chain is not
changed (Unless the current
expression is no longer a part of
the expression being edited, e.g.,
if the current expression is ... C)
and the user performs (DELETE 1),
2 . 43
the tail, (C), will have been cut
off. Similarly, if the current
expression is (CDR Y) and the user
performs (REPLACE WITH (CAR X)).),
but UNFIND is set to the edit chain
after the DELETE was performed.
Example: (DELETE -1), (DELETE COND 3)
Note that if $ is NIL (empty), the corresponding
operation is performed here (on the current edit chain),
e.g., (REPLACE WITH (CAR X)) is equivalent to (:(CAR X)).
For added readability, HERE is also permitted, e.g., (INSERT
(PRINT X) BEFORE HERE) will insert (PRINT X) before the
current expression (but not change the edit chain).
Note also that $ does not have to specify a location
WITHIN the current expression, i.e., it is perfectly legal
to ascend to INSERT, REPLACE, or DELETE. For example
(INSERT (RETURN) AFTER ^ PROG -1) will go to the top, find
the first PROG, and insert a (RETURN) at its end, and not
change the current edit chain.
Finally, the A, B, and : commands, (and consequently
INSERT, REPLACE, and CHANGE), all make special checks in E1
thru Em for expressions of the form (## . coms). In this
case, the expression used for inserting or replacing is a
copy of the current expression after executing coms, a list
of edit commands. (The execution of coms does not change
the current edit chain.) For example, (INSERT (## F COND -1
-1) AFTER3) [not (INSERT F COND -1 (## -1 ) AFTER 3), which
inserts four elements after the third element, namely F,
COND, -1, and a copy of the last element in the current
expression] will make a copy of the last form in the last
clause of the next COND, and insert it after the third
element of the current expression.
2 . 44
Form Oriented Editing and the Role of UP
The UP that is performed before A, B, and : commands
(and therefore in INSERT, CHANGE, REPLACE, and DELETE
commands after the location portion of the operation has
been performed.), makes these operations form-oriented. For
example, if the user types F SETQ, and then DELETE, or
simply (DELETE SETQ), he will delete the entire SETQ
expression, whereas (DELETE X) if X is a variable, deletes
just the variable X. In both cases, the operation is
performed on the corresponding FORM and in both cases is
probably what the user intended. Similarly, if the user
types (INSERT (RETURN Y) BEFORE SETQ), he means before the
SETQ expression, not before the atom SETQ. (*There is some
ambiguity in (INSERT expr AFTER functionname), as the user
might mean make expr be the function's first argument.
Similarly, the user cannot write (REPLACE SETQQ WITH SETQ)
meaning change the name of the function. The user must in
these cases write (INSERT expr AFTER functionname 1), and
(REPLACE SETQQ 1 WITH SETQ).) A consequent of this
procedure is that a pattern of the form (SETQ Y --) can be
viewed as simply an elaboration and further refinement of
the pattern SETQ. Thus (INSERT (RETURN Y) BEFORE SETQ) and
(INSERT (RETURN Y) BEFORE (SETQ Y --)) perform the same
operation (Assuming the next SETQ is of the form (SETQ
Y-)).) and, in fact, this is one of the motivations behind
making the current expression after F SETQ, and F (SETQ Y
--) be the same.
Occasionally, however, a user may have a data structure
in which no special significance or meaning is attached to
the position of an atom in a list, as LISP attaches to atoms
that appear as CAR of a list, versus those appearing
elsewhere in a list. In general, the user may not even know
whether a particular atom is at the head of a list or not.
Thus, when he writes (INSERT expression AFTER FOO), he means
after the atom FOO, whether or not it is CAR of a list. By
setting the variable UPFINDFLG to NIL (Initially, and
usually, set to T.) the user can suppress the implicit UP
that follows searches for atoms, and thus achieve the
desired effect. With UPFINDFLG = NIL then following F FOO,
for example, the current expression will be the atom FOO.
In this case, the A, B, and : operations will operate with
respect to the atom FOO. If the user intends the operation
to refer to the list which FOO heads, he simply uses instead
the pattern (FOO --).
2 . 45
Extract and Embed
Extraction involves replacing the current expression with
one of its subexpressions (from any depth).
(XTR . $)
Replaces the original current
expression with the expression that
is current after performing (LCL .
$).
For example, if the current expression is (COND ((NULL X)
(PRINT Y))), (XTR PRINT), or (XTR 2 2) will replace the COND
by the PRINT.
If the current expression after (LCL
. $) is a tail of a higher
expression, its first element is
used.
For example, if the current expression is
(COND ((NULL X) Y) (T Z)), then (XTR Y) will replace the
COND with Y.
If the extracted expression is a
list, then after XTR has finished,
the current expression will be that
list.
Thus, in the first example, the current expression after the
XTR would be (PRINT Y).
If the extracted expression is not a
list, the new current expression
will be a tail whose first element
is that non-list.
Thus, in the second example, the current expression after
the XTR would be ... Y followed by whatever followed by
COND.
If the current expression initially is a tail,
extraction works exactly the same as though the current
expression were the first element in that tail. Thus is the
current expression is (XTR PRINT) will replace the COND by
the PRINT, leaving (PRINT Y) as the current expression.
2 . 46
The extract command can also incorporate a location
specification.
(EXTRACT $1 FROM $2)
($1 is the segment between EXTRACT
and FROM.)
Performs (LC . $2) And then (XTR .
$1). Current edit chain is not
changed, but UNFIND is set to the
edit chain after the XTR was
performed.
Example: If the current expression is
(PRINT (COND ((NULL X) Y) (T Z))) then following
(EXTRACT Y FROM COND), the current expression will be
(PRINT Y).
(EXTRACT 2 -1 FROM COND), (EXTRACT Y FROM 2),
(EXTRACT 2 -1 FROM 2) will all produce the same result.
2 . 47
While extracting replaces the current expression by a
subexpression, embedding replaces the current expression
with one containing it as a subexpression.
(MBD x)
X is a list, substitutes (a la
SUBST, i.e., a fresh copy is used
for each substitution) the current
expression for all instances of the
atom * in x, and replaces the
current expression with the result
of that substitution.
Example: If the current expression is (PRINT Y), (MBD (COND
((NULL X) *) ((NULL (CAR Y)) * (GO LP))) would replace
(PRINT Y) with (COND((NULL X) (PRINT Y)) ((NULL (CAR Y))
(PRINT Y) (GO LP))).
(MBD e1 ... em)
Equivalent to (MBD (e1 ... em *)).
Example: If the current expression is (PRINT Y), then (MBD
SETQ X) will replace it with (SETQ X (PRINT Y)).
(MBD x)
X atomic, same as (MBD (x *)).
Example: If the current expression is (PRINT Y), (MBD
RETURN) will replace it with (RETURN (PRINT Y)).
All three forms of MBD leave the edit chain so that the
larger expression is the new current expression.
If the current expression initially is a tail,
embedding works exactly the same as though the current
expression were the first element in that tail. Thus if the
current expression were (PRINT Y) with (SETQ X (PRINT Y)).
The embed command can also incorporate a location
specification.
2 . 48
(EMBED $ IN . x)
($ is the segment between EMBED and
IN.) Does (LC . $) and then (MBD .
x). Edit chain is not changed, but
UNFIND is set to the edit chain
after the MBD was performed.
Example: (EMBED PRINT IN SETQ X), (EMBED 3 2 IN RETURN),
(EMBED COND 3 1 IN (OR * (NULL X))).
WITH can be used for IN, and SURROUND can be used for EMBED,
e.g., (SURROUND NUMBERP WITH (AND * (MINUSP X ))).
2 . 49
The MOVE Command
The MOVE command allows the user to specify (1) the
expression to be moved, (2) the place it is to be moved to,
and (3) the operation to be performed there, e.g., insert it
before, insert it after, replace, etc.
(MOVE $1 TO com . $2)
($1 is the segment between MOVE and
TO.) Where COM is BEFORE, AFTER, or
the name of a list command, e.g., :,
N, etc. Performs (LC . $1),
Obtains the current expression there
(or its first element, if it is a
tail), let us call this expr; MOVE
then goes back to original edit
chain, performs (LC . $2), Peforms
(com expr), then goes back to $1 and
deletes expr. Edit chain is not
changed. UNFIND is set to edit
chain after (com expr) was
performed.
For example, if the current expression is (A B D C), (
MOVE 2 TO AFTER 4) will make the new current expression be
(A C D B). Note that 4 was executed as of the original edit
chain, and that the second element had not yet been removed.
2 . 50
As the following examples taken from actual editing
will show, the MOVE command is an extremely versatile and
powerful feature of the editor.
#?
(PROG (L) (EDLOC (CDDR C)) (RETURN (CAR L)))
#(MOVE 3 TO : CAR)
#?
(PROG (L) (RETURN (EDLOC (CDDR C))))
#
#P
... (SELECTQ OBJPR & &) (RETURN &) LP2 (COND & & ))
#(MOVE 2 TO N 1)
#P
... (SELECTQ OBJPR & & &) LP2 (COND & &))
#
#P
(OR (EQ X LASTAIL) (NOT &) (AND & & &))
#(MOVE 4 TO AFTER (BELOW COND))
#P
(OR (EQ X LASTAIL) (NOT &))
#\ P
... (& &) (AND & & &) (T & &))
#
#P
((NULL X) (COND & &))
#(-3 (GO DELETE))
#(MOVE 4 TO N (_ PROG))
#P
((NULL X) (GO DELETE))
#\ P
(PROG (&) (COND & & &) (COND & & &) (COND & &))
#(INSERT DELETE BEFORE -1)
#P
(PROG (&) (COND & & &) (COND & & &) DELETE (COND & &))
#
Note that in the last example, the user could have
added the prog label DELETE and moved the COND in one
operation by performing (MOVE 4 TO N (_ PROG) (N DELETE)).
2 . 51
Similarly, in the next example, in the course of specifying
$2, the location where the expression was to be moved to,
the user also performs a structure modification, via (N
(T)), thus creating the structure that will receive the
expression being moved.
#P
((CDR &) (SETQ CL &) (EDITSMASH CL & &))
#(MOVE 4 TO N 0 (N (T)) - 1]
#P
((CDR &) (SETQ CL &))
#\ P
(T (EDITSMASH CL & &))
#
If $2 is NIL, or (HERE), the current position specifies
where the operation is to take place. In this case, UNFIND
is set to where the expression that was moved was originally
located, i.e., $1. For example:
#P
(TENEX)
#(MOVE ^ F APPLY TO N HERE)
#P
(TENEX (APPLY & & ))
#
#P
(T (PRIN1 C-EXP))
#(MOVE BF PRIN1 TO N HERE)
#P
(T (PRIN1 C-EXP) (PRIN1 &))
#
Finally, if $1 is NIL, the MOVE command allows the user
to specify some place the current expression is to be moved
to. In this case, the edit chain is changed, and is the
chain where the current expression was moved to; UNFIND is
set to where it was.
#P
(SELECTQ OBJPR (&) (PROGN & &))
2 . 52
#(MOVE TO BEFORE LOOP)
#P
...(SELECTQ OBJPR & &) LOOP (RPLACA DFPRP &) (RPLACD DFPRP &))
#
2 . 53
Commands That "Move Parentheses"
The commands presented in this section permit
modification of the list structure itself, as opposed to
modifying components thereof. Their effect can be described
as inserting or removing a single left or right parenthesis,
or pair of left and right parentheses. Of course, there
will always be the same number of left parentheses as right
parentheses in any list structure, since the parentheses are
just a notational guide to the structure provided by PRINT.
Thus, no command can insert or remove just one parenthesis,
but this is suggestive of what actually happens.
In all six commands, n and m are used to specify an
element of a list, usually of the current expression. In
practice, n and m are usually positive or negative integers
with the obvious interpretation. However, all six commands
use the generalized NTH command, p. 2.34, To find their
element(s), so that nth element means the first element of
the tail found by performing (NTH n). In other words, if
the current expression is (LIST (CAR X) (SETQ Y (CONS W
Z))), then (BI 2 CONS), (BI X -1), and (BI X Z) all specify
the exact same operation.
All six commands generate an error if the element is
not found, i.e., the NTH fails. All are undoable.
(BI n m)
Both in, inserts a left parentheses
before the nth element and after the
mth element in the current
expression. Generates an error if
the mth element is not contained in
the nth tail, i.e., the mth element
must be "to the right" of the nth
element.
Example: If the current expression is (A B (C D E) F G),
then (BI 2 4) will modify it to be (A (B (C D E) F) G).
(BI n)
Same as (BI n n).
Example: If the current expression is (A B (C D E) F G),
then (BI -2) will modify it to be (A B (C D E) (F) G).
2 . 54
(BO n)
Both out. Removes both parentheses
from the nth element. Generates an
error if nth element is not a list.
Example: If the current expression is (A B (C D E) F G),
then (BO D) will modify it to be (A B C D E F G).
(LI n)
Left in, inserts a left parenthesis
before the nth element (and a
matching right parenthesis at the
end of the current expression),
i.e., equivalent to (BI n -1).
Example: If the current expression is (A B (C D E) F G),
then (LI 2) will modify it to be (A (B (C D E) F G)).
(LO n)
Left out, removes a left parenthesis
from the nth element. All elements
following the nth element are
deleted. Generates an error if nth
element is not a list.
Example: If the current expression is (A B (C D E) F G),
then (LO 3) will modify it to be (A B C D E).
(RI n m)
Right in, inserts a right
parenthesis after the mth element of
the nth element. The rest of the
nth element is brought up to the
level of the current expression.
Example: If the current expression is (A (B C D E) F G), (RI
2 2) will modify it to be (A (B C) D E F G). Another way of
thinking about RI is to read it as "move the right
parenthesis at the end of the nth element IN to after the
mth element."
2 . 55
(RO n)
Right out, removes the right
parenthesis from the nth element,
moving it to the end of the current
expression. All elements following
the nth element are moved inside of
the nth element. Generates an error
if nth element is not a list.
Example: If the current expression is (A B (C D E) F G), (RO
3) will modify it to be (A B (C D E F G)). Another way of
thinking about RO is to read it as "move the right
parenthesis at the end of the nth element OUT to the end of
the current expression."
2 . 56
TO and THRU
EXTRACT, EMBED, DELETE, REPLACE, and MOVE can be made
to operate on several contiguous elements, i.e., a segment
of a list, by using the TO or THRU command in their
respective location specifications.
($1 THRU $2)
Does a (LC . $1), Followed by an
UP, and then a (BI 1 $2), thereby
grouping the segment into a single
element, and finally does a 1,
making the final current expression
be that element.
For example, if the current expression is (A (B (C D) (E) (F
G H) I) J K), following (C THRU G), the current expression
will be ((C D) (E) (F G H)).
($1 TO $2)
Same as THRU except last element not
included, i.e., after the BI, an (RI
1 -2) is performed.
If both $1 and $2 are numbers, and $2 is greater than
$1, then $2 counts from the beginning of the current
expression, the same as $1. In other words, if the current
expression is (A B C D E F G), (3 THRU 4) means (C THRU D),
not (C THRU F). In this case, the corresponding BI command
is (BI 1 $2-$1+1).
THRU and TO are not very useful commands by themselves,
and are not intended to be used "solo", but in conjunction
with EXTRACT, EMBED, DELETE, REPLACE, and MOVE. After THRU
and TO have operated, they set an internal editor flag
informing the above commands that the element they are
operating on is actually a segment, and that the extra pair
of parentheses should be removed when the operation is
complete. Thus:
#P
(PROG NIL (SETQ A &) (RPLACA & &) (PRINT &) (RPLACD & &))
#(MOVE (3 THRU 4) TO BEFORE 5) P
(PROG NIL (PRINT &) (SETQ A &) (RPLACA & &) (RPLACD & &))
#
Note that when specifing $2 in the MOVE, 5 was used instead
2 . 57
of 6. This is because the $2 is located after $1 is. The
THRU location groups items together and thus changes the
numeric location of the following items.
#P
(PROG NIL (PRIN1 &) (PRIN1 &) (SETQ IND &) (SETQ VAL &) (PRINT &))
#(MOVE (5 THRU 7) TO BEFORE 3)
#P
(PROG NIL (SETQ IND &) (SETQ VAL &) (PRINT &) (PRIN1 &) (PRIN1 &))
#(DELETE (SETQ THRU PRI@))
= PRINT
#P
(PROG NIL (PRIN1 &) (PRIN1 &))
#
#P
... LP (SELECTQ & & &) (SETQ Y &) OUT (SETQ FLG &) (RETURN Y))
#(MOVE (1 TO OUT) TO N HERE)
#P
... OUT (SETQ FLG &) (RETURN Y) LP (SELECTQ & & &) (SETQ Y &))
#
#PP
(PROG (TEMP1 TEMP2)
(COND ((NOT (MEMQ REMARG LISTING))
(SETQ TEMP1 (ASSOC REMARG NAMEDREMARKS))
(SETQ TEMP2 (CADR TEMP1)))
(T (SETQ TEMP1 REMARG)))
(NCONC LISTING REMARG)
(RETURN (CONS TEMP1 TEMP2)))
#(EXTRACT (SETQ THRU CADR) FROM COND) PP
(PROG (TEMP1 TEMP2)
(SETQ TEMP1 (ASSOC REMARG NAMEDREMARKS))
(SETQ TEMP2 (CADR TEMP1))
(NCONS LISTING REMARG)
(RETURN (CONS TEMP1 TEMP2)))
#
TO and THRU can also be used directly with XTR.
(Because XTR involves a location specification while A,B,:,
and MBD do not.) Thus in the previous example, if the
current expression had been the COND, e.g., the user had
first performed F COND , he could have used (XTR (SETQ THRU
CADR)) to perform the extraction.
2 . 58
($1 TO), ($1 THRU)
Both same as ($1 THRU -1), i.e.,
from $1 thru the end of the list.
#P
(VAL (RPLACA DFPRP &) (RPLACD & &) (RPLACA VARS &) (RETURN &))
#(MOVE (2 TO) TO N (_ PROG))
#(N (GO VAR))
#P
(VAL (GO VAR))
#
#P
(T (COND &) (EDITSMASH CL & &) (COND &))
#(-2 (GO REPLACE))
#(MOVE (COND TO) TO N PROG (N REPLACE))
#P
(T (GO REPLACE))
#\ P
(PROG (&) (COND & & &) (COND & & &) DELETE (COND & &) REPLACE
(COND &) (EDITSMASH CL & &) (COND &))
#
#PP
(LAMBDA(CLAUSALA X)
(PROG (A D)
(SETQ A CLAUSALA)
LP (COND ((NULL A) (RETURN NIL)))
(SERCH X A)
(RUMARK (CAR A))
(NOTICECL (CAR A))
(SETQ A (CDR A))
(GO LP)))
#(EXTRACT (SERCH THRU NOT@) FROM PROG) P
= NOTICECL
(LAMBDA (CLAUSALA X) (SERCH X A) (RUMARK &) (NOTICECL &))
#(EMBED (SERCH TO) IN (MAP [FUNCTION (LAMBDA (A) *] CLAUSALA]
#PP
(LAMBDA(CLAUSALA X)
(MAP (FUNCTION
(LAMBDA(A)
(SERCH X A)
(RUMARK (CAR A))
(NOTICECL (CAR A))))
CLAUSALA))
2 . 59
(R x y)
Replaces all instances of x by y in
the current expression, e.g., (R
CAADR CADAR). Generates an error if
there is not at least one instance.
R operates by performing a DSUBST. The current
expression is the third argument to DSUBST, i.e., the
expression being substituted into, and y is the first
argument to DSUBST, i.e., the expression being substituted.
R computes the second argument to DSUBST, the expression to
be substituted for, by performing (F x T). The second
argument is then the current expression at that point, or if
that current expression is a list and x is atomic, then the
first element of that current expression. Thus x can be the
S-expression (or atom) to be substituted for, or can be a
pattern which specifies that S-expression (or atom).
For example, if the current expression is (LIST FUNNYATOM1
FUNNYATOM2 (CAR FUNNYATOM1)), then (R FUN@ FUNNYATOM3 ) will
substitute FUNNYATOM3 for FUNNYATOM1 throughout the current
expression. Note that FUNNYATOM2, even though it would have
matched with the pattern FUN@, is NOT replaced.
Similarly, if (LIST(CAR X) (CAR Y)) is the first
expression matched by (LIST --), then (R (LIST --) (LIST
(CAR Y) (CAR Z))) is equivalent to (R (LIST (CARX) (CARY))
(LIST (CAR Y) (CAR Z))), i.e., both will replace all
instances of (LIST (CAR X) (CAR Y)) by (LIST (CAR Y) (CAR
Z)). Note that other forms beginning with LIST will not be
replaced, even though they would have matched with (LIST
--). To change all expressions of the form (LIST --) to
(LIST (CAR Y) (CAR Z)), the user should perform (LP (REPLACE
(LIST --) WITH (LIST (CAR Y) (CAR].
UNFIND is set to the edit chain following the find command
so that \ will make the current expression be the place
where the first substitution occurred.
2 . 60
(SW n m)
Switches the nth and mth elements of
the current expression.
For example, if the current expression is (LIST (CONS (CAR
X) (CAR Y)) (CONS (CDR Y))), (SW 2 3) will modify it to be
(LIST (CONS (CDR X) (CDR Y)) (CONS (CAR X) (CAR Y))). The
relative order of n and m is not important, ie, (SW 3 2)
and (SW 2 3 ) are equivalent.
SW uses the generalized NTH command
to find the nth and mth elements, a
la the BI-BO commands.
Thus in the previous example, (SW CAR CDR) would produce the
same result.
2 . 61
Commands That Print
P
Prints current expression as though
PRINTLEV were given a depth of 2.
(P m)
Prints mth element of current
expression as though PRINTLEV were
given a depth of 2.
(P 0)
Same as P
(P m n)
Prints mth element of current
expression as though PRINTLEV were
given a depth of N.
(P 0 n)
Prints current expression as though
PRINTLEVEL were given a depth of N.
?
Same as (P 0 100)
Both (P m) and (P m n) use the general NTH command to
obtain the corresponding element, so that m does not have to
be a number, e.g. (P COND 3) will work.
All printing functions print to the teletype,
regardless of the primary output file. No printing function
ever changes the edit chain. All record the current edit
chain for use by \P, p. 2.37.
2 . 62
Commands That Evaluate
E
Only when typed in, (i.e., (INSERT D
BEFORE E) will treat E as a pattern)
causes the editor to call the LISP
interpreter giving it the next input
as argument.
Example:
#E (BREAK FIE FUM)
(FIE FUM)
#E (FOO)
(FIE BROKEN)
1:
(E x)
Evaluates X, i.e., performs (EVAL
x), and prints the result on the
teletype.
(E x T)
Same as (E x) but does not print.
The (E x) and (E x T) commands are mainly intended for
use by MACROS and subroutine calls to the editor; the user
would probably type in a form for evaluation using the more
convenient format of the (atomic) E command.
(I c x1 ... xn)
Same as (c y1 ... yn) where
yi=(EVAL xi).
Example: (I 3 (GETD (QUOTE FOO)) will replace the 3rd
element of the current expression with the definition of
FOO. (The I command sets an internal flag to indicate to
the structure modification commands not to copy
expression(s) when inserting, replacing, or attaching.) (I N
FOO (CAR FIE)) will attach the value of FOO and CAR of the
value of FIE to the end of the current expression. (I F=
FOO T) will search for an expression EQ to the value of FOO.
If c is not an atom, it is evaluated
as well.
Example: (I (COND ((NULL FLG) (QUOTE -1)) (T 1)) FOO), if
FLG is NIL, inserts the value of FOO before the first
element of the current expression, otherwise replaces the
2 . 63
first element by the value of FOO.
(## com[1] com[2] ... com[n])
is an FSUBR (not a command). Its
value is what the current expression
would be after executing the edit
commands com[1] ... com[n] starting
from the present edit chain.
Generates an error if any of com[1]
thru com[n] cause errors. The
current edit chain is never changed.
(Recall that A,B,:,INSERT, REPLACE,
and CHANGE make special checks for
## forms in the expressions used for
inserting or replacing, and use a
copy of ## form instead (see p.
2.44). thus, (INSERT (## 3 2) AFTER
1) is equivalent to (I INSERT (COPY
(## 3 2 )) (QUOTE AFTER) 1).)
Example: (I R (QUOTE X) (## (CONS ..Z))) replaces all X's in
the current expression by the first CONS containing a Z.
The I command is not very convenient for computing an
entire edit command for execution, since it computes the
command name and its arguments separately. Also, the I
command cannot be used to compute an atomic command. The
following two commands provide more general ways of
computing commands.
(COMS x1 ... xn)
Each xi is evaluated and its value
executed as a command.
For example, (COMS (COND (X (LIST 1 X)))) will replace the
first element of the current expression with the value of X
if non-NIL, otherwise do nothing. (NIL as a command is a
NOP, see p. 2.71.)
(COMSQ com[1] ... com[n])
Executes com[1] ... com[n].
COMSQ is mainly useful in conjunction with the COMS command.
For example, suppose the user wishes to compute an entire
list of commands for evaluation, as opposed to computing
each command one at a time as does the COMS command. He
would then write (COMS (CONS (QUOTE COMSQ) x)) where x
computed the list of commands, e.g.,
2 . 64
(COMS (CONS (QUOTE COMSQ) (GET FOO (QUOTE COMMANDS)))).
2 . 65
Commands That Test
(IF x)
Generates an error unless the value
of (EVAL x) is true, i.e., if (EVAL
x) causes an error or (EVAL x)=NIL,
IF will cause an error.
For some editor commands, the occurrence of an error
has a well defined meaning, i.e., they use errors to branch
on as COND uses NIL and non-NIL. For example, an error
condition in a location specification may simply mean "not
this one, try the next." Thus the location specification
(*PLUS (E (OR (NUMBERP (## 3)) (ERR NIL)) T))
specifies the first *PLUS whose second argument is a number.
The IF command, by equating NIL to error, provides a more
natural way of accomplishing the same result. Thus, an
equivalent location specification is (*PLUS (IF (NUMBERP (##
3)))).
The IF command can also be used to select between two
alternate lists of commands for execution.
(IF x coms1 coms2)
If (EVAL x) is true, execute coms1;
if (EVAL x) causes an error or is
equal to NIL, execute coms2.
For example, the command (IF (NULL A) NIL (P)) will print
the current expression provided A=NIL.
(IF x coms1)
If (EVAL x) is true, execute coms1;
otherwise generate an error.
(LP . coms)
Repeatedly executes coms, a list of
commands, until an error occurs.
For example, (LP F PRINT (N T)) will attach a T at the
end of every PRINT expression. (LP F PRINT (IF (## 3) NIL
((N T)))) will attach a T at the end of each print
expression which does not already have a second argument.
(i.e. The form (## 3) will cause an error if the edit
command 3 causes an error, thereby selecting ((N T)) as the
list of commands to be executed. The IF could also be
written as (IF (CDDR (##)) NIL ((N T))).)
2 . 66
When an error occurs, LP prints n
OCCURRENCES, where n is the number
of times COMS was successfully
executed. The edit chain is left as
of the last complete successful
execution of COMS.
(LPQ . Coms)
Same as LP but does not print n
OCCURRENCES.
In order to prevent non-terminating loops, both LP and
LPQ terminate when the number of iterations reaches MAXLOOP,
initially set to 30.
(ORR coms[1] ... Coms[n])
ORR begins by executing coms[1], a
list of commands. If no error
occurs, ORR is finished. Otherwise,
ORR restores the edit chain to its
original value, and continues by
executing coms[2], etc. If none of
the command lists execute without
errors, i.e., the ORR "drops off the
end", ORR generates an error.
Otherwise, the edit chain is left as
of the completion of the first
command list which executes without
error. (NIL as a command list is
perfectly legal, and will always
execute successfully. Thus, making
the last 'argument' to ORR be NIL
will insure that the ORR never
causes an error. Any other atom is
treated as (atom), i.e., the example
given below could be written as (ORR
NX !NX NIL).)
For example, (ORR (NX) (!NX) NIL) will perform a NX, if
possible, otherwise a !NX, if possible, otherwise do
nothing. Similarly, DELETE could be written as (ORR (UP
(1)) (BK UP (2)) (UP (: NIL))).
2 . 67
Macros
Many of the more sophisticated branching commands in
the editor, such as ORR, IF, etc., are most often used in
conjunction with edit macros. The macro feature permits the
user to define new commands and thereby expand the editor's
repertoire. (However, built in commands always take
precedence over macros, i.e., the editor's repertoire can be
expanded, but not modified.) Macros are defined by using the
M command.
(M c . coms)
For c an atom, M defines c as an
atomic command. (If a macro is
redefined, its new definition
replaces its old.) Executing c is
then the same as executing the list
of commands COMS.
For example, (M BP BK UP P) will define BP as an atomic
command which does three things, a BK, an UP, and a P. Note
that macros can use commands defined by macros as well as
built in commands in their definitions. For example,
suppose Z is defined by (M Z -1 (IF (NULL (##)) NIL (P))),
i.e. Z does a -1, and then if the current expression is not
NIL, a P. Now we can define ZZ by (M ZZ -1 Z), and ZZZ by
(M ZZZ -1 -1 Z) or (M ZZZ -1 ZZ).
Macros can also define list commands, i.e., commands
that take arguments.
(M (c) (arg[1] ... arg[n]) . coms)
C an atom. M defines c as a list
command. Executing (c e1 ... en)
is then performed by substituting e1
for arg[1], ... en for arg[n]
throughout COMS, and then executing
COMS.
For example, we could define a more general BP by (M
(BP) (N) (BK N) UP P). Thus, (BP 3) would perform (BK 3),
followed by an UP, followed by a P.
A list command can be defined via a macro so as to take
a fixed or indefinite number of 'arguments'. The form given
above specified a macro with a fixed number of arguments, as
indicated by its argument list. If the 'argument list' is
atomic, the command takes an indefinite number of arguments.
2 . 68
(M (c) args . coms)
Name, args both atoms, defines c as
a list command. executing (c e1 ...
en) is performed by substituting (e1
... en), i.e., CDR of the command,
for args throughout coms, and then
executing coms.
For example, the command SECOND, p. 2.31, can be
defined as a macro by (M (2ND) X (ORR ((LC . X) (LC .
X)))). Note that for all editor commands, 'built in'
commands as well as commands defined by macros, atomic
definitions and list definitions are completely independent.
In other words, the existence of an atomic definition for c
in no way affects the treatment of c when it appears as CAR
of a list command, and the existence of a list definition
for c in no way affects the treatment of c when it appears
as an atom. in particular, c can be used as the name of
either an atomic command, or a list command, or both. In
the latter case, two entirely different definitions can be
used.
Note also that once c is defined as an atomic command
via a macro definition, it will not be searched for when
used in a location specification, unless c is preceded by an
F. Thus (INSERT -- BEFORE BP) would not search for BP, but
instead perform a BK, an UP, and a P, and then do the
insertion. The corresponding also holds true for list
commands.
Occasionally, the user will want to employ the S
command in a macro to save some temporary result. For
example, the SW command could be defined as
(M (SW) (N M) (NTH N) (S FOO 1) MARK 0 (NTH M) (S FIE 1) (I
1 FOO) __ (I 1 FIE))
(A more elegant definition would be (M (SW) (N M) (NTH N)
MARK 0 (NTH M) (S FIE 1) (I 1 (## _ 1)) __ (I 1 FIE)), but
this would still use one free variable.)
Since SW sets FOO and FIE, using SW may have
undesirable side effects, especially when the editor was
called from deep in a computation. Thus we must always be
careful to make up unique names for dummy variables used in
edit macros, which is bothersome. Furthermore, it would be
impossible to define a command that called itself
recursively while setting free variables. The BIND command
2 . 69
solves both problems.
(BIND . coms)
Binds three dummy variables #1, #2,
#3, (initialized to NIL), and then
executes the edit commands COMS.
Note that these bindings are only in
effect while the commands are being
executed, and that BIND can be used
recursively; it will rebind #1, #2,
and #3 each time it is invoked.
(BIND is implemented by (PROG (#1 #2
#3) (EDITCOMS (CDR COM))) where COM
corresponds to the BIND command, and
EDITCOMS is an internal editor
function which executes a list of
commands.)
thus we could now write SW safely as
(M (SW) (N M) (BIND (NTH N) (S #1 1) MARK 0 (NTH M) (S #2 1)
(I 1 #1) __ (I 1 #2))).
User macros are stored on a list USERMACROS.
(USERMACROS is initially NIL.) thus if the user wants to
save his macros, he should save the value of USERMACROS.
(The user probably should also save the value of EDITCOMSL).
2 . 70
Miscellaneous Commands
NIL
Unless preceded by F or BF, is
always a NOP.
TTY:
Calls the editor recursively. The
user can then type in commands, and
have them executed. The TTY:
command is completed when the user
exits from the lower editor. (See
OK and STOP below.)
The TTY: command is extremely useful. It enables the
user to set up a complex operation, and perform interactive
attention-changing commands part way through it. For
example the command (MOVE 3 TO AFTER COND 3 P TTY:) allows
the user to interact, in effect, within the MOVE command.
Thus he can verify for himself that the correct location has
been found, or complete the specification "by hand". In
effect, TTY: says "I'll tell you what you should do when you
get there."
The TTY: command operates by printing TTY: and then
calling the editor. The initial edit chain in the lower
editor is the one that existed in the higher editor at the
time the TTY: command was entered. Until the user exits
from the lower editor, any attention changing commands he
executes only affect the lower editor's edit chain. (Of
course, if the user performs any structure modification
commands while under a TTY: command, these will modify the
structure in both editors, since it is the same structure.)
When the TTY: command finishes, the lower editor's edit
chain becomes the edit chain of the higher editor.
OK
Exits from the editor.
2 . 71
STOP
Exits from the editor with an error.
Mainly for use in conjunction with
TTY: commands that the user wants to
abort.
Since all of the commands in the editor are ERRSET
protected, the user must exit from the editor via a command.
STOP provides a way of distinguishing between a successful
and unsuccessful (from the user's standpoint) editing
session. For example, if the user is executing (MOVE 3 TO
AFTER COND TTY:), and he exits from the lower editor with an
OK, the MOVE command will then complete its operation. If
the user wants to abort the MOVE command, he must make the
TTY: command generate an error. He does this by exiting
from the lower editor with a STOP command. In this case,
the higher editor's edit chain will not be changed by the
TTY: command.
SAVE
Exits from the editor and saves the
'state of the edit' on the property
list of the function/variable being
edited under the property EDIT-SAVE.
If the editor is called again on the
same structure, the editing is
effectively "continued," i.e., the
edit chain, mark list, value of
UNFIND and UNDOLST are restored.
For example:
#P
(NULL X)
#F COND P
(COND (& &) (T &))
#SAVE
FOO
.
.
.
*(EDITF FOO)
EDIT
#P
(COND (& &) (T &))
#\ P
(NULL X)
#
2 . 72
SAVE is necessary only if the user is editing many
different expressions; an exit from the editor via OK always
saves the state of the edit of that call to the editor. (On
the property list of the atom EDIT, under the property name
LASTVALUE. OK also remprops EDIT-SAVE from the property
list of the function/variable being edited.) Whenever the
editor is entered, it checks to see if it is editing the
same expression as the last one edited. In this case, it
restores the mark list, the undolst, and sets UNFIND to be
the edit chain as of the previous exit from the editor. For
example:
*(EDITF FOO)
EDIT
#P
(LAMBDA (X) (PROG & & LP & & & &))
.
.
.
#P
(COND & &)
#OK
FOO
#
.
. Any number of inputs except for
. calls to the editor.
*(EDITF FOO)
EDIT
#P
(LAMBDA (X) (PROG & & LP & & & &))
#\ P
(COND & &)
#
The user can always continue editing, including undoing
changes from a previous editing session, if
(1) No other expressions have been edited since
that session; (since saving takes place at exit
time, intervening calls that were exited via STOP
will not affect the editor's memory of this last
session.) or
(2) It was ended with a SAVE command.
2 . 73
REPACK
Permits the 'editing' of an atom or
string.
For example:
#P
... "THIS IS A LOGN STRING")
#REPACK
EDIT
1#P
(/" T H I S / I S / A / L O G N / S T R I N G /")
1#(SW G N)
1#OK
"THIS IS A LONG STRING"
#
REPACK operates by calling the editor recursively on
UNPACK of the current expression, or if it is a list, on
UNPACK of its first element. If the lower editor is exited
successfully, i.e. via OK as opposed to STOP, the list of
atoms is made into a single atom or string, which replaces
the atom or string being 'repacked.' The new atom or string
is always printed.
(REPACK $)
Does (LC . $) followed by REPACK,
e.g. (REPACK THIS@).
2 . 74
(MAKEFN form args n m)
Makes (CAR form) an EXPR with the
nth through mth elements of the
current expression with each
occurance of an element of (CDR
form) replaced by the corresponding
element of args. The nth through
mth elements are replaced by form.
For example:
#P
... (SETQ A NIL) (SETQ B T) (CONS C D))
#(MAKEFN (SETUP C D) (W X) 1 3) P
... (SETUP C D))
#E (GRINDEF SETUP)
(DEFPROP SETUP
(LAMBDA(W X) (SETQ A NIL) (SETQ B T) (CONS W X))
EXPR)
#
(MAKEFN form args n)
Same as (MAKEFN form args n n).
2 . 75
UNDO
Each command that causes structure modification
automatically adds an entry to the front of UNDOLST
containing the information required to restore all pointers
that were changed by the command.
UNDO
Undoes the last, i.e., most recent,
structure modification command that
has not yet been undone, (Since UNDO
and !UNDO causes structure
modification, they also add an entry
to UNDOLST. However, UNDO and !UNDO
entries are skipped by UNDO, e.g.,
if the user performs an INSERT, and
then an MBD, the first UNDO will
undo the MBD, and the second will
undo the INSERT. However, the user
can also specify precisely which
command he wants undone. In this
case, he can undo an UNDO command,
e.g., by typing UNDO UNDO, or undo a
!UNDO command, or undo a command
other than that most recently _
performed.) and prints the name of
that command, e.g., MBD UNDONE. The
edit chain is then exactly what it
was before the 'undone' command had
been performed. If there are no
commands to undo, UNDO types NOTHING
SAVED.
!UNDO
Undoes all modifications performed
during this editing session, i.e.,
this call to the editor. As each
command is undone, its name is
printed a la UNDO. If there is
nothing to be undone, !UNDO prints
NOTHING SAVED.
Whenever the user continues an editing session as
described on pages 2.72-2.73, the undo information of the
previous session(s) is protected by inserting a special
blip, called an undo-block on the front of UNDOLST. This
undo-block will terminate the operation of a !UNDO, thereby
confining its effect to the current session, and will
2 . 76
similarly prevent an UNDO command from operating on commands
executed in the previous session.
Thus, if the user enters the editor continuing a
session, and immediately executes an UNDO or !UNDO, UNDO and
!UNDO will type BLOCKED, instead of NOTHING SAVED.
Similarly, if the user executes several commands and then
undoes them all, either via several UNDO commands or a !UNDO
command, another UNDO or !UNDO will also type BLOCKED.
UNBLOCK
Removes an undo-block. If executed
at a non-blocked state, i.e., if
UNDO or !UNDO could operate, types
NOT BLOCKED.
TEST
Adds an undo-block at the front of
UNDOLST.
Note that TEST together with !UNDO provide a
'tentative' mode for editing, i.e., the user can perform a
number of changes, and then undo all of them with a single
!UNDO command.
??
Prints the entries on UNDOLST. The
entries are listed in the reverse
order of their execution, i.e., the
most recent entry first. For
example:
#P
(CONS (T &) (& &))
#(1 COND) (SW 2 3) P
(COND (& &) (T &))
#??
SW (1 --)
#
2 . 77
Editdefault
Whenever a command is not recognized, i.e., is not
'built in' or defined as a macro, the editor calls an
internal function, EDITDEFAULT to determine what action to
take. If a location specification is being executed, an
internal flag informs EDITDEFAULT to treat the command as
though it had been preceded by an F.
If the command is atomic and typed in directly, the
procedure followed is as given below.
1)
If the command is one of the list commands, i.e.,
a member of EDITCOMSL, and there is additional input on
the same teletype line, treat the entire line as a
single list command. (Uses READLINE. Thus the line
can be terminated by carriage return, right parenthesis
or square bracket, or a list.) Thus, the user may omit
parentheses for any list command typed in at the top
level (which is not also an atomic command, e.g., NX,
BK). For example:
#P
(COND (& &) (T &))
#(XTR 3 2)
#MOVE TO AFTER LP
#
If the command is on the list EDITCOMSL but no
additional input is on the teletype line, an error is
generated, e.g.,
#P
(COND (& &) (T &))
#MOVE
MOVE ?
#
2)
If the last character in the command is P, and the
first n-1 characters comprise the command __, _, UP,
NX, BK, !NX, UNDO, or REDO, assume that the user
intended two commands, e.g.,
2 . 78
#P
(COND (& &) (T &))
#2 NXP
(T (CONS X Y))
3)
Otherwise, generate an error.
2 . 79
Editor Functions
(EDITL L coms atm marklst mess)
EDITL is the editor. Its first
argument is the edit chain, and its
value is an edit chain, namely the
value of L at the time EDITL is
exited. (L is a special variable,
and so can be examined or set by
edit commands. For example, ^ is
equivalent to (E (SETQ L(LAST L))
T).)
Coms is an optional list of
commands. For interactive editing,
coms is NIL. In this case, EDITL
types EDIT and then waits for input
from the teletype. (If mess is not
NIL EDITL types it instead of EDIT.
For example, the TTY: command is
essentially (SETQ L (EDITL L NIL NIL
NIL (QUOTE TTY:))).) Exit occurs
only via an OK, STOP, or SAVE
command.
If coms is NOT NIL, no message is
typed, and each member of coms is
treated as a command and executed.
If an error occurs in the execution
of one of the commands, no error
message is printed , the rest of the
commands are ignored, and EDITL
exits with an error, i.e., the
effect is the same as though a STOP
command had been executed. If all
commands execute successfully, EDITL
returns the current value of L.
Marklst is the list of marks.
On calls from EDITF, Atm is the name
of the function being edited; on
calls from EDITV, the name of the
variable, and calls from EDITP, the
atom of which some property of its
property list is being edited. The
property list of atm is used by the
SAVE command for saving the state of
2 . 80
the edit. Thus SAVE will not save
anything if atm=NIL i.e., when
editing arbitrary expressions via
EDITE or EDITL directly.
(EDITF x)
FSUBR function for editing a
function. (CAR x) is the name of
the function, (CDR x) an optional
list of commands. For the rest of
the discussion, fn is (CAR x), and
coms is (CDR x).
If x is NIL, fn is set to the value
of LASTWORD, coms is set to NIL, and
the value of LASTWORD is printed.
The value of EDITF is fn.
(1) In the most common case, fn is an non-compiled
function, and EDITF simply performs
(EDITE (CADR (GETL fn (QUOTE (FEXPR EXPR MACRO)))) coms fn)
and sets LASTWORD to fn.
(2) If fn is not an editable function, but has a value,
EDITF assumes the user meant to call EDITV, prints =EDITV,
calls EDITV and returns.
Otherwise, EDITF generates an fn NOT EDITABLE error.
(EDITE expr coms atm)
Edits an expression. Its value is
the last element of (EDITL (LIST
expr) coms atm NIL NIL). Generates
an error if expr is not a list.
2 . 81
(EDITV editvx)
FSUBR function, similar to EDITF,
for editing values. (CAR editvx)
specifies the value, (CDR editvx) is
an optional list of commands.
If editvx is NIL, it is set to the
value of (NCONS LASTWORD) and the
value of LASTWORD is printed.
If (CAR editvx) is a list, it is evaluated and its
value given to EDITE, e.g. (EDITV (CDR (ASSOC (QUOTE FOO)
DICTIONARY)))). In this case, the value of EDITV is T.
However, in most cases, (CAR editvx) is a variable,
e.g. (EDITV FOO); and EDITV calls EDITE on the value of the
variable.
If the value of (CAR editvx) is atomic then EDITV
prints a NOT EDITABLE error message.
When (if) EDITE returns, EDITV sets the variable to the
value returned, and sets LASTWORD to the name of the
variable.
The value of EDITV is the name of the variable whose
value was edited.
(EDITP x)
FSUBR function, similar to EDITF for
editing property lists. Like EDITF,
LASTWORD is used if x is NIL. EDITP
calls EDITE on the property list of
(CAR x). When (if) EDITE returns,
EDITP RPLACD's (CAR x) with the
value returned, and sets LASTWORD to
(CAR x).
The value of EDITP is the atom whose
property list was edited.
2 . 82
(EDITFNS x)
FSUBR function, used to perform the
same editing operations on several
functions. (CAR x) is evaluated to
obtain a list of functions. (CDR x)
is a list of edit commands. EDITFNS
maps down the list of functions,
prints the name of each function,
and calls the editor (via EDITF) on
that function.
For example, (EDITFNS FOOFNS (R FIE FUM)) will change
every FIE to FUM in each of the functions on FOOFNS.
The call to the editor is ERRSET
protected, so that if the editing of
one function causes an error,
EDITFNS will proceed to the next
function.
Thus in the above example, if one of the functions did
not contain a FIE, the R command would cause an error, but
editing would continue with the next function.
The value of EDITFNS is NIL
(EDIT4E pat y)
Is the pattern match routine. Its
value is T if pat matches y. See
pp. 2.22-2.23 For definition of
'match'.
Note: before each search operation in the editor
begins, the entire pattern is scanned for atoms or strings
that end in at-signs. These are replaced by patterns of the
form
(CONS (QUOTE /@) (EXPLODEC atom)).
Thus from the standpoint of EDIT4E, pattern type 5, atoms or
strings ending in at-signs, is really "If car[pat] is the
atom @ (at-sign), PAT will match with any literal atom or
string whose initial character codes (up to the @) are the
same as those in cdr[pat]."
If the user wishes to call EDIT4E directly, he must
therefore convert any patterns which contain atoms or
strings ending in at-signs to the form recgnized by EDIT4E.
This can be done via the function EDITFPAT.
2 . 83
(EDITFPAT pat flg)
Makes a copy of pat with all
patterns of type 5 converted to the
form expected by EDIT4E. Flg should
be passed as NIL (flg=T is for
internal use by the editor).
(EDITFINDP x pat flg)
Allows a program to use the edit
find command as a pure predicate
from outside the editor. X is an
expression, pat a pattern. The
value of EDITFINDP is T if the
command F pat would succeed, NIL
otherwise. EDITFINDP calls EDITFPAT
to convert pat to the form expected
by EDIT4E, unless flg=T. Thus, if
the program is applying EDITFINDP to
several different expressions using
the same pattern, it will be more
efficient to call EDITFPAT once, and
then call EDITFINDP with the
converted pattern and flg=T.
(EDITRACEFN com)
Is available to help the user debug
complex edit macros, or subroutine
calls to the editor. EDITRACEFN is
to be defined by the user. Whenever
the value of EDITRACEFN is non-NIL,
the editor calls the function
EDITRACEFN before executing each
command (at any level), giving it
that command as its argument.
For example, defineing EDITRACEFN as
(LAMBDA (C) (PRINT C) (PRINT (CAR L)))
will print each command and the corresponding current
expression. (LAMBDA (C) (BREAK1 T T NIL NIL NIL)) will
cause a break before executing each command.
EDITRACEFN is initially equal to
NIL, and undefined.
2 . 84