ANS Forth permits and supports using control structures in a non-nested way. Information about incomplete control structures is stored on the control-flow stack. This stack may be implemented on the Forth data stack, and this is what we have done in Gforth.
An orig entry represents an unresolved forward branch, a dest entry represents a backward branch target. A few words are the basis for building any control structure possible (except control structures that need storage, like calls, coroutines, and backtracking).
IFcompilation -- orig ; run-time f -- core ``IF''
AHEADcompilation -- orig ; run-time -- tools-ext ``AHEAD''
THENcompilation orig -- ; run-time -- core ``THEN''
BEGINcompilation -- dest ; run-time -- core ``BEGIN''
UNTILcompilation dest -- ; run-time f -- core ``UNTIL''
AGAINcompilation dest -- ; run-time -- core-ext ``AGAIN''
CS-PICK... u -- ... destu tools-ext ``CS-PICK''
CS-ROLLdestu/origu .. dest0/orig0 u -- .. dest0/orig0 destu/origu tools-ext ``CS-ROLL''
On many systems control-flow stack items take one word, in Gforth they
currently take three (this may change in the future). Therefore it is a
really good idea to manipulate the control flow stack with
cs-roll, not with data stack manipulation
Some standard control structure words are built from these words:
ELSEcompilation orig1 -- orig2 ; run-time f -- core ``ELSE''
WHILEcompilation dest -- orig dest ; run-time f -- core ``WHILE''
REPEATcompilation orig dest -- ; run-time -- core ``REPEAT''
Gforth adds some more control-structure words:
ENDIFcompilation orig -- ; run-time -- gforth ``ENDIF''
?DUP-IFcompilation -- orig ; run-time n -- n| gforth ``question-dupe-if''
This is the preferred alternative to the idiom "?DUP IF", since it can be better handled by tools like stack checkers. Besides, it's faster.
?DUP-0=-IFcompilation -- orig ; run-time n -- n| gforth ``question-dupe-zero-equals-if''
Counted loop words constitute a separate group of words:
?DOcompilation -- do-sys ; run-time w1 w2 -- | loop-sys core-ext ``question-do''
+DOcompilation -- do-sys ; run-time n1 n2 -- | loop-sys gforth ``plus-do''
U+DOcompilation -- do-sys ; run-time u1 u2 -- | loop-sys gforth ``u-plus-do''
-DOcompilation -- do-sys ; run-time n1 n2 -- | loop-sys gforth ``minus-do''
U-DOcompilation -- do-sys ; run-time u1 u2 -- | loop-sys gforth ``u-minus-do''
DOcompilation -- do-sys ; run-time w1 w2 -- loop-sys core ``DO''
FORcompilation -- do-sys ; run-time u -- loop-sys gforth ``FOR''
LOOPcompilation do-sys -- ; run-time loop-sys1 -- | loop-sys2 core ``LOOP''
+LOOPcompilation do-sys -- ; run-time loop-sys1 n -- | loop-sys2 core ``plus-loop''
-LOOPcompilation do-sys -- ; run-time loop-sys1 u -- | loop-sys2 gforth ``minus-loop''
NEXTcompilation do-sys -- ; run-time loop-sys1 -- | loop-sys2 gforth ``NEXT''
LEAVEcompilation -- ; run-time loop-sys -- core ``LEAVE''
?LEAVEcompilation -- ; run-time f | f loop-sys -- gforth ``question-leave''
unloop-- core ``unloop''
DONEcompilation orig -- ; run-time -- gforth ``DONE''
The standard does not allow using
do-sys. Our system allows it, but it's your job to ensure that for
?DO etc. there is exactly one
UNLOOP on any path
through the definition (
LOOP etc. compile an
UNLOOP on the
fall-through path). Also, you have to ensure that all
resolved (by using one of the loop-ending words or
Another group of control structure words are
casecompilation -- case-sys ; run-time -- core-ext ``case''
endcasecompilation case-sys -- ; run-time x -- core-ext ``end-case''
ofcompilation -- of-sys ; run-time x1 x2 -- |x1 core-ext ``of''
endofcompilation case-sys1 of-sys -- case-sys2 ; run-time -- core-ext ``end-of''
case-sys and of-sys cannot be processed using
In order to ensure readability we recommend that you do not create arbitrary control structures directly, but define new control structure words for the control structure you want and use these words in your program.
E.g., instead of writing
begin ... if [ 1 cs-roll ] ... again then
we recommend defining control structure words, e.g.,
: while ( dest -- orig dest ) POSTPONE if 1 cs-roll ; immediate : repeat ( orig dest -- ) POSTPONE again POSTPONE then ; immediate
and then using these to create the control structure:
begin ... while ... repeat
That's much easier to read, isn't it? Of course,
WHILE are predefined, so in this example it would not be
necessary to define them.
Go to the first, previous, next, last section, table of contents.