PreDebug.mesa
Copyright Ó 1990, 1991 by Xerox Corporation. All rights reserved.
Created by Christian Jacobi, July 19, 1990 3:35 pm PDT
Christian Jacobi, July 24, 1990 1:26:14 pm PDT
This interface should be used to give the user information about raised errors before entering the system debugger which might be too heavy weight for non-programmers or for casual users.
DIRECTORY
Rope USING [ROPE];
PreDebug: CEDAR DEFINITIONS = BEGIN
SIGANY: TYPE ~ UNSAFE SIGNAL ANY RETURNS ANY;
ERRANY: TYPE ~ UNSAFE ERROR ANY;
Useful for clients declaring errors
Explainer: TYPE = PROC [signalOrError: SIGANY, args: POINTER, registerData: REF] RETURNS [msg: Rope.ROPE ¬ NIL];
Procedures of this type convert uncaught errors and signals into ROPEs for human consumption. The arguments to the signal/error can be retrieved by a code fragment like this:
Raise[signalOrError, args ! SpecificError => {<<args available here>>; CONTINUE}];
RegisterSignalExplainer: PROC [signal: SIGANY, explain: Explainer ¬ NIL, data: REF ¬ NIL];
RegisterErrorExplainer: PROC [error: ERRANY, explain: Explainer ¬ NIL, data: REF ¬ NIL];
A registerd Explainer is called when a raised signal or error is not caught, just before calling the system debugger. It might also be called by certain clients providing a catch phrase. The returned rope is presented to the user. Explainers are procedures, not catch phrases, there are no GOTOs out of them.
It is ok for Explainers to raise errors, but watch for infinite recursion. However, in general Explainers should be fast and avoid raising further unintended errors: after all they are used to avoid some debugging sessions.
It is ok to use NIL for Explainer's if data is a Rope.ROPE or a REF TEXT.
Note that fancy packages might use ProcessProps-like mechanisms to pass more information about an error to a Explainer.
Registering an Explainer for a signal not implemented by the client itself is considered rude. There is only one registered Explainer for every signal; if this mechanism is only used by the signal implementor there will never be any confusion.
Raise: PROC [signalOrError: SIGANY, args: POINTER];
Raises the given signal/error with the given arguments (and no place to store results).
Should it not be possible to raise the signal/error, Raise acts as no op. (PrincOps!)
Useful for clients trying to handle errors
It might be handy for clients to handle uncaught errors explicitely: the client might have a designated high level feedback area ready.
Protect: PROC [inner: PROC, rejectP: PROC [Rope.ROPE] RETURNS [BOOL]] RETURNS [ok: BOOL];
Executes inner inside a catch phrase for all uncaught signals and errors. If a signal/error is caught, Protect obtains a human-readable explanation for it and calls rejectP with the explanation. If rejectP returns TRUE the error is rejected, else it is caught. ok means no error was caught.
Explain: PROC [signalOrError: SIGANY, args: POINTER] RETURNS [Rope.ROPE];
Gets a human-readable explanation for this uncaught signal/error if there is one; if there is no registered explainer, the result is pretty uninformative (as in "unrecognized error").
args is parameters from RuntimeError.UNCAUGHT: By declaring it to be a POINTER type the client has to loophole it and bear the unsafe-ness right where it belongs.
END.