Node:Exception Handling, Previous:Calls and returns, Up:Control Structures



Exception Handling

If a word detects an error condition that it cannot handle, it can throw an exception. In the simplest case, this will terminate your program, and report an appropriate error.

throw       y1 .. ym nerror -- y1 .. ym / z1 .. zn error         exception       ``throw''
If nerror is 0, drop it and continue. Otherwise, transfer control to the next dynamically enclosing exception handler, reset the stacks accordingly, and push nerror.

Throw consumes a cell-sized error number on the stack. There are some predefined error numbers in ANS Forth (see errors.fs). In Gforth (and most other systems) you can use the iors produced by various words as error numbers (e.g., a typical use of allocate is allocate throw). Gforth also provides the word exception to define your own error numbers (with decent error reporting); an ANS Forth version of this word (but without the error messages) is available in compat/except.fs. And finally, you can use your own error numbers (anything outside the range -4095..0), but won't get nice error messages, only numbers. For example, try:

-10 throw                    \ ANS defined
-267 throw                   \ system defined
s" my error" exception throw \ user defined
7 throw                      \ arbitrary number

exception       addr u -- n         gforth       ``exception''
n is a previously unused throw value in the range (-4095...-256). Consecutive calls to exception return consecutive decreasing numbers. Gforth uses the string addr u as an error message.

A common idiom to THROW a specific error if a flag is true is this:

( flag ) 0<> errno and throw

Your program can provide exception handlers to catch exceptions. An exception handler can be used to correct the problem, or to clean up some data structures and just throw the exception to the next exception handler. Note that throw jumps to the dynamically innermost exception handler. The system's exception handler is outermost, and just prints an error and restarts command-line interpretation (or, in batch mode (i.e., while processing the shell command line), leaves Gforth).

The ANS Forth way to catch exceptions is catch:

catch       ... xt -- ... n         exception       ``catch''

The most common use of exception handlers is to clean up the state when an error happens. E.g.,

base  >r hex \ actually the hex should be inside foo, or we h
['] foo catch ( nerror|0 )
r> base !
( nerror|0 ) throw \ pass it on

A use of catch for handling the error myerror might look like this:

['] foo catch
CASE
  myerror OF ... ( do something about it ) ENDOF
  dup throw \ default: pass other errors on, do nothing on non-errors
ENDCASE

Having to wrap the code into a separate word is often cumbersome, therefore Gforth provides an alternative syntax:

TRY
  code1
RECOVER     \ optional
  code2 \ optional
ENDTRY

This performs Code1. If code1 completes normally, execution continues after the endtry. If Code1 throws, the stacks are reset to the state during try, the throw value is pushed on the data stack, and execution constinues at code2, and finally falls through the endtry into the following code.

try       compilation  -- orig ; run-time  --         gforth       ``try''

recover       compilation  orig1 -- orig2 ; run-time  --         gforth       ``recover''

endtry       compilation  orig -- ; run-time  --         gforth       ``endtry''

The cleanup example from above in this syntax:

base  >r TRY
  hex foo \ now the hex is placed correctly
  0       \ value for throw
RECOVER ENDTRY
r> base ! throw

And here's the error handling example:

TRY
  foo
RECOVER
  CASE
    myerror OF ... ( do something about it ) ENDOF
    throw \ pass other errors on
  ENDCASE
ENDTRY

Programming style note: As usual, you should ensure that the stack depth is statically known at the end: either after the throw for passing on errors, or after the ENDTRY (or, if you use catch, after the end of the selection construct for handling the error).

There are two alternatives to throw: Abort" is conditional and you can provide an error message. Abort just produces an "Aborted" error.

The problem with these words is that exception handlers cannot differentiate between different abort"s; they just look like -2 throw to them (the error message cannot be accessed by standard programs). Similar abort looks like -1 throw to exception handlers.

ABORT"       compilation 'ccc"' -- ; run-time f --         core,exception-ext       ``abort-quote''
If any bit of f is non-zero, perform the function of -2 throw, displaying the string ccc if there is no exception frame on the exception stack.
abort       ?? -- ??         core,exception-ext       ``abort''
-1 throw.