6.10.6 Arbitrary control structures

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”