[comp.lang.forth] Truly postfix conditionals

wmb@SUN.COM (01/23/90)

John Wavrik posted some code a while back to do closures (nested anonymous
colon definitions) in Forth.  This can be used as the basis for postfix
conditionals, e.g.

    <evaluate flag>
       { ... true clause ... }
       { ... false clause ... }
    IFELSE

Here is a copy of Dr. Wavrik's posting:


------------------------------------------------


Here is a solution to a problem which arose in the San Diego Chapter of FIG.
Forth allows words to be passed to other words as parameters (and also,
incidentally to be returned). The usual mechanism for passing WORD1 to WORD2
is to use  ' WORD1 WORD2. This requires WORD1 to be named. The problem is to
find a way to pass an unnamed code fragment to WORD2. (This arises, for
example, in a writing a general sorting program where an array, bounds, and a
comparison function must be passed).

This solution is for F-83:

VARIABLE OSTATE      ' : @ CONSTANT DOCOL
: <[:>   R> DUP @ >R  2+  ;

: [:   STATE @  DUP OSTATE !
          IF    COMPILE <[:>  ?>MARK  DOCOL ,
          ELSE  HERE DOCOL ,  !CSP ]    THEN ; IMMEDIATE

: ;]   OSTATE @
          IF    COMPILE EXIT ?>RESOLVE
          ELSE  ?CSP  COMPILE EXIT
                [COMPILE] [             THEN ; IMMEDIATE

The new words are used like this:
   [:  4 5 +  ;]   puts an address on the stack which, when executed,
                   puts 9 on the stack -- the address is the address
                   of the code fragment in brackets.

  : Ex1  [: 4 5 + ;]  ;
                   when Ex1 is executed, the address of the code fragment
                   is put on the stack.

To understand these words (and make them more portable) we remove the compiler
protection and special branching words:

VARIABLE OSTATE      ' <any colon word> @ CONSTANT DOCOL
: <[:>   R> DUP @ >R  cell +  ;

: [:   STATE @  DUP OSTATE !
          IF    COMPILE <[:>  here 0 , DOCOL ,
          ELSE  HERE DOCOL ,   ]    THEN ; IMMEDIATE

: ;]   OSTATE @
          IF    COMPILE EXIT  here swap !
          ELSE  COMPILE EXIT
                [COMPILE] [             THEN ; IMMEDIATE

The simplest case is that in which STATE is zero (the words are used in the
interpretive mode). [: puts the current dictionary position on the stack,
compiles the address of the inner interpreter used by all colon words, and
switches the system to the compiling mode. ;] compiles the EXIT which ends
colon definitions and switches the system back to the interpretive mode. We
are left with the stuff between the brackets compiled into the dictionary
(with the appropriate code field) and the address of the fragment on the
stack.

When STATE is non-zero, a "code literal handler" <[:> is compiled followed by
an address (to be filled in later) followed by the address of the inner
interpreter, followed by the code fragment and any other code. ;] compiles
an EXIT (after the code fragment) and fills in the address where the remaining
code starts. When this word executes, the "handler" gets the "return address"
from the return stack -- knowing that it points to what would normally be the
next instruction to be executed. (The "next instruction" is actually the
address of the code after the end of our code fragment). The handler puts this
address back in the return stack (thus fooling the system into jumping over
the code fragment) and it puts the address of the code fragment on the stack.

These words are interesting because they bring up a point: they do not involve
assembly language, but they involve a knowledge of how traditional Forth is
implemented. They involve knowing what is put in the return stack, how a
dictionary entry is compiled, what a dictionary entry looks like, etc. (The
amazing thing is that the definitions above will work on any traditionally
implemented Forth regardless of processor! It's like having a totally portable
assembly language.)  An aspect of Forth which hasn't been touched on is a
Forth programmer's ability to use his or her knowledge of the implementation.
(Which, again, accounts for a small fraction of code but some of the most
powerful results.)

I should note that these definitions even work on my friend's XFORTH written
in 'C' (which is, in many respects, a conventional Forth) but they do not work
in F-PC (which is a non-traditional Forth even though it is written in Forth
plus assembler).


                                                  John J Wavrik
             jjwavrik@ucsd.edu                    Dept of Math  C-012
                                                  Univ of Calif - San Diego
                                                  La Jolla, CA  92093