NST exception types are identified by string names with a simple hierarchical syntax. In particular, prog_units can be used to throw and catch exceptions, including exceptions thrown from prog-unit #imported library functions.
...
catch (Accept1) { ... handle exceptions matching Accept1
... }
catch (Accept2) { ... dito, for Accept2 ...}
...
catch (Acceptk) { ... }
The arguments Accepti specify the exceptions that can be "caught" (handled) by a particular catch block (cf. below).
If the throw point is some other, "opaque" NST unit, the first handler candidate is the nearest suitably designated (cf. below) NST "handler unit" in the NST execution sequence behind the throw point.
In both cases, the search follows the same general pattern: when the current candidate does not handle the exception (cf. below), or the search hits the border of the current invocation scope (= prog function or container unit) before a candidate is found, the search "propagates" to the invocation point (=prog function call or container) at the next outer level and continues recursively from there.
If the exception remains unhandled along all the way, the search will finally end at the outermost calling level, issueing a runtime error message there.
The prog_unit offers the simplest way to implement NST exception handlers. This requires that the entire "main" portion of the prog_unit source code consists of a single catch block chain only (additional function definitions may be present). Such prog_units have no directly executable code and will act as exception handler for those exceptions that are handled by their catch chain.
When needed, exception handlers can also be implemented in C, using
the system functions described in Section
System routines below.
Each catch block argument Accepti is a constant string with the same syntax (e.g., Accept1 = "badop:array"). When an argument matches in all of its ident-fields a corresponding initial portion of the current exception's pcErr (e.g., Accept1 would match "badop:array:anything" and "badop:array", but not "badop"), the code of the associated catch block will be executed. When this happens, the current exception is considered as "handled" and normal processing continues with the first statement after the chain (a "break" statement can be used to leave the chain before all statements of a block are executed).
A catch block can also re-instate the exception by which it was triggered by a parameterless call of throw(). This leaves the code block immediately and lets the search resume at the next block in the chain.
Each field in pcAccept can also be the wildcard character '*' to allow more flexible matching.
The two implicitly defined prog unit char arrays
char errtype[];
char errtext[];
contain always the full type string plus an (optionally specified) description text of the most recent exception. The convenience routine
int errmatch(char *pcAccept)
with binary return value allows to test whether a particular accept string matches the current exception and may be useful inside a catch block.
pcErr = "ident1:ident2:...identk"
which is passed as the main argument to one of the three routines
which are provided for the three contexts (1)-(3) above. The additional
(optional) parameters are processed as in a printf statement (i.e., the
format string coming first) to assemble an (optional) print message for
the exception (stored in the errtext variable and issued when the exception
is not caught).
The first call initiates the search for a suitable exception handler, first among the available catch blocks in the current prog_unit, then, if the exception remained uncaught, the exception is propagated up to the NST calling level (thereby terminating the prog_unit execution).
Similarly, any call of (2) inside the implementation of a #import function will cause an immediate return to its invocation point at the prog_unit calling level, after which subsequent processing will continue as if the exception had been caused with a call of throw(...) at this point.
The third call (3) is only important when implementing new NST units at the C (or C++) level. In contrast to (1) and (2), here the return must be explicitly specified, i.e., (3) must always be used in the form
return nst_throw(pcErr, ...);
[this causes returning the special value NST_EXCEPTION which tells the enclosing NST exec routine to initiate the search for a suitable handler].
char *nst_errtype(); // returns pointer to private
exception type string
char *nst_errtext(); // returns pointer to private
error message
unitptr *nst_errunit()
// ptr to unit that caused the exception
void nst_errtrace(FILE *fp)
// prints call chain
char *nst_errmatch(char *pcAccept)
// tests if pcAccept matches exception type
The last routine returns a pointer to a copy of the entire pcErr string in case of a match, and NULL otherwise Therefore, a NST handler can use simple code such as
if (nst_errmatch("badop:array")) { ... handle badop:array
exceptions }
else if (nst_errmatch("badop")) { ... handle any other
badop error}
else return nst_throw(NULL); // pass exception unchanged
on to other handlers
When the unit finally returns a value different from NST_EXCEPTION, NST considers the exception as handled (whatever the unit actually might have done; only the return value counts). When it instead returns NST_EXCEPTION (which is what the nst_throw() function does) he exception will be further propagated.
At the implementation level of an #import function, pointers to the current contents of errtype and errtext can be obtained with
char *nst_prog_errtype();
char *nst_prog_errtext();
It should also only be used for NST runtime situations, not when units
are created or destroyed, since during such operations the NST execution
queue is reconfigured and NST exceptions thrown in such situations may
fail to propagate to all prospective handlers.