[comp.lang.pascal] Dynamic exception handling for Extended Pascal

bobd@ihf1.UUCP (Bob Dietrich) (03/28/89)

Here is a simplistic proposal for dynamic exception handling that I
included as part of my public comment on Extended Pascal.  It was not
accepted nor dealt with in detail (as I expected), but it was retained
for future study.

The basic approach is to define a required (module) interface that allows you
to dynamically push and pop exception handlers. An exception handler is
really just a user-defined procedure which receives information about the
exception that caused the procedure to be invoked. Before the handler is
invoked, it is removed from the handler stack, so exiting the handler causes
the previous (or default) handler to be activated for the same exception.
No ability to "retry" a failed operation is provided since it was felt too
difficult to provide in a machine-independent manner.

Beware, all Ye who enter here: Standardese lies ahead. There is an example
at the end, however.

				Bob Dietrich
				Intel Corporation, Hillsboro, Oregon
				(503) 696-2092
		usenet:		uunet!littlei!intelhf!ihf1!bobd
		  or		tektronix!tessi!agora!ihf1!bobd
		  or		tektronix!ogccse!omepd!ihf1!bobd
		  or		tektronix!psu-cs!omepd!ihf1!bobd

===================
Simplistic Dynamic Exception Handling

This proposal draws heavily from JPC/84-151 Exception Handling Report,
which details a static scheme that JPC chose to exclude from Extended
Pascal.  As a dynamic facility, this proposal does exhibit many of the
drawbacks  cited in the Exception Handling Report, but this proposal
might have a slightly higher chance of approval. Dynamic exception
handling has also be written as a required interface so that at some
time in the future it can be replaced by the static proposal without
too many headaches.

Text to be modified [based on the current draft proposed Extended Pascal
document JPC/88-151]:

(1) Add the following clause to 5.1(f):

(5) the processor shall cause an exception (see 6.2.5) corresponding to the
violation;

(2) Add a third note to 6.2.2.19:

(3) The required identifier Dynamic_Exceptions and its constituent-identifiers
are not included, since Dynamic_Exceptions denotes a required interface (see
6.11.4.3).

(3) Add a new section 6.2.5:

6.2.5 Exceptions. An exception  shall be a state of execution that occurs when
an error is detected during execution of a program. An exception shall be
designated raised  when the processor detects the corresponding error or by
invocation of a procedure that causes the processor to go into a state similar
to that of detecting an error. An exception handler is a procedure that is
activated by the processor as a result of an exception being raised. Normal
execution shall resume when an exception handler for the associated exception is
activated (see 6.11.4.3).

The exception names and the errors causing the exception shall be as follows:

	Insert here section 6.6.X from 84-151, changing "state of
	execution" to "error" throughout. Note that as they exist in
	84-151, the statements of error are not complete; they need to
	be updated to indicate some additional errors and the newer I/O
	procedures and functions.

	[For those who have never seen this document, it essentially
	enumerates an exception for each error, such as Divide_By_Zero.
	Also, 84-151 was not based on Extended Pascal, which adds some errors
	to those of Pascal.]

Continuing the text to be added:

Notes:
(1) Because the detection of errors may vary from processor to processor, so may
the invocation of an exception handler.
(2) An implementation may cause an exception when an implementation-defined
limit is exceeded.

(4)  Add a new section 6.11.4.3:

6.11.4.3  Dynamic_Exceptions. 

6.11.4.3.1  General.   The required interface-identifier Dynamic_Exceptions
shall denote the required interface composed of the required constituents
Push_Handler, Pop_Handler, Raise_Exception, Handler_Exists, Exception_Codes,
Exception_Set, Exception_Info, and the exceptions named in section 6.2.5. The
constituent-identifiers shall denote a procedure, procedure, procedure,
function, type, type, type, and values, respectively.

6.11.4.3.2  Interface Constituents.  The required type-identifier
Exception_Codes shall denote the enumeration-type whose values are denoted by
the exception names as specified in section 6.2.5. The ordering of the values
shall be implementation-defined.

The required type-identifier Exception_Set shall denote a set-type designated
packed whose base-type is the type Exception_Codes.

The required type-identifier Exception_Info shall denote a record-type
designated packed. The required field-identifier cause shall denote an
associated required field of the record-type, and that field shall have the type
denoted by the required type-identifier Exception_Codes.

      Note: A processor may provide additional fields as an extension.

The required procedure-identifier Push_Handler shall denote the corresponding
required procedure possessing a procedure-heading equivalent to:

procedure Push_Handler(eset: Exception_Set;
                       procedure handler( var i: Exception_Info));

The effect of invoking the required procedure Push_Handler shall be to save the
current exception handler, if any, for the exceptions designated by eset, and
the procedure denoted by the procedural parameter shall become the exception
handler for the designated exceptions. The current exception handler prior to
any invocations of Push_Handler shall be implementation-defined.

The required procedure-identifier Pop_Handler shall denote the corresponding
required procedure possessing a single value parameter. The type of the formal
parameter associated with the value parameter shall be denoted by the
required-type Exception_Set.

The effect of invoking Pop_Handler shall be to replace the current exception
handler for the exceptions designated by the value parameter with the
previously-current exception handler for those exceptions. It shall be an error
if there is no previous exception handler for the designated exceptions.

The required procedure-identifier Raise_Exception shall denote the corresponding
required procedure possessing a single value parameter. The type of the formal
parameter associated with the value parameter shall be denoted by the
required-type Exception_Set.

The effect of invoking Raise_Exception shall be to raise the exception
designated by the cause field of the value parameter, and to pass the value
parameter as the actual parameter to the current exception handler for the
designated exception.
 
The required function-identifier Handler_Exists shall denote the corresponding
required function returning a value of type Boolean and possessing a single
value parameter. The type of the formal parameter associated with the value
parameter shall be denoted by the required-type Exception_Set.

The result of invoking Handler_Exists shall be true if there exists a current
exception handler for the exceptions designated by the value parameter, and
false otherwise.

6.11.4.3.3  Exception Handlers.  Prior to the activation of the block of an
exception handler made current by Push_Handler, the equivalent of invoking
Pop_Handler shall be performed for the exception that has been raised, and the
cause field of the actual parameter to the exception handler shall be attributed
a value of type Error_Codes that designates the raised exception. If no
exception handler is currently associated with the exception that has been
raised, the processor shall report the lack of an exception handler and
terminate the program.

The activation of an exception handler shall terminate in one of the following
ways.

(1) Execution of the halt procedure (see 6.7.5.7).

(2) Execution of a goto-statement (see 6.9.2.4) to a program-point not contained
by the block of the exception handler.

(3) Completion of the algorithm of the exception handler.

Completion of the algorithm of the exception handler shall cause the exception
that caused the activation of the exception handler to be raised again.

6.11.4.3.4  Exception Handling Example.  

program average( input, output);

  import Dynamic_Exceptions;

  label 9999; { When input finished }

  var
     sum: integer value 0;
     count: 0..maxint;

  function GetInteger: integer;
    label 1; { To retry read operation }
    var number: integer;

    procedure BadInput( var info: Exception_Info);
      begin
        writeln(output, 'The number was incorrectly formatted.');
        writeln(output, 'Please try again');
        goto 1;
      end;

    begin
    1:
      Push_Handler( [Conversion_Error], BadInput);
      read(input, number);
      GetInteger := number;
      Pop_Handler( [Conversion_Error]);
    end;

    procedure Unexpected_EOF( var info: Exception_Info );
      begin
        goto 9999 { Treat as normal eof }
      end;

    procedure BadComputation( var info: Exception_Info );
      begin
        write(output, 'Error in computation due to ');
        case info.cause of
          Overflow: write(output, 'overflow');
          Divide_By_Zero: write(output, 'division by zero');
          Undefined_Result: write(output, 'invalid function call');
          otherwise
            write(output, 'program bug')
        end;
        writeln( output);
        writeln( output, 'Cannot continue: Good bye');
        halt;
      end;

    begin
      Push_Handler( [End_of_File], Unexpected_EOF);
          { Note the following set is equivalent to
            [Overflow, Divide_By_Zero, Invalid_Operand, Invalid_Argument,       
     	     Incompatible _Argument, Undefined_Result, Nonexistent_Result]
	  }
      Push_Handler( [Operator_Error, Activation_Error], BadComputation);
      count := 0;
      while not eof(input) do
        begin
          count := count + 1;
          write(output, 'Please enter a number: ');
          sum := sum + GetInteger;
        end;
   9999:
      writeln(output, 'The average is ', sum div count);
    end.