{Begin SubSec Breaks}
{Title Breaks}
{Text

{Tag BreakPackage}

One of the most useful debugging facilities in Interlisp is the ability to put the system into a "break", stopping a computation at any point and allowing the user to interrogate the state of the world and affect the course of the computation.  A break appears to the user like a top-level executive, except that a break uses the prompt character{index prompt character}{index *PRIMARY* : (Printed by System)} "{lisp :}" to indicate it is ready to accept input(s), in the same way that "{lisp ←}" is used at the top-level.{index ← (Printed by System)}  However, a break saves the environment where the break occurred, so that the user may evaluate variables and expressions in the environment that was broken.  In addition, the break program recognizes a number of useful "break commands", which provide an easy way to interrogate the state of the broken computation.


Note:  In Interlisp-D, the break package has been extended to include window operations (see {PageRef fn WBREAK}).


Breaks may be entered in several different ways.  Some interrupt characters ({PageRef Tag InterruptChars}) automatically cause a break to be entered whenever they are typed.  Functions errors may also cause a break, depending on the depth of the computation (see {PageRef Tag WhenToBreak}).  Finally, Interlisp provides functions which make it easy to "break" suspect functions so that they always cause a break whenever they are entered, to allow examination and debugging (see {PageRef Fn BREAK}).


Within a break the user has access to all of the power of Interlisp; he can do anything that he can do at the top-level executive.  For example, the user can evaluate an expression, see that the value is incorrect, call the editor, change the function, and evaluate the expression again, all without leaving the break.  The user can even type in commands to the programmer's assistant ({PageRef Tag PROGASST}), e.g. to redo or undo previously executed events, including break commands.


Similarly, the user can prettyprint functions, define new functions or redefine old ones, load a file, compile functions, time a computation, etc.  In short, anything that he can do at the top level can be done while inside of the break.  In addition the user can examine the stack (see {PageRef Tag STACK}), and even force a return back to some higher function via the function {fn RETFROM} or {fn RETEVAL}.


It is important to emphasize that once a break occurs, the user is in complete control of the flow of the computation, and the computation will not proceed without specific instruction from him.
If the user types in an expression whose evaluation causes an error, the break is maintained.  Similarly if the user aborts a computation initiated from within the break (by typing {index control-E}control-E), the break is maintained.  Only if the user gives one of the commands that exits from the break, or evaluates a form which does a {fn RETFROM} or {fn RETEVAL} back out of {fn BREAK1}, will the computation continue.{foot 
Except that {fn BREAK1} does not "turn off" control-D, i.e., a {index control-D}control-D will force an immediate return back to the top level.
}{comment endfootnote}



The basic function of the break package is {fn BREAK1}.  Note that {fn BREAK1} is just another Interlisp function, not a special system feature like the interpreter or the garbage collector.It has arguments, and returns a value, the same as any other function.  The value returned by {fn BREAK1} is called "the value of the break."  The user can specify this value explicitly by using the {lisp RETURN} command described below.  But in most cases, the value of a break {index *PRIMARY* value of a break} is given implicitly, via a {breakcom GO} or {breakcom OK} command, and is the result of evaluating "the break expression," {var BRKEXP}, which is one of the arguments to {fn BREAK1}.  For more information on the function {fn BREAK1}, see {PageRef Fn BREAK1}.


The break expression,{index break expression} stored in the variable {index *PRIMARY* BRKEXP Var}{var BRKEXP}, is an expression equivalent to the computation that would have taken place had no break occurred.  For example, if the user breaks on the function {lisp FOO}, the break expression is the body of the definition of {lisp FOO}.  When the user types {breakcom OK} or {breakcom GO}, the body of {lisp FOO} is evaluated, and its value returned as the value of the break, i.e., to whatever function called {lisp FOO}.  {var BRKEXP} is set up by the function that created the call to {fn BREAK1}.  For functions broken with {index BREAK FN}{fn BREAK} or {index TRACE FN}{fn TRACE}, {var BRKEXP} is equivalent to the body of the definition of the broken function (see {PageRef Fn BREAK}).  For functions broken with {index BREAKIN FN}{fn BREAKIN}, using {lisp BEFORE}{index BEFORE (as argument to BREAKIN)} or {lisp AFTER},{index AFTER (as argument to BREAKIN)} {var BRKEXP} is {lisp NIL}.  For {index BREAKIN FN}{fn BREAKIN} {index AROUND (as argument to BREAKIN)}{lisp AROUND}, {var BRKEXP} is the indicated expression (see {PageRef Fn BREAKIN}).


{fn BREAK1} recognizes a large set of break commands.  These are typed in {it without} parentheses.  In order to facilitate debugging of programs that perform input operations, the carriage return that is typed to complete the {breakcom GO}, {breakcom OK}, {breakcom EVAL}, etc. commands is discarded by {fn BREAK1}, so that it will not be part of the input stream after the break.


{Tag BreakCommands}

{index *BEGIN* break commands}


{Def {Type BreakCom}
{Name GO}
{Text
Evaluates {var BRKEXP}, prints this value, and returns it as the value of the break.  Releases the break and allows the computation to proceed.
}}


{Def {Type BreakCom}
{Name OK}
{Text
Same as {breakcom GO} except that the value of {var BRKEXP} is not printed.
}}


{Def {Type BreakCom}
{Name EVAL}
{Text
Same as {breakcom OK} except that the break is maintained after the evaluation.
The value of this evaluation is bound to the local variable {index  *PRIMARY* !VALUE Var}{var !VALUE}, which the user can interrogate.  Typing {breakcom GO} or {breakcom OK} following {breakcom EVAL} will not cause {var BRKEXP} to be reevaluated, but simply return the value of {var !VALUE} as the value of the break.  Typing another {breakcom EVAL} will cause reevaluation.  {breakcom EVAL} is useful when the user is not sure whether the break will produce the correct value and wishes to examine it before continuing with the computation.
}}


{Def {Type BreakCom}
{Name RETURN} {Args FORM}  {Noparens}
{Text
{arg FORM} is evaluated, and returned as the value of the break.  For example, one could use the {breakcom EVAL} command and follow this with {lisp RETURN (REVERSE !VALUE)}.
}}


{Def {Type BreakCom}
{Name ↑}
{Text
Calls {index ERROR! FN}{fn ERROR!} and aborts the break, making it "go away" without returning a value.  This is a useful way to unwind to a higher level break.  All other errors, including those encountered while executing the {breakcom GO}, {breakcom OK}, {breakcom EVAL}, and {breakcom RETURN} commands, maintain the break.
}}


The following four commands refer to "the broken function."  This is the function that caused the break, whose name is stored in the {fn BREAK1} argument {index BRKFN Var}{var BRKFN}.

{note If this break was not caused by a broken function, trying to unbreak this function will print an expression such as {lisp (({arg FN} NOT BROKEN))}.}

{note will these commands ever rebreak a fn that wasn't broken in the first place??}



{Def {Type BreakCom}
{Name !EVAL}
{Text
The broken function is first unbroken, then the break expression is evaluated (and the value stored in {var !VALUE}), and then the function is rebroken.  This command is very useful for dealing with recursive functions.
}}


{Def {Type BreakCom}
{Name !GO}
{Text
Equivalent to {breakcom !EVAL} followed by {breakcom GO}.  The broken function is unbroken, the break expression is evaluated, the function is rebroken, and then the break is exited with the value typed.
}}


{Def {Type BreakCom}
{Name !OK}
{Text
Equivalent to {breakcom !EVAL} followed by {breakcom OK}.  The broken function is unbroken, the break expression is evaluated, the function is rebroken, and then the break is exited.
}}



{Def {Type BreakCom}
{Name UB}
{Text
Unbreaks the broken function.
}}



{Def {Type BreakCom}
{Name @}
{Text
Resets the variable {index *PRIMARY* LASTPOS Var}{var LASTPOS}, which establishes a context for the commands {breakcom ?=},
{breakcom ARGS}, {breakcom BT}, {breakcom BTV}, {breakcom BTV*}, {breakcom EDIT}, and {breakcom IN?} described below.  {var LASTPOS} is the position of a function call on the stack.  It is initialized to the function just before the call to {fn BREAK1}, i.e.,
{lisp (STKNTH -1 'BREAK1)}.{foot
When control passes from {fn BREAK1}, e.g. as a result of an
{breakcom EVAL}, {breakcom OK}, {breakcom GO}, {breakcom REVERT}, {breakcom ↑} command, or via a {fn RETFROM} or {fn RETEVAL} typed in by the user, {lisp (RELSTK LASTPOS)} is executed to release this stack pointer.
}{comment endfootnote}


{breakcom @} treats the rest of the teletype line as its argument(s).
It first resets {var LASTPOS} to {lisp (STKNTH -1 'BREAK1)} and then for
each atom on the line, {breakcom @} searches down the stack for a call to that atom.  The following atoms are treated specially:

{Begin LabeledList The following atoms are treated specially}

{Indent 10percent}

{Label {lisp @}}
{Text
{index @ (use with @ break command)}Do not reset {var LASTPOS} to {lisp (STKNTH -1 'BREAK1)} but leave it as it was, and continue searching from that point.
}

{Label a number {arg N}}
{Text
If negative, move {var LASTPOS} down the stack {arg N} frames.  If positive, move {var LASTPOS} up the stack {arg N} frames.
}

{Label {lisp /}}
{Text
{index / (use with @ break command)}The next atom on the line (which should be a number) specify that the {it previous} atom should be searched for that many times.  For example, "{lisp @ FOO / 3}" is equivalent to "{lisp @ FOO FOO FOO}".
{note what if prev atom is a command atom?}
}

{Label {lisp =}}
{Text
{index = (use with @ break command)}Resets {var LASTPOS} to the {it value} of the next expression, e.g., if the value of {lisp FOO} is a stack pointer,
"{lisp @ = FOO FIE}" will search for {lisp FIE} in the environment specified by (the value of) {lisp FOO}.
{note what if FOO is NOT a stack ptr??}
}

{End LabeledList The following atoms are treated specially}


For example, if the push-down stack looks like:

{lispcode
BREAK1      {it [9]}
FOO         {it [8]}
COND        {it [7]}
FIE         {it [6]}
COND        {it [5]}
FIE         {it [4]}
COND        {it [3]}
FIE         {it [2]}
FUM         {it [1]}}

then "{lisp @ FIE COND}" will set {index LASTPOS Var}{var LASTPOS} to the position corresponding to {it [5]}; "{lisp @ @ COND}" will then set {lisp LASTPOS} to {it [3]}; and "{lisp @ FIE / 3 -1}" to {it [1]}.

If {breakcom @} cannot successfully complete a search for function {arg FN}, it searches the stack again from that point looking for a call to a function whose name is close to that of {arg FN}, in the sense of the spelling corrector ({PageRef Tag SpellingCorrector}).  If the search is still unsuccessful, {breakcom @} types {lisp ({arg FN} NOT FOUND)}, and then aborts.

{indexX {Name NOT FOUND}
        {Type (printed by break)}
        {Text {lisp ({arg FN} NOT FOUND)}} }

When {breakcom @} finishes, it types the name of the function at {var LASTPOS}, i.e., {lisp (STKNAME LASTPOS)}.

{breakcom @} can be used on {index BRKCOMS Var}{var BRKCOMS} (see {PageRef Var BRKCOMS}).  In this case, the {it next} command on {var BRKCOMS} is treated the same as the rest of the teletype line.
}}


{Def {Type BreakCom}
{Name ?=}
{Text
This is a multi-purpose command.{foot
In fact, {lisp ?=} is a universal mnemonic for displaying argument names and their corresponding values.  In addition to being a break command, {lisp ?=} is an edit macro which prints the argument names and values for the current expression ({PageRef EditCom ?=}), and a read-macro (actually {lisp ?} is the read-macro character){index ? (Read Macro)} which does the same for the current level list being read.{index *PRIMARY* ?= PAcom}
}{comment endfootnote}
Its most common use is to interrogate the value(s) of the arguments of the broken function.  For example, if {lisp FOO} has three arguments {lisp (X Y Z)}, then typing {breakcom ?=} to a break on {lisp FOO} will produce:

{lispcode
:?=
X = {it value of X}
Y = {it value of Y}
Z = {it value of Z}
:}

{breakcom ?=} operates on the rest of the teletype line as its arguments.
If the line is empty, as in the above case, it operates on all of the arguments of the broken function.
If the user types {lisp ?= X (CAR Y)}, he will see the value of {lisp X}, and the value of {lisp (CAR Y)}.{foot
The value of each variable is printed with the function
{fn SHOWPRINT}{index SHOWPRINT FN} ({PageRef Fn SHOWPRINT}),
so that if {index SYSPRETTYFLG Var}{var SYSPRETTYFLG}={lisp T},
the value will be prettyprinted.
}{comment endfootnote}
The difference between using {breakcom ?=} and typing {lisp X} and {lisp (CAR Y)} directly to {fn BREAK1} is that {breakcom ?=}
evaluates its inputs as of the stack frame {index LASTPOS Var}{var LASTPOS}, i.e., it uses {index STKEVAL FN}{fn STKEVAL}.
This provides a way of examing variables or performing computations {it as of a particular point on the stack.}
For example, {lisp @ FOO / 2} followed by {lisp ?= X} will allow the user to examine the value of {lisp X} in the previous call to {lisp FOO}, etc.

{breakcom ?=} also recognizes numbers as referring to the correspondingly numbered argument, i.e., it uses {index STKARG FN}{fn STKARG} in this case.
Thus

{lispcode
:@ FIE
FIE
:?= 2}

will print the name and value of the second argument of {lisp FIE}.

{breakcom ?=} can also be used on {index BRKCOMS Var}{var BRKCOMS} ({PageRef Var BRKCOMS}, in which case the next command on {var BRKCOMS} is treated as the rest of the teletype line.  For example, if {var BRKCOMS} is {lisp (EVAL ?= (X Y) GO)}, {index BRKEXP Var}{var BRKEXP} will be evaluated, the values of {lisp X} and {lisp Y} printed, and then the function exited with its value being printed.
}}


{Begin Note}
doc??  where??

Date:  2 JUL 1982 2305-PDT
From: MASINTER.PA
Subject: little known feature in ? handling

Just as ?= in typin prints out args of current function, ?↑ will attempt to PF it.
{End Note}


{Def {Type BreakCom}
{Name PB}
{Text
Prints the bindings of a given variable.  Similar to {breakcom ?=}, except ascends the stack starting from {var LASTPOS}, and, for each frame in which the given variable is bound, prints the frame name and value of the variable (with {fn PRINTLEVEL} reset to {lisp (2 . 3)}), e.g.

{lispcode
:PB FOO
@  FN1:  3
@  FN2:  10
@  TOP:  NOBIND}

{lisp PB} is also a programmer's assistant command ({PageRef PACom PB}) that can be used when not in a break.  {lisp PB} is implemented via the function {fn PRINTBINDINGS}.{index PRINTBINDINGS FN}

{note is it really important what function is used to implement it?? flushp??}
}}


{Def {Type BreakCom}
{Name BT}
{Text
Prints a {index *PRIMARY* backtrace}backtrace of function names only starting at {index LASTPOS Var}{var LASTPOS}.  The value of {var LASTPOS} is changed by selecting an item from the backtrace menu {PageRef Fn WBREAK} or by the {breakcom @} command.  The several nested calls in system packages such as break, edit, and the top level executive appear as the single entries {index **BREAK**  (in backtrace)}{lisp **BREAK**}, {index **EDITOR**  (in backtrace)}{lisp **EDITOR**}, and {index **TOP**  (in backtrace)}{lisp **TOP**} respectively.
}}


{Def {Type BreakCom}
{Name BTV}
{Text
Prints a {index backtrace}backtrace of function names {it with} variables beginning at {index LASTPOS Var}{var LASTPOS}.

The value of each variable is printed with the function {fn SHOWPRINT}{index SHOWPRINT FN} ({PageRef Fn SHOWPRINT}), so that if {var SYSPRETTYFLG}={lisp T}{index SYSPRETTYFLG Var}, the value will be prettyprinted.
}}


{Def {Type BreakCom}
{Name BTV+}
{Text
Same as {breakcom BTV} except also prints local variables and arguments to {lisp SUBR}s.
}}


{Def {Type BreakCom}
{Name BTV*}
{Text
Same as {breakcom BTV} except prints arguments to {lisp SUBR}s, local variables, and temporaries of the interpreter, i.e. eval blips (see {PageRef Tag InterpreterBlips}).
}}


{Def {Type BreakCom}
{Name BTV!}
{Text
Same as {breakcom BTV} except prints {it everything} on the stack.
}}


{breakcom BT}, {breakcom BTV}, {breakcom BTV+}, {breakcom BTV*}, and {breakcom BTV!} all take optional functional arguments.  These arguments are used to choose functions to be {it skipped} on the backtrace.  As the backtrace scans down the stack, the name of each stack frame is passed to each of the functional arguments to the backtrace command.  If any of these functions returns a non-{lisp NIL} value, then that frame is skipped, and not shown in the backtrace.  For example, {lisp BT SUBRP} will skip all {lisp SUBR}s, {lisp BTV (LAMBDA (X) (NOT (MEMB X FOOFNS)))} will skip all but those functions on {lisp FOOFNS}.  If used on {var BRKCOMS} ({PageRef Var BRKCOMS}) the functional argument is no longer optional, i.e., the next element on {var BRKCOMS} must either be a list of functional arguments, or {lisp NIL} if no functional argument is to be applied.

For {breakcom BT}, {breakcom BTV}, {breakcom BTV+}, {breakcom BTV*}, and {breakcom BTV!}, if {index control-P}control-P is used to change a printlevel during the backtrace, the printlevel will be restored after the backtrace is completed.

The value of {var BREAKDELIMITER}{index BREAKDELIMITER Var}, initially {lisp "{CRsymbol}"}, is printed to delimit the output of {breakcom ?=} and backtrace commands.  This can be reset (e.g. to {lisp "{lisp ,}"}) for more linear output.


{Def {Type BreakCom}
{Name ARGS}
{Text
Prints the names of the variables bound at {index LASTPOS Var}{var LASTPOS}, i.e., {index VARIABLES FN}{lisp (VARIABLES LASTPOS)} ({PageRef Fn VARIABLES}).  For most cases, these are the arguments to the function entered at that position, i.e., {index ARGLIST FN}{lisp (ARGLIST (STKNAME LASTPOS))}.
}}


{Def {Type BreakCom}
{Name REVERT}
{Text
Goes back to position {var LASTPOS} on stack and reenters the function
called at that point with the arguments found on the stack.  If the function is not already broken, {breakcom REVERT} first breaks it, and then unbreaks it after it is reentered.

{breakcom REVERT} can be given the position using the conventions described for {breakcom @}, e.g., {lisp REVERT FOO -1} is equivalent to {lisp @ FOO -1} followed by {lisp REVERT}.

{breakcom REVERT} is useful for restarting a computation in the situation where a bug is discovered at some point {it below} where the problem actually occurred.  {breakcom REVERT} essentially says "go back there and start over in a break."  {breakcom REVERT} will work correctly if the names or
arguments to the function, or even its function type, have been changed.
}}


{Def {Type BreakCom}
{Name ORIGINAL}
{Text
For use in conjunction with {var BREAKMACROS} (see {PageRef Var BREAKMACROS}).  Form is {lisp (ORIGINAL . {arg COMS})}.  {arg COMS} are executed without regard for {var BREAKMACROS}.  Useful for redefining a break command in terms of itself.
}}


The following two commands are for use only with unbound atoms or undefined function breaks.


{Def {Type BreakCom}
{Name =} {Args FORM}  {Noparens}
{Text
Can only be used in a break following an unbound atom error.  Sets the atom to the value of {arg FORM}, exits from the break returning that value, and continues the computation, e.g.,

{lispcode
UNBOUND ATOM

(FOO BROKEN)
:= (COPY FIE)}

sets {lisp FOO} and goes on.

Note:  {arg FORM} may be given in the form {lisp {arg FN}[{arg ARGS}]}.
}}


{Def {Type BreakCom}
{Name ->} {Args EXPR}   {Noparens}
{Text
Can be used in a break following either with unbound atom error, or an undefined function error.
Replaces the expression containing the error with {arg EXPR} (not the value of {arg EXPR}), and continues the computation.  {lisp ->} does not just change {index BRKEXP Var}{var BRKEXP}; it changes the function or expression containing the erroneous form.  In other words, the user does not have to perform any additional editing.

For example,

{lispcode
UNDEFINED CAR OF FORM

(FOO1 BROKEN)
:-> FOO}

changes the {lisp FOO1} to {lisp FOO} and continues the computation.  {arg EXPR} need not be atomic, e.g.,

{lispcode
UNBOUND ATOM

(FOO BROKEN)
:-> (QUOTE FOO)}

For undefined function breaks, the user can specify a function {it and} initial arguments, e.g.,

{lispcode
UNDEFINED CAR OF FORM


(MEMBERX BROKEN)
:-> MEMBER X}

Note that in the case of a undefined function error occurring immediately following a call to {fn APPLY} (e.g., {lisp (APPLY X Y)} where the value of {lisp X} is {lisp FOO} and {lisp FOO} is undefined), or a unbound atom error immediately following a call to {fn EVAL} (e.g., {lisp (EVAL X)}, where the value of {lisp X} is {lisp FOO} and {lisp FOO} is unbound), there {it is} no expression containing the offending atom.  In this case, {breakcom ->} cannot operate, so {lisp ?} is printed and no action is taken.
}}


{Def {Type BreakCom}
{Name EDIT}
{Text
Designed for use in conjunction with breaks caused by errors.  Facilitates editing the expression causing the break:

{lispcode
NON-NUMERIC ARG
NIL
(IPLUS BROKEN)
:EDIT
IN FOO...
(IPLUS X Z)
EDIT
*(3 Y)
*OK
FOO
:}

and the user can continue by typing {breakcom OK}, {breakcom EVAL}, etc.
}}


This command is very simple conceptually, but complicated in its implementation by all of the exceptional cases involving interactions with compiled functions, breaks on user functions, error breaks, breaks within breaks, et al.
Therefore, we shall give the following simplified explanation which will account for 90% of the situations arising in actual usage.
For those others, {breakcom EDIT} will print an appropriate failure message and return to the break.


{breakcom EDIT} begins by searching up the stack beginning at {index LASTPOS Var}{var LASTPOS} (set by {index @ BreakCom}{breakcom @} command, initially position of the break) looking for a form, i.e., an internal call to {fn EVAL}.  Then {index EDIT BreakCom}{breakcom EDIT} continues from that point looking for a call to an interpreted function, or to {fn EVAL}.
It then calls the editor on either the {lisp EXPR} or the argument to {fn EVAL} in such a way as to look for an expression {fn EQ} to the form that it first found.  It then prints the form, and permits interactive editing to begin.  Note that the user can then type successive {lisp 0}'s to the editor to see the chain of superforms for this computation.


If the user exits from the edit with an {breakcom OK}, the break expression{index break expression} is reset, if possible, so that the user can continue with the computation by simply typing {index OK BreakCom}{breakcom OK}.  (Note that evaluating the new {index BRKEXP Var}{var BRKEXP} will involve reevaluating the form that causes the break, so that if {lisp (PUTD (QUOTE (FOO)) {arg BIG-COMPUTATION})} were handled by {breakcom EDIT}, {arg BIG-COMPUTATION} would be reevaluated.)
However, in some situations, the break expression cannot be reset.{index NOTE: BRKEXP NOT CHANGED. (Printed by Break)}
For example, if a compiled function {lisp FOO} incorrectly called {fn PUTD} and caused the error {lisp ARG NOT ATOM} followed by a break on {fn PUTD}, {breakcom EDIT} might be able to find the form headed by {lisp FOO}, and also find {it that} form in some higher interpreted function.
But after the user corrected the problem in the {lisp FOO}-form, if any, he would still not have in any way informed {breakcom EDIT}
what to do about the immediate problem, i.e., the incorrect call to {fn PUTD}.
However, if {lisp FOO} were {it interpreted} {breakcom EDIT} would find the {fn PUTD} form itself, so that when the user corrected that form, {breakcom EDIT} could use the new corrected form to reset the break expression.
The two cases are shown below:

If {lisp FOO} is compiled:

{lispcode
{it {lisp FOO} compiled}                       {it {lisp FOO} interpreted}

ARG NOT ATOM                     ARG NOT ATOM
(FUM)                            (PUTD BROKEN)
(PUTD BROKEN)                    :EDIT
:EDIT                            IN FOO...
IN FIE...                        (PUTD X)
(FOO X)                          EDIT
EDIT                             *(2 (CAR X))
*(2 (CAR X))                     *OK
*OK                              :OK
NOTE: BRKEXP NOT CHANGED         PUTD
FIE
:?=
U = (FUM)
:(SETQ U (CAR U))
FUM
:OK
PUTD}


{Def {Type BreakCom}
{Name IN?}
{Text
Similar to {breakcom EDIT}, but just prints parent form, and superform, but does not call editor, e.g.,

{lispcode
ATTEMPT TO RPLAC NIL
T
(RPLACD BROKEN)
:IN?
FOO:  (RPLACD X Z)}
}}


Although {index EDIT BreakCom}{breakcom EDIT} and {breakcom IN?}{index IN? BreakCom} were designed for error breaks, they can also be useful for user breaks.  For example, if upon reaching a break on his function {lisp FOO}, the user determines that there is a problem in the {it call} to {lisp FOO}, he can edit the calling form and reset the break expression with one operation by using {lisp EDIT}.
The following two protocol's with and without the use of {breakcom EDIT}, illustrate this:

{lispcode
{it Without {lisp EDIT}:}                      {it With {lisp EDIT}:}

(FOO BROKEN)                     (FOO BROKEN)
:?=                              :?=
X = (A B C)                      X = (A B C)
Y = D                            Y = D
:BT                              :EDIT
                                 *(SW 2 3)
FOO                              *OK
SETQ                             FIE{foot 
{lisp X} and {lisp Y} have not been changed, but {index BRKEXP Var}{var BRKEXP} has.}{comment endfootnote}
COND                             :OK
PROG                             FOO
FIE
COND            {it find which function}
                {it FOO is called from}
                {it (aborted with ↑E)}
:EDITF(FIE)
EDIT
*F FOO P
(FOO V U)	  {it edit it}
*(SW 2 3)
*OK
FIE
:(SETQ Y X)	{it reset X and Y}
(A B C)
:(SETQQ X D)
D
:?=
X = D
Y = (A B C)	{it check them}
:OK
FOO}



{index *END* break commands}

}{End SubSec Breaks}





{Begin SubSec When to Break}
{Title When to Break}
{Text

{Tag WhenToBreak}

When an error occurs, the system has to decide whether to reset and unwind the stack, or go into a break.  In the middle of a complex computation, it is usually helpful to go into a break, so that the user may examine the state of the computation.  However, if the computation has only proceeded a little when the error occurs, such as when the user mistypes a function name, the user would normally just terminate a break, and it would be more convenient for the system to simply cause an error and unwind the stack in this situatuation.  The decision over whether or not to induce a break depends on the depth of computation, and the amount of time invested in the computation.  The actual algorithm is described in detail below; suffice it to say that the parameters affecting this decision have been adjusted empirically so that trivial type-in errors do not cause breaks, but deep errors do.


{FnDef {Name BREAKCHECK} {Args ERRORPOS ERXN}
{Text
{fn BREAKCHECK} is called by the error routine to decide whether or not to induce a break when a error occurs.  {arg ERRORPOS} is the stack position at which the error occurred; {arg ERXN} is the error number.  Returns {lisp T} if a break should occur; {lisp NIL} otherwise.

{fn BREAKCHECK} returns {lisp T} (and a break occurs) if the "computation depth" is greater than or equal to {index *PRIMARY* HELPDEPTH Var}{var HELPDEPTH}.  {var HELPDEPTH} is initially set to 7, arrived at empirically by taking into account the overhead due to {fn LISPX} or {fn BREAK}.

If the depth of the computation is less than {var HELPDEPTH}, {fn BREAKCHECK} next calculates the length of time spent in the computation.  If this time is greater than {index *PRIMARY* HELPTIME Var}{var HELPTIME} milliseconds, initially set to 1000, then {fn BREAKCHECK} returns {lisp T} (and a break occurs), otherwise {lisp NIL}.
}}


{fn BREAKCHECK} determines the "computation depth" by searching back up the stack looking for an {fn ERRORSET}{index ERRORSET FN} frame ({fn ERRORSET}s indicate how far back unwinding is to take place when an error occurs, see {PageRef Fn ERRORSET}).  At the same time, it counts the number of internal calls to {fn EVAL}.  As soon as (if) the number of calls to {fn EVAL} exceeds {var HELPDEPTH}, {fn BREAKCHECK} immediately stops searching for an {fn ERRORSET} and returns {lisp T}.  Otherwise, {fn BREAKCHECK} continues searching until either an {fn ERRORSET} is found or the top of the stack is reached.  (Note:  If the second argument to {fn ERRORSET} is {lisp INTERNAL}, the {fn ERRORSET} is ignored by {fn BREAKCHECK} during this search.)  {fn BREAKCHECK} then counts the number of function calls between the error and the last {fn ERRORSET}, or the top of the stack.  The number of function calls plus the number of calls to {fn EVAL} (already counted) is used as the "computation depth".


{fn BREAKCHECK} determines the computation time by subtracting the value of the variable {index *PRIMARY* HELPCLOCK Var}{var HELPCLOCK} from the value of {lisp (CLOCK 2)}, the number of milliseconds of compute time (see {PageRef Fn CLOCK}).  {var HELPCLOCK} is rebound to the current value of {lisp (CLOCK 2)} for each computation typed in to {fn LISPX} or to a break.  The time criterion for breaking can be suppressed by setting {var HELPTIME} to {lisp NIL} (or a very big number), or by setting {var HELPCLOCK} to {lisp NIL}.  Note that setting {var HELPCLOCK} to {lisp NIL} will not have any effect beyond the current computation, because {var HELPCLOCK} is rebound for each computation typed in to {fn LISPX} and {fn BREAK}.


The user can suppress all error breaks by setting the top level binding of the variable {index *PRIMARY* HELPFLAG Var}{var HELPFLAG} to {lisp NIL} using {fn SETTOPVAL} ({var HELPFLAG} is bound as a local variable in {fn LISPX}, and reset to the global value of {var HELPFLAG} on every {fn LISPX} line, so just {fn SETQ}ing it will not work.)  If {var HELPFLAG}={lisp T} (the initial value), the decision whether to cause an error or break is decided based on the computation time and the computation depth, as described above.  Finally, if {var HELPFLAG}={lisp BREAK!}, a break will always occur following an error.


}{End SubSec When to Break}