Path: utzoo!utgpu!news-server.csri.toronto.edu!bonnie.concordia.ca!thunder.mcrcim.mcgill.edu!snorkelwacker.mit.edu!apple!decwrl!purdue!bouma From: bouma@cs.purdue.EDU (William J. Bouma) Newsgroups: comp.lang.forth Subject: Re: CATCH/THROW Message-ID: <12991@medusa.cs.purdue.edu> Date: 15 Jan 91 21:32:04 GMT Organization: Department of Computer Science, Purdue University Lines: 100 > A long time ago Mitch Bradley wrote: > > In F83, and indeed in most Forth systems, the following code will work: > > VARIABLE HANDLER \ Most recent exception handler > > : CATCH ( execution-token -- error# | 0 ) > ( token ) \ Return address is already on the stack > SP@ >R ( token ) \ Save data stack pointer > HANDLER @ >R ( token ) \ Previous handler > RP@ HANDLER ! ( token ) \ Set current handler to this one > EXECUTE ( ) \ Execute the word passed in on the stack > R> HANDLER ! ( ) \ Restore previous handler > R> DROP ( ) \ Discard saved stack pointer > 0 ( 0 ) \ Signify normal completion > ; > > : THROW ( ??? error#|0 -- ??? error# ) \ Returns in saved context > ?DUP IF > HANDLER @ RP! ( err# ) \ Return to saved return stack context > R> HANDLER ! ( err# ) \ Restore previous handler > ( err# ) \ Remember error# on return stack > ( err# ) \ before changing data stack pointer > R> SWAP >R ( saved-sp ) \ err# is on return stack > SP! DROP R> ( err# ) \ Change stack pointer > THEN > \ This return will return to the caller of catch, because the return > \ stack has been restored to the state that existed when CATCH began > \ execution . > ; This is fine, but there is one thing that I do not like. Each CATCH handler has the responsibility of checking if it handles the specific error it has been passed and, if not, THROWing it on to the next. We can slightly modify the scheme above to get an easier to use mechanism. VARIABLE HANDLER \ Most recent exception handler : CATCH ( execution-token tag -- error# | 0 ) ( token tag ) \ Return address is already on the stack SP@ >R ( token tag ) \ Save data stack pointer HANDLER @ >R ( token tag ) \ Previous handler >R ( token ) \ Save tag RP@ HANDLER ! ( token ) \ Set current handler to this one EXECUTE ( ) \ Execute the word passed in on the stack R> DROP ( ) \ Trash the tag R> HANDLER ! ( ) \ Restore previous handler R> DROP ( ) \ Discard saved stack pointer 0 ( 0 ) \ Signify normal completion ; : THROW ( ??? error# tag -- ??? error# ) \ Returns in saved context HANDLER @ >R ( err# tag ) \ Get saved return stack context BEGIN \ Find matching tag in the stack R> RP! ( err# tag ) \ Return to saved stack context R> OVER = ( err# tag flag ) \ Compare tags UNTIL R> HANDLER ! ( err# tag ) \ Restore previous handler DROP ( err# ) \ Remember error# on return stack ( err# ) \ before changing data stack pointer R> SWAP >R ( saved-sp ) \ err# is on return stack SP! DROP R> ( err# ) \ Change stack pointer ; A simple example of use would be: VARIABLE DIVBY0 ' SOMEARITHMETIC DIVBY0 CATCH IF ." Divide by 0 error " THEN I don't envision the THROW itself doing any error checking as Mitch has done. I am not sure why he wants to do that? : SOMEARITHMETIC ... DUP 0 = IF 1 DIVBY0 THROW THEN ... ; I suggest using variables as the tags since they always will have a unique address value, plus the THROWing word could possibly send back extra info to the CATCH in the tag. This example doesn't take much advantage of the full powers of this scheme as described below. The reason to have both a tag and an error number is because a tag is used to select the handler, then the handler is free to choose what to do about any specific error. In Mitch's scheme the error numbers have to be unique across all handlers to keep the handler from catching the wrong error. Thus in each handler the selection mechanism will be a sequence of cascaded IFs. In my scheme the error numbers need only be unique within the handler. Thus a value branching select mechanism can be used which is much faster. Also, any THROW on a sequence of nested CATCHes will be faster in my scheme. But if no THROW occurs, mine is slightly slower as 3 things are pushed on the stack rather than two. But if no THROWs occur, what good are they 8^). DISCLAIMER: I have not programmed in Forth for 10 yrs. However, in my spare time I like to program Forth. -- Bill