Page 1 of 5
Not your run of the mill control flow
Posted: Sun Sep 18, 2022 10:39 pm
by JimBoyd
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.
Code: Select all
: 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:
Code: Select all
TEST0 ACTION0 TEST1 ACTION1 ... TESTN ACTIONN
Here is an example.
Code: Select all
XTTABLE <SEE> ( CFA -- ??? )
] ?CODE >DIS
?HL >:DIS
?DEFER DDIS
DEFAULT CREATEDIS [
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:
Code: Select all
: (SEE) ( CFA -- )
DUP CR .NAME
SETWIDTH <SEE> ;
: SEE ( -- ) ' (SEE) ;
So, what other unique control methods are out there, or even the traditional ones used in unique and interesting ways?
Re: Not your run of the mill control flow
Posted: Mon Sep 19, 2022 3:29 pm
by barrym95838
So, what other unique control methods are out there, or even the traditional ones used in unique and interesting ways?
Courtesy of "HAA" on usenet:
Code: Select all
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
Re: Not your run of the mill control flow
Posted: Tue Sep 20, 2022 1:00 pm
by Dr Jefyll
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.
-- Jeff
Re: Not your run of the mill control flow
Posted: Tue Sep 20, 2022 9:14 pm
by GARTHWILSON
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...
Re: Not your run of the mill control flow
Posted: Tue Sep 20, 2022 10:13 pm
by barrym95838
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.
Re: Not your run of the mill control flow
Posted: Wed Sep 21, 2022 9:40 pm
by JimBoyd
Courtesy of "HAA" on usenet:
Monday evening I think I figured this out and wrote FOR NEXT words. The diagrams were not much help. This better represents what my FOR NEXT words do.
Code: Select all
\ 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 .
Code: Select all
: ?PAIRS ( N1 N2 -- )
<>
ABORT" STRUCTURE MISMATCH" ;
: >MARK ( -- >SYS )
HERE 1 0 , ;
: >RESOLVE ( >SYS -- )
1 ?PAIRS HERE SWAP ! ;
: <MARK ( -- <SYS )
HERE 2 ;
: <RESOLVE ( <SYS -- )
2 ?PAIRS , ;
Here are the FOR NEXT words. The only new primitive is (NEXT) .
Code: Select all
' ?BRANCH @ 8 + CONSTANT 2.IP.+!
CODE (NEXT) ( -- )
PLA TAY
0= IF
PLA
0= IF
2.IP.+! JMP
THEN
SEC 1 # SBC PHA
THEN
DEY TYA PHA
0 # LDY
' BRANCH @ JMP
END-CODE
And the high level words.
Code: Select all
: FOR ( -- )
( N -- )
COMPILE >R <MARK ; IMMEDIATE
: NEXT ( -- )
COMPILE (NEXT)
<RESOLVE ; IMMEDIATE
: AFT ( -- )
[COMPILE] CS-DROP COMPILE BRANCH
>MARK <MARK
[COMPILE] CS-SWAP ; IMMEDIATE
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.
Here are the test words:
Code: Select all
: 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 ;
And what got compiled:
Code: Select all
SEE N1
N1
31296 3419 CLIT 9
31299 4656 >R
31301 4740 R@
31303 7568 .
31305 31202 (NEXT)
31307 31301
31309 2970 EXIT
15
OK
SEE N2
N2
31320 3419 CLIT 9
31323 4656 >R
31325 3419 CLIT 5
31328 4740 R@
31330 4448 <
31332 2781 ?BRANCH 31352
31336 4740 R@
31338 7568 .
31340 31202 (NEXT)
31342 31325
31344 7809 (.") X
31348 3042 BRANCH 31360
31352 4672 R>
31354 3031 DROP
31356 7809 (.") Y
31360 2970 EXIT
42
OK
SEE N3
N3
31371 3419 CLIT 9
31374 4656 >R
31376 7809 (.") X
31380 3042 BRANCH 31388
31384 7809 (.") Y
31388 4740 R@
31390 7568 .
31392 31202 (NEXT)
31394 31384
31396 2970 EXIT
27
OK
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.
Code: Select all
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 OK
[Edit: Added some clarification about my SEE .]
Re: Not your run of the mill control flow
Posted: Wed Sep 21, 2022 10:12 pm
by JimBoyd
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.
Re: Not your run of the mill control flow
Posted: Thu Sep 22, 2022 1:16 am
by JimBoyd
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:
Code: Select all
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 .
Code: Select all
: N2 ( -- )
9
FOR
5 R@ <
WHILE
R@ .
NEXT
." X"
ELSE
R> DROP ." Y"
THEN ;
I also think this might be possible, but I need to test it:
Code: Select all
v-------------------------------------------<<
FOR A0 ... AFT A1 ... WHILE A2 ... THEN A3... THEN A4... NEXT A5...
>>------------------------^
>>--------------------------^
Re: Not your run of the mill control flow
Posted: Thu Sep 22, 2022 4:46 am
by barrym95838
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 ...

Re: Not your run of the mill control flow
Posted: Sun Sep 25, 2022 7:05 am
by GARTHWILSON
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.
IF0 or IF_0 would be pretty intuitive.
Re: Not your run of the mill control flow
Posted: Mon Sep 26, 2022 2:35 am
by IamRob
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.
IF0 or IF_0 would be pretty intuitive.
How about "ZIF" or "ZEQIF"?
Re: Not your run of the mill control flow
Posted: Mon Sep 26, 2022 3:32 pm
by BruceRMcF
... 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.
Re: Not your run of the mill control flow
Posted: Mon Sep 26, 2022 11:01 pm
by JimBoyd
What about 0IF ?
Also 0WHILE and 0UNTIL ?
Code: Select all
...
BEGIN
2DUP @ EXECUTE
0WHILE
2+ 2+
REPEAT
...
Code: Select all
...
BEGIN
2DUP @ EXECUTE
WHILE0
2+ 2+
REPEAT
...
Which do you think reads better?
I also have:
Code: Select all
?EXIT \ pop top of stack. exit if true.
0EXIT \ pop top of stack. exit if false.
Re: Not your run of the mill control flow
Posted: Mon Sep 26, 2022 11:49 pm
by Dr Jefyll
What about 0IF ?
Also 0WHILE and 0UNTIL ?
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...
Code: Select all
?EXIT \ pop top of stack. exit if true.
0EXIT \ pop top of stack. exit if false.
... my own preference would be...
Code: Select all
?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
Re: Not your run of the mill control flow
Posted: Tue Sep 27, 2022 12:26 am
by JimBoyd
... my own preference would be...
Code: Select all
?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.