Forth has the IF ELSE THEN and BEGIN WHILE REPEAT control flow structures as well as DO LOOP's. There are even some Forths with additional control flow words such as AHEAD and ELIF . I was wondering about other methods of control flow.
PETTIL, by chitselb, has a CASE# ELSE THEN structure. It also has ?:.
I was looking at one of my CREATE DOES> words and it reminded me of a CASE statement.
The CREATE DOES> word XTTABLE bears a resemblance to a CASE statement in sofar as there are tests and actions.
: XTTABLE ( -- )
CREATE
DOES> ( N -- )
BEGIN
2DUP @ EXECUTE 0=
WHILE
2+ 2+
REPEAT
2+ @ EXECUTE ;
: DEFAULT ( N -- T )
DROP TRUE ;
XTTABLE creates a word without a parameter field. The parameter field is filled in after the child of XTTABLE is defined. The parameter field consists of tests and actions. A test word is passed the same parameter the XTTABLE word received and is expected to consume that parameter and return a flag. An XTTABLE word will keep going down the list of word pairs until a test returns a TRUE flag. When that happens, the corresponding action word is executed with the same parameter on the stack. After this action word executes, the XTTABLE word exits. Since the XTTABLE word will continue down the list of word pairs until a test returns a TRUE flag, the last test must be the default case. DEFAULT just drops the parameter and returns a TRUE flag. The words used by an XTTABLE word are compiled in this order:
Rather than using ' SOMEWORD , , I reverse the normal order of [ and ] to compile the words used by the XTTABLE word <SEE> .
The word <SEE> is used like this:
FOR NEXT came from cmForth as a replacement for DO LOOP. The latter
was deemed too expensive to implement on Forth chips. Implementations
vary e.g. cmForth's iterates n+1 times for reasons peculiar to Novix.
eForth creator Bill Muench provides this diagram & test to explain how
FOR NEXT works with AFT and WHILE. Use fixed-width font to view.
\ v--------<<
\ FOR ... NEXT a1 ...
\
\ v---------------------<< >>----------v
\ FOR ... WHILE a1 ... NEXT a2 ... ELSE a3 ... THEN ...
\ >>-------------------------^
\
\ v-----------------<<
\ FOR ... AFT a1 ... THEN ... NEXT a2 ...
\ >>----------^
( test FOR-NEXT ============================================= )
: N1 ( -- ) 9 FOR R@ . NEXT ;
: N2 ( -- ) 9 FOR 5 R@ < WHILE R@ . NEXT ." X" ELSE R> DROP ." Y" THEN ;
: N3 ( -- ) 9 FOR ." X" AFT ." Y" THEN R@ . NEXT ;
Results using Bill's eForth
ok N1 9 8 7 6 5 4 3 2 1 0
ok N2 9 8 7 6 Y
ok N3 X9 Y8 Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!
One of the words I added to my version of FIG Forth is NIF (short for "not if"). Predictably, it compiles a primitive which behaves like 0=IF. And NUNTIL was "not until."
These are pretty trivial, of course, but seemed worthwhile given my own relative priorities re the speed/memory tradeoff.
One of the words I added to my version of FIG Forth is NIF (short for "not if").
Wow, looking at my own code, there are enough occurrences of 0= IF that I think that would pay for itself in memory, so there's no penalty for the faster execution. I think I would call it something else though unless "NIF" is already in common use, because it looks like a short form of "nifty" and definitely did not make me think "NOT IF." NOT in Forth XOR's the value with -1, rather than doing 0=.
I believe FOR...NEXT is in the newest standards, probably as a concession to reduce newcomers' resistance to Forth, but I do like DO...LOOP and friends. I have 32-bit equivalents too, at http://wilsonminesco.com/Forth/32DOLOOP.FTH .
Michael, can you give further explanation of what the flow is in FOR...WHILE a1...NEXT a2...ELSE a3...THEN...
I asked the question and received that from HAA as the kindest answer, but I'm not embarrassed to admit that I still don't fully understand the answer.
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!
\ v--------<<
\ FOR a0 ... NEXT a1 ...
\
\ v--------------------<< >>------------v
\ FOR a0... WHILE a1 ... NEXT a2 ... ELSE a3 ... THEN a4 ...
\ >>-------------------------^
\
\ v--------------------<<
\ FOR a0 ... AFT a1 ... THEN a2 ... NEXT a3 ...
\ >>-------------^
This is how it works in my Forth.
First a brief refresher on the control flow words >MARK >RESOLVE <MARK <RESOLVE . >MARK leaves an address to be resolved and reserves a cell in the dictionary while >RESOLVE resolves the forward reference. <MARK leaves a destination address for a backward branch and <RESOLVE compiles that address in the dictionary. This is from my Forth. The (minimal) compiler security is just to make sure that a <MARK is resolved by a <RESOLVE and a >MARK is resolve by a >RESOLVE .
The FOR and NEXT loop structure is similar to a BEGIN UNTIL loop. AFT is the unusual one. It drops the control flow data from FOR , compiles BRANCH and adds control flow data for a forward branch and backward branch. AFT then swaps the control flow data because the forward branch gets resolved before the backward branch.
The address after (NEXT) is the branch address. My SEE doesn't recognize (NEXT) as a branching word, but that is not a problem.
The results match those provided.
One of the words I added to my version of FIG Forth is NIF (short for "not if").
Wow, looking at my own code, there are enough occurrences of 0= IF that I think that would pay for itself in memory, so there's no penalty for the faster execution. I think I would call it something else though unless "NIF" is already in common use, because it looks like a short form of "nifty" and definitely did not make me think "NOT IF." NOT in Forth XOR's the value with -1, rather than doing 0=.
I think I saw it called -IF but it has been a while and I can't remember where.
I need to run some more tests on my FOR NEXT loops. I suspect the following will be true:
When nested, an inner FOR NEXT loop can be in any part of the outer loop as long as the control structures do not cross. WHILE can even be used with a FOR ... AFT ... THEN ... NEXT loop. Because AFT places two sets of control flow data on the control flow stack (in my case, the data stack), WHILE cannot be used to branch past NEXT from between AFT and THEN . In other words, I don't think this will work:
FOR a0 ... AFT a1 ... WHILE a2 ... THEN a3 ... NEXT a4 ... THEN a5 ...
Multiple WHILEs can be used. Each must have a matching THEN after NEXT.
I think my Forth's compiler security of making sure each >MARK is matched with a >RESOLVE , each <MARK is matched with a <RESOLVE and a colon or CODE is matched with a semicolon or END-CODE will cause an ABORT if these conditions are not met.
Also, if any WHILE's are used to branch out of the FOR NEXT loop, the loop parameter will still be on the return stack as shown in test word N2 .
v-------------------------------------------<<
FOR A0 ... AFT A1 ... WHILE A2 ... THEN A3... THEN A4... NEXT A5...
>>------------------------^
>>--------------------------^
Jim, I knew you'd be the guy here to figure out how eForth's AFT works. Now all I have to do is figure out how you figured it out, then figure out a proper use for it ...
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!
One of the words I added to my version of FIG Forth is NIF (short for "not if").
Wow, looking at my own code, there are enough occurrences of 0= IF that I think that would pay for itself in memory, so there's no penalty for the faster execution. I think I would call it something else though unless "NIF" is already in common use, because it looks like a short form of "nifty" and definitely did not make me think "NOT IF." NOT in Forth XOR's the value with -1, rather than doing 0=.
I think I saw it called -IF but it has been a while and I can't remember where.
One of the words I added to my version of FIG Forth is NIF (short for "not if").
Wow, looking at my own code, there are enough occurrences of 0= IF that I think that would pay for itself in memory, so there's no penalty for the faster execution. I think I would call it something else though unless "NIF" is already in common use, because it looks like a short form of "nifty" and definitely did not make me think "NOT IF." NOT in Forth XOR's the value with -1, rather than doing 0=.
I think I saw it called -IF but it has been a while and I can't remember where.
... I think I saw it called -IF but it has been a while and I can't remember where.
IF0 or IF_0 would be pretty intuitive.
-IF is the one that I saw previously, but I have to say that IF0 is the best combination of intuitive and fast to type for me.
I think -IF is most intuitive if you have a dictionary with a leading "-" for a number of words that invert the test for the underlying word. Otherwise I'd have a risk of reading it as taking the following action if the top of the stack is negative.
I would say that "I have FOR NEXT in xForth", but I haven't had an opportunity to work on it over the past year, and until I have a working CREATE / DOES> word pair, I don't think it's fair to say that I actually "have" xForth -- rather I have code that hopes to grow up into xForth on of these days.
To me, the leading zero seems good. Terse yet clear. Of course I liked the leading N too (example: NIF ), and I knew well enough that it didn't mean NOT (or "nifty"!)
But the leading 0 seems fine for 0IF and likewise for 0WHILE.
As for this...
?EXIT \ pop top of stack. exit if true.
?0EXIT \ pop top of stack. exit if false.
... because they both conditionally execute based on TOS (which gets popped), and that's a scenario I associate with the question mark. And the 0 gets added if you wanna reverse the sense.
?EXIT \ pop top of stack. exit if true.
?0EXIT \ pop top of stack. exit if false.
... because they both conditionally execute based on TOS (which gets popped), and that's a scenario I associate with the question mark. And the 0 gets added if you wanna reverse the sense.
-- Jeff
I like it. Good thing I haven't released Fleet Forth version 2.0 yet.