====== catch throw ====== ''sp@ ( -- addr )'' returns the address corresponding to the top of data stack. ''rp@ ( -- addr )'' returns the address corresponding to the top of return 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 -- )'' 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@. Legend: * #exc = exeption number * i*x = any stack items underneath #exc * i*adr = any returnstack items above last catchframe * xt = execution address used inside of catch by EXECUTE * RS: = Return Stack Picture ===== Step by step explanation of CATCH ===== variable handler \ Most recent exception handler \ Put catchframe on return stack. \ v v : CATCH ( xt -- exc# | 0 ) ( 1) ( 2) sp@ >r ( -- xt) ( RS: -- sp ) ( 3) handler @ >r ( -- xt) ( RS: -- sp hlr ) ( 4) rp@ handler ! ( -- xt) ( RS: -- sp hlr ) ( 5) execute ( -- ) ( RS: -- sp hlr ) ( 6) r> handler ! ( -- ) ( RS: -- sp ) ( 7) r> drop ( -- ) ( RS: -- ) ( 8) 0 ( -- 0 ) ( 9) ; - Build a catchframe for throw. Pass the exception number of the word executeted. - Execution token XT is already on the stack. - Save data stack pointer; exclude current xt on data stack. - Save previous handler. - Set current handler to this one. ''sp'', ''hlr'' and ''handler'' are current catchframe now. - Now execute the word passed in on the stack. It is now wrapped by your own exception handler, created with the word where you put your catch in. - Execution comes back without exception. So restore previous handler. - Discard saved data stack pointer. - Signify normal completion by 0 on data stack. ===== Step by step explanation of THROW ===== \ TROW exits to saved context if exc# <> 0 : THROW ( i*x exc# -- i*x exc# | i*x exc# ) ( RS: -- sp hlr i*adr ) ( 1) dup 0= ( -- i*x exc# f ) ( RS: -- sp hlr i*adr ) ( 2) if drop exit then ( -- i*x ) ( RS: -- sp hlr i*adr ) ( 3) handler @ rp! ( -- i*x exc# ) ( RS: -- sp hlr ) ( 4) r> handler ! ( -- i*x exc# ) ( RS: -- sp ) ( 5) r> ( -- i*x exc# sp ) ( RS: -- ) ( 6) swap ( -- i*x sp exc# ) ( RS: -- ) ( 7) >r ( -- i*x sp ) ( RS: -- exc# ) ( 8) sp! ( -- xt ) ( RS: -- exc# ) ( 9) drop r> ( -- exc# ) ( RS: -- ) ( 10) ; - ''throw'' expects an exception numer on the stack, passing or dropping it, and its returnstack picture has a catchframe underneath the current returnaddresses. - Get flag. - Noop, don't throw 0. - Return to saved return stack context. - Restore previous handler. - Get saved stack pointer. - Ready to save #exc on return stack for use in step ( 9). - Now save it - Return to saved data stack context; we can see XT again now, but it's no use, so drop it. - Pass exception number. We are ready to proceed error handling words compiled behind CATCH in calling word. The caling word is that which has the CATCH phrase in its compilat. ===== Intended use is: ===== \ enable "spam" to THROW an exeption number in case of exeption. : spam ( -- ) ... #exc THROW ; \ put "spam" into exception handling frame using CATCH : sapm-exc ( i*x -- ) ... ['] spam CATCH ( -- #exc ) dup 0= IF drop exit then \ = NOOP dup #exc = IF ... \ known error code, do execption handling else ... throw \ unknown error code; re-throw system code then ; ( finis)