[comp.lang.forth] CATCH/THROW, and counting applications

wmb@MITCH.ENG.SUN.COM (Mitch Bradley) (08/05/90)

> Well, thanks to Mitch Bradley, I now have an implementation of CATCH and
> THROW to look at...  Now I have a question to pose to the net...

> How are they exactly used?   I get the general idea of it but I'd like to
> be told the exact behavior so I don't make any wrong assumptions in thier use
> as I intend to implement them in the Forth kernels I have source on hand
 for...

I'll address this issue after I comment on your other point...

> BTW- Whomever it was who said that he could count all the successful Forth
> projects on one hand a while back, had better look around again...

Actually, I believe it was me that made this point.  As I recall, though,
I was not talking about successful applications, but instead about
portable Forth programs (ones that run with little or no modification
on a variety of different Forth implementations from different vendors).

There are indeed a number of successful Forth programs, and I can name
numerous others beyond the ones you listed.  On the other hand, there
are relatively few portable Forth programs in the sense that I am speaking
of.  In contrast, there are literally hundreds of portable C programs that
have been posted to various networks, encompassing everything from games
to spreadsheets to editors to utilities to you name it.  I did not mean
to imply that Forth cannot be used to write successful programs (on the
contrary, my Forth-based boot firmware is shipping on the largest-selling
Unix workstation in the industry; over 100,000 have been sold to date).
I meant to imply that present Forth standards are missing features that
are necessary to write many popular classes of program, e.g. file access
and floating point.  Many Forth implementations include those features,
but their lack of prior standardization prevents the use of such features
from being portable.

Now, back to the real issue of CATCH/THROW.  If you intend to put it in
your kernel (which is an excellent thing to do), the best way is to
hook it into ABORT and QUIT .

For example:

: ABORT  ( -- )  1 THROW  ;
: (ABORT")  ( flag -- )
   IF
      <print in-line string>  ABORT
   ELSE
      <skip in-line string>
   THEN
;
\ In F-PC, ABORT and ABORT" are factored differently.  F-PC implements
\ (ABORT") in terms of a deferred word ERROR? .  The default implementation
\ of ERROR? , named (?ERROR) , could be modified to use THROW instead of
\ QUIT as follows:

: (?ERROR)  ( addr len f -- )
   IF
      PRINTING OFF
      SPACE TYPE SPACE
      1 THROW
   ELSE
      2DROP
   THEN
;

...

\ The following implementation of QUIT will probably work for F-PC;
\it was written by looking at the F-PC source code for QUIT, but has
\ not been tested (however, similar code is known to work on other systems).

: INTERACT  ( -- )
   BEGIN   STATUS  QUERY  RUN  STATE @ NOT  UNTIL
;

: QUIT  ( -- )				\ Derived from the F-PC version
   SP0 @ 'TIB !
   BEGIN
      [COMPILE] [
      ['] INTERACT CATCH  IF  ." Aborted"  ELSE  ." ok"  THEN
      CR
   AGAIN
;

Several things to note:

1) The use of CATCH/THROW eliminates the need to clear the data
   stack in ABORT" ; CATCH/THROW automatically restores the data
   stack depth to its original state, which presumably is "empty"
   when QUIT is first called.

2) This simplifies the implementation of (?ERROR) by eliminating
   the following code:   2>R  SP0 @ SP!  2R>

3) The use of CATCH/THROW eliminates the need to clear the return
   stack in QUIT ; CATCH/THROW automatically restores the return
   stack to the correct value.

4) With ABORT implemented in terms of THROW instead of calling QUIT ,
   the one previously-unavoidable forward reference in the kernel
   source code is eliminated.

5) All this goes a long way toward making it possible to recursively
   call the text interpreter!  Left to do: save and restore the
   contents of TIB and >IN , add a controlled exit from the
   BEGIN .. AGAIN loop in QUIT .  The controlled exit could even
   be done using CATCH/THROW , e.g.

   CREATE DONE-SIGNAL
   : DONE  DONE-SIGNAL THROW  ;
   : QUIT  ( -- )
      SAVE-OLD-TIB
      BEGIN
         [COMPILE] [
         ['] INTERACT CATCH   ( 0 or error-code)
	 CASE
            0            OF  ." ok"       FALSE       ENDOF
	    DONE-SIGNAL  OF  ." Exiting"  TRUE        ENDOF
	    ( error# )       ." Aborted." FALSE SWAP
         ENDCASE
        CR
      UNTIL
      RESTORE-TIB
   ;

Mitch Bradley