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