CATCH

( 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.

See:

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:

ContributeContributions

GerryJacksonavatar of GerryJackson The specification of CATCH Request for clarification2019-07-19 14:52:07

The specification for CATCH states:

"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."

Given: ' ' CATCH foo [if] : foo 1234 . ; foo [then] where foo is undefined. My system displays 1234 (as do GForth,SwiftForth, VFX Forth and Win32 Forth)

The 'undefined word' exception doesn't return to just after CATCH. This is also true if the word executed by CATCH parses the rest of the line.

This behaviour seems reasonable to me. Either the specification should be reworded or the above 5 systems are non-compliant. Which is it?

AntonErtlavatar of AntonErtl 2019-07-20 07:09:07

' ' CATCH foo produces a non-zero value because ' produces an exception (although the standard does not guarantee that). Therefore the code between [if] and [then] is text-interpreted.

If you think that "just after CATCH" means that THROW should restore the input stream to where it was when CATCH was entered, no, I don't think that the standard means this. If you think that needs clarifying, make a proposal.

What it means is that in the following example:

: bar parse-name type 1 throw ." end of bar" ;
: bar1 bar ." end of bar1" ;
: bar2 ['] bar catch ." end of bar2" ;
bar2 bla \ prints "blaend of bar2"

the THROW jumps to the point between CATCH and .". In your example, the point just after CATCH is inside the text interpreter.

Another aspect is that THROW specifies "restore the input source specification". Gforth (and apparently other popular systems) have implemented this as unnesting all the INCLUDEDs, EVALUATEs, and LOADs that the THROW terminates, but not as restoring anything if there is no such word unnested. Given that there have been no complaints about this behaviour in the last 25 years, and that it could be very expensive to restore the input stream of the user input device (an arbitrary number of REFILLs could happen between entering the CATCH and the THROW), we may want to clarify/fix the specification of THROW.

Reply