Conditions
In this chapter, the REXX concept of "conditions" is described. Conditions allow the programmer to handle abnormal control flow, and enable him to assign special pieces of REXX code to be executed in case of certain incidences.
In the first section the concept of conditions is explained.
Then, there is a description of how a standard condition in REXX would work, if it existed.
In the third section, all the existing conditions in REXX are presented, and the differences compared to the standard condition described in the previous section are listed.
The fourth sections contains a collections of random notes on the conditions in REXX.
The last section describes differences, extensions and peculiarities in Regina on the of subject conditions, and the lists specific behavior.
In this section, the concept of "conditions" are explained: What they are, how they work, and what they mean in programming.
First, let's look at the terminology used in this chapter. If you don't get a thorough understanding of these terms, you will probably not understand much of what is said in the rest of this chapter.
[Incident:]
A situation, external or internal to the interpreter, which it is required to respond to in certain pre-defined manners. The interpreter recognizes incidents of several different types. The incident will often have a character of "suddenness", and will also be independent of the normal control flow.
[Event:]
Data Structure describing one incident, used as a descriptor to the incident itself.
[Condition:]
Names the REXX concept that is equivalent to the incident.
[Raise a Condition:]
The action of transforming the information about an incident into an event. This is done after the interpreter senses the condition. Also includes deciding whether to ignore or produce an event.
[Handle a Condition:]
The act of executing some pre-defined actions as a response to the event generated when a condition was raised.
[(Condition) Trap:]
Data Structure containing information about how to handle a condition.
[(Trap) State:]
Part of the condition trap.
[(Condition) Handler:]
Part of the condition trap, which points to a piece of REXX code which is to be used to handle the condition.
[(Trap) Method:]
Part of the condition trap, which defined how the condition handler is to be invoked to handle the condition.
[Trigger a Trap:]
The action of invoking a condition handler by the method specified by the trap method, in order to handle a condition.
[Trap a Condition:]
Short of trigger a trap for a particular condition.
[Current Trapped Condition:]
The condition currently being handled. This is the same as the most recent trapped condition on this or higher procedure level.
[(Pending) Event Queue:]
Data Structure storing zero or more events in a specific order. There are only one event queue. The event queue contains events of all condition types, which have been raised, but not yet handled.
[Default-Action:]
The pre-defined default way of handling a condition, taken if the trap state for the condition raised is OFF.
[Delay-Action:]
The pre-defined default action taken when a condition is raised, and the trap state is DELAY.
REXX Language Level 4.00 has six different conditions, and REXX Language Level 5.00 has seven. However, each of these is a special case of a mythical, non-existing, standard condition. In order to better understand the real conditions, we start by explaining how a standard condition work.
In the examples below, we will call our non-existing standard condition MYTH. Note that these examples will not be executable on any REXX implementation.
There are mainly five conceptual data structures involved in conditions.
[Event queue.]
There is one interpreter-wide queue of pending conditions. Raising a condition is identical to adding information about the condition to this queue (FIFO). The order of the queue is the same order in which the conditions are to be handled.
Every entry in the queue of pending conditions contains some information about the event: the line number of the REXX script when the condition was raised, a descriptive text and the condition type.
[Default-Action.]
To each, there exists information about the default-action to take if this condition is raised but the trap is in state OFF. This is called the "default-action". The standard default-action is to ignore the condition, while some conditions may abort the execution.
[Delay-Action.]
Each condition will also have delay-action, which tells what to do if the condition is raised when condition trap is in state DELAY. The standard delay-action is to queue the condition in the queue of pending conditions, while some conditions may ignore it.
[Condition traps.]
For each condition there is a trap which contains three pieces of status information: the state; the handler; and the method. The state can be ON, OFF or DELAY.
The handler names the REXX label in the start of the REXX code to handle the event. The method can be either SIGNAL or CALL, and denotes the method in which the condition is to be handled. If the state is OFF, then neither handler nor method is defined.
[Current Trapped Condition.]
This is the most recently handled condition, and is set whenever a trap is triggered. It contains information about method, which condition, and a context-dependent description. In fact, the information in the current trapped condition is the same information that was originally put into the pending event queue.
Note that the event queue is a data structure connected to the interpreter itself. You operate on the same event queue, independent of subroutines, even external ones. On the other hand, the condition traps and the current trapped condition are data structures connected to each single routine. When a new routine is called, it will get its own condition traps and a current trapped condition. For internal routines, the initial values will be the same values as those of the caller. For external routines, the values are the defaults.
The initial value for the event queue is to be empty. The default-action and the delay-action are static information, and will always retain their values during execution. The initial values for the condition traps are that they are all in state OFF. The initial value for the current trapped condition is that all information is set to the nullstring to signalize that no condition is currently being trapped.
How do you set the information in a condition trap? You do it with a SIGNAL or CALL clause, with the ON or OFF subkeyword. Remember that a condition trap contain three pieces of information? Here are the rules for how to set them:
To set the trap method, use either SIGNAL or CALL as keyword.
To set state to ON or OFF, use the appropriate subkeyword in the clause. Note that there is no clause or function in REXX, capable of setting the state of a trap to DELAY.
To set the condition handler, append the term "NAME handler" to the command. Note that this term is only legal if you are setting the state to ON; you can not specify a handler when setting the state to OFF.
The trap is said to be "enabled" when the state is either ON or DELAY, and "disabled" when the state is OFF. Note that neither the event queue, nor the current trapped condition can be set explicitly by REXX clauses. They can only be set as a result of incidents, when raising and trapping conditions.
It sounds very theoretical, doesn't it? Look at the following examples, which sets the trap MYTH:
/* 1 */ SIGNAL ON MYTH NAME TRAP_IT
/* 2 */ SIGNAL OFF MYTH
/* 3 */ CALL ON MYTH NAME MYTH_TRAP
/* 4 */ CALL ON MYTH
/* 5 */ CALL OFF MYTH
Line 1 sets state to ON, method to SIGNAL and handler to TRAP_IT. Line 2 sets state to OFF, handler and method becomes undefined. Line 3 sets state to ON, method to CALL, and handler to MYTH_TRAP. Line 4 sets state to ON, method to CALL and handler to MYTH (the default). Line 5 sets state to OFF, handler and method become undefined.
Why should method and handler become undefined when the trap in state OFF? For two reasons: firstly, these values are not used when the trap is in state OFF; and secondly, when you set the trap to state ON, they are redefined. So it really does not matter what they are in state OFF.
What happens to this information when you call a subroutine? All information about traps are inherited by the subroutine, provided that it is an internal routine. External routines do not inherit any information about traps, but use the default values. Note that the inheritance is done by copying, so any changes done in the subroutine (internal or external), will only have effect until the routine returns.
How do you raise a condition? Well, there are really no explicit way in REXX to do that. The conditions are raised when an incident occurs. What sort of situations that is, depends on the context. There are in general three types of incidents, classified by the origin of the event:
Internal origin. The incident is only dependent on the behavior of the REXX script. The SYNTAX condition is of this type.
External origin. The REXX script and the interpreter has really no control over when this incident. It happens completely independent of the control of the REXX script or interpreter. The HALT condition is of this type.
Mixed origin. The incident is of external origin, but the situation that created the incident, was an action by the REXX script or the interpreter. The ERROR condition is of this type: the incident is a command returning error, but it can only occur when the interpreter is executing commands.
For conditions trapped by method CALL, standard REXX requires an implementation to at least check for incidents and raise condition at clause boundaries. (But it is allowed to do so elsewhere too; although the actual triggering must only be performed at clause boundaries.) Consequently, you must be prepared that in some implementations, conditions trappable by method CALL might only be raised (and the trap triggered) at clause boundaries, even if they are currently trapped by method SIGNAL.
The seven standard conditions will be raised as result of various situations, read the section describing each one of them for more information.
+--------+ +----------+ /-----\ +--------+
|Incident| |Condition | / Trap \ Off |Default |
| occurs | -> |is raised | -> \ State / --> | action |
+--------+ +----------+ \-----/ +--------+
/ |
/On |Delay
/ |
/ v
+--------+/ /---------\ +------+
| Queue | Yes /DelayAction\ No |Ignore|
|an event| <-- \is queue? / --> | event|
+--------+ \---------/ +------+
|
v
/-------\
/Method is\
\ CALL? /
\-------/ \
/ \
/No Yes\
/ \ /---------\
/ \ / \
+-----------+ +-----------+ \ Decision /
| Set state | | Set state | \---------/
| OFF | | DELAY |
+-----------+ +-----------+ +-----------+
| Trigger | | | | |
| trap | | Return | | Action |
+-----------+ +-----------+ +-----------+
The triggering of a condition
When an incident occurs and the condition is raised, the interpreter will check the state of the condition trap for that particular condition at the current procedure level.
If the trap state is OFF, the default-action of the condition is taken immediately. The "standard" default-action is to ignore the condition.
If the trap state is DELAY, the action will depend on the delay-action of that condition. The standard delay-action is to ignore, then nothing further is done. If the delay-action is to queue, the interpreter continues as if the state was ON.
If the state of the trap is ON, an event is generated which describes the incident, and it is queued in the pending event queue. The further action will depend on the method of trapping.
If the method is CALL, the state of the trap will be set to DELAY. Then the normal execution is resumed. The idea is that the interpreter will check the event queue later (at a clause boundary), and trigger the appropriate trap, if it finds any events in the event queue.
Else, if method of trapping is SIGNAL, then the action taken is this: First set the trap to state OFF, then terminate clause the interpreter was executing at this procedure level. Then it explicitly trigger the condition trap.
This process has be shown in the figure above. It shows how an incident makes the interpreter raise a condition, and that the state of the condition trap determines what to do next. The possible outcomes of this process are: to take the default-action; to ignore if delay-action is not to queue; to just queue and the continue execution; or to queue and trigger the trap.
What are the situations where a condition trap might be triggered? It depends on the method currently set in the condition trap.
If the method is SIGNAL, then the interpreter will explicitly trigger the relevant trap when it has raised the condition after having sensed the incident. Note that only the particular trap in question will be triggered in this case; other traps will not be triggered, even if the pending event queue is non-empty.
In addition, the interpreter will at each clause boundary check for any pending events in the event queue. If the queue is non-empty, the interpreter will not immediately execute the next normal statement, but it will handle the condition(s) first. This procedure is repeated until there are no more events queued. Only then will the interpreter advance to execute the next normal statement.
Note that the REXX standard does not require the pending events to be handled in any particular order, although the model shown in this documentation it will be in the order in which the conditions were raised. Consequently, if one clause generates several events that raise conditions before or at the next clause boundary, and these conditions are trapped by method CALL. Then, the order on which the various traps are triggered is implementations-dependent. But the order in which the different instances of the same condition is handled, is the same as the order of the condition indicator queue.
Assume that a condition is being trapped by method SIGNAL, that the state is ON and the handler is MYTH_TRAP. The following REXX clause will setup the trap correctly:
SIGNAL ON MYTH NAME MYTH_TRAP
Now, suppose the MYTH incident occurs. The interpreter will sense it, queue an event, set the trap state to OFF and then explicitly trigger the trap, since the method is SIGNAL. What happens when the trap is triggered?
It collects the first event from the queue of pending events. The information is removed from the queue.
The current trapped condition is set to the information removed from the pending event queue.
Then, the interpreter simulates a SIGNAL clause to the label named by trap handler of the trap for the condition in question.
As all SIGNAL clauses, this will have the side-effects of setting the SIGL special variable, and terminating all active loops at the current procedure level.
That's it for method SIGNAL. If you want to continue trapping condition MYTH, you have to execute a new SIGNAL ON MYTH clause to set the state of the trap to ON. But no matter how quick you reset the trap, you will always have a short period where it is in state OFF. This means that you can not in general use the method SIGNAL if you really want to be sure that you don't loose any MYTH events, unless you have some control over when MYTH condition may arise.
Also note that since the statement being executed is terminated; all active loops on the current procedure level are terminated; and the only indication where the error occurred is the line number (the line may contain several clauses), then it is in general impossible to pick up the normal execution after a condition trapped by SIGNAL. Therefore, this method is best suited for a "graceful death" type of traps. If the trap is triggered, you want to terminate what you were doing, and pick up the execution at an earlier stage, e.g. the previous procedure level.
Assume that the condition MYTH is being trapped by method CALL, that the state is ON and the handler is MYTH_HANDLER.
The following REXX clause will setup the trap correctly:
CALL ON MYTH NAME MYTH_HANDLER
Now, suppose that the MYTH incident occurs. When the interpreter senses that, it will raise the MYTH condition. Since the trap state is ON and the trap method is CALL, it will create an event and queue it in the pending event queue and set the trap state to DELAY. Then it continues the normal execution. The trap is not triggered before the interpreter encounters the next clause boundary. What happens then?
At the every clause boundaries, the interpreter check for any pending events in the event queue. If one is found, it is handled. This action is done repeatedly, until the event queue is empty.
It will simulate a normal function call to the label named by the trap handler. As with any CALL clause, this will set the special variable SIGL to the line of from which the call was made. This is done prior to the call. Note that this is the current line at the time when the condition was raised, not when it was triggered. All other actions normally performed when calling a subroutine are done. Note that the arguments to the subroutine are set to empty.
However, just before execution of the routine starts, it will remove the first event in the pending event queue, the information is instead put into the current trapped condition. Note that the current trapped condition is information that is saved across subroutine calls. It is set after the condition handler is called, and will be local to the condition handler (and functions called by the condition handler). To the "caller" (i.e. the procedure level active when the trap was triggered), it will seem as if the current trapped condition was never changed.
Then the condition handler finishes execution, and returns by executing the RETURN clause. Any expression given as argument to RETURN will be ignored, i.e. the special variable RESULT will not be set upon return from a condition handler.
At the return from the condition handler, the current trapped condition and the setup of all traps are restored, as with a normal return from subroutine. As a special case, the state of the trap just triggered, will not be put back into DELAY state, but is set to state ON.
Afterwards (and before the next normal clause), the interpreter will again check for more events in the event queue, and it will not continue on the REXX script before the queue is empty.
During the triggering of a trap by method CALL at a clause boundary, the state of the trap is not normally changed, it will continue to be DELAY, as was set when the condition was raised. It will continue to be in state DELAY until return from the condition handler, at which the state of the trap in the caller will be changed to ON. If, during the execution of the condition trap, the state of the condition being trapped is set, that change will only last until the return from the condition handler.
Since new conditions are generally delayed when an condition handler is executing, new conditions are queued up for execution. If the trap state is changed to ON, the pending event queue will be processed as named at the next clause boundary. If the state is changed to OFF, the default action of the conditions will be taken at the next clause boundary.
The interpreter maintains a data structure called the current trapped condition. It contains information relating the most recent condition trapped on this or higher procedure level. The current trapped condition is normally inherited by subroutines and functions, and restored after return from these.
When trapped by method SIGNAL the current trapped condition of the current procedure level is set to information describing the condition trapped.
When trapped by method CALL, the current trapped condition at the procedure level which the trap occurred at, is not changed. Instead, the current trapped condition in the condition handler is set to information describing the condition.
The information stored in the current trapped condition can be retrieved by the built-in function CONDITION(). The syntax format of this function is:
CONDITION(option)
where option is an option string of which only the first character matters. The valid options are: Condition name, Description, Instruction and State. These will return: the name of the current trapped condition; the descriptive text; the method; and the current state of the condition, respectively. The default option is Instruction. See the documentation on the built-in functions. See also the description of each condition below.
Note that the State option do not return the state at the time when the condition was raised or the trap was triggered. It returns the current state of the trap, and may change during execution. The other information in the current trapped condition may only change when a new condition is trapped at return from subroutines.
We have now described how the standard condition and condition trap works in REXX. Let's look at the seven conditions defined which do exist. Note that none of these behaves exactly as the standard condition.
The SYNTAX condition is of internal origin, and is raised when any syntax or runtime error is discovered by the REXX interpreter. It might be any of the situations that would normally lead to the abortion of the program and the report of a REXX error message, except error message number 4 (Program interrupted), which is handled by the HALT condition.
There are several differences between this condition and the standard condition:
It is not possible to trap this condition with the method CALL, only method SIGNAL. The reason for this is partly that method CALL tries to continue execution until next boundary before triggering the trap. That might not be possible with syntax or runtime errors.
When this condition is trapped, the special variable RC is set to the REXX error number of the syntax or runtime error that caused the condition. This is done just before the setting of the special variable SIGL.
The default action of this condition if the trap state is OFF, is to abort the program with a traceback and error message.
There is not delay-action for condition SYNTAX, since it can not be trapped by method CALL, and consequently never can get into state DELAY.
The descriptive text returned by CONDITION() when called with the Description option for condition SYNTAX, is implementation dependent, and may also be a nullstring. Consult the implementation-specific documentation for more information.
The HALT condition of external origin, which is raised as a result of an action from the user, normally a combination of keys which tries to abort the program. Which combination of keys will vary between operating systems. Some systems might also simulate this event by other means than key combinations. Consult system for more information.
The differences between HALT and the standard condition are:
The default-action for the HALT condition is to abort execution, as though a REXX runtime error number 4 (Program interrupted) had been reported. But note that SYNTAX will never be raised if HALT is not trapped.
The delay-action of this condition is to ignore, not queue.
The standard allows the interpreter to limit the search for situations that would set the HALT condition, to clause boundaries. As a result, the response time from pressing the key combination to actually raising the condition or triggering the trap may vary, even if HALT is trapped by method SIGNAL. If a clause for some reason has blocked execution, and never finish, you may not be able to break the program.
The descriptive text returned by CONDITION() when called with the Description option for condition HALT, is implementation dependent, and may also be a nullstring. In general, it will describe the way in which the interpreter was attempted halted, in particular if there are more than one way to do raise a HALT condition. Consult the implementation documentation for more information.
The ERROR is a condition of mixed origin, it is raised when a command returns a return value which indicates error during execution. Often, commands return a numeric value, and a particular value is considered to mean success. Then, other values might raise the ERROR condition.
Differences between ERROR and the standard condition:
The delay action of ERROR is to ignore, not to queue.
The special variable RC is always set before this condition is raised. So even if it is trapped by method SIGNAL, you can rely on RC to be set to the return value of the command.
Unfortunately, there is no universal standard on return values. As stated, they are often numeric, but some operating system use non-numeric return values. For those which do use numeric values, there are no standard telling which values and ranges are considered errors and which are considered success. In fact, the interpretation of the value might differ between commands within the same operating system.
Therefore, it is up to the REXX implementation to define which values and ranges that are considered errors. You must expect that this information can differ between implementations as well as between different environments within one implementation.
The descriptive text returned by CONDITION() when called with the Description option for condition ERROR, is the command which caused the error. Note that this is the command as the environment saw it, not as it was entered in the REXX script source code.
The FAILURE is a condition of mixed origin, it is raised when a command returns a return value which indicates failure during execution, abnormal termination, or when it was impossible to execute a command. It is a subset of the ERROR condition, and if it is in state OFF, then the ERROR condition will be raised instead. But note that an implementation is free to consider all return codes from commands as ERRORs, and none as FAILURES. In that case, the only situation where a FAILURE would occur, is when it is impossible to execute a command.
Differences between FAILURE and the standard condition:
The delay action of FAILURE is to ignore, not to queue.
The special variable RC is always set before this condition is raised. So even if it is trapped by method SIGNAL, you can rely on RC to be set to the return value of the command, or the return code that signalize that the command was impossible to execute.
As for ERROR, there is no standard the defines which return values are failures and which are errors. Consult the system and implementation independent documentation for more information.
The descriptive text returned by CONDITION() when called with the Description option for condition FAILURE, is the command which caused the error. Note that this is the command as the environment saw it, not as it was entered in the REXX script source code.
The NOVALUE condition is of internal origin. It is raised in some circumstances if the value of an unset symbol (which is not a constant symbol) is requested. Normally, this would return the default value of the symbol. It is considered bad programming practice not to initialize variables, and setting the NOVALUE condition is one method of finding the parts of your program that uses this programming practice.
Note however, there are only three instances where this condition may be raised: that is when the value of an unset (non-constant) symbol is used requested: in an expression; after the VAR subkeyword in a PARSE clause; and as an indirect reference in either a template, a DROP or a PROCEDURE clause. In particular, this condition is not raised if the VALUE() or SYMBOL() built-in functions refer to an unset symbol.
Differences between NOVALUE and the standard condition are:
It may only be trapped by method SIGNAL, never method CALL. This requirement might seem somewhat strange, but the idea is that since an implementation is only forced to check for conditions trapped by method CALL at clause boundaries, incidences that may occur at any point within clauses (like NOVALUE) can only be trapped by method SIGNAL. (However, condition NOTREADY can occur within a clause, and may be trapped by method CALL so this does not seem to be absolute consistent.)
There is not delay-action for condition NOVALUE, since it can not be trapped by method CALL, and consequently never can get into state DELAY.
The descriptive text returned by calling CONDITION() with the Description option, is the derived (i.e. tail has be substituted if possible) name of the variable that caused the condition to be raised.
The condition NOTREADY is a condition of mixed origin. It is raised as a result of problems with stream I/O. Exactly what causes it, may vary between implementations, but some of the more probable causes are: waiting for more I/O on transient streams; access to streams not allowed; I/O operation would block if attempted; etc. See the chapter; Stream Input and Output for more information.
Differences between NOTREADY and the standard condition are:
It will be ignored rather than queued if condition trap is in state DELAY.
This condition differs from the rest in that it can be raised during execution of a clause, but can still be trapped by method CALL.
The descriptive text returned by CONDITION() when called with the Description option for condition NOTREADY, is the name of the stream which caused the problem. This is probably the same string that you used as the first parameter to the functions that operates on stream I/O. For the default streams (default input and output stream), the string returned by CONDITION() will be nullstrings.
Note that if the NOTREADY trap is in state DELAY, then all I/O for files which has tried to raise NOTREADY within the current clause will be simulated as if operation had succeeded.
The condition LOSTDIGITS was introduced in Language Level 5.00. It is raised as a result of any arithmetic operation which results in the loss of any digits. i.e. If the number of significant digits in the result of an artihmetic operation would exceed the currently defined number of digits via NUMERIC DIGITS, then the LOSTDIGITS condition is raised.
Differences between LOSTDIGITS and the standard condition are:
It may only be trapped by method SIGNAL, never method CALL.
There is not delay-action for condition NOVALUE, since it can not be trapped by method CALL, and consequently never can get into state DELAY.
The descriptive text returned by CONDITION() when called with the Description option for condition NOTREADY, is the name of the stream which caused the problem. This is probably the same string that you used as the first parameter to the functions that operates on stream I/O. For the default streams (default input and output stream), the string returned by CONDITION() will be nullstrings.
The concept of conditions was very much expanded from REXX language level 3.50 to level 4.00. Many of the central features in conditions are new in level 4.00, these include:
The CALL method is new, previously only the SIGNAL method was available, which made it rather difficult to resume execution after a problem. As a part of this, the DELAY state has been added too.
The condition NOTREADY has been added, to allow better control over problems involving stream I/O.
The built-in function CONDITION() has been added, to allow extraction of information about the current trapped condition.
There are several pitfalls when using conditions:
Remember that some information are saved across the functions. Both the current trapped condition and the settings of the traps. Consequently, you can not set a trap in a procedure level from a lower level. (I.e. calling a subroutine to set a trap is will not work.)
Remember that SIGL is set when trapped by method CALL. This means that whenever a condition might be trapped by CALL, the SIGL will be set to a new value. Consequently, never trust the contents of the SIGL variable for more than one clause at a time. This is very frustrating, but at least it will not happen often. When it do happen, though, you will probably have a hard time debugging it.
Also remember that if you use the PROCEDURE clause in a condition handler called by method CALL, remember to EXPOSE the special variables SIGL if you want to use it inside the condition handler. Else it will be shadowed by the PROCEDURE.
In this description of conditions in REXX, I have gone further in the description of how conditions work, their internal data structures, the order in which things are executed etc., than the standard does. I have tried to interpret the set of distinct statements that is the documentation on condition, and design a complete and consistent system describing how such conditions work. I have done this to try to clarify an area of REXX which at first glance is very difficult and sometimes non-intuitive.
I hope that the liberties I have taken have helped describe conditions in REXX. I do not feel that the adding of details that I have done in any way change how conditions work, but at least I owe the reader to list which concepts that are genuine REXX, and which have been filled in by me to make the picture more complete. These are not a part of the standard REXX.
REXX does not have anything called a standard condition. There just "are" a set of conditions having different attributes and values. Sometimes there are default values to some of the attributes, but still the are no default condition.
The terms "event" and "incident" are not used. Instead the term "condition" is somewhat overloaded to mean several things, depending on the situation. I have found it advantageous to use different terms for each of these concepts.
Standard REXX does not have condition queue, although a structure of such a kind is needed to handled the queuing of pending conditions when the trap state is DELAY.
The values default-action and delay-action are really non-existing in the Standard REXX documentation. I made them up to make the system more easy to explain.
The two-step process of first raising the flag, and then (possibly at a later stage) triggering the trap, is not really a REXX concept. Originally, REXX seems to allow implementations to select certain places of the interpreter where events are sought for. All standard conditions that can be called by method CALL, can be implemented by checking only at clause boundaries.
Consequently, a REXX implementation can choose to trigger the trap immediately after a condition are raised (since conditions are only raised immediately before the trap would trigger anyway). This is also the common way used in language level 3.50, when only method SIGNAL was implemented.
Unfortunately, the introduction of the state DELAY forces the interpreter to keep a queue of pending conditions, so there is nothing to gain on insisting that raising should happen immediately before triggering. And the picture is even more muddied when the NOTREADY condition is introduced. Since it explicitly allows raising of condition to be done during the clause, even though the triggering of the trap must happen (if method is CALL) at the end of the clause.
I really hope that these changes has made the concept of conditions easier to understand, not harder. Please feel free to flame me for any of these which you don't think is representative for REXX.
Here comes documentation that are specific for the Regina implementation of REXX.
The implementation connect the HALT condition to an external event, which might be the pressing of certain key combination. The common conventions of the operating system will dictate what that combination of keystrokes is.
Below is a list, which describes how to invoke an event that will raise the HALT condition under various the operating systems which Regina runs under.
Under various variants of the Unix operating system, the HALT event it connected to the signal "interrupt" (SIGINT). Often this signal is bound to special keystrokes. Depending on your version of Unix, this might be <ctrl>-<c> (mostly BSD-variants) or the <del> key (mostly System V). It is also possible to send this signal from the command line, in general using the program kill(1); or from program, in general using the call signal(3). Refer to your Unix documentation for more information.
Under VAX/VMS, the key sequence <ctrl>-<c> is used to raise the HALT condition in the interpreter.
Here is a list of possible future extensions to REXX which has not been implemented into Regina. Some of these exist in other implementations of REXX, and some of them are just suggestions or ideas thrown around by various people.
Another extension could have been included, but have been left out so far. It is the delay-action, which in standard REXX can be either to ignore or to queue. There is at least one other action that make sense: to replace. That is, when a trap is in state DELAY, and a new condition has been raised, the pending queue is emptied, before the new condition is queued. That way, the new condition will effectively replace any conditions already in the queue.
If there are several new conditions raised while the condition handler is executing (and the trap state is DELAY), only the very last of them is remembered.
It should be possible to set the state for a trap to DELAY, so that any new instances of the condition is handles by the delay-action. As a special case, the SYNTAX condition trap might not be set in state DELAY
Table of Contents
Conditions 1
1What are Conditions 1
1.1What Do We Need Conditions for? 1
1.1.1Terminology 1
2The Mythical Standard Condition 2
2.1Information Regarding Conditions (data structures) 2
2.2How to Set up a Condition Trap 3
2.3How to Raise a Condition 4
2.4How to Trigger a Condition Trap 6
2.5Trapping by Method SIGNAL 6
2.6Trapping by Method CALL 7
2.7The Current Trapped Condition 8
3The Real Conditions 9
3.1The SYNTAX condition 9
3.2The HALT condition 10
3.3The ERROR condition 10
3.4The FAILURE condition 11
3.5The NOVALUE condition 11
3.6The NOTREADY condition 12
3.7The LOSTDIGITS condition 12
4Further Notes on Conditions 13
4.1Conditions under Language Level 3.50 13
4.2Pitfalls when Using Condition Traps 13
4.3The Correctness of this Description 13
5Conditions in Regina 14
5.1How to Raise the HALT condition 15
6Possible Future extensions 15