Skip to content

Error Trapping Tutorial for IPSA APL

Vector 6:3, page 102

programming techniques

Transcribed from the printed issue; not yet reviewed. Read it in the PDF of the issue, page 102

Abstract

This article describes the event trapping facilities available in Sharp APL. As the basic elements of event trapping were covered by the articles in VECTOR 6.1, they are not repeated here. Readers wanting to refresh their memory should use Pauline Brand’s article on error trapping in Dyalog, as Sharp APL, like Dyalog, traps specific events rather than ‘any error’. This article does not cover the use of error trapping in areas specific to Sharp APL such as batch and non-terminal tasks.

Errors and Events

In Sharp APL we trap events, not just errors. Events include interrupts and ‘return to immediate execution’. When we trap an event, we simply specify an alternate set of actions to be performed when an event occurs. This is appropriate when

(i) a particular event does not always happen, or may happen at any time; and
(ii) we know the required action when it does happen.

Handling of Untrapped Events

If an untrapped event occurs, then APL execution halts, and an error message is displayed in the usual manner. The text of this error message is stored in the system variable ⎕er, which is normally a 3-row character matrix.

The rows of ⎕er contain the event number and an event title, the function line or APL statement in which the error occurred, and the caret indicating the point reached by the APL interpreter.

Event Numbers

Event numbers are divided into the following four groups (The number ranges shown are those reserved for event groups, not the ones actually used):

1-499 ……… Errors detected by the system
These include some events (e.g. FILE SYSTEM NOT AVAILABLE) which can’t be fixed by actions taken inside the active workspace.
500-999 ……… User-defined errors
System function ⎕signal can be used to signal an error in the same fashion as an error detected by the system, and such errors can be trapped. Any event number from 1 to 999 can be used by ⎕signal, but it is bad practice to use numbers below 501.
1000-1999 ……… Interrupts
These are signals which cause execution to be halted, and are classified by the activity in progress (e.g. INPUT INTERRUPT), as well as the type of interrupt.
2001 ……… Return to immediate execution
This event does not cause ⎕er to be set, but can be trapped. The range of actions which can be carried out when it is detected is restricted. This event is often trapped as a security measure in systems where the user should not be using immediate execution mode.

Using ⎕signal for User-defined Errors

Syntax: [error text] ⎕signal event

where error text is optional, and event is treated like a right argument of ‘branch’ - if it is empty, no error is signalled, otherwise the first number is used, and the event is signalled in the same way as an error detected by the system, with the optional left argument error text included in ⎕er. User-defined errors can be trapped.

The format of ⎕trap

Each ⎕trap setting can include any number of trap definitions. The data assigned to ⎕trap may be a character matrix or character vector. The matrix form contains one trap definition per line. The vector form (which is more commonly used) must contain a delimiter as its first character.

The Format of Trap Definitions

Each trap definition contains at least one part, and two optional others, separated by blanks:

  1. Event number or numbers (optional) - a list of event numbers or ranges of numbers to which the definition applies. If omitted, a default set of event numbers (depending on action code) is used.
  2. Action code (mandatory) - a single letter, optionally preceded by an action code modifier.
  3. Trap line or keyword (optional for some actions). Tells the system what to do following the event, and how to resume execution if this is required. A trap line may use multiple statements separated by diamonds.

Treatment of ‘bad’ ⎕trap Settings

If a ⎕trap setting does not obey these rules (e.g. if the delimiter is omitted from the start of a vector trap setting), then it is set by the system to an empty vector - no error is signalled. Because trap lines are not used until the relevant event occurs, a trap line which causes an error will not come to light until the trap line is tested or the error occurs. Moral: test your trap lines!

How the System Decides Which ⎕trap to Use

When a trappable event occurs, the system searches the most local value of ⎕trap (from left to right) until it finds an action for the event. If it finds none, it continues searching in the next most local shadowed value, until either a value is found or the stack is exhausted. This search does not of itself affect the state indicator or ⎕LC. This treatment is different to that of other local variables, and means that:

  1. As long as a trap definition exists for a particular event, it applies unless a more local definition supersedes it.
  2. We cannot suspend event trapping by setting a localised ⎕trap to an empty vector, but in any particular function we can replace the standard trap definitions by using trap definitions with event ranges covering all events.

Possible Actions in Trap Definitions

There are six possible action codes and one action code modifier, which are used in definitions as follows:

Format Name
[evnums] [O] C [trap line] Cut
[evnums] [O] E [trap line] Execute
[evnums] [O] N Next
[evnums] [O] S Stop
[evnums] [O] D [keyword] Do
6 [O] I execute In mid-line

Where evnums is a an event number list or range.

C - Cut back the stack and execute trap line
If an event in evnums is detected, all functions called later than the one in which this definition is localised are removed from the SI stack. The state indicator and ⎕LC are amended, and ⎕er is set. Note that any line label included in the trap definition is interpreted in the context of the function where the definition is localised, not the function where the error is detected. Trap settings which use action code C should be localised.
E - Execute trap line

If an event in evnums is detected, the system immediately sets ⎕er, and then executes the trap line, acting as if execution had been halted and the trap line entered at the terminal. This means that any line labels in this setting are interpreted in the context of the function in which the error occurred, not necessarily the one containing the trap definition. This means that the only branch whose effect is certain is to ⎕lc.

Example - using a trap definition in a ‘main’ function to handle errors which are not trapped at a more local stack level:

⎕trap←'∇ 900 C →errline ∇ 0 e errlog ⋄ ⎕signal 900'

(errlog is a function which files details of the error, errline is a line label in this function. Event number 0 denotes the range 1-999.)

N - Skip to next level
If an error in evnums is detected, the system abandons the search for trap definitions at this level of localisation, and moves to the next one where ⎕trap is localised. It can be used just before a trap definition which uses a range of event numbers, in order to remove a few event numbers from the range.
S - Stop
When an event in evnums is detected, the system halts work and displays ⎕er. This action code is mainly used in debugging to suspend the trapping of certain events.
D - Do keyword

This is the only action which may be used with event 2001 (return to immediate execution). If no keyword is used, no action is taken. Valid keywords are:

EXIT ……… signals event 2001 to the next level of localisation, where event 2001 may be trapped in turn.
CLEAR ……… clears the active WS.
OFF ……… does an automatic )OFF.

I - execute In mid-line

This code can only be used with event 6 (VALUE ERROR). It is similar to E except that (i) information about the execution of the current line is retained, (ii) if no branch is specified, execution is resumed at the same point as the error occurred, and (iii) an additional line is catenated onto ⎕er, containing the name of the missing object.

Example - use of a global trap definition to trigger a function which fetches objects from file if they are not in the WS when required:

⎕trap←'∇ 6 i fetch ⎕er[⎕io+3;]'
O - Only (Action code modifier)
This restricts a trap definition to the environment of the function in which it is set. This includes uses of ⍎ but not calls to other functions.

Watch out!

Event trapping is a double-edged sword, and if misused can cause as many problems as it solves. Some reasons for this are:

  1. It provides a method of branching which is not explicit in all the functions which could be affected.
  2. A trap line containing an error may not be executed for many months or years after it is written.
  3. Badly formed traps are ignored without comment.
  4. It is possible to write trap lines which cause endless loops.
  5. The system can see trap definitions which a programmer can’t. In a suspended function, ⎕trap entered at the terminal will only show definitions at the current stack level.

Anyone about to embark on using ⎕trap is well advised to read the advice in Chapter 13 of the Sharp APL reference manual on testing functions which use event trapping.

⎕ec - Environment Condition

This system variable exists to add an extra level of security in systems where it is desired to prevent users from interrupting processing. Although ⎕trap can be used in the first line of a function to trap interrupts, it is possible to generate an interrupt before ⎕trap has been assigned, and thus suspend the function.

⎕ec may be set to 0 (the global default) or 1. Whenever it is localised in a function, the system immediately sets it to 1 when the function starts execution. If any error occurs when ⎕ec is 1, the SI stack is cut back one level, and the error is signalled again. If ⎕ec is 1 at any level of the stack, this process is repeated until ⎕ec is zero at all stack levels.

ATTENTION (event 1002) is ignored , but left pending and responded to as soon as ⎕ec is zero at all SI levels. Other Interrupt events are treated like errors. Return to immediate execution and stops via s∆ are treated normally, which allows us to use stop vectors for debugging purposes without removing assignments of ⎕ec.

How ⎕ec Works when Event Traps are Set

When an event occurs, each level of the SI stack is examined, starting at the most local level. The following tests are made, and if neither is satisfied at any level, the default action for handling events is taken.

(1) Does the visible ⎕trap contain a definition for the event? If so, and the visible trap definition is localised at this level, execute the trap line.
(2) Is ⎕ec localised at this level? If so and it is set to 1, handle the event as in (1). If it is 0, continue the search at the next level.

Summary

Event trapping is a very useful facility, provided it is used correctly. In combination with ⎕ec, it can be used to make systems secure, and to ensure that users are not required to type APL expressions or system commands.