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.

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 [97] 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

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

AidanPitt-Brookeavatar of AidanPitt-Brooke

Just to make sure that I understand correctly:

In GerryJackson's example, it looks like CATCH returns to a strange place because ' is a parsing word. CATCH is thus "infixed" between the xt it executes and the text that the xt parses; CATCH transfers control back to the interpreter without restoring the parse area to its original state because that's not a reasonable thing to require. Given the following definitions:

: cstr' ( c-addr -- xt )
    FIND 0= IF DROP -13 THROW THEN ;
: foo-str ( -- c-addr ) C" foo" ; /The standard doesn't require C" to be interpretable

then foo-str cstr' is equivalent to ' foo, except that it doesn't parse (and is guaranteed, rather than generally agreed, to throw). We can thus replace ' ' CATCH foo in GerryJackson's example with foo-str ' cstr' CATCH to illustrate that control is indeed transferred to the point immediately after CATCH.

Have I got all that right?

ruvavatar of ruv

In GerryJackson's example, [...] CATCH transfers control back to the interpreter

Yes. I think, "a point just after CATCH" should be interpreted not as a point in the source code, but as a point in run-time that is performed just after performing catch if throw was not performed (so it's the same point regardless whether throw was performed or not).

For example in the phrase "['] catch execute ." (which is a part of a definition) a point after performing catch is the point after execute in this source code.

When catch is encountered by the Forth text interpreter in interpretation state, a point just after performing catch is a point after execute inside the Forth text interpreter. And when this execute returns, the Forth text interpreter continues to parse the input source according its current state.

without restoring the parse area to its original state because that's not a reasonable thing to require.

Yes. And formally it must not restore this state, because restoring of this state is not specified.

Reply New Version