Trailing-Edge
-
PDP-10 Archives
-
decus_20tap1_198111
-
decus/20-0004/22lisp.doc
There are no other files named 22lisp.doc in the archive.
SECTION 22
1
THE PROGRAMMER'S ASSISTANT AND LISPX
22.1 Introduction
This chapter describes one of the newer additions to INTERLISP: the
programmer's assistant. The central idea of the programmer's assistant
is that the user, rather than talking to a passive system which merely
responds to each input and waits for the next, is instead addressing an
active intermediary, namely his assistant. Normally, the assistant is
invisible to the user, and simply carries out the user's requests.
However, since the assistant remembers what the user has told him, the
user can instruct him to repeat a particular operation or sequence of
operations, with possible modifications, or to undo the effect of
certain specified operations. Like DWIM, the programmer's assistant is
not implemented as a single function or group of functions, but is
2
instead dispersed throughout much of INTERLISP. Like DWIM, the
programmer's assistant embodies a philosophy and approach to system
design whose ultimate goal is to construct a programming environment
which would "cooperate" with the user in the development of his
programs, and free him to concentrate more fully on the conceptual
difficulties and creative aspects of the problem he is trying to solve.
Example
The following dialogue, taken from an actual session at the console,
gives the flavor of the programmer's assistant facility in INTERLISP.
The user is about to edit a function loadf, which contains several
constructs of the form (PUTD FN2 (GETD FN1)). The user plans to replace
each of these by equivalent MOVD expressions.
------------------------------------------------------------------------
1
The programmer's assistant was designed and implemented by W.
Teitelman. It is discussed in [Tei4].
2
Some of the features of the programmer's assistant have been
described elsewhere, e.g., the UNDO command in the editor, the file
package, etc.
22.1
_EDITF(LOADFF] [1]
=LOADF
EDIT
*PP
[LAMBDA (X Y)
[COND
((NULL (GETD (QUOTE READSAVE)))
(PUTD (QUOTE READSAVE)
(GETD (QUOTE READ]
(PUTD (QUOTE READ)
(GETD (QUOTE REED)))
(NLSETQ (SETQ X (LOAD X Y)))
(@UTD (QUOTE READ)
(GETD (QUOTE READSAVE)))
X]
*F PUTD (1 MOVD) [2]
*3 (XTRR 2) [3]
=XTR
*0P [4]
=0 P
(MOVD (QUOTE READSAVE) (QUOTE READ))
*(SW 2 3) [5]
*
3
At [1], the user begins to edit loadf. At [2] the user finds PUTD and
replaces it by MOVD. He then shifts context to the third subexpression,
[3], extracts its second subexpression, and ascends one level [4] to
print and result. The user now switches the second and third
subexpression [5], thereby completing the operation for this PUTD. Note
that up to this point, the user has not directly addressed the
assistant. The user now requests that the assistant print out the
operations that the user has performed, [6], and the user then instructs
the assistant to REDO FROM F, [7], meaning repeat the entire sequence of
operations 15 through 20. The user then prints the current expression,
and observes that the second PUTD has now been successfully transformed.
*?? FROM F [6]
15. *F PUTD
16. *(1 MOVD)
17. *3
18. *(XTR 2)
19. *0
20. *(SW 2 3)
*REDO FROM F [7]
------------------------------------------------------------------------
3
We prefer to consider the programmer's assistant as the moving force
behind this type of spelling correction (even though the program
that does the work is part of the DWIM package). Whereas correcting
@PRINT to PRINT, or XTRR to XTR does not require any information
about what this user is doing, correcting LOADFF to LOADF clearly
required noticing when this user defined loadf.
22.2
*P
(MOVD (QUOTE REED) (QUOTE READ))
*
The user now asks the assistant to replay the last three steps to him,
[8]. Note that the entire REDO FROM F operation is now grouped together
as a single unit, [9], since it corresponded to a single user request.
Therefore, the user can instruct the assistant to carry out the same
operation again by simply saying REDO. This time a problem is
encountered [10], so the user asks the assistant what it was trying to
do [11].
*?? FROM -3 [8]
19. *0
20. *(SW 2 3)
21. REDO FROM F [9]
*F PUTD
*(1 MOVD)
*3
*(XTR 2)
*0
*(SW 2 3)
*REDO
PUTD ? [10]
*?? -1 [11]
22. REDO
*F PUTD
*(1 MOVD)
*3
*(XTR 2)
*0
The user then realizes the problem is that the third PUTD is misspelled
in the definition of LOADF (see page 22.2). He therefore instructs the
assistant to USE @UTD FOR PUTD, [12], and the operation now concludes
successfully.
22.3
*USE @UTD FOR PUTD [12]
*P
(MOVD (QUOTE READSAVE) (QUOTE READ))
*^ PP
[LAMBDA (X Y)
[COND
((NULL (GETD (QUOTE READSAVE)))
(MOVD (QUOTE READ)
(QUOTE READSAVE]
(MOVD (QUOTE REED)
(QUOTE READ))
(NLSETQ (SETQ X (LOAD X Y)))
(MOVD (QUOTE READSAVE)
(QUOTE READ))
X]
*OK
LOADF
_
An important point to note here is that while the user could have
defined a macro to execute this operation, the operation is sufficiently
complicated that he would want to try out the individual steps before
attempting to combine them. At this point, he would already have
executed the operation once. Then he would have to type in the steps
again to define them as a macro, at which point the operation would only
be repeated once more before failing. Then the user would have to
repair the macro, or else change @UTD to PUTD by hand so that his macro
would work correctly. It is far more natural to decide after trying a
series of operations whether or not one wants them repeated or
forgotten. In addition, frequently the user will think that the
operation(s) in question will never need be repeated, and only discover
afterwards that he is mistaken, as occurs when the operation was
incorrect, but salvageable:
*P
(LAMBDA (STR FLGCQ VRB) **COMMENT** (PROG & & LP1 & LP2 & &))
*-1 -1 P
(RETURN (COND &))
*(-2 ((EQ BB (QUOTE OUT)) BB] [1]
*P
(RETURN (& BB) (COND &))
[2]
*UNDO
(-2 --) UNDONE
*2 P
(COND (EXPANS & & T))
*REDO EQ
*P
(COND (& BB) (EXPANS & & T)
*
Here the operation was correct, [1], but the context in which it was
executed, [2], was wrong.
This example also illustrates one of the most useful functions of the
programmer's assistant: its UNDO capability. In most systems, if a user
suspected that a disaster might result from a particular operation,
e.g., an untested program running wild and chewing up a complex data
22.4
structure, he would prepare for this contingency by saving the state of
part or all of his environment before attempting the operation. If
anything went wrong, he would then back up and start over. However,
saving/dumping operations are usually expensive and time consuming,
especially compared to a short computation, and are therefore not
performed that frequently, and of course there is always the case when
diaster strikes as a result of a "debugged" or at least innocuous
operation, as shown in the following example:
_(MAPC ELTS (FUNCTION (LAMBDA (X) (REMPROP X (QUOTE MORPH] [1]
NIL
_UNDO [2]
MAPC UNDONE.
_USE ELEMENTS FOR ELTS
[3]
NIL
_
The user types an expression which removes the property MORPH from every
member of the list ELTS [1], and then realizes that he meant to remove
that property only from those members of the list ELEMENTS, a much
shorter list. In other words, he has deleted a lot of information that
he actually wants saved. He therefore simply reverses the effect of the
MAPC by typing UNDO [2], and then does what he intended via the USE
command [3].
22.2 Overview
The programmer's assistant facility is built around a memory structure
called the "history list." The history list is a list of the information
associated with each of the individual "events" that have occurred in
4
the system, where each event corresponds to one user input. For
example, (XTR 2) ([3] on page 22.2) is a single event, while REDO FROM F
([7] on page 22.2) is also a single event, although the latter includes
executing the operation (XTR 2), as well as several others.
Associated with each event on the history list is its input and its
value, plus other optional information such as side-effects, formatting
information, etc. If the event corresponds to a history command, e.g.,
REDO FROM F, the input corresponds to what the user would have had to
type to execute the same operation(s), although the user's actual input,
i.e., the history command, is saved in order to clarify the printout of
that event ([9] on page 22.3). Note that if a history command event
combines several events, it will have more than one value:
------------------------------------------------------------------------
4
For various reasons, there are two history lists: one for the
editor, and one for lispx, which processes inputs to evalqt and
break, see page 22.36.
22.5
_(LOG (ANTILOG 4))
4.0
_USE 4.0 40 400 FOR 4
4.0
40.0
ARG NOT IN RANGE
400
_USE -40.0 -4.00007 -19.
-40.0
-4.00007
-19.0
_USE LOG ANTILOG FOR ANTILOG LOG IN -2 AND -1
4.0
40.0
400.0
4.00007
19.0
_??
4. USE LOG ANTILOG FOR ANTILOG LOG IN -2 -1
_(ANTILOG (LOG 4.0))
4.0
_(ANTILOG (LOG 40))
40.0
_(ANTILOG (LOG 400))
400.0
_(ANTILOG (LOG -40.0))
40.0
_(ANTILOG (LOG -4.00007))
4.00007
_(ANTILOG (LOG -19.0))
19.0
3. USE -40.0 -4.00007 -19.0
_(LOG (ANTILOG -40.0))
-40.0
_(LOG (ANTILOG -4.00007))
-4.00007
_(LOG (ANTILOG -19.0))
-19.0
2. USE 4.0 40 400 FOR 4
_(LOG (ANTILOG 4.0))
4.0
(LOG (ANTILOG 40.0)
40.0
_(LOG (ANTILOG 400))
1. _(LOG (ANTILOG 4))
4.0
As new events occur, existing events are aged, and the oldest event is
"forgotten." For efficiency, the storage used to represent the forgotten
event is cannibalized and reused in the representation of the new event,
so the history list is actually a ring buffer. The size of this ring
22.6
5
buffer is a system parameter called the 'time-slice.' Larger time-
slices enable longer "memory spans," but tie up correspondingly greater
amounts of storage. Since the user seldom needs really "ancient
history," and a NAME and RETRIEVE facility is provided for saving and
remembering selected events, a relatively small time slice such as 30
events is more than adequate, although some users prefer to set the time
slice as large as 100 events.
Events on the history list can be referenced in a number of ways. The
output on page 22.8 shows a printout of a history list with time-slice
16. The numbers printed at the left of the page are the event numbers.
More recent events have higher numbers; the most recent event is event
6
number 52, the oldest and about-to-be-forgotten event is number 37. At
this point in time, the user can reference event number 51,
RECOMPILE(EDIT), by its event number, 51; its relative position, -2
(because it occurred two events back from the current time), or by a
"description" of its input, e.g., (RECOMPILE (EDIT)), or (& (EDIT)), or
even just EDIT. As new events occur, existing events retain their
absolute event numbers, although their relative positions change.
Similarly, descriptor references may require more precision to refer to
an older event. For example, the description RECOMPILE would have
sufficed to refer to event 51 had event 52, also containing a RECOMPILE,
not intervened. Event specification will be described in detail later.
------------------------------------------------------------------------
5
Initially 30 events. The time-slice can be changed with the function
changeslice, page 22.43.
6
When the event number of the current event is 100, the next event
will be given number 1. (If the time slice is greater than 100, the
"roll-over" occurs at the next highest hundred, so that at no time
will two events ever have the same event number. For example, if the
time slice is 150, event number 1 follows event number 200.)
22.7
_??
52. ... HIST UNDO
_RECOMPILE(HIST)
HIST.COM
_RECOMPILE(UNDO)
UNDO.COM
51. _RECOMPILE(EDIT)
EDIT.COM
50. _LOGOUT]
49. _MAKEFILES]
(EDIT UNDO HIST)
48. _EDITF(UNDOLISPX)
UNDOLISPX
47. REDO GETD
_GETD(FIE)
(LAMBDA (X) (MAPC X (F/L (PRINT X))))
46. _UNDO
FIE
45. _GETD(FIE)
(LAMBDA (X) (MAPC X (FUNCTION (LAMBDA (X) (PRINT X)))))
44. _FIE]
NIL
43. _DEFINEQ((FIE (LAMBDA (X) (MAPC X (F/L (PRINT X))))))
(FIE)
42. REDO GETD
_GETD(FIE)
(LAMBDA (Y) Y)
41. _UNDO
MOVD
40. REDO GETD
_GETD(FIE)
(LAMBDA (X) X)
39. _MOVD(FOO FIE)
FIE
38. _DEFINEQ((FOO (LAMBDA (X) X)))
(FOO)
37. _GETD(FIE)
(LAMBDA (Y) Y)
The most common interaction with the programmer's assistant occurs at
the top level evalqt, or in a break, where the user types in expressions
for evaluation, and sees the values printed out. In this mode, the
assistant acts much like a standard LISP evalqt, except that before
attempting to evaluate an input, the assistant first stores it in a new
entry on the history list. Thus if the operation is aborted or causes
an error, the input is still saved and available for modification and/or
reexecution. The assistant also notes new functions and variables to be
added to its spelling lists to enable future corrections. Then the
assistant executes the computation (i.e., evaluates the form or applies
the function to its arguments), saves the value in the entry on the
history list corresponding to the input, and prints the result, followed
22.8
7
by a prompt character to indicate it is again ready for input.
If the input typed by the user is recognized as a history command, the
assistant takes special action. Commands such as UNDO, ??, NAME, and
RETRIEVE are immediately performed. Commands that involved reexecution
of previous inputs, e.g., REDO and USE, are achieved by computing the
corresponding input expression(s) and then unreading them. The effect
of this unreading operation is to cause the assistant's input routine,
lispxread, to act exactly as though these expression were typed in by
the user. Except for the fact that these inputs are not saved on new
and separate entries on the history list, but associated with the
history command that generated them, they are processed exactly as
though they had been typed.
The advantage of this implementation is that it makes the programmer's
assistant a callable facility for other system packages as well as for
users with their own private executives. For example, break1 accept
user inputs, recognizes and executes certain break commands and macros,
and interprets anything else as INTERLISP expressions for evaluation.
To interface break1 with the programmer's assistant required three small
modifications to break1: (1) input was to be obtained via lispxread
instead of read; (2) instead of calling eval or apply directly, break1
was to give those inputs it could not interpret to lispx, and (3) any
commands or macros handled by break1, i.e., not given to lispx, were to
be stored on the history list by break1 by calling the function
historysave, a part of the assistant package.
Thus when the user typed in a break command, the command would be stored
on the history list as a result of (3). If the user typed in an
expression for evaluation, it would be evaluated as before, with the
expression and its value both saved on the history list as a result of
(2). Now if the user entered a break and typed three inputs: EVAL, (CAR
!VALUE), and OK, at the next break, he could achieve the same effect by
typing REDO FROM EVAL. This would cause the assistant to unread the
three expressions EVAL, (CAR !VALUE), and OK. Because of (1), the next
"input" seen by break1 would then be EVAL, which break1 would interpret.
Next would come (CAR !VALUE), which would be given to lispx to evaluate,
and then would come OK, which break1 would again process. Thus, by
virtue of unreading, history operations will work even for those inputs
not interpretable by lispx, in this case, EVAL and OK.
------------------------------------------------------------------------
7
The function that accepts a user input, saves the input on the
history list, performs the indicated computation or history command,
and prints the result, is lispx. lispx is called by evalqt and
break1, and in most cases, is synonymous with "programmer's
assistant." However, for various reasons, the editor saves its own
inputs on a history list, carries out the requests, i.e., edit
commands, and even handles undoing independently of lispx. The
editor only calls lispx to execute a history command, such as
REDO, USE, etc. Therefore we use the term assistant (loosely) when
the discussion applies to features shared by evalqt, break and the
editor, and the term lispx when we are discussing the specific
function.
22.9
The net effect of this implementation of the programmer's assistant is
to provide a facility which is easily inserted at many levels, and
embodies a consistent set of commands and conventions for talking about
past events. This gives the user the subjective feeling that a single
agent is watching everything he does and says, and is always available
to help.
22.3 Event Specification
All history commands use the same conventions and syntax for indicating
which event or events on the history list the command refers to, even
though different commands may be concerned with different aspects of the
corresponding event(s), e.g., side-effects, value, input, etc.
Therefore, before discussing the various history commands in the next
section, this section describes the types of event specifications
currently implemented. All examples refer to the history list on page
22.8.
An event address identifies one event on the history list. It consists
of a sequence of "commands" for moving an imaginary cursor up or down
the history list, much in the manner of the arguments to the @ command
in break (see Section 15). The event identified is the one "under" the
imaginary cursor when there are no more commands. (If any command
fails, an error is generated and the history command is aborted.)
The commands are interpreted as follows:
n (n > 1) move forward n events, i.e., in direction of
increasing event number. If given as the first
"command," n specifies the event with event
number n.
n (n < -1) move backward -n events.
_atom specifies an event whose function matches atom
(i.e., for apply format only), e.g., whereas FIE
would refer to event 47, _FIE would refer to
8
event 44. Similarly, ED$ would specify event
51, whereas _ED$ event 48.
_ next search is to go forward instead of
backward, (if given as the first "command", next
search begins with last, i.e., oldest, event on
history list), e.g., _ LAMBDA refers to event
38; MAKEFILES _ RECOMPILE refers to event 51.
------------------------------------------------------------------------
8
i.e., EDalt-mode.
22.10
F next object is to be searched for, regardless of
what it is, e.g., F -2 looks for an event
containing a -2.
= next object (presumably a pattern) is to be
matched against values, instead of inputs, e.g.,
= UNDO refers to event 49; 45 = FIE refers to
event 43; _ = LAMBDA refers to event 37.
\ specifies the event last located.
SUCHTHAT pred specifies an event for which pred, a function of
two arguments, when given the input portion of
the event as its first argument, and the event
itself as its second argument, returns true.
E.g., SUCHTHAT (LAMBDA (X Y) (MEMB
(QUOTE *ERROR*) Y)) specifies an event in which
9
an error occurred.
pat anything else specifies an event whose input
contains an expression that matches pat as
described in Section 9.
Note: each search skips the current event, i.e., each command always
moves the cursor. For example, if FOO refers to event n, FOO FIE will
refer to some event before event n, even if there is a FIE in event n.
An event specification specifies one or more events:
FROM #1 THRU #2 the sequence of events from the
#1 THRU #2 event with address #1 through event with address
10
#2, e.g., FROM GETD THRU 49 specifies events
47, 48, and 49. #1 can be more recent than #2,
e.g., FROM 49 THRU GETP specifies events 49, 48,
and 47 (note reversal of order).
FROM #1 TO #2 Same as THRU but does not include event #2.
#1 TO #2
------------------------------------------------------------------------
9
See page 22.36 for discussion of the format of events on the
history list.
10
i.e., the symbol #1 corresponds to all words between FROM and THRU
in the event specification, and #2 to all words from THRU to the end
of the event specification. For example, in FROM FOO 2 THRU FIE -
1, #1 is (FOO 2), and #2 is (FIE -1).
22.11
FROM #1 Same as FROM #1 THRU -1, e.g., FROM 49 specifies
events 49, 50, 51, and 52.
THRU #2 Same as FROM -1 THRU #2, e.g., THRU 49 specifies
events 52, 51, 50, and 49. Note reversal of
order.
TO Same as FROM -1 TO #2.
#1 AND #2 AND ... AND #n
i.e., a sequence of event specifications
separated by AND's, e.g., FROM 47 TO LOGOUT
would be equivalent to 47 AND 48 AND MAKEFILES.
ALL #1 specifies all events satisfying #1, e.g., ALL
LOAD, ALL SUCHTHAT FOO.
empty i.e., nothing specified, same as -1, unless last
11
event was an UNDO, in which case same as -2.
@ atom refers to the events named by atom, via the NAME
command, page 22.21 e.g., if the user names a
particular event or events FOO, @ FOO specifies
those events.
@@ B B is an event specification and interpreted as
above, but with respect to the archived history
list, as specified on page 22.23.
If no events can be found that satisfy the event specification, spelling
correction on each word in the event specification is performed using
lispxfindsplst as a spelling list, e.g., REDO 3 THRUU 6 will work
correctly. If the event specification still fails to specify any events
after spelling correction, an error is generated.
22.4 History Commands
All history commands can be input as either lists, or as lines (see
readline Section 14, and also page 22.39).
B is used to denote an event specification. Unless specified otherwise,
B omitted is the same as B =-1, e.g., REDO and REDO -1 are the same.
------------------------------------------------------------------------
11
For example, if the user types (NCONC FOO FIE), he can then type
UNDO, followed by USE NCONC1.
22.12
REDO B redoes the event or events specified by B, e.g.,
REDO FROM -3 redoes the last three events.
REDO B N TIMES redoes the event or events specified by B N
times, e.g., REDO 10 TIMES redoes the last event
ten times. If n is not a positive number, e.g.,
REDO MANY TIMES, the effect is the same as
though n were infinite: the events(s) are
repeated until an error occurs, or user types
control-d.
USE exprs FOR args IN B substitutes exprs for args in B, and redoes the
result, e.g.,
USE LOG ANTILOG FOR ANTILOG LOG IN -2 AND -1.
Substitution is done by esubst, Section 9, and
is carried out as described below. exprs and
args can include non-atomic members.
USE exprs1 FOR args1 AND ... AND exprsn FOR argsn IN B
More general form of USE command. See
description of substitution algorithm below.
Every USE command involves three pieces of information: the expressions
to be substituted, the arguments to be substituted for, and an event
specification, which defines the expression (input) in which the
12
substitution takes place. Any expression to be substituted can be
preceded by a !, meaning that the expression is to be substituted as a
segment, e.g., LIST(A B C) followed by USE ! (X Y Z) FOR B will produce
(A X Y Z C), and USE ! NIL FOR B will produce (A C).
If args are omitted, i.e., the form of the command is USE exprs IN B, or
just USE exprs (which is equivalent to USE exprs IN -1), and the event
referred to was itself a USE command, the arguments and expression
substituted into are the same as for the indicated USE command. In
effect, this USE command is thus a continuation of the previous USE
command. For example, on page 22.6, when the user types
(LOG (ANTILOG 4)), followed by USE 4.0 40 400 FOR 4, followed by
USE -40.0 -4.00007 -19., the latter command is equivalent to
USE -40.0 -4.00007 -19. FOR 4 IN -2.
If args are omitted and the event referred to was not a USE command,
substitution is for the operator in that command, i.e., if a lispx
input, the name of the function, if an edit command, the name of the
command. For example ARGLIST(FF) followed by USE CALLS is equivalent to
USE CALLS FOR ARGLIST.
------------------------------------------------------------------------
12
The USE command is parsed by a small finite state parser to
distinguish the expressions and arguments. For example,
USE FOR FOR AND AND AND FOR FOR will be parsed correctly.
22.13
If B is omitted, but args are specified, the first member of args is
used for B, e.g., USE PUTD FOR @UTD is equivalent to USE PUTD FOR @UTD
13
IN F @UTD.
If the USE command has the same number of expressions as arguments, the
14
substitution procedure is straightforward, i.e., USE X Y FOR U V means
substitute X for U and Y for V, and is equivalent to
USE X FOR U AND Y FOR V. However, the USE command also permits
distributive substitutions, i.e., substituting several expressions for
the same argument. For example, USE A B C FOR X means first substitute
A for X then substitute B for X (in a new copy of the expression), then
substitute C for X. The effect is the same as three separate USE
commands. Similarly, USE A B C FOR D AND X Y Z FOR W is equivalent to
USE A FOR D AND X FOR W, followed by USE B FOR D AND Y FOR W, followed
15
by USE C FOR D AND Z FOR W. USE A B C FOR D AND X FOR Y also
corresponds to three substitions, the first with A for D and X for Y,
the second with B for D, and X for Y, and the third with C for D, and
again X for Y. However, USE A B C FOR D AND X Y FOR Z is ambiguous and
will cause an error. Essentially, the USE command operates by
proceeding from left to right handling each "AND" separately. Whenever
the number of expressions exceeds the number of expressions available,
16
the expressions multiply.
FIX B puts the user in the editor looking at a copy of
the input(s) for B, Whenever the user exits via,
OK, the result is unread and reexecuted exactly
as with REDO.
------------------------------------------------------------------------
13
The F is inserted to handle correctly the case where the first
member of args is a number, e.g., USE 4.0 4.0 400 FOR 4. Obviously
the user means find the event containing a 4 and perform the
indicated substitutions, whereas USE 4.0 40 400 FOR 4 IN 4 would
mean perform the substitutions in event number 4.
14
Except when one of the arguments and one of the expressions are the
same, e.g., USE X Y FOR Y X, or USE X FOR Y AND Y FOR X. This
situation is noticed when parsing the command, and handled
correctly.
15
or USE X FOR Y AND A B C FOR D.
16
Thus USE A B C D FOR E F means substitute A for E at the same time
as substituting B for F, then in another copy of the indicated
expression, substitute C for E and D for F. Note that this is also
equivalent to USE A C FOR E AND B D FOR F.
22.14
FIX is provided for those cases when the modifications to the input(s)
are not of the type that can be specified by USE, i.e., not
substitutions. For example:
_(DEFINEQ FOO (LAMBDA (X) (FIXSPELL SPELLINGS2 X 70]
INCORRECT DEFINING FORM
FOO
_FIX
EDIT
*P
(DEFINEQ FOO (LAMBDA & &))
*(LI 2)
OK
(FOO)
_
The user can also specify the edit command(s) to lispx, by typing -
followed by the command(s) after the event specification, e.g., FIX -
(LI 2). In this case, the editor will not type EDIT, or wait for an OK
after executing the commands.
Implementation of REDO, USE, and FIX
The input portion of an event is represented internally on the history
list simply as a linear sequence of the expressions which were read.
For example, an input in apply format is a list consisting of two
expressions, and an input in eval format is a list of just one
17
expression. Thus if the user wishes to convert an input in apply
format to eval format, he simply moves the function name inside of the
argument list:
------------------------------------------------------------------------
17
For inputs in eval format, i.e., single expressions, FIX calls the
editor so that the current expression is that input, rather than the
list consisting of that input - see the example on the preceding
page. However, the entire list is actually being edited. Thus if the
user typed ^ P in that example, he would see ((DEFINEQ FOO &)).
22.15
_MAPC(FOOFNS (F/L (AND (EXPRP X) (PRINT X]
NIL
_EXPRP(FOO1)
T
_FIX MAPC
EDIT
*P
(MAPC (FOOFNS &))
*(BO 2)
*(LI 1)
*P
((MAPC FOOFNS &))
OK
FOO1
FIE2
FUM
NIL
_
By simply converting the input from two expressions to one expression,
the desired effect, that of mapping down the list that was the value of
foofns, was achieved.
REDO, USE, and FIX all operate by obtaining the input portion of the
corresponding event, processing the input (except for REDO), and then
storing it on the history list as the input portion of a new event. The
history command completes operating by simply unreading the input. When
the input is subsequently "reread", the event which already contains the
input will be retrieved and used for recording the value of the
operation, saving side-effects, etc., instead of creating a new event.
Otherwise the input is treated exactly the same as if it had been typed
in directly.
If B specifies more than one event, the inputs for the corresponding
events are simply concatenated into a linear sequence, with special
18
markers (called pseudo-carriage returns) representing carriage returns
inserted between each input to indicate where new lines start. The
result of this concatenation is then treated as the input referred to by
B. For example, when the user typed REDO FROM F ([7] on page 22.2) the
inputs for the corresponding six events were concatenated to produce:
(F PUTD "<c.r.>" (1 MOVD) "<c.r.>" 3 "<c.r.>" (XTR 2) "<c.r.>"
0 "<c.r.>" (SW 2 3)). Similarly, if the user had typed
USE PUTD FOR @UTD IN 15 THRU 20, the above list would have been
constructed, and then PUTD substituted for @UTD throughout it.
The same convention is used for representing multiple inputs when a USE
command involves sequential substitutions. For example, if the user
types GETD(FOO) and then USE FIE FUM FOR FOO, the input sequence that
------------------------------------------------------------------------
18
The value of the variable histstr0 is used to represent a carriage
return. For readability, this value is the string "<c.r.>". Note
that since the comparison is made using eq, this marker will never
be confused with a string that was typed in by the user.
22.16
will be constructed is (GETD (FIE) "<c.r.>" GETD (FUM)), which is the
result of substituting FIE for FOO in (GETD (FOO)) concatenated with the
result of substituting FUM for FOO in (GETD (FOO)).
Once such a multiple input is constructed, it is treated exactly the
same as a single input, i.e., the input sequence is recorded in a new
event, and then unread, exactly as described above. When the inputs are
"reread," the "pseudo-carriage-returns" are treated by lispxread and
readline exactly as real carriage returns, i.e., they serve to
distinguish between apply and eval formats on inputs to lispx, and to
delimit line commands to the editor. Note that once this multiple input
has been entered as the input portion of a new event, that event can be
treated exactly the same as one resulting from type in. In other words,
no special checks have to be made when referencing an event, to see if
it is simple or multiple. Thus, when the user types REDO following
REDO FROM F, ([10] page 22.3) REDO does not even notice that the input
retrieved from the previous event is (F PUTD "<c.r.>" ... (SW 2 3))
i.e., a multiple input, it simply records this input and unreads it.
Similarly, when the user then types USE @UTD FOR PUTD on this multiple
input, the USE command simply carries out the substitution, and the
result is the same as though the user had typed
USE @UTD FOR PUTD IN 15 THRU 20.
In sum, this implementation permits B to refer to a single simple event,
or to several events, or to a single event originally constructed from
several events (which may themselves have been multiple input events,
etc.) without having to treat each case separately.
History Commands Applied to History Commands
Since history commands themselves do not appear in the input portion of
events (although they are stored elsewhere in the event), they do not
interfere with or affect the searching operations of event
specifications. In effect, history commands are invisible to event
19
specifications. As a result, history commands themselves cannot be
recovered for execution in the normal way. For example, if the user
types USE A B C FOR D and follows this with USE E FOR D, he will not
produce the effect of USE A B C FOR E (but instead will simply cause E
to be substituted for D in the last event containing a D). To produce
this effect, i.e., USE A B C FOR E, the user should type
USE D FOR E IN USE. The appearance of the word REDO, USE or FIX in an
event address specifies a search for the corresponding history command.
(For example, the user can also type UNDO REDO.) It also specifies that
the text of the history command itself be treated as though it were the
input. However, the user must remember that the context in which a
history command is reexecuted is that of the current history, not the
original context. For example, if the user types USE FOO FOR FIE IN -1,
and then later types REDO USE, the -1 will refer to the event before the
REDO, not before the USE.
------------------------------------------------------------------------
19
With the exception described below under "History Commands that
Fail".
22.17
History Commands that Fail
The one exception to the statement that "history commands are invisible
to event specifications" occurs when a history command fails to produce
any input. For example, suppose the user types
USE LOG FOR ANTILOG AND ANTILOG FOR LOGG, causing lispx to respond
LOGG ?. Since the USE command did not produce any input, the user can
repair it by typing USE LOG FOR LOGG (i.e., does not have to specify IN
USE). This latter USE command will invoke a search for LOGG, which will
find the bad USE command. lispx then performs the indicated
substitution, and unreads USE LOG FOR ANTILOG AND ANTILOG FOR LOG. In
turn, this USE command invokes a search for ANTILOG, which, because it
was not typed in but reread, ignores the bad USE command which was found
by the earlier search for LOGG, and which is still on the history list.
In other words, history commands that fail to produce input are visible
to searches arising from event specifications typed in by the user, but
not to secondary event specifications.
In addition, if the most recent event is a history command which failed
to produce input, a secondary event specification will effectively back
up the history list one event so that relative event numbers for that
event specification will not count the bad history command. For
example, suppose the user types
USE LOG FOR ANTILOG AND ANTILOG FOR LOGG IN -2 AND -1, and after lispx
types LOGG ?, the user types USE LOG FOR LOGG. He thus causes the
command USE LOG FOR ANTILOG AND ANTILOG FOR LOG IN -2 AND -1 to be
constructed and unread. In the normal case, -1 would refer to the last
event, i.e., the "bad" USE command, and -2 to the event before it.
However, in this case, -1 refers to the event before the bad USE
command, and the -2 to the event before that. In short, the caveat that
"the user must remember that the context in which a history command is
reexecuted is that of the current history, not the original context"
does not apply if the correction is performed immediately.
More History Commands
RETRY B similar to REDO except sets helpclock so that
any errors that occur while executing B will
cause breaks.
... vars similar to USE except substitutes for the
(first) operand.
For example, EXPRP(FOO) followed by ... FIE FUM is equivalent to
USE FIE FUM FOR FOO. See also event 52 on page 22.8.
?? B prints history list. If B is omitted, ?? prints
the entire history list, beginning with most
recent events. Otherwise ?? prints only those
events specified in B (and in the order
specified), e.g., ?? -1, ?? 10 THRU 15, etc.
?? commands are not entered on the history list, and so do not affect
relative event numbers. In other words, an event specification of -1
typed following a ?? command will refer to the event immediately
preceding the ?? command.
22.18
?? will print the history command, if any, associated with each event as
shown at [9] on page 22.3 and page 22.6. Note that these history
commands are not preceded by prompt characters, indicating they are not
20
stored as input.
?? prints multiple input events under one event number (see page 22.6).
Since events are initially stored on the history list with their value
field equal to bell (control-G), if an operation fails to complete for
any reason, e.g., causes an error, is aborted, etc., its "value" will be
bell. This is the explanation for the blank line in event 2, page 22.6,
and event 50, page 22.8.
?? is implemented via the function printhistory, page 22.48, which can
also be called directly by the user.
UNDO B undoes the side effects of the specified events.
For each event undone, UNDO prints a message:
e.g., RPLACA UNDONE, REDO UNDONE etc. If
nothing is undone because nothing was saved,
UNDO types NOTHING SAVED. If nothing was undone
because the event(s) were already undone, UNDO
types ALREADY UNDONE. If B is empty, UNDO
searches back for the last event that contained
side effects, was not undone, and itself was not
21 22
an UNDO command.
UNDO B : x1 ... xn Each xi refers to a message printed by DWIM in
------------------------------------------------------------------------
20
REDO, RETRY, USE, ..., and FIX commands, i.e., those commands that
reexecute previous events, are not stored as inputs, because the
input portion for these events are the expressions to be "reread".
The history commands UNDO, NAME, RETRIEVE, BEFORE, and AFTER are
recorded as inputs, and ?? prints them exactly as they were typed.
21
Note that the user can undo UNDO commands themselves by specifying
the corresponding event address, e.g., UNDO -7 or UNDO UNDO.
22
UNDOing events in the reverse order from which they were executed is
guaranteed to restore all pointers correctly, e.g., to undo all
effects of last five events, perform UNDO THRU -5, not UNDO FROM -5.
Undoing out of order may have unforseen effects if the operations
are dependent. For example, if the user performed (NCONC1 FOO FIE),
followed by (NCONC1 FOO FUM), and then undoes the (NCONC1 FOO FIE),
he will also have undone the (NCONC1 FOO FUM). If he then undoes the
(NCONC1 FOO FUM), he will cause the FIE to reappear, by virtue of
restoring FOO to its state before the execution of (NCONC1 FOO FUM).
For more details, see page 22.34.
22.19
the event(s) specified by B. The side effects
of the corresponding DWIM corrections, and only
those side effects, are undone.
For example, if the message PRINTT [IN FOO] -> PRINT were printed,
23
UNDO : PRINTT or UNDO : PRINT would undo the correction.
$ is a special form of the USE command for conveniently specifying
character substitutions. In addition, it has a number of useful default
options in connection with events that involve errors.
$ x FOR y equivalent to USE $x$ FOR $y$
For example, the user types MOVD(FOO FOOSAVE T), he can then type
$ FIE FOR FOO to perform MOVD(FIE FIESAVE T). Note that USE FIE FOR FOO
would perform MOVD(FIE FOOSAVE T).
An abbreviated form of $ is available:
$ y x same as $ x FOR y, i.e., y's are changed to x's.
can also be written as $ y TO x, $ y = x, or
$ y -> x.
$ does event location the same as the USE command, i.e., if IN -- is not
24
specified, it searches for y.
After $ finds the event, it looks to see if an error was involved in
25
that event, and if the indicated character substitution can be
performed in the offender. If so, $ assumes the substitution refers to
the offender, performs the substitution, and then substitutes the result
for the offender throughout. For example, the user types
(PRETTYDEF FOOFNS 'FOO FOOVARS) causing a U.B.A. FOOOVARS error message.
------------------------------------------------------------------------
23
Some portions of the messages printed by DWIM are strings, e.g., the
message FOO UNSAVED is printed by printing FOO and then " UNSAVED".
Therefore, if the user types UNDO : UNSAVED, the DWIM correction
will not be found. He should instead type UNDO : FOO or
UNDO : $UNSAVED$ (alt-modeUNSAVEDalt-mode, see R command in editor,
Section 9).
24
However, unlike USE, $ can only be used to specify one substitution
at a time.
25
Whenever an error occurs, the object of the error message, called
the offender, is automatically saved on that event's entry in the
history list, under the property *ERROR*.
22.20
The user can now type $ OO O, which will change FOOOVARS to FOOVARS, but
not change FOOFNS or FOO.
If an error did occur in the specified event, the user can also omit
specifying y, in which case the offender is used. Thus, the user could
have corrected the above example by simply typing $ FOOVARS. Similarly,
if the user types LOAD(PRSTRUC PROP), causing the error
FILE NOT FOUND PRSTRUC, he can request the file to be loaded from LISP's
directory by simply typing $ <LISP>$. Since esubst is used for
substituting, this is equivalent to performing (R PRSTRUC <LISP>$) on
the event, and therefore replaces PRSTRUC by <LISP>PRSTRUC (see
Section 9). Note also the usefulness of $ '$, meaning: put a ' in front
of the offender.
$ also works for events in the editor. For example, if the user types
(MOVE COND 33 2 TO BEFORE HERE), and editor types 33 ?, the user can
type $ 3, causing 3 to be substituted for 33 in the MOVE command.
Finally, the user can omit both x and y. This specifies that two alt-
modes be packed onto the end of the offender, and the result substituted
throughout the specified event. For example, suppose the user types to
the editor (MOVE 3 2 TO AFTER CONDD 1), and gets the error message
CONDD ?. because the find command failed to find CONDD. $ will cause
the edit command (MOVE 3 2 TO AFTER CONDD$$ 1) to be executed, which
will search for an atom that is "close" to CONDD in the sense used by
26
the spelling corrector (see pattern type 6b, Section 9).
Note that $ never searches for an error. Thus, if the user types
LOAD(PRSTRUC PROP) causing a FILE NOT FOUND error, types CLOSEALL(), and
then types $ <LISP>$, lispx will complain that there is no error in
CLOSEALL(). In this case, the user would have to type
$ <LISP>$ IN LOAD, or $ PRS <LISP>PRS (which would cause a search for
PRS).
Note also that $ operates on input, not on programs. If the user types
FOO(), and within the call to FOO gets a U.D.F. CONDD error, he cannot
repair this by $ COND. lispx will type CONDD NOT FOUND IN FOO().
NAME atom B saves the event(s) (including side effects)
specified by B on the property list of atom
(under the property HISTORY) e.g., NAME FOO 10
THRU 15. NAME commands are undoable.
Commands defined by NAME can also be typed in directly as though they
------------------------------------------------------------------------
26
The same effect could be achieved by $ COND, which specifies
substituting COND for CONDD, but not by $ CONDD$$, since the latter
is equivalent to performing (R CONDD CONDD$$) on the event, which
would result in CONDDCONDDCONDD being substituted for CONDD (as
described in Section 9).
22.21
27
were built-in commands, e.g., FOOC is equivalent to REDO @ FOO.
Commands defined by NAME can also be parameterized, i.e., be defined to
take arguments:
NAME name (args) : B args are interpreted the same as the arguments
or for a USE command. See page 22.13. When name
NAME name ... args ... : B
is invoked, the argument values are substituted
for args using the same substitution algorithm
as for USE.
For example, following the event (PUTD 'FOO (COPY (GETP 'FIE 'EXPR))),
the user types NAME MOVE FOO FIE : PUTD. Then typing MOVE TEST1 TEST2
would cause (PUTD 'TEST1 (COPY (GETP 'TEST2 'EXPR))) to be executed,
i.e., would be equivalent to typing USE TEST1 TEST2 FOR FOO FIE IN @
MOVE. Typing MOVE A B C D would cause two PUTD's to be executed. Note
that !'s and $'s can also be employed the same as with USE. For
example, if following
_PREPINDEX(<MANUAL>14LISP.XGP)
_FIXFILE(<MANUAL>14LISP.XGPIDX)
the user performed NAME FOO $14$ : -2 AND -1, then FOO $15$ would
28
perform the indicated two operations with 14 replaced by 15.
RETRIEVE atom Retrieves and reenters on the history list the
events named by atom. Causes an error if atom
was not named by a NAME command.
For example, if the user performs NAME FOO 10 THRU 15, and at some time
later types RETRIEVE FOO, 6 new events will be recorded on the history
list (whether or not the corresponding events have been forgotten yet).
Note that RETRIEVE does not reexecute the events, it simply retrieves
them. The user can then REDO, UNDO, FIX, etc. any or all of these
events. Note that the user can combine the effects of a RETRIEVE and a
subsequent history command in a single operation by using an event
specification of the form @ atom, as described on page 22.12, e.g.,
REDO @ FOO is equivalent to RETRIEVE FOO, followed by an appropriate
------------------------------------------------------------------------
27
However, if FOO is the name of a variable, it would be evaluated,
i.e., FOOC would return the value of FOO.
28
NAME FOO B is equivalent to NAME FOO : B. In either case, if FOO is
invoked with arguments, an error is generated.
22.22
29
REDO. Note that UNDO @ FOO and ?? @ FOO are permitted.
BEFORE atom undoes the effects of the events named by atom.
AFTER atom undoes a BEFORE atom.
BEFORE/AFTER provide a convenient way of flipping back and forth between
two states, namely that state before a specified event or events were
executed, and that state after execution. For example, if the user has
a complex data structure which he wants to be able to interrogate before
and after certain modifications, he can execute the modifications, name
the corresponding events with the NAME command, and then can turn these
30
modifications off and on via BEFORE or AFTER commands. Both BEFORE and
AFTER are NOPs if the atom was already in the corresponding state; both
generate errors if atom was not named by a NAME command.
Note: since UNDO, NAME, RETRIEVE, BEFORE, and AFTER are recorded as
inputs they can be referenced by REDO, USE, etc. in the normal way.
However, the user must again remember that the context in which the
command is reexecuted is different than the original context. For
example, if the user types NAME FOO DEFINEQ THRU COMPILE, then
types ... FIE, the input that will be reread will be
NAME FIE DEFINEQ THRU COMPILE as was intended, but both DEFINEQ and
COMPILE, will refer to the most recent event containing those atoms,
namely the event consisting of NAME FOO DEFINEQ THRU COMPILE!
ARCHIVE B records the events specified by B on a permanent
history list. This history list can be
referenced by preceding a standard event
specification with @@, e.g., ?? @@ prints the
archived history list, REDO @@ -1 will recover
the corresponding event from the archived
history list and redo it, etc.
The user can also provide for automatic archiving of selected events by
appropriately defining archivefn, or by putting the property *ARCHIVE*,
value T, on the event. Events that are referenced by history commands
are automatically marked for archiving in this fashion. For more
details, see page 22.28.
------------------------------------------------------------------------
29
Actually, REDO @ FOO is better than RETRIEVE followed by REDO since
in the latter case, the corresponding events would be entered on the
history list twice, once for the RETRIEVE and once for the REDO.
30
The alternative to BEFORE/AFTER for repeated switching back and
forth involves UNDO, UNDO of the UNDO, UNDO of that etc. At each
stage, the user would have to locate the correct event to undo, and
furthermore would run the risk of that event being "forgotten" if he
did not switch at least once per time-slice.
22.23
FORGET B permanently erases the record of the side
effects for the events specified by B. If B is
omitted, forgets side effects for entire history
list.
FORGET is provided for users with space problems. For example, if the
user has just performed sets, rplacas, rplacds, putd, remprops, etc. to
release storage, the old pointers would not be garbage collected until
the corresponding events age sufficiently to drop off the end of the
history list and be forgotten. FORGET can be used to force immediate
forgetting (of the side-effects only). FORGET is not undoable
(obviously).
22.5 Miscellaneous Features and Commands
TYPE-AHEAD is a command that allows the user to type-ahead
an indefinite number of inputs.
The assistant responds to TYPE-AHEAD with a prompt character of >. The
user can now type in an indefinite number of lines of input, under
errorset protection. The input lines are saved and unread when the user
exits the type-ahead loop with the command $GO (alt-modeGO). While in
the type-ahead loop, ?? can be used to print the type-ahead, FIX to edit
the type-ahead, and $Q to erase the last input (may be used repeatedly).
For example:
22.24
_TYPE-AHEAD
>SYSOUT(TEM)
>MAKEFILE(EDIT)
>BRECOMPILE((EDIT WEDIT))
>F
>$Q
\\F
>$Q
\\BRECOMPILE
>LOAD(WEDIT PROP)
>BRECOMPILE((EDIT WEDIT))
>F
>MAKEFILE(BREAK)
>LISTFILES(EDIT BREAK)
>SYSOUT(CURRENT)
>LOGOUT]
>??
>SYSOUT(TEM)
>MAKEFILE(EDIT)
>LOAD(WEDIT PROP)
>BRECOMPILE((EDIT WEDIT))
31
>F
>MAKEFILE(BREAK)
>LISTFILES(EDIT BREAK)
>SYSOUT(CURRENT)
>LOGOUT]
>FIX
EDIT
*(R BRECOMPILE BCOMPL)
*P
((LOGOUT) (SYSOUT &) (LISTFILES &) (MAKEFILE &) (F) (BCOMPL &)
(LOAD &) (MAKEFILE &) (SYSOUT &))
*(DELETE LOAD)
*OK
>$GO
The TYPE-AHEAD command may be aborted by $STOP; control-E simply aborts
the current line of input.
$BUFS (alt-modeBUFS) is a command for recovering the input buffers.
Whenever an error occurs in executing a lispx input or edit command, or
a control-E or control-D is typed, the input buffers are saved and
cleared. The $BUFS command is used to restore the input buffers, i.e.,
its effect is exactly the same as though the user had retyped what was
"lost." For example:
------------------------------------------------------------------------
31
Note that type-ahead can be addressed to the compiler, since it uses
lispxread for input. Type-ahead can also be directed to the editor,
but type-ahead to the editor and to lispx cannot be intermixed.
22.25
*(-2 (SETQ X (COND ((NULL Z) (CONS (user typed control-E)
*P
(COND (& &) (T &))
*2
*$BUFS
(-2 (SETQ X (COND ((NULL Z) (CONS
and user can now finish typing the (-2 ..) command.
Note: the type-ahead does not have to have been seen by INTERLISP, i.e.,
echoed, since the system buffer is also saved.
Input buffers are not saved on the history list, but on a free variable.
Thus, only the contents of the input buffer as of the last clearbuf can
ever be recovered. However, input buffers cleared at evalqt are saved
independently from those cleared by break or the editor. The procedure
followed when the user types $BUFS is to recover first from the local
32
buffer, otherwise from the top level buffer. Thus the user can lose
input in the editor, go back to evalqt, lose input there, then go back
into the editor, recover the editor's buffer, etc. Furthermore, a
buffer cleared at the top can be recovered in a break, and vice versa.
The following four commands, DO, !F, !E, and !N, are only recognized in
the editor:
DO com allows the user to supply the command name when
it was omitted. (USE is used when a command
name is incorrect).
For example, suppose the user wants to perform
(-2 (SETQ X (LIST Y Z))) but instead types just (SETQ X (LIST Y Z)).
The editor will type SETQ ?, whereupon the user can type DO -2. The
effect is the same as though the user had typed FIX, followed by
(LI 1), (-1 -2), and OK, i.e., the command (-2 (SETQ X (LIST Y Z))) is
executed. DO also works if the last command is a line command.
!F same as DO F.
In the case of !F, the previous command is always treated as though it
were a line command, e.g., if the user types (SETQ X &) and then !F, the
effect is the same as though he had typed F (SETQ X &), not
(F (SETQ X &)).
------------------------------------------------------------------------
32
The local buffer is stored on lispxbufs; the top level buffer on
toplispxbufs. The forms of both buffers are (CONS (LINBUF) (SYSBUF))
(see Section 14). Recovery of a buffer is destructive, i.e., $BUFS
sets the corresponding variable to NIL. If the user types $BUFS when
both lispxbufs and toplispxbufs are NIL, the message NOTHING SAVED
is typed, and an error generated.
22.26
!E same as DO E. Note !E works correctly for
"commands" typed in eval or apply format.
!N same as DO N.
control-U when typed in at any point during an input being
read by lispxread, permits the user to edit the
input before it is returned to the calling
function.
This feature is useful for correcting mistakes noticed in typing before
the input is executed, instead of waiting till after execution and then
performing an UNDO and a FIX. For example, if the user types
(DEFINEQ (FOO (LAMBDA (X) (FIXSPELL X and at that point notices the
missing left parenthesis, instead of completing the input and allowing
the error to occur, and then fixing the input, he can simply type
33
control-U, finish typing normally, whereupon the editor is called on
(FOO (LAMBDA (X) (FIXSPELL X -- ], which the user can then repair, e.g.,
by typing (LI 1). If the user exits from the editor via OK, the
(corrected) expression will be returned to whoever called lispxread
34
exactly as though it had been typed. If the user exits via STOP, the
expression is returned so that it can be stored on the history list.
However it will not be executed. In other words, the effect is the same
as though the user had typed control-E at exactly the right instant.
valueof[x] is an nlambda function for obtaining the value
35
of a particular event, e.g., (VALUEOF -1),
(VALUEOF _FOO -2).
The value of an event consisting of several
operations is a list of the values for each of
the individual operations.
------------------------------------------------------------------------
33
Control-U can be typed at any point, even in the middle of an atom;
it simply sets a variable checked by lispxread.
34
Control-U also works for calls to readline, i.e., for line commands.
35
Although the input for valueof is entered on the history list before
valueof is called, valueof[-1] still refers to the value of the
expression immediately before the valueof input, because valueof
effectively backs the history list up one entry when it retrieves
the specified event. Similarly, (VALUEOF FOO) will find the first
event before this one that contains a FOO.
22.27
Note: the value field of a history entry is initialized to bell
(control-G). Thus a value of bell indicates that the corresponding
operation did not complete, i.e., was aborted or caused an error (or
else returned bell).
prompt#flg is a flag which when set to T causes the current
event number to be printed before each _,: and *
prompt characters. See description of
promptchar, page 22.42.
prompt#flg is initially NIL.
archivefn allows the user to specify events to be
automatically archived.
When archivefn is set to T, and an event is about to drop off the end of
the history list and be forgotten, archivefn is called giving it as its
first argument the input portion of the event, and as its second
36
argument, the entire event. If archivefn returns T, the event is
archived. For example, some users like to keep a record of all calls to
load. Defining archivefn as: (LAMBDA (X Y) (EQ (CAR X) (QUOTE LOAD)))
will accomplish this. Note that archivefn must be both set and defined.
archivefn is initially NIL and undefined.
The user can also specify that a particular event be archived when it is
about to drop off the end of the history list by putting the property
*ARCHIVE*, value T, on the event, e.g., by means of an appropriately
defined lispxuserfn (see below). One use of this feature is that the
system automatically adds the *ARCHIVE* property to all events that are
37
referenced by history commands. Thus once an event is redone, it is
guaranteed to be saved.
lispxmacros provides a macro facility for lispx.
lispxmacros allows the user to define his own lispx commands. It is a
list of elements of the form (command def). Whenever command appears as
the first expression on a line in a lispx input, the variable lispxline
is bound to the rest of the line, the event is recorded on the history
list, and def is evaluated. Similarly, whenever command appears as car
of a form in a lispx input, the variable lispxline is bound to cdr of
the form, the event recorded, and def is evaluated. (See page 22.48
for an example of a lispxmacro). RETRIEVE, BEFORE, and AFTER are
------------------------------------------------------------------------
36
In case archivefn needs to examine the value of the event, its side
effects, etc. See page 22.36 for discussion of the format of
history lists.
37
unless archiveflg=NIL. archiveflg is initially T.
22.28
implemented as lispxmacros. In addition in INTERLISP-10, LISP, SNDMSG,
TECO, and EXEC are lispxmacros which perform the corresponding calls to
subsys (Section 21), and CONTIN is a lispxmacro which performs
(SUBSYS T). SY is a lispxmacro which performs the SYSTAT command.
Finally, DIR is a lispxmacro which calls the function directory, e.g.,
DIR *.COM;* lists all compiled files. (For more details, see Section
21.)
lispxhistorymacros provides a macro facility for history commands.
lispxhistorymacros allows the user to define his own history commands.
The format of lispxhistorymacros is the same as that of lispxmacros,
except that the result of evaluating def is treated as a list of
expressions to be unread, exactly as though the expressions had been
38
retrieved by a REDO command, or computed by a USE command.
lispxuserfn provides a way for a user function to process
selected inputs.
39
When lispxuserfn is set to T, it is applied to all inputs not
recognized as one of the commands described above. If lispxuserfn
decides to handle this input, it simply processes it (the event was
already stored on the history list before lispxuserfn was called), sets
lispxvalue to the value for the event, and returns T. lispx will then
know not to call eval or apply, and will simply store lispxvalue into
the value slot for the event, and print it. If lispxuserfn returns NIL,
lispx proceeds by calling eval or apply in the usual way. Thus by
appropriately defining (and setting) lispxuserfn, the user can with a
minimum of effort incorporate the features of the programmer's assistant
into his own executive (actually it is the other way around).
------------------------------------------------------------------------
38
See page 22.15 for discussion of implementation of REDO, USE, and
FIX.
39
Like archivefn, lispxuserfn must be both set and defined.
22.29
40
The following output illustrates such a coupling.
**SETQ(ALTFORM (MAPCONC NASDIC (F/L (GETP X 'ALTFORMS] [1]
=NASDICT
(AL26 BE7 CO56 CO57 CO60 C13 H3 MN54 NA22 SC46 S34 TI44)
**(GIVE ME LINES CONTAINING COBALT) [2]
SAMPLE PHASE CONSTIT. CONTENT UNIT CITATION TAG
S10002 OVERALL C056 40.0 DPM/KG D70-237 0
C13 8.8 DEL D70-228 0
H3 314.0 DPM/KG
MN54 28
**GETP(COBALT ALTFORMS) [3]
(CO56 CO57 CO60 C13 H3 MN54 NA22 SC46 S34 T144)
**UNDO MAPCONC [4]
SETQ UNDONE.
**REDO GETP [5]
(CO56 CO57 CO60)
**REDO COBALT [6]
SAMPLE PHASE CONSTIT. CONTENT UNIT CITATION TAG
S10002 OVERALL CO57 40.0 DPM/KG D70-237 0
S10003 OVERALL CO 15.0 D70-203 0
14.1 D70-216
CO56 43.0 DPM/KG D70-237 0
CO57 43.0 D70-241 0
CO60 1.0
**USE MANGANESE FOR COBALT
The user is running under his own executive program which accepts
requests in the form of sentences, which it first parses and then
executes. The user first "innocently" computes a list of all
ALTERNATIVE-FORMS for the elements in his system [1]. He then inputs a
request in sentence format [2] expecting to see under the column
CONSTIT. only cobalt, CO, or its alternate forms, CO56, CO57, or CO60.
Seeing C13, H3, and MN54, he aborts the output, and checks the property
ALTFORMS for COBALT [3]. The appearance of C13, H3, MN54, he aborts the
output, and checks the property ALTFORMS for COBALT [3]. The appearance
of C13, H3, MN54 et al, remind him that the mapconc is destructive, and
that in the process of making a list of the ALTFORMS, he has
inadvertently strung them all together. Recovering from this situation
would require him to individually examine and correct the ALTFORMs for
each element in his dictionary, a tedious process. Instead, he can
simply UNDO MAPCONC, [4] check to make sure the ALTFORM has been
corrected [5], then redo his original request [6] and continue. The
UNDO is possible because the first input was executed by lispx; the
(GIVE ME LINES CONTAINING COBALT) is possible because the user defined
lispxuserfn appropriately; and the REDO and USE are possible because the
(GIVE ME LINES CONTAINING COBALT) was stored on the history list before
it was transmitted to lispxuserfn and the user's parsing program.
------------------------------------------------------------------------
40
The output is from the Lunar Sciences Natural Language Information
System being developed for the NASA Manned Spacecraft Center by
William A. Woods of Bolt Beranek and Newman Inc., Cambridge, Mass.
22.30
lispxuserfn is a function of two arguments, x and line, where x is the
first expression typed, and line the rest of the line, as read by
readline (see page 22.39). For example, if the user types FOO(A B C),
x=FOO, and line=((A B C)); if the user types (FOO A B C), x=(FOO A B C),
and line=NIL; and if the user types FOO A B C, x=FOO and line=(A B C).
Thus in the above example, lispxuserfn would be defined as:
[LAMBDA (X LINE)
(COND
((AND (NULL LINE)
(LISTP X))
(SETQ LISPXVALUE (PARSE X))
T]
Note that since lispxuserfn is called for each input (except for p.a.
commands), it can also be used to monitor some condition or gather
statistics.
In addition to saving inputs and values, lispx saves most system
messages on the history list, e.g., FILE CREATED --
, (fn REDEFINED), (var RESET), output of
TIME, BREAKDOWN, STORAGE, DWIM messages, etc. When printhistory prints
the event, this output is replicated. This facility is implemented via
the functions lispxprint, lispxprin1, lispxprin2, lispxspaces,
41
lispxterpri, and lispxtab. In addition to performing the corresponding
output operation, these functions store an appropriate expression on the
42
history event under the property *LISPXPRINT*. This expression is used
by printhistory to reproduce the output.
In addition to the above features, lispx checks to see if car or cdr of
NIL or car of T have been clobbered, and if so, restores them and prints
a message. Lispx also performs spelling corrections using lispxcoms, a
list of its commands, as a spelling list whenever it is given an unbound
atom or undefined function, i.e., before attempting to evaluate the
------------------------------------------------------------------------
41
The function userlispxprint is available to aid the user in defining
additional lispxprinting functions for already existing printing
functions. The user can define a lispxprinting function by simply
giving it the definition of userlispxprint, e.g.,
MOVD(USERLISPXPRINT LISPXPRINTDEF), as long as the new function name
is formed by adding "LISPX" to the front of the name of an existing
printing function, and that this function takes three or fewer
arguments. userlispxprint is defined to look back on the stack, find
the name of the calling function, strip off the leading "LISPX",
perform the appropriate saving information, and then call the
function to do the actual printing.
42
unless lispxprintflg is NIL.
22.31
43
input.
22.6 Undoing
The UNDO capability of the programmer's assistant is implemented by
requiring that each operation that is to be undoable be responsible
itself for saving on the history list enough information to enable
reversal of its side effects. In other words, the assistant does not
"know" when it is about to perform a destructive operation, i.e., it is
not constantly checking or anticipating. Instead, it simply executes
operations, and any undoable changes that occur are automatically saved
44
on the history list by the responsible function. The operation of
UNDOing, which involves recovering the saved information and performing
the corresponding inverses, works the same way, so that the user can
UNDO an UNDO, and UNDO that etc.
At each point, until the user specifically requests an operation to be
undone, the assistant does not know, or care, whether information has
been saved to enable the undoing. Only when the user attempts to undo
an operation does the assistant check to see whether any information has
been saved. If none has been saved, and the user has specifically named
the event he wants undone, the assistant types NOTHING SAVED. (When the
user simply types UNDO, the assistant searches for the last undoable
event, ingnoring events already undone as well as UNDO operations
themselves.)
This implementation minimizes the overhead for undoing. Only those
operations which actually make changes are affected, and the overhead is
small: two or three cells of storage for saving the information, and an
extra function call. However, even this small price may be too
expensive if the operation is sufficiently primitive and repetitive,
i.e., if the extra overhead may seriously degrade the overall
------------------------------------------------------------------------
43
lispx is also responsible for rebinding helpclock, used by
breakcheck, Section 16, for computing the amount of time spent in a
computation, in order to determine whether to go into a break if and
when an error occurs.
44
When the number of changes that have been saved exceeds the value of
#undosaves (initially set to 50), the user is asked if he wants to
continue saving the undo information for this event. The purpose of
this feature is to avoid tying up large quantities of storage for
operations that will never need to be undone. The interaction is
handled by the same routines used by DWIM, so that the input buffers
are first saved and cleared, the message typed, then the system
waits dwimwait seconds, and if there is no response, assumes the
default answer, which in this case is NO. Finally the input buffers
are restored. See page 22.45 for details.
22.32
45
performance of the program. Hence not every destructive operation in a
program should necessarily be undoable; the programmer must be allowed
to decide each case individually.
Therefore for each primitive destructive operation, we have implemented
two separate functions, one which always saves information, i.e., is
always undoable, and one which does not, e.g., /rplaca and rplaca, /put
46
and put. In the various system packages, the appropriate function is
used. For example, break uses /putd and /remprop so as to be undoable,
47
and DWIM uses /rplaca and /rplacd, when it makes a correction.
Similarly the user can simply use the corresponding / function if he
wants to make a destructive operation in his own program undoable. When
the / function is called, it will save the undo information in the
current event on the history list.
However, all operations that are typed in to lispx are made undoable,
48
simply by substituting the corresponding / function for any
49
destructive function throughout the input. For example, on page 22.7,
------------------------------------------------------------------------
45
The rest of the discussion applies only to lispx; the editor handles
undoing itself in a slightly different fashion, as described on page
22.49.
46
The "slash" functions currently implemented are /addprop, /attach,
/closer, /control, /deletecontrol, /dremove, /dreverse, /dsubst,
/echocontrol, /echomode, /lconc, /listput, /listput1, /mapcon,
/mapconc, /movd, /nconc, /nconc1, /putassoc, /putd, /putdq,
/puthash, /putprop, /remprop, /rplaca, /rplacd, /rplnode, /rplnode2,
/set, /seta, /setbrk, /setd, /setproplist, /setsepr, /setsyntax,
/settopval, and /tconc. Note that /setq and /setqq are not included.
If the user wants a set operation undoable in his program, he must
see /set, or /rplaca.
47
The effects of the following functions are always undoable
(regardless of whether or not they are typed in): define, defineq,
defc (used to give a function a compiled code definition), deflist,
load, savedef, unsavedef, break, unbreak, rebreak, trace, breakin,
unbreakin, changename, editfns, editf, editv, editp, edite, editl,
esubst, advise, unadvise, readvise, plus any changes caused by DWIM.
48
Since there is no /setq, setq's appearing in type-in are handled
specially by substituting a call to saveset, page 22.35.
49
The substitution is performed by the function lispx/, described on
page 22.46.
22.33
when the user typed (MAPCONC NASDIC (F/L ...)) it was
(/MAPCONC NASDIC (F/L ...)) that was evaluated. Since the system cannot
know whether efficiency and overhead are serious considerations for the
execution of an expression in a user program, the user must decide,
e.g., call /mapconc if he wants the operation undoable. However,
expressions that are typed-in rarely involve iterations or lengthy
computations directly. Therefore, if all primitive destructive
functions that are immediately contained in a type-in are made undoable,
there will rarely be a significant loss of efficiency. Thus lispx scans
all user input before evaluating it, and substitutes the corresponding
undoable function for all primitive destructive functions. Obviously
with a more sophisticated analysis of both user input and user programs,
the decision concerning which operations to make undoable could be
better advised. However, we have found the configuration described here
to be a very satisfactory one. The user pays a very small price for
being able to undo what he types in, and if he wishes to protect himself
from malfunctioning in his own programs, he can have his program
specifically call undoable functions, or go into testmode as described
next.
Testmode
Because of efficiency considerations, the user may not want certain
functions undoable after his program becomes operational. However,
while debugging he may find it desirable to protect himself against a
program running wild, by making primitive destructive operations
undoable. The function testmode provides this capability by temporarily
making everything undoable.
testmode[flg] testmode[T] redefines all primitive destructive
50
functions with their corresponding undoable
versions and sets testmodeflg to T. testmode[]
restores the original definitions, and sets
51
testmodeflg to NIL.
Note that setq's are not undoable, even in testmode. To make the
corresponding operation undoable in testmode, set or rplaca should be
used.
Undoing Out of Order
/rplaca and /rplacd operate by saving the pointer that is to be changed
and its original contents (i.e., /rplaca saves car and /rplacd saves
------------------------------------------------------------------------
50
i.e., the "slash" functions; see footnote on page 22.33.
51
testmode will have no effect on compiled mapconc's, since they
compile open with frplacd's.
22.34
cdr). Undoing /rplaca and /rplacd simply restores the pointer. Thus,
if the user types (RPLACA FOO 1), followed by (RPLACA FOO 2), then
undoes both events by undoing the most recent event first, then undoing
the older event, FOO will be restored to its state before either rplaca
operated. However if the user undoes the first event, then the second
event, (CAR FOO) will be 1, since this is what was in car of FOO before
(RPLACA FOO 2) was executed. Similarly, if the user performs
(NCONC1 FOO 1) then (NCONC1 FOO 2), undoing just (NCONC1 FOO 1) will
remove both 1 and 2 from FOO. The problem in both cases is that the two
operations are not "independent." In general, operations are always
independent if they affect different lists or different sublists of the
52
same list. Undoing in reverse order of execution, or undoing
independent operations, is always guaranteed to do the "right" thing.
However, undoing dependent operations out of order may not always have
the predicted effect.
Saveset
Setq's are made undoable on type in by substituting a call to saveset
(described in detail on page 22.44), whenever setq is the name of the
function to be applied, or car of the form to be evaluated. In addition
to saving enough information on the history list to enable undoing,
saveset operates in a manner analogous to savedef when it resets a top
level value, i.e., when it changes a top level binding from a value
other than NOBIND to a new value that is not equal to the old one. In
this case, saveset saves the old value of the variable being set on the
variable's property list under the property VALUE, and prints the
message (variable RESET). The old value can be restored via the
53
function unset, which also saves the current value (but does not print
a message). Thus unset can be used to flip back and forth between two
values.
rpaq and rpaqq are implemented via calls to saveset. Thus old values
will be saved and messages printed for any variables that are reset as
------------------------------------------------------------------------
52
Property list operations, (i.e., put, addprop and remprop) are
handled specially so that they are always independent, even when
they affect the same property list. For example, if the user types
PUT(FOO FIE1 FUM1) then PUT(FOO FIE2 FUM2), then undoes the first
event, the FIE2 property will remain, even though CDR(FOO) may have
been NIL at the time the first event was executed.
53
Of course, UNDO can be used as long as the event containing this
call to saveset is still active. Note however that the old value
will remain on the property list, and therefore be recoverable via
unset, even after the original event has been forgotten.
22.35
54
the result of loading a file. Calls to set and setqq appearing in type
in are also converted to appropriate calls to saveset.
For top level variables, saveset also adds the variable to the
appropriate spelling list, thereby noticing variables set in files via
rpaq or rpaqq, as well as those set via type in.
Undonlsetq and Resetundo
The function undonlsetq provides a limited form of backtracking: if an
error occurs under the undonlsetq, all undoable side effects executed
under the undonlsetq are undone. resetundo, for use in conjunction with
resetlst and resetsave (Section 5), provides a more general undo
capability in that the user can specify that the side effects be undone
after the specified computation finishes, is aborted by an error, or by
a control-D. undonlsetq and resetundo are described in detail on page
22.47.
22.7 Format and Use of the History List
There are currently two history lists, lispxhistory and edithistory.
Both history lists have the same format, and in fact, each use the same
function, historysave, for recording events, and the same set of
functions for implementing commands that refer to the history list,
55
e.g., historyfind, printhistory, undosave, etc.
Each history list is a list of the form (l event# size mod), where l is
the list of events with the most recent event first, event# is the event
number for the most recent event on l, size is the size of the time-
slice, i.e., the maximum length of l, and mod is the highest possible
event number (see footnote on page 22.7). lispxhistory and edithistory
are both initialized to (NIL 0 30 100). Setting lispxhistory or
edithistory to NIL is permitted, and simply disables all history
features, i.e., lispxhistory and edithistory act like flags as well as
repositories of events.
Each individual event on l is a list of the form
(input id value . props), where input is the input sequence for the
event, as described on page 22.15-17, id the prompt character,
------------------------------------------------------------------------
54
To complete the analogy with define, saveset will not save old
values on property lists if dfnflg=T, e.g., when load is called with
second argument T, (however, the call to saveset will still be
undoable,) and when dfnflg=ALLPROP, the value is stored directly on
the property list under property VALUE (the latter applies only to
calls from rpaqq and rpaq).
55
A third history list, archivelst, is used when events are archived,
as described on page 22.23. It too uses the same format.
22.36
56
e.g., _, :, *, and value is the value of the event, and is initialized
57
to bell.
props is a property list, i.e., of the form (property value property
value --). props can be used to associate arbitrary information with a
particular event. Currently, the properties SIDE, *ARCHIVE*, *GROUP*,
*HISTORY*, *PRINT*, USE-ARGS, ...ARGS, *ERROR*, *CONTEXT* and
*LISPXPRINT* are being used. The value of property SIDE is a list of
the side effects of the event. (See discussion of undosave, page
22.45, and undolispx, page 22.47.) The *HISTORY* and *GROUP*
properties are used for commands that reexecute previous events, i.e.,
REDO, RETRY, USE, ..., and FIX. The value of the *HISTORY* property is
the history command itself, i.e., what the user actually typed, e.g.,
REDO FROM F, and is used by the ?? command for printing the event. The
value of the property *PRINT* is also for use by the ?? command, when
special formatting is required, for example, in printing events
corresponding to the break commands OK, GO, EVAL, and ?=. USE-ARGS and
...ARGS are used to save the arguments and expression for the
corresponding history command. *ERROR* and *CONTEXT* are used to save
information when errors occur for subsequent use by the $ and ?
commands. The property *ARCHIVE* on an event causes the event to be
automatically archived when it "falls off the end" of the history list
(see page 22.28). *LISPXPRINT* is used to record calls to lispxprint,
lispxprin1, et al, (see page 22.31).
When lispx is given an input, it calls historysave to record the input
58
in a new event. Normally, historysave returns as its value the new
event. lispx binds lispxhist to the value of historysave, so that when
the operation has completed, lispx knows where to store the value,
59
namely in caddr of lispxhist. lispxhist also provides access to the
------------------------------------------------------------------------
56
id is one of the arguments to lispx and to historysave. A user can
call lispx giving it any prompt character he wishes (except for *,
since in certain cases, lispx must use the value of id to tell
whether or not it was called from the editor.) For example, on page
22.30, the user's prompt character was **.
57
On edithistory, this field is used to save the side effects of each
command.
58
The commands ??, FORGET, TYPE-AHEAD, $BUFS, and ARCHIVE are executed
immediately, and are not recorded on the history list.
59
Note that by the time it completes, the operation may no longer
correspond to the most recent event on the history list. For
example, all inputs typed to a lower break will appear later on the
history list.
22.37
property list for the current event. For example, the / functions are
all implemented to call undosave, which simply adds the corresponding
information to lispxhist under the property SIDE, or if there is no
property SIDE, creates one, and then adds the information.
After binding lispxhist, lispx executes the input, stores its value in
caddr of lispxhist, prints the value, and returns.
When the input is a REDO, RETRY, USE, ..., or FIX command, the procedure
is similar, except that the event is also given a *GROUP* property,
initially NIL, and a *HISTORY* property, and lispx simply unreads the
input and returns. When the input is "reread", it is historysave, not
lispx, that notices this fact, and finds the event from which the input
60
originally came. historysave then adds a new (input id value . props)
entry to the *GROUP* property for this event, and returns this entry as
the "new event." lispx then proceeds exactly as when its input was typed
directly, i.e., it binds lispxhist to the value of historysave, executes
the input, stores the value in caddr of lispxhist, prints the value, and
returns. In fact, lispx never notices whether it is working on freshly
typed input, or input that was reread. Similarly, undosave will store
undo information on lispxhist under the property SIDE the same as
always, and does not know or care that lispxhist is not the entire
event, but one of the elements of the *GROUP* property. Thus when the
event is finished, its entry will look like:
(input id value *HISTORY* command *GROUP* ((input1 id1
value1 SIDE side1)
(input2 id2
value2 SIDE side2)
61
...))
This implementation removes the burden from the function calling
historysave of distinguishing between new input and reexecution of input
62
whose history entry has already been set up.
------------------------------------------------------------------------
60
If historysave cannot find the event, for example if a user program
unreads the input directly, and not via a history command,
historysave proceeds as though the input were typed.
61
In this case, the value field is not being used; valueof instead
collects each of the values from the *GROUP* property, i.e., returns
mapcar[get[event;*GROUP*];CADDR]. Similarly, undo operates by
collecting the SIDE properties from each of the elements of the
*GROUP* property, and then undoing them in reverse order.
62
Although we have not yet done so, this implementation, i.e., keeping
the various "sub-events" separate with respect to values and
properties, also permits constructing commands for operating on just
one of the sub-events.
22.38
22.8 Lispx and readline
lispx is called with the first expression typed on a line as its first
argument, lispxx.
If this is not a list, lispx always does a readline, and treats lispxx
plus the line as the input for the event, and stores it accordingly on
63
the history list. Then it decides what to do with the input, i.e., if
it is not recognized as a command, a lispxmacro, or is processed by
64
lispxuserfn, call eval or apply. readline normally is terminated
either by (1) a carriage return that is not preceded by a space, or (2)
a list that is terminated by a ], or (3) an unmatched ) or ], which is
not included in the line. However, when called from lispx, readline
operates differently in two respects:
(1) If the line consists of a single ) or ], readline returns (NIL)
instead of NIL, i.e., the ) or ] is included in the line. This
permits the user to type FOO) or FOO], meaning call the
function' FOO with no arguments, as opposed to FOOC
(FOOcarriage-return), meaning evaluate the variable FOO.
(2) If the first expression on the line is a list that is not
preceded by any spaces, the list terminates the line regardless
of whether or not it is terminated by ]. This permits the user
to type EDITF(FOO) as a single input.
Note that if any spaces are inserted between the atom and the left
parentheses or bracket, readline will assume that the list does not
terminate the line. This is to enable the user to type a line command
such as USE (FOO) FOR FOO. In this case, a carriage return will be
typed after (FOO) followed by "..." as described in Section 14.
Therefore, if the user accidentially puts an extra space between a
function and its arguments, he will have to complete the input with
another carriage return, e.g.,
_EDITF(FOO)
...C
EDIT
*
------------------------------------------------------------------------
63
If lispxx is a list car of which is LAMBDA or NLAMBDA, lispx calls
lispxread to obtain the arguments.
64
If the input consists of one expression, eval is called; if two,
apply; if more than two, the entire line is treated as a single form
and eval is called.
22.39
22.9 Functions
65
lispx[lispxx;lispxid;lispxxmacros;lispxxuserfn]
lispx is like eval/apply. It carries out a
single computation, and returns its value. The
first argument, lispxx is the result of a single
call to lispxread. lispx will call readline, if
necessary as described on page 22.39. lispx
prints the value of the computation, as well as
66
saving the input and value on lispxhistory.
If lispxx is a history command, lispx calls
historysave, executes the command, and returns
the value of historysave.
If the value of the fourth argument,
lispxxmacros, is not NIL, it is used as the
lispx macros, otherwise the top level value of
lispxmacros is used. If the value of the fifth
argument, lispxxuserfn, is not NIL, it is used
as lispxuserfn. In this case, it is not
necessary to both set and define lispxuserfn as
described on page 22.29.
The overhead for a call to lispx (in INTERLISP-10) is approximately 17
milliseconds, of which 12 milliseconds are spent in maintaining the
spelling lists. In other words, in INTERLISP, the user pays 17 more
milliseconds for each eval or apply input over a conventional LISP
executive, in order to enable the features described in this chapter.
userexec[lispxid;lispxxmacros;lispxxuserfn]
repeatedly calls lispx under errorset protection
specifying lispxxmacros and lispxxuserfn, and
using lispxid (or _ if lispxid=NIL) as a prompt
character. Userexec is exited via the
lispxmacro OK, or else with a retfrom.
lispxread[file;rdtbl] is a generalized read. If readbuf=NIL,
lispxread performs read[file;rdtbl], which it
------------------------------------------------------------------------
65
lispxid corresponds to id on page 22.36. Lispx also has a fifth
argument, lispxflg, which is used by the E command in the editor.
66
Note that the history is not one of the arguments to lispx, i.e.,
the editor must bind (reset) lispxhistory to edithistory before
calling lispx to carry out a history command.
Lispx will continue to operate as an eval/apply function if
lispxhistory is NIL. Only those functions and commands that involve
the history list will be affected.
22.40
returns as its value. (If the user types
control-U during the call to read, lispxread
calls the editor and returns the edited value.)
If readbuf is not NIL, lispxread "reads" the
next expression on readbuf, i.e., essentially
returns
(PROG1 (CAR READBUF)
67
(SETQ READBUF (CDR READBUF))).
readline, described in Section 14, also uses this generalized notion of
reading. When readbuf is not NIL, readline "reads" expressions from
readbuf until it either reaches the end of readbuf, or until it reads a
pseudo-carriage return (see page 22.16). In both cases, it returns a
list of the expressions it has "read". (The pseudo-carriage return is
not included in the list.)
When readbuf is not NIL, both lispxread and readline actually obtain
their input by performing (APPLY* LISPXREADFN FILE), where lispxreadfn
is initially set to READ. Thus, if the user wants lispx, the editor,
break, et al to do their reading via a different input function, e.g.,
uread, he simply sets lispxreadfn to the name of that function
(or an appropriate LAMBDA expression).
lispxreadp[flg] is a generalized readp. If flg=T, lispxreadp
returns T if there is any input waiting to be
"read", a la lispxread. If flg=NIL, lispxreadp
returns T only if there is any input waiting to
be "read" on this line. In both cases, leading
spaces are ignored, i.e., skipped over with
readc, so that if only spaces have been typed,
lispxreadp will return NIL.
lispxunread[lst] unreads lst, a list of expressions to be read.
If readbuf is not NIL, lispxunread attaches lst
at the front of readbuf, separating it from the
rest of readbuf with a histstr0. The definition
of lispxunread is:
(LISPXUNREAD
[LAMBDA (LST)
(SETQ READBUF (COND
((NULL READBUF)
LST)
(T (APPEND LST (CONS HISTSTR0
READBUF])
------------------------------------------------------------------------
67
Except that pseudo-carriage returns, as represented by the value of
histstr0, are ignored, i.e., skipped. Lispxread also sets rereadflg
to NIL when it reads via read, and sets rereadflg to the value of
readbuf when rereading.
22.41
promptchar[id;flg;hist] prints the prompt character id.
promptchar will not print anything when the next
input will be "reread", i.e., readbuf is not
NIL. promptchar will also not print when
readp[]=T, unless flg is T.
Thus the editor calls promptchar with flg=NIL so that extra *'s are not
printed when the user types several commands on one line. However,
evalqt calls promptchar with flg=T, since it always wants the _ printed
(except when "rereading").
Finally, if prompt#flg is T and hist is not NIL,
promptchar prints the current event number (of
hist) before printing id.
lispxeval[lispxform;lispxid]
evaluates lispxform (using eval) the same as
though it were typed in to lispx, i.e., the
event is recorded, and the evaluation is made
undoable by substituting the slash functions for
the corresponding destructive functions, as
described on page 22.33. lispxeval returns the
value of the form, but does not print it.
historysave[history;id;input1;input2;input3;props]
records one event on history. If input1 is not
NIL, the input is of the form
(input1 input2 . input3). If input1 is NIL, and
input2 is not NIL, the input is of the form
(input2 . input3). Otherwise, the input is just
input3.
historysave creates a new event with the
corresponding input, id, value field initialized
to bell, and props. If the history has reached
its full size, the last event is removed and
cannibalized.
The value of historysave is the new event.
However, if rereadflg is not NIL, and the most
recent event on the history list contains the
history command that produced this input,
historysave does not create a new event, but
simply adds an (input id bell . props) entry to
the *GROUP* property for that event and returns
that entry. See discussion on page 22.38.
lispxfind[history;line;type;backup]
line is an event specification, type specifies
the format of the value to be returned by
lispxfind, and can be either ENTRY, ENTRIES,
COPY, COPIES, INPUT, or REDO. lispxfind parses
line, and uses historyfind to find the
corresponding events. lispxfind then assembles
and returns the appropriate structure.
22.42
lispxfind incorporates the following special features:
1) if backup=T, lispxfind interprets line in the context of the history
list before the current event was added. This feature is used, for
example, by valueof, so that (VALUEOF -1) will not refer to the
valueof event itself;
2) if line=NIL and the last event is an UNDO, the next to the last
event is taken. This permits the user to type UNDO followed by REDO
or USE;
3) lispxfind recognizes @@, and substitutes archivelst for history (see
page 22.12); and
4) lispxfind recognizes @, and retrieves the corresponding event(s)
from the property list of the atom following @ (see page 22.12).
historyfind[lst;index;mod;x;y]
searches lst and returns the tails of lst
beginning with the event corresponding to x.
lst, index, and mod are as described on page
22.36.
x is an event address, as described on page
22.10-12, e.g., (43), (-1), (FOO FIE),
68
(LOAD _ FOO), etc. If historyfind cannot find
x, it generates an error.
entry#[hist;x] hist is a history list, i.e., of the form
described on page 22.36. x is one of the events
on hist, i.e., (MEMB X (CAR HIST)) is true. The
value of entry# is the event number for x.
valueof[x] is an nlambda, nospread function for obtaining
the value of the event specified by x, e.g.,
(VALUEOF -1), (VALUEOF LOAD 1), etc. valueof
returns a list of the corresponding values if x
specifies a multiple event.
69
changeslice[n;history] changes time-slice for history to n. If history
is NIL, changes both edithistory and
lispxhistory.
------------------------------------------------------------------------
68
If y is given, the event address is the list difference between x
and y, e.g., x=(FOO FIE AND \ -1), y=(AND \ -1) is equivalent to
x=(FOO FIE), y=NIL.
69
changeslice has a third argument used by the system for undoing a
changeslice.
22.43
Note: the effect of increasing a time-slice is gradual: the history list
is simply allowed to grow to the corresponding length before any events
are forgotten. Decreasing a time-slice will immediately remove a
sufficient number of the older events to bring the history list down to
the proper size. However, changeslice is undoable, so that these events
are (temporarily) recoverable. Thus if the user wants to recover the
storage associated with these events without waiting n more events for
the changeslice event to be forgotten, he must perform a FORGET command.
saveset[name;value;topflg;flg]
an undoable set. (see page 22.35). saveset
scans the pushdown list looking for the last
binding of name, sets name to value, and returns
value.
If the binding changed was a top level binding,
name is added to spellings3 (see Section 17).
Furthermore, if the old value was not NOBIND,
and was also not equal to the new value, saveset
calls the file package to update the necessary
file records. Then, if dfnflg is not equal to
T, saveset prints (name RESET), and saves the
old value on the property list of name, under
the property VALUE. If flg=NOPRINT, saveset
saves the old value, but does not print the
message. This option is used by unset.
If topflg=T, saveset operates as above except
that it does not scan the pushdown list but goes
right to name's value cell, e.g., rpaqq[x;y] is
simply saveset[x;y;T]. When topflg is T, and
dfnflg is ALLPROP and the old value was not
NOBIND, saveset simply stores value on the
property list of name under the property VALUE,
and returns value. This option is used for
loading files without disturbing the current
value of variables (see Section 14).
If flg=NOSAVE, saveset does not save the old
value on the property list, nor does it add name
to spellings3. However, the call to saveset is
still undoable. This option is used by /set.
unset[name] if name does not contain a property VALUE, unset
generates an error. Otherwise unset calls
saveset with name, the property value, topflg=T,
and flg=NOPRINT.
22.44
70
undosave[undoform] if lispxhist is not NIL (see discussion on page
22.37), and get[lispxhist;SIDE] is not equal to
NOSAVE, undosave adds undoform to the value of
the property SIDE on lispxhist, creating a SIDE
property if one does not already exist. The
71
form of undoform is (fn . args), i.e.,
undoform is undone by performing
apply[car[undoform];cdr[undoform]].
For example, if the definition of FOO is def,
/putd[FOO;newdef] will cause a call undosave
with undoform =(/PUTD FOO def).
car of the SIDE property is the number of
"undosaves", i.e., length of cdr of the SIDE
property, which is the list of undoforms. Each
call to undosave increments this count, and adds
undoform to the front of the list, i.e., just
after the count. When the count reaches the
72
value of #undosaves (initially 50), undosave
prints a message asking the user if he wants to
continue saving. If the user answers NO or
defaults, undosave makes NOSAVE be the value of
the property SIDE, which disables any further
saving for this event. If the user answers YES,
undosave changes the count to -1, which is then
73
never incremented, and continues saving.
/rplnode[x;a;d] Undoably performs rplaca[x;a] and rplacd[x;d].
Value is x. Generates an error, ILLEGAL ARG, if
x is not a list. The principle advantage of
/rplnode is that when x is a list, /rplnode
saves its undo information as
cons[x;cons[car[x];cdr[x]]], i.e.,
(x originalcar . originalcdr), and therefore
requires only 3 cells of storage, instead of the
------------------------------------------------------------------------
70
Undosave has a second optional argument, histentry, which can be
used to specify lispxhist directly, saving the time to look it up.
If both histentry and lispxhist are NIL, undosave is a NOP.
71
Except for /rplnode, as described below.
72
#undosaves=NIL is equivalent to #undosaves=infinity.
73
load initializes the count on SIDE to -1, so that regardless of the
value of #undosaves, no message will be printed, and the load will
be undoable.
22.45
8 that would be required for a /rplaca and a
/rplacd that saved their information as
74
described earlier.
/rplnode has a BLKLIBRARYDEF.
/rplnode2[x;y] same as /rplnode[x;car[y];cdr[y]].
Note: for consistency, there are definitions for both rplnode and
rplnode2, although there primary reason for existence is the undoable
versions.
new/fn[fn] After the user has defined /fn, new/fn performs
the necessary housekeeping operations to make fn
be undoable.
For example, the user could define /radix as
(LAMBDA (X) (UNDOSAVE (LIST (QUOTE /RADIX) (RADIX X))) and then perform
new/fn[radix], and radix would then be undoable when typed in or in
testmode.
lispx/[x;fn;vars] performs the substitution of / functions for
destructive functions. If fn is not NIL, it is
the name of a function, and x is its argument
list. If fn is NIL, x is a form. In both
cases, lispx/ returns x with the appropriate
substitutions. Vars is a list of bound
variables (optional).
lispx/ incorporates information about the syntax
and semantics of INTERLISP expressions. For
example, it does not bother to make undoable
operations involving variables bound in x. It
does not perform substitution inside of
expressions car of which is NLAMBDA, i.e., has
argtype 1 or 3 (unless car of the form has the
property INFO value EVAL, as described in
Section 20). For example, (BREAK PUTD) typed to
lispx, will break on putd, not /putd.
Similarly, substitution should be performed in
the arguments for functions like mapc, rptq,
etc., since these contain expressions that will
be evaluated or applied. For example, if the
user types mapc[(FOO1 FOO2 FOO3);PUTD] the putd
must be replaced by /putd.
------------------------------------------------------------------------
74
Actually, /rplaca and /rplacd also use this format for saving their
undo information when their first arguments are lists. However, if
both a /rplaca and /rplacd are to be performed, it is still more
efficient to use /rplnode (3 cells versus 6 cells).
22.46
undolispx[line] line is an event specification. undolispx is
the function that executes UNDO commands by
calling undolispx1 on the appropriate entry(s).
undolispx1[event;flg] undoes one event. The value of undolispx1 is
NIL if there is nothing to be undone. If the
event is already undone, undolispx1 prints
75
ALREADY UNDONE and returns T. Otherwise,
undolispx1 undoes the event, prints a message,
e.g., SETQ UNDONE, and returns T.
Undoing an event consists of mapping down (cdr of) the property value
for SIDE, and for each element, applying car to cdr, and then marking
the event undone by attaching (with /attach) a NIL to the front of its
SIDE property. Note that the undoing of each element on the SIDE
property will usually cause undosaves to be added to the current
lispxhist, thereby enabling the effects of undolispx1 to be undone.
undonlsetq[form] is an nlambda function similar to nlsetq.
undonlsetq evaluates form, and if no error
occurs during the evaluation, returns
list[eval[form]] and passes the undo information
76
from form (if any) upwards. If an error does
occur, the value of undonlsetq is NIL, and any
changes made by / functions during the
evaluation of form are undone.
undonlsetq compiles open.
undonlsetq will operate even if lispxhistory or
lispxhist are NIL, or if #undosaves is or has
been exceeded for this event.
resetundo[x;stopflg] For use in conjunction with resetlst (Section
------------------------------------------------------------------------
75
If flg=T and the event is already undone, or is an undo command,
undolispx1 takes no action and returns NIL. Undolispx uses this
option to search for the last event to undo. Thus when line=NIL,
undolispx simply searches history until it finds an event for which
undolispx1 returns T, i.e., undolispx performs
(SOME (CDAR LISPXHISTORY) (F/L (UNDOLISPX1 X T)))
76
Actually, undonlsetq does not rebind lispxhist, so that any undo
information is stored directly on the history event, exactly as
though there were no undonlsetq. Instead, undonlsetq simply marks
the state of the undo information when it starts, so that if an
error occurs, it can then know how much to undo. The purpose of this
is so that if the user control-D's out of the undonlsetq, the event
is still undoable.
22.47
5). resetundo[] initializes the saving of undo
information and returns a value which when given
back to resetundo undoes the intervening side
effects.
For example, (RESETLST (RESETSAVE (RESETUNDO)) . forms) will undo the
side effects of forms on normal exit, or if an error occurs or a
control-D is typed. Note that (UNDOLSETQ form) could be written as:
(RESETLST (RESETSAVE (RESETUNDO)
(AND (EQ RESETSTATE 'ERROR)
(RESETUNDO OLDVALUE)))
. forms)
If stopflg=T, resetundo stops accumulating undo
77
information it is saving on x.
For example,
(RESETLST (SETQ FOO (RESETUNDO))
(RESETSAVE NIL (LIST 'RESETUNDO FOO))
(ADVISE --)
(RESETUNDO FOO T)
. forms)
would cause the advice to be undone, but not any of the side effects in
forms.
printhistory[history;line;skipfn;novalues]
line is an event specification. printhistory
prints the events on history specified by line,
e.g., (-1 THRU -10). skipfn is an (optional)
functional argument that is applied to each
event before printing. If its value is true,
the event is skipped, i.e., not printed. If
novalues=T, or novalues applied to the
corresponding event is true, the value is not
78
printed.
For example, the following lispxmacro will define ??' as a command for
printing the history list while skipping all "large events" and not
printing any values.
(??' (PRINTHISTORY LISPXHISTORY LISPXLINE
(FUNCTION (LAMBDA (X)
(IGREATERP (COUNT (CAR X)) 5)))
------------------------------------------------------------------------
77
Note that this has no bearing on the saving of undo information on
higher resetundo's, or on being able to undo the entire event.
78
For example, novalues is T when printing events on edithistory.
22.48
T))
22.10 The Editor and the Assistant
As mentioned earlier, all of the remarks concerning "the assistant"
apply equally well to user interactions with evalqt, break or the
editor. The differences between the editor's implementation of these
features and that of lispx are mostly obvious or inconsequential.
However, for completeness, this section discusses the editor's
implementation of the programmer's assistant.
The editor uses promptchar to print its prompt character, and lispxread,
lispxreadp, and readline for obtaining inputs. When the editor is given
an input, it calls historysave to record the input in a new event on its
79
history list, edithistory. Edithistory follows the same conventions
and format as lispxhistory. However, since edit commands have no value,
the editor uses the value field for saving side effects, rather than
storing them under the property SIDE.
The editor processes DO, !E, !F, and !N commands itself, since lispx
does not recognize these commands. The editor also processes UNDO
80
itself, as described below. All other history commands are simply
given to lispx for execution, after first binding (resetting)
lispxhistory to edithistory. The editor also calls lispx when given an
81
E command as described in Section 9.
The major implementation difference between the editor and lispx occurs
------------------------------------------------------------------------
79
Except that the atomic commands OK, STOP, SAVE, P, ?, PP and E are
not recorded. In addition, number commands are grouped together in a
single event. For example, 3 3 -1 is considered as one command for
changing position.
80
as indicated by their appearance on historycoms, a list of the
history commands. editdefault interrogates historycoms before
attempting spelling correction. (All of the commands on historycoms
are also on editcomsa and editcomsl so that they can be corrected if
misspelled in the editor.) Thus if the user defines a lispxmacro and
wishes it to operate in the editor as well, he need simply add it to
historycoms. For example, RETRIEVE is implemented as a lispxmacro
and works equally well in lispx and the editor.
81
In this case, the editor uses the fifth argument to lispx, lispxflg,
to specify that any history commands are to be executed by a
recursive call to lispx, rather than by unreading. For example, if
the user types E REDO in the editor, he wants the last event on
lispxhistory processed as lispx input, and not to be unread and
processed by the editor.
22.49
in undoing. Edithistory is a list of only the last n commands, where n
is the value of the time-slice. However the editor provides for undoing
all changes made in a single editing session, even if that session
consisted of more than n edit commands. Therefore, the editor saves
undo information independently of the edithistory on a list call
undolst, (although it also stores each entry on undolst in the field of
the corresponding event on edithistory.) Thus, the commands UNDO, !UNDO,
82
and UNBLOCK, are not dependent on edithistory, i.e., UNDO specifies
undoing the last command on undolst, even if that event no longer
appears on edithistory. The only interaction between UNDO and the
history list occurs when the user types UNDO followed by an event
specification. In this case, the editor calls lispxfind to find the
event, and then undoes the corresponding entry on undolst. Thus the
user can only undo a specified command within the scope of the
edithistory. (Note that this is also the only way UNDO commands
themselves can be undone, that is, by using the history feature, to
specify the corresponding event, e.g., UNDO UNDO.)
The implementation of the actual undoing is similar to the way it is
done in lispx: each command that makes a change in the structure being
edited does so via a function that records the change on a variable.
After the command has completed, this variable contains a list of all
the pointers that have been changed and their original contents.
Undoing that command simply involves mapping down that list and
restoring the pointers.
22.11 Statistics
The programmer's assistant keeps various statistics about system usage,
e.g., number of lispx inputs, number of undo saves, number of calls to
editor, number of edit commands, number of p.a. commands, cpu time,
console time, et al. These can be viewed via the function lispxstats.
lispxstats[] prints statistics.
The user can add his own statistics to the lispx statistics via the
function addstats.
addstats[statlst] no spread, nlambda. Statlst is a list of
elements of the form (statistic-name . message),
e.g., (EDITCALLS CALLS TO EDITOR) (UNDOSTATS
CHANGES UNDONE), etc. statistic-name is set to
the cell in an unboxed array, where the
corresponding statistic will be stored. This
statistic can then be incremented by lispxwatch.
------------------------------------------------------------------------
82
and in fact will work if edithistory=NIL, or even in a system which
does not contain lispx at all.
22.50
lispxwatch[stat;n] increments stat by n (or 1 if n=NIL).
lispxwatch has a BLKLIBRARYDEF.
The user can save his statistics for loading into a new system by
performing MAKEFILE(DUMPSTATS). After the file DUMPSTATS is loaded, the
statistics printed by lispxstats will be the same as those that would be
printed following the makefile.
22.12 Greeting and User Initialization
Many of the features of INTERLISP are parameterized to allow the user to
adjust the system to his or her own tastes. Among the more commonly
adjusted parameters are prompt#flg, dwimwait, changeslice, #rpars,
lowercase, archivefn, #undosaves, fltfmt, etc. In addition, the user
can modify the action of system functions in ways not specifically
provided for by using advise (Section 19).
In order to encourage this procedure, and to make it as painless and
automatic as possible, the p.a. includes a facility for a user-defined
profile. When INTERLISP is first run, it obtains and evaluates a list
83
of user-specified expressions for initializing the system, and the
p.a. prints a greeting, e.g., "HELLO, WARREN." or "GOOD AFTERNOON,
DANNY.", etc.
Greeting (i.e., the initialization) is undoable, and is stored as a
separate event on the history list. The user can also specifically
invoke the greeting operation via the function greet, for example, if he
wishes to effect another user's initialization.
greet[name;flg] performs greeting for user whose username is
name, or if name=NIL, for login name (see
username and usernumber, Section 21), i.e., when
INTERLISP first starts up, it performs greet[].
Before greet performs the indicated
initialization, it first undoes the effects of
------------------------------------------------------------------------
83
In INTERLISP-10, a specially formatted file on the LISP directory
contains the initializations for all users. This file is indexed
into using the user's usernumber as a key. The expressions (if any)
found there are then evaluated. The user can also specify
initializations by maintaining a file INIT.LISP on hs directory.
See discussion below concerning implementation.
22.51
84 85
the previous greeting. If flg=T, greet also
resets the counters for the various statistics
reported by lispxstats (page 22.50).
greet also sets the variable username to the name for which the greeting
was performed. Sysin is advised to compare username with username[].
If they are the same, sysin prints heraldstring, followed by the
86
greeting message. Ohterwise, sysin prints a message alerting the user.
For example, if user HARTLEY performs a sysin of a sysout made by user
GOODWIN, the following message is printed:
****ATTENTION USER HARTLEY:
THIS SYSOUT IS INITIALIZED FOR USER GOODWIN.
TO REINITIALIZE, TYPE GREET()
INTERLISP-10 Implementation of Greeting
greet operates off the file <LISP>USERNAMEFILE. To change an existing
initialization, or create a new one, a new <LISP>USERNAMEFILE must be
written. This is accomplished by loading the file <LISP>USERNAMES,
editing usernamelst, and then performing makeusernames[], which will
create new versions of both USERNAMEFILE and USERNAMES. (Note that the
person performing this operation must therefore either be connected to
the LISP directory, or have write access to it.)
usernamelst is a list of elements of the form
(username firstname T . forms), e.g., (TEITELMAN WARREN T (CHANGESLICE
100) (SETQ DWIMWAIT 5)). cadr of the list is used in the greeting
message. cdddr is a list of forms that are evaluated.
usernamelst can be edited just like any other list, e.g., with editv.
The file USERNAMEFILE, created by makeusernames, contains usernamelst
along with an index block which contains for each user on usernamelst
the address in the file (i.e., byte position) of the start of his entry.
greet then simply does an sfptr and a read.
------------------------------------------------------------------------
84
The side effects of the greeting operation are stored on a global
variable as well as the history list, thus enabling the previous
greeting to be undone even if it is no longer on the history list.
85
In addition, makesys is advised to undo the effects of the previous
greeting, thereby returning the system to a pristine state.
86
sysout first checks the value of the variable sysoutgag, initially
NIL. If sysoutgag is T, no message is printed following a sysin. If
sysoutgag is a list, it will be evaluated in lieu of printing a
message. For example, the user can use this option to print his own
message.
22.52
If usernamelst contains an element for which the username is NIL, i.e.,
an element of the form (NIL . forms), this is interpreted to mean that
forms are evaluated regardless of user name. This feature can be used
to "patch" an INTERLISP system when a bug is found, or to change some
default for INTERLISP at a particular site, e.g., turn off DWIM, perform
lowercase[T], etc. Individual user initialization will still be
performed following this system initialization.
If a user wishes to be able to change or add to his initialization
frequently without the necessity of making a new USERNAMEFILE, he can
accomplish this by putting initializing forms on to a file named
INIT.LISP in his login directory. greet checks to see if the file
INIT.LISP exists, and if so, reads and evaluates (undoably) the
expressions in the file.
22.53