UNBCIC@BRFAPESP.BITNET (08/02/90)
Can anyone post the source of CATCH and THROW for F-PC? (8-DCS)
wmb@MITCH.ENG.SUN.COM (Mitch Bradley) (08/04/90)
> Can anyone post the source of CATCH and THROW for F-PC?
The "canonical" version should work just fine in F-PC, since it
implements SP@, SP!, RP@, and RP! in the same was as F83.
Here it is:
\ CATCH/THROW Error Handling Wordset
\ by Mitch Bradley
\
\ This implementation uses the non-standard words SP@ , SP! , RP@ , and
\ RP! . These words, or their equivalents, are present in most systems.
\ Another implementation which does not use those non-standard words
\ follows this implementation.
\ Thanks to Don Colburn and Dean Sanderson for implementation suggestions.
VARIABLE HANDLER \ Most recent error handler (should be a USER variable)
: CATCH ( cfa -- error# | 0 )
( cfa ) \ Return address is already on the stack
SP@ >R ( cfa ) \ Save data stack pointer
EXCEPTION @ >R ( cfa ) \ Previous handler
RP@ HANDLER ! ( cfa ) \ 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# -- ??? error# ) \ Returns in saved context
?DUP IF
HANDLER @ RP! ( err# ) \ Return to saved return stack context
R> HANDLER ! ( err# ) \ Restore previous handler
\ Remember error# on return stack before changing data stack pointer
R> SWAP >R ( saved-sp ) \ err# is on return stack
SP! R> ( err# ) \ Change stack pointer
\ 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 .
THEN
;
\ This is a portable implementation which does not use any non-standard
\ words. This implementation has a problem: if the return stack happens
\ to contain a number which is the same as MAGIC# , then the wrong error
\ frame would be found. This problem can be minimized by choosing a
\ magic number which is unlikely to appear on the return stack, or by
\ placing 2 different magic numbers on the return stack instead of just 1.
6775 CONSTANT MAGIC#
: CATCH ( cfa -- error# | 0 )
( cfa ) \ Return address is already on the stack
DEPTH >R ( cfa ) \ Save data stack size
MAGIC# >R ( cfa ) \ "magic" number to mark return stack
EXECUTE ( ) \ Execute the word passed in on the stack
R> R> 2DROP 0 ( 0 ) \ Drop handler and signify normal completion
;
: THROW ( ??? error# -- ??? error# ) \ Returns in saved context
?DUP IF
BEGIN R> MAGIC# = UNTIL ( err# ) \ Unwind return stack frame
\ Remember err# on return stack before changing data stack depth
R> SWAP >R >R ( return-stack: err# depth )
\ The following code sets the stack depth to a known depth
\ without using any nonstandard words (such as perhapse "sp!")
\ The desired depth is kept on the return stack during the process.
BEGIN DEPTH R@ > WHILE NIP REPEAT \ Remove any extra items
\ Depth is now <= correct depth
BEGIN DEPTH R@ < WHILE 0 REPEAT \ Add items if necessary
R> DROP R> ( err# ) \ Discard old depth
\ 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 .
THEN
;
wmb@MITCH.ENG.SUN.COM (09/17/90)
> 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. ... > when CATCH1 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 CATCH1 via NEXT, > CATCH1 simply removes the catch frame and falls into NEXT, too, returning > from the containing routine. (Note: The author used the name "CATCH". I changed it to "CATCH1" for the purposes of discussion to avoid confusion with the ANS CATCH.) This mechanism differs from the current ANS CATCH/THROW in the way that it delimits the section of code that is "guarded" by the catch frame. In the current ANS approach, the "guarded" code is an entire colon definition, i.e.: ['] FOO CATCH \ "FOO" is executed under the protection \ of a CATCH frame In the approach described above, the "guarded" code is the remainder of the definition which contains the CATCH1 , i.e.: : xxx ... CATCH1 <guarded code> ; This adds yet another form of "closure" (a fragment of code that constitutes the body of a control structure) to Forth, as the stuff between CATCH1 and ; is a closure. In my opinion, Forth has too many different kinds of closures already. Also items placed on the return stack before the execution of CATCH1 are inaccessible to the code following CATCH1 . Finally, it is unclear whether or not CATCH1 works in interpret state, due to its unbalanced return stack effect. Mitch Bradley, wmb@Eng.Sun.COM
mip@IDA.LiU.SE (Mikael Patel) (09/18/90)
In article <9009172348.AA19506@ucbvax.Berkeley.EDU> wmb%MITCH.ENG.SUN.COM@SCFVM.GSFC.NASA.GOV writes: >In the approach described above, the "guarded" code is the remainder of >the definition which contains the CATCH1 , i.e.: > > : xxx > ... > CATCH1 > <guarded code> > ; > As object-orientation, exception handling comes in many flavors and abstraction levels. Depending on your opinion about what a standard definition of a language should achieve the current words for exception might be too low level and give "away" too much of implementation. Details as where the exception frame is stored etc gives problems with for instance Forth chips as these often have problems picking things on the stacks. Just to present yet an alternative form of exception handling, this this the form in TILE forth: : xxx ( a1..an -- ...) <guarded code> exception> ( a1..an e -- ..) <exception code> ; The principle words for defining exceptions (user level) and activating them are: exception <name> ( -- e) Creates an exception name which when used will push its reference onto the parameter stack. raise ( e -- ) Activates the latest exceptions block with the given exception. Stacks are restore accordingly. Typically a set of exceptions are defined to catch low level signals such as arithmetic and addressing errors. An example of the usage would be a source module for handling stacks (the typical academia example :-) exception stack-full ( -- e) exception stack-empty ( -- e) : push ( x s -- ) <check for full stack> if stack-full raise then <push element onto stack> ; : pop ( s -- x) <check for empty stack> if stack-empty raise then <pop element of stack> ; And usage of the stack: 10 STACK aStack ( -- s) : add ( -- ) aStack pop aStack pop + aStack push exception> ( e -- ) case stack-full of <exception handling> endof stack-empty of <exception handling endof raise endcase ; The 'raise' in the 'case' block will cause the exception to be sent to the next exception handling frame. The form above is very close to that in Ada and Eiffel and (now to the important part) it does not say anything about implementation! One of the most important aspects of exceptions is that it allows us to separate the 'normal' action (algorithm) from the 'error' case. Calls to operating systems often return error code which forces us to mix the normal and error case of an algorithm. >In my opinion, Forth has too many different kinds of closures already. > Closures are not that bad. They give use a method of abstraction. If Forth had a code block definition (like for instance PostScript) closures would not be needed. But...forth is forth! >Finally, it is unclear whether or not CATCH1 works in interpret state, >due to its unbalanced return stack effect. Oops! implementation detail again. But why use exception in interpret state after all. Do you have an example of this, Mitch? Mikael Patel, mip@ida.liu.se
wmb@MITCH.ENG.SUN.COM (Mitch Bradley) (09/19/90)
> As object-orientation, exception handling comes in many flavors and > abstraction levels. Depending on your opinion about what a standard > definition of a language should achieve the current words for exception > might be too low level and give "away" too much of implementation. In the case of exception handling, the obvious place to put the exception frames is on the return stack, and one must assume that many implementors would choose to put them there, if allowed. Since the Forth return stack is accessible to the programmer, one must include in the definition of the exception handling method some statement about its effects on the return stack. This could take the form of either a positive statement: Items placed on the return stack before a CATCH1 are accessible afterwards (probably implying that the implementor is not allowed to use the return stack for the exception frame). or a negative statement: Items placed on the return stack before a CATCH1 are NOT accessible afterwards (implying that the implementor may use the return stack for the exception frame at his discretion). One nice feature of the ANS CATCH / THROW is that it completely avoids this issue. The "guarded" code is an entire colon definition, separate from the definition that installs the exception handler. Thus, there is no question about return stack usage (because the return stack is only ever accessible to the programmer within the context of a single colon definition). > Details as where the exception frame is stored etc gives problems with > for instance Forth chips as these often have problems picking things > on the stacks. The ANS CATCH / THROW mechanism works just fine on Forth chips, because it doesn't require "picking" the return stack. THROW pops the return stack until the exception frame is exposes, then pops the frame. >> Finally, it is unclear whether or not CATCH1 works in interpret state, >> due to its unbalanced return stack effect. > Oops! implementation detail again. Like I said, the fact that CATCH1 *might* (and probably will, on most implementations) have a return stack effect means that its specification must account for the possibility. > But why use exception in interpret state after all. Do you have an > example of this, Mitch? The classic example is debugging. When debugging, it is a pain to have to remember that there are certain things that you can't just type in at the keyboard and have them work the same way that the work inside a colon definition. It is even more of a pain to *explain* to novices why certain things don't work. It is nicer if everything just works. As Forth "catches on" here at Sun, I find that teaching and support is becoming more and more critical to success. Right or wrong, people expect things to work the same when interpreted and when compiled, and they are confused and unhappy when the expection is violated. (The worst offender is ' vs. ['] ). Mitch Bradley, wmb@Eng.Sun.COM
wmb@MITCH.ENG.SUN.COM (09/22/90)
It is my belief that CATCH & THROW were the only possible way of getting exception handling into ANS Forth. The reason: Control structures are a subject of much controversy. They are difficult to precisely specify (proof: the discussions about how to properly describe them spanned many many meetings, and caused much heated debate). In fact, there is pretty much a one-to-one correspondence between non-postfix constructs and hard-to-specify constructs. Any exception handling solution that involves a new form of syntactic control structure was certain to fail, because it would have required the rethinking and the rewriting of so much of the other control structure text. For instance, the interactions between CATCH1 and all other control structures would have to be specified (i.e. you can't use CATCH1 inside a loop), as well as its return stack restrictions, and also there would have to be a decision about whether it is immediate or not and whether or not it is defined for interpret state and what happens when you POSTPONE it. This is the can of worms that one opens when one considers the addition of a new non-postfix control structure to a Forth standard. CATCH and THROW, being "pure postfix", avoids all these problems. Mitch Bradley, wmb@Eng.Sun.COM