Standard 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).
IF
( compilation – orig ; run-time f – ) core “IF”
At run-time, if f=0, execution continues after the
THEN
(or ELSE
) that consumes the orig,
otherwise right after the IF
(see Selection).
AHEAD
( compilation – orig ; run-time – ) tools-ext “AHEAD”
At run-time, execution continues after the THEN
that
consumes the orig.
THEN
( compilation orig – ; run-time – ) core “THEN”
The IF
, AHEAD
, ELSE
or WHILE
that
pushed orig jumps right after the THEN
(see Selection).
BEGIN
( compilation – dest ; run-time – ) core “BEGIN”
The UNTIL
, AGAIN
or REPEAT
that consumes
the dest jumps right behind the BEGIN
(see General Loops).
UNTIL
( compilation dest – ; run-time f – ) core “UNTIL”
At run-time, if f=0, execution continues after the
BEGIN
that produced dest, otherwise right after
the UNTIL
(see General Loops).
AGAIN
( compilation dest – ; run-time – ) core-ext “AGAIN”
At run-time, execution continues after the BEGIN
that
produced the dest (see General Loops).
CS-PICK
( dest0/orig0 dest1/orig1 ... destu/origu u – ... dest0/orig0 ) tools-ext “c-s-pick”
CS-ROLL
( destu/origu .. dest0/orig0 u – .. dest0/orig0 destu/origu ) tools-ext “c-s-roll”
CS-DROP
( dest/orig – ) gforth-1.0 “CS-DROP”
The Standard words cs-pick
and cs-roll
allow you to
manipulate the control-flow stack in a portable way. Without them, you
would need to know how many stack items are occupied by a control-flow
entry (Many systems use one cell. In Gforth they currently take four
cells, but this may change in the future).
When using cs-pick
and cs-drop
on an orig, you need to
use one cs-drop
for every cs-pick
(and vice versa) of a
given orig, because the orig must be resolved by then
exactly
once.
Some standard control structure words are built from these words:
ELSE
( compilation orig1 – orig2 ; run-time – ) core “ELSE”
At run-time, execution continues after the THEN
that
consumes the orig; the IF
, AHEAD
, ELSE
or WHILE
that pushed orig1 jumps right after the
ELSE
. (see Selection).
WHILE
( compilation dest – orig dest ; run-time f – ) core “WHILE”
At run-time, if f=0, execution continues after the
REPEAT
(or THEN
or ELSE
) that consumes the
orig, otherwise right after the WHILE
(see General Loops).
REPEAT
( compilation orig dest – ; run-time – ) core “REPEAT”
At run-time, execution continues after the BEGIN
that
produced the dest; the WHILE
, IF
,
AHEAD
or ELSE
that pushed orig jumps right
after the REPEAT
. (see General Loops).
Gforth adds some more control-structure words:
ENDIF
( compilation orig – ; run-time – ) gforth-0.2 “ENDIF”
Same as THEN
.
?dup-IF
( compilation – orig ; run-time n – n| ) gforth-0.2 “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=-IF
( compilation – orig ; run-time n – n| ) gforth-0.2 “question-dupe-zero-equals-if”