guest@willett.pgh.pa.us (Guest Account) (09/15/90)
[I am posting this with the author's permission. I will forward any *posted* replies back to the author. -dwp] Message-Id: <4660872@AppleLink.Apple.COM> Subject: FORTH CATCH/THROW To: WILLETT!DWP@goofy.apple.com From: D2044@AppleLink.Apple.COM (Flavors Tech, Doug Currie,PRT) Date: 12 Sep 90 18:31 GMT 12Sep90 Doug Phillips... I saw your note in Best of GENIE, FORTH Dimensions XII#3, re: CATCH/THROW. I had no idea that CATCH/THROW had caught on since I recommended the mechanism to George Shaw last year. Your "for free" analysis of the requirements for CATCH/THROW implementation is not quite complete. There is a middle ground between your two approaches. I have used this technique for years in my FORTH systems, and the mechanism is free if your program doesn't use it, and quite cheap if it does. The idea is to have a pointer to the top most catch frame on the return stack, and links from catch frame to catch frame. So far, this is just like your first choice. Now when CATCH is executed, it builds the catch frame, and _calls_ the remainder of its containing routine (instead of returning). When the containing routine returns to the CATCH via NEXT, CATCH simply removes the catch frame and falls into NEXT, too, returning from the containing routine. NEXT itself is not changed in the least! The return stack can have anything at all on it (in fact I use it for my LOCAL mechanism). Now THROW is implemented as a return from the outermost CATCH (which hasn't returned yet... it called its return point) after restoring the catch frame pointer. I also have a routine called PUNT-CATCH which removes the top most catch frame from the return stack. Reproduced below are the CATCH, THROW, and PUNT-CATCH routines in M68000 assembly code (this is a native code FORTH, i.e., NEXT = RTS). The references to the frame pointer (A2) are for local variables. EXPORT catch catch ; ( - n T or F ) catch MOVE.W #0, -(A4) ; rtn val MOVEA.L (SP), A0 ; rtn addr, leave it for throw MOVE.L A2, -(SP) ; save frame ptr MOVE.L _catches_(A6), -(SP) ; setup catch frame MOVE.L SP, _catches_(A6) ; link it in JSR (A0) ; return first time ; colon word RTS comes here MOVE.L (SP)+, _catches_(A6) ; unlink ADDQ.L #8, SP ; remove catch frame RTS ; return from colon word EXPORT throw throw ; ( n - n T ) throw - doesn't return to caller but to catch MOVE.W #-1, -(A4) ; rtn val T MOVE.L _catches_(A6), SP ; get catch frame MOVE.L (SP)+, _catches_(A6) ; unlink it MOVE.L (SP)+, A2 ; restore frame ptr RTS ; return from catch again EXPORT punt_catch punt_catch ; ( - ) removes catch frame MOVEA.L (SP)+, A0 ; rtn addr ADDQ.L #4, SP ; remove catch's rtn addr MOVE.L (SP)+, _catches_(A6) ; unlink ADDQ.L #8, SP ; remove catch frame JMP (A0) ; return As a further optimization, I have two mechanisms to set up local variables. One allows CATCHes in the routine with locals, the other doen't and is very cheap. The two mechanisms may be mixed in one program. This is just one more way to make CATCH/THROW inexpensive when not used. Regards, e (Doug Currie, Flavors Technology, Inc., 3 Northern Blvd., Amherst NH 03031) cc: FORTH Dimensions FORTH Interest Group 1330 S. Bascom Ave., Suite D San Jose CA 95155 SIGFORTH, ANS FORTH Project c/o George Shaw POBox 3471 Hayward CA 94540-3471 P.S. Have you guys (FORTH Dimensions, SIGFORTH, ANS FORTH Project) got INTERNET addresses? I'm not on GENIE!
bouma@cs.purdue.EDU (William J. Bouma) (01/16/91)
> 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 <bouma@cs.purdue.edu>
UNBCIC@BRFAPESP.BITNET (01/17/91)
If you want named catchs, do it yourself! It's possible to do named catchs from the current ANS Forth Catch & Throw... and the ones that don't need it don't have to pay the cost (speed?). (8-DCS) Daniel C. Sobral UNBCIC@BRFAPESP.BITNET