9.6.1.0875
CATCH
 
EXCEPTION
 
( i * x xt -- j * x 0 | i * x n )

Push an exception frame on the exception stack and then execute the execution token xt (as with EXECUTE) in such a way that control can be transferred to a point just after CATCH if THROW is executed during the execution of xt.

If the execution of xt completes normally (i.e., the exception frame pushed by this CATCH is not popped by an execution of THROW) pop the exception frame and return zero on top of the data stack, above whatever stack items would have been returned by xt EXECUTE. Otherwise, the remainder of the execution semantics are given by THROW.

Implementation:
This sample implementation of CATCH uses the non-standard words described below. They or their equivalents are available in many systems. Other implementation strategies, including directly saving the value of DEPTH, are possible if such words are not available.

SP@
( -- addr )
returns the address corresponding to the top of data stack.

SP!
( addr -- )
sets the stack pointer to addr, thus restoring the stack depth to the same depth that existed just before addr was acquired by executing SP@.

RP@
( -- addr )
returns the address corresponding to the top of return stack.

RP!
( addr -- )
sets the return stack pointer to addr, thus restoring the return stack depth to the same depth that existed just before addr was acquired by executing RP@.

VARIABLE HANDLER 0 HANDLER ! \ last exception handler

: CATCH ( xt -- exception# | 0 \ return addr on stack
   SP@ >R             ( xt )       \ save data stack pointer
   HANDLER @ >R       ( xt )       \ and previous handler
   RP@ HANDLER !      ( xt )       \ set current handler
   EXECUTE            ( )          \ execute returns if no THROW
   R> HANDLER !       ( )          \ restore previous handler
   R> DROP            ( )          \ discard saved stack ptr
    0                 ( 0 )        \ normal completion
;

In a multi-tasking system, the HANDLER variable should be in the per-task variable area (i.e., a user variable).

This sample implementation does not explicitly handle the case in which CATCH has never been called (i.e., the ABORT behavior). One solution would be to execute a CATCH within QUIT, so that there is always an "exception handler of last resort" present, as shown in E.6.1.2050 QUIT.

Testing: