;;; -*- Mode:gate; Fonts:(HL12 HL12I HL12B CPTFONTB HL12BI HL12B HL12I ) -*- =Node: 4Proceeding* =Text: 3PROCEEDING* Both condition handlers and the user (through the debugger) have the option of proceeding certain conditions. Each condition name can define, as a convention, certain 1proceed types*, which are keywords that signify a certain conceptual way to proceed. For example, condition name 2sys:wrong-type-argument* defines the proceed type 2:argument-value* which means, ``Here is a new value to use as the argument.'' Each signaler may or may not implement all the proceed types which are meaningful in general for the condition names being signaled. For example, it is futile to proceed from a 2sys:wrong-type-argument* error with 2:argument-value* unless the signaler knows how to take the associated value and store it into the argument, or do something else that fits the conceptual specifications of 2:argument-value*. For some signalers, it may not make sense to do this at all. Therefore, one of the arguments to 2signal-condition* is a list of the proceed types that this particular signaler knows how to handle. In addition to the proceed types specified by the individual signaler, other proceed types can be provided nonlocally; they are implemented by a 1resume handler* which is in effect through a dynamic scope. See below, 4(DEBUGGING-3)Nonlocal Proceed Types*. A condition handler can use the operations 2:proceed-types* and 2:proceed-type-p* on the condition instance to find out which proceed types are available. It can request to proceed by returning one of the available proceed types as a value. This value is returned from 2signal-condition*, and the condition's signaler can take action as appropriate. If the handler returns more than one value, the remaining values are considered 1arguments* of the proceed type. The meaning of the arguments to a proceed type, and what sort of arguments are expected, are part of the conventions associated with the condition name that gives the proceed type its meaning. For example, the 2:argument-value* proceed type for 2sys:wrong-type-argument* errors conventionally takes one argument, which is the new value to use. All the values returned by the handler are returned by 2signal-condition* to the signaler. Here is an example of a condition handler that proceeds from 2sys:wrong-type-argument* errors. It makes any atom effectively equivalent to 2nil* when used in 2car* or any other function that expects a list. The handler uses the 2:description* operation, which on 2sys:wrong-type-argument* condition instances returns a keyword describing the data type that was desired. 3(condition-bind* 3 ((sys:wrong-type-argument* 3 #'(lambda (condition)* 3 (if (eq (send condition :description) 'cons)* 3 (values :argument-value nil)))))* 1 body3...)** Here the argument to the 2:argument-value* proceed type is 2nil*. 3:proceed-types* 1Operation on 2condition** Returns a list of the proceed types available for this condition instance. This operation should be used only within the signaling of the condition instance, as it refers to the special variable in which 2signal-condition* stores its second argument. 3:proceed-type-p* 1proceed-type* 1Operation on 2condition** 2t* if 1proceed-type* is one of the proceed types available for this condition instance. This operation should be used only within the signaling of the condition instance, as it refers to the special variable in which 2signal-condition* stores its second argument. =Node: 4Proceeding and the Debugger* =Text: 3PROCEEDING AND THE DEBUGGER* If the condition invokes the debugger, then the user has the opportunity to proceed. When the debugger is entered, the available proceed types are assigned command characters starting with 2Super-A*. Each character becomes a command to proceed using the corresponding proceed type. Three additional facilities are required to make it convenient for the user to proceed using the debugger. Each is provided by methods defined on condition flavors. When you define a new condition flavor, you must provide methods to implement these facilities. Documentation: It must be possible to tell the user what each proceed type is 1for*. Prompting for arguments: The user must be asked for the arguments for the proceed type. Each proceed type may have different arguments to ask for. Not always the same proceed types: Usually the user can choose among the same set of proceed types that a handler can, but sometimes it is useful to provide the user with a few extra ones, or to suppress some of them for him. These three facilities are provided by methods defined on condition flavors. Each proceed type that is provided by signalers should be accompanied by suitable methods. This means that you must normally define a new flavor if you wish to use a new proceed type. The 2:document-proceed-type* operation is supposed to print documentation of what a proceed type is for. For example, when sent to a condition instance describing an unbound variable error, if the proceed type specified is 2:new-value*, the text printed is something like ``Proceed, reading a value to use instead.'' 3:document-proceed-type* 1proceed-type* 1stream* 1Operation on 2condition** Prints on 1stream* a description of the purpose of proceed type 1proceed-type*. This operation uses 2:case* method combination (see 4(FLAVOR-4)Method Combination*), to make it convenient to define the way to document an individual proceed type. The string printed should start with an imperative verb form, capitalized, and end with a period. Example: This example is an 2:or* method so that it can consider any proceed type. If it returns non-2nil*, the system considers that it has handled the proceed type and no other methods get a chance. 2eh:places* is an instance variable of the flavor 2sys:failed-assertion*; its values are the proceed types this method understands. 3(defmethod (sys:failed-assertion :or :document-proceed-type)* 3 (proceed-type stream ignore)* 3 (when (memq proceed-type eh:places)* 3 (format stream* 3 "Try again, setting ~S.~* 3 You type an expression for it."* 3 proceed-type)* 3 t))* As a last resort, if the condition instance has a 2:case* method for 2:proceed-asking-user* with 1proceed-type* as the suboperation, and this method has a documentation string, it is printed. This is in fact the usual way that a proceed type is documented. The 2:proceed-asking-user* operation is supposed to ask for suitable arguments to pass with the proceed type. Sending 2:proceed-asking-user* to an instance of 2sys:unbound-variable* with argument 2:new-value* would read and evaluate one expression, prompting appropriately. 3:proceed-asking-user* 1proceed-type* 1continuation* 1read-object-fn* 1Operation on 2condition** The method for 2:proceed-asking-user* embodies the knowledge of how to prompt for and read the additional arguments that go with 1proceed-type*. 2:case* method combination is used (see 4(FLAVOR-4)Method Combination*), making it possible to define the handling of each proceed type individually in a separate function. The documentation string of the 2:case* method for a proceed type is also used as the default for 2:document-proceed-type* on that proceed type. The argument 1continuation* is an internal function of the debugger which actually proceeds from the signaled condition if the 2:proceed-asking-user* method calls it. This is the only way to cause proceeding actually to happen. Call 1continuation* with 2funcall*, giving a proceed type and suitable arguments. The proceed type passed to 1continuation* need not be the same as the one given to 2:proceed-asking-user*; it should be one of the proceed types available for handlers to use. Alternatively, the 2:prompt-and-read* method can return without calling 1continuation*; then the debugger continues to read commands. The options which the 2fs:no-more-room* condition offers in the debugger, to run Dired or expunge a directory, work this way. The argument 1read-object-fn* is another internal function of the debugger whose purpose is to read arguments from the user or request confirmation. If you wish to do those things, you must 2funcall* 1read-object-function* to do it. Use the calling sequence documented for the function 2prompt-and-read* (see 4(IOSYSTEM-1)Interactive Input with Prompting*). (The 1read-object-fn* may or may not actually use 2prompt-and-read*.) Here is how 2sys:proceed-with-value-mixin* provides for the proceed type 2:new-value*. Note the documentation string, which is automatically use by 2:document-proceed-type* since no 2:case* method for that operation is provided. 3(defmethod (proceed-with-value-mixin * 3 :case :proceed-asking-user :new-value)* 3 (continuation read-object-function)* 3 "Return a value; the value of an expression you type."* 3 (funcall continuation :new-value* 3 (funcall read-object-function* 3 :eval-read* 3 "~&Form whose value to use instead: ")))* The 2:user-proceed-types* operation is given the list of proceed types actually available and is supposed to return the list of proceed types to offer to the user. By default, this operation returns its argument: all proceed types are available to the user through the debugger. For example, the condition name 2sys:unbound-variable* conventionally defines the proceed types 2:new-value* and 2:no-action*. The first specifies a new value; the second attempts to use the variable's current value and gets another error if the variable is still unbound. These are clean operations for handlers to use. But it is more convenient for the user to be offered only one choice, which will use the variable's new value if it is bound now, but otherwise ask for a new value. This is implemented with a 2:user-proceed-types* method that replaces the two proceed types with a single one. Or, you might wish to offer the user two different proceed types that differ only in how they ask the user for additional information. For handlers, there would be only one proceed type. Finally, there may be proceed types intended only for the debugger which do not actually proceed; these should be inserted into the list by the 2:user-proceed-types* method. 3:user-proceed-types* 1proceed-types* 1Operation on 2condition** Assuming that 1proceed-types* is the list of proceed types available for condition handlers to return, this operation returns the list of proceed types that the debugger should offer to the user. Only the proceed types offered to the user need to be handled by 2:document-proceed-type* and 2:proceed-asking-user*. The flavor 2condition* itself defines this to return its argument. Other condition flavors may redefine this to filter the argument in some appropriate fashion. 2:pass-on* method combination is used (see 4(FLAVOR-4)Method Combination*), so that if multiple mixins define methods for 2:user-proceed-types*, each method gets a chance to add or remove proceed types. The methods should not actually modify the argument, but should cons up a new list in which certain keywords are added or removed according to the other keywords that are there. Elements should be removed only if they are specifically recognized. This is to say, the method should make sure that any unfamiliar elements present in the argument are also present in the value. Arranging to omit certain specific proceed types is legitimate; returning only the intersection with a constant list is not legitimate. Here is an example of nontrivial use of 2:user-proceed-types*: 3(defflavor my-error () (error))* 3(defmethod (my-error :user-proceed-types) (proceed-types)* 3 (if (memq :foo proceed-types)* 3 (cons :foo-two-args proceed-types)* 3 proceed-types))* 3(defmethod (my-error :case :proceed-asking-user :foo) * 3 (cont read-object-fn)* 3 "Proceeds, reading a value to foo with."* 3 (funcall cont :foo* 3 (funcall read-object-fn :eval-read* 3 "Value to foo with: ")))* 3(defmethod (my-error :case :proceed-asking-user :foo-two-args)* 3 (cont read-object-fn)* 3 "Proceeds, reading two values to foo with."* 3 (funcall cont :foo* 3 (funcall read-object-fn :eval-read* 3 "Value to foo with: ")* 3 (funcall read-object-fn :eval-read* 3 "Value to foo some more with: ")))* In this example, if the signaler provides the proceed type 2:foo*, then it is described for the user as ``proceeds, reading a value to foo with''; and if the user specifies that proceed type, he is asked for a single value, which is used as the argument when proceeding. In addition, the user is offered the proceed type 2:foo-two-args*, which has its own documentation and which reads two values. But for condition handlers there is really only one proceed type, 2:foo*. 2:foo-two-args* is just an alternate interface for the user to proceed type 2:foo*, and this is why the 2:user-proceed-types* method offers 2:foo-two-args* only if the signaler is willing to accept 2:foo*. =Node: 4How Signalers Provide Proceed Types* =Text: 3HOW SIGNALERS PROVIDE PROCEED TYPES* Each condition name defines a conceptual meaning for certain proceed types, but this does not mean that all of those proceed types may be used every time the condition is signaled. The signaler must specifically implement the proceed types in order to make them do what they are conventionally supposed to do. For some signalers it may be difficult to do, or may not even make sense. For example, it is no use having a proceed type 2:store-new-value* if the signaler does not have a suitable place to store, permanently, the argument the handler supplies. Therefore, we require each signaler to specify just which proceed types it implements. Unless the signaler explicitly specifies proceed types one way or another, no proceed types are allowed (except for nonlocal ones, described in the following section). One way to specify the proceed types allowed is to call 2signal-condition* and pass the list of proceed types as the second argument. Another way that is less general but more convenient is 2signal-proceed-case*. 3signal-proceed-case* 1((variables...)* 1make-condition-arguments...)* 1clauses...* 1Macro* Signals a condition, providing proceed types and code to implement them. Each clause specifies a proceed type to provide, and also contains code to be run if a handler should proceed with that proceed type. 3(signal-proceed-case ((1argument*-1vars...*)* 3 1signal-name* 1signal-name-arguments*...)* 3 (1proceed-type* 1forms*...)* 3 (1proceed-type* 1forms*...)* 3 ...)* A condition-object is created with 2make-condition* using the 1signal-name* and 1signal-name-arguments*; then it is signaled giving a list of the proceed types from all the clauses as the list of proceed types allowed. The variables 1argument-vars* are bound to the values returned by 2signal-condition*, except for the first value, which is tested against the 1proceed-type* from each clause, using a 2selectq*. The clause that matches is executed. Example: 3(defsignal my-wrong-type-arg * 3 (eh:wrong-type-argument-error sys:wrong-type-argument)* 3 (old-value arg-name description)* 3 "Wrong type argument from my own code.")* 3(signal-proceed-case* 3 ((newarg)* 3 'my-wrong-type-arg* 3 "The argument ~A was ~S, which is not a cons."* 3 'foo foo 'cons)* 3 (:argument-value (car newarg)))* The signal name 2my-wrong-type-arg* creates errors with condition name 2sys:wrong-type-argument*. The 2signal-proceed-case* shown signals such an error, and handles the proceed type 2:argument-value*. If a handler proceeds using that proceed type, the handler's value is put in 2newarg*, and then its car is returned from the 2signal-proceed-case*. =Node: 4Nonlocal Proceed Types* =Text: 3NONLOCAL PROCEED TYPES* When the caller of 2signal-condition* specifies proceed types, these are called 1local proceed types*. They are implemented at the point of signaling. There are also 1nonlocal proceed types*, which are in effect for all conditions (with appropriate condition names) signaled during the execution of the body of the establishing macro. We say that the macro establishes a 1resume handler* for the proceed type. The most general construct for establishing a resume handler is 2condition-resume*. For example, in 3(condition-resume* 3 '(fs:file-error :retry-open t* 3 ("Proceeds, opening the file again.")* 3 (lambda (ignore) (throw 'tag nil)))* 3 (do-forever* 3 (catch 'tag (return (open pathname)))))* the proceed type 2:retry-open* is available for all 2fs:file-error* conditions signaled within the call to 2open*. 3condition-resume* 1handler-form* &body 1body* 1Macro* 3condition-resume-if* 1cond-form* 1handler-form* &body 1body* Both execute 1body* with a resume handler in effect for a nonlocal proceed type according to the value of 1handler-form*. For 2condition-resume-if*, the resume handler is in effect only if 1cond-form*'s value is non-2nil*. The value of the 2handler-form* should be a list with at least five elements: 3(1condition-names* 1proceed-type* 1predicate* 1format-string-and-args* * 3 1handler-function* 1additional-args*...)* 1condition-names* is a condition name or a list of them. The resume handler applies to these conditions only. 1proceed-type* is the proceed type implemented by this resume handler. 1predicate* is either 2t* or a function that is applied to a condition instance and determines whether the resume handler is in effect for that condition instance. 1format-string-and-args* is a list of a string and additional arguments that can be passed to 2format* to print a description of what this proceed type is for. These are needed only for anonymous proceed types. 1handler-function* is the function called to do the work of proceeding, once this proceed type has been returned by a condition handler or the debugger. 2catch-error-restart-explicit-if* makes it easy to establish a particular simple kind of resume handler. 3catch-error-restart-explicit-if* 1cond-form* 1Macro* 1(condition-names* 1proceed-type* 1format-string* 1format-args...)* 1body...* Executes 1body* with (if 1cond-form* produces a non-2nil* value) a resume handler for proceed type 1proceed-type* and condition(s) 1condition-names*. 1condition-names* should be a symbol or a list of symbols; it is not evaluated. 1proceed-type* should be a symbol. If proceeding is done using this resume handler, control returns from the 2catch-error-restart-explicit-if* form. The first value is 2nil* and the second is non-2nil*. 1format-string* and the 1format-args*, all of which are evaluated, are used by the 2:document-proceed-type* operation to describe the proceed type, if it is anonymous. For condition handlers there is no distinction between local and nonlocal proceed types. They are both included in the list of available proceed types returned by the 2:proceed-types* operation (all the local proceed types come first), and the condition handler selects one by returning the proceed type and any conventionally associated arguments. The debugger's 2:user-proceed-types*, 2:document-proceed-type* and 2:proceed-asking-user* operations are also make no distinction. The difference comes after the handler or the debugger returns to 2signal-condition*. If the proceed type is a local one (one of those in the second argument to 2signal-condition*), 2signal-condition* simply returns. If the proceed type is not among those the caller handles, 2signal-condition* looks for a resume handler associated with the proceed type, and calls its handler function. The arguments to the handler function are the condition instance, the 1additional-args* specified in the resume handler, and any arguments returned by the condition handler in addition to the proceed type. The handler function is supposed to do a throw. If it returns to 2signal-condition*, an error is signaled. You are allowed to use ``anonymous'' nonlocal proceed types, which have no conventional meaning and are not specially known to the 2:document-proceed-type* and 2:proceed-asking-user* operations. The anonymous proceed type may be any Lisp object. The default definition of 2:proceed-asking-user* handles an anonymous proceed type by simply calling the continuation passed to it, reading no arguments. The default definition of 2:document-proceed-type* handles anonymous proceed types by passing 2format* the 1format-string-and-args* list found in the resume handler (this is what that list is for). Anonymous proceed types are often lists. Such proceed types are usually made by some variant of 2error-restart*, and they are treated a little bit specially. For one thing, they are all put at the end of the list returned by the 2:proceed-types* operation. For another, the debugger command 2Control-C* or 2Resume* never uses a proceed type which is a list. If no atomic proceed type is available, 2Resume* or 2Control-C* is not allowed. 3error-restart* 1(condition-names* 1format-string* 1format-args...)* 1body...* 1Macro* 3error-restart-loop* 3catch-error-restart* 3catch-error-restart-if* 1cond-form* 1(condition-names* 1format-string* 1format-args...)* 1body...* All execute body with an anonymous resume handler for 1condition-names*. The proceed type for this resume handler is a list, so the 2Resume* key will not use it. 1condition-names* is either a single condition name or a list of them, or 2nil* meaning all conditions; it is not evaluated. 1format-string* and the 1format-args*, all of which are evaluated, are used by the 2:document-proceed-type* operation to describe the anonymous proceed type. If the resume handler made by 2error-restart* is invoked by proceeding from a signal, the automatically generated resume handler function does a throw back to the 2error-restart* and the body is executed again from the beginning. If body returns, the values of the last form in it are returned from the 2error-restart* form. 2error-restart-loop* is like 2error-restart* except that it loops to the beginning of body even if body completes normally. It is like enclosing an 2error-restart* in an iteration. 2catch-error-restart* is like 2error-restart* except that it never loops back to the beginning. If the anonymous proceed type is used for proceeding, the 2catch-error-restart* form returns with 2nil* as the first value and a non-2nil* second value. 2catch-error-restart-if* is like 2catch-error-restart* except that the resume handler is only in effect if the value of the 1cond-form* is non-2nil*. All of these variants of 2error-restart* can be written in terms of 2condition-resume-if*. These forms are typically used by any sort of command loop, so that aborting within the command loop returns to it and reads another command. 2error-restart-loop* is often right for simple command loops. 2catch-error-restart* is useful when aborting should terminate execution rather than retry, or with an explicit conditional to test whether a throw was done. 2error-restart* forms often specify 2(error sys:abort)* as the 1condition-names*. The presence of 2error* causes them to be listed (and assigned command characters) by the debugger, for all errors, and the presence of 2sys:abort* causes the 2Abort* key to use them. If you would like a procede type to be offered as an option by the debugger, but do not want the 2Abort* key to use it, specify just 2error*. 3eh:invoke-resume-handler* 1condition-instance* 1proceed-type* &rest 1args* Invokes the innermost applicable resume handler for 1proceed-type*. Applicability of a resume handler is determined by matching its condition names against those possessed by 1condition-instance* and by applying its predicate, if not 2t*, to 1condition-instance*. If 1proceed-type* is 2nil*, the innermost applicable resume handler is invoked regardless of its proceed type. However, in this case, the scan stops if 2t* is encountered as an element of 2eh:condition-resume-handlers*. 3eh:condition-resume-handlers* 1Variable* The current list of resume handlers for nonlocal proceed types. 2condition-resume* works by binding this variable. Elements are usually lists that have the format described above under 2condition-resume*. The symbol 2t* is also meaningful as an element of this list. It terminates the scan for a resume handler when it is made by 2signal-condition* for a condition that was not handled. 2t* is pushed onto the list by break loops and the debugger to shield the evaluation of your type-in from automatic invocation of resume handlers established outside the break loop or the error. The links of this list, and its elements, are often created using 2with-stack-list*. so be careful if you try to save the value outside the context in which you examine it. 3sys:abort* (3condition*) 1Condition* This condition is signaled by the 2Abort* key; it is how that key is implemented. Most command loops use some version of 2error-restart* to set up a resume handler for 2sys:abort* so that it will return to the innermost command loop if (as is usually the case) no handler handles it. These resume handlers usually apply to 2error* as well as 2sys:abort*, so that the debugger will offer a specific command to return to the command loop even if it is not the innermost one.