====== 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)