標準 Forth は、 ネストされない方法での制御構造の使用を許可・サポートします。 まだ完成されてない制御構造に関する情報は、 制御フロー・スタック(control-flow stack)に保存されます。 このスタックは Forth のデータ・スタック上に実装でき、 Gforth はそうしました。
orig エントリは未解決の前方分岐を表し、 dest エントリは後方分岐ターゲットを表します。 いくつかのワードは、 可能なあらゆる制御構造を構築するための基礎となります(呼び出しやコルーチンやバックトラッキングのような、 ストレージを必要とする制御構造を除く)。
IF
( compilation – orig ; run-time f – ) core “IF”
実行時(run-time)、 f=0 の場合、 (コンパイル時に) orig を消費する THEN
(または
ELSE
) の後から実行が続行されます。 それ以外の場合は、 IF
の直後に続きます(see Selection)。
AHEAD
( compilation – orig ; run-time – ) tools-ext “AHEAD”
実行時、 (コンパイル時に) orig を消費する THEN
の後から実行が続行されます(訳注: つまり、 単純に
THEN
へジャンプする)。
THEN
( compilation orig – ; run-time – ) core “THEN”
(コンパイル時に) orig をプッシュした IF
または AHEAD
または ELSE
または
WHILE
は、 THEN
の直後にジャンプします(see Selection)。
BEGIN
( compilation – dest ; run-time – ) core “BEGIN”
(コンパイル時に) dest を消費する UNTIL
または AGAIN
または REPEAT
は、
BEGIN
の直後へジャンプします(see Simple Loops)。
UNTIL
( compilation dest – ; run-time f – ) core “UNTIL”
実行時、 f=0 の場合、 (コンパイル時に) dest をプッシュした BEGIN
の直後から実行が続行されます。
それ以外の場合は、 UNTIL
の直後から実行が続行されます(see Simple Loops)。
AGAIN
( compilation dest – ; run-time – ) core-ext “AGAIN”
実行時、 (コンパイル時に) dest をプッシュした BEGIN
の直後から実行が続行されます(see Simple Loops)。
CS-PICK
( orig0/dest0 orig1/dest1 ... origu/destu u – ... orig0/dest0 ) tools-ext “c-s-pick”
CS-ROLL
( destu/origu .. dest0/orig0 u – .. dest0/orig0 destu/origu ) tools-ext “c-s-roll”
CS-DROP
( dest – ) gforth-1.0 “CS-DROP”
標準ワードの CS-PICK
や CS-ROLL
を使用すると、 移植可能な方法で制御フロー・スタックを操作できます。
これら無しだと制御フロー・エントリが占めるスタック項目の数を知る必要があります(多くのシステムは 1 つのセルを使用します。 Gforth では現在 4
つを使用しますが、 これは将来変更される可能性があります)。
orig は 1 回だけ解決する必要があるため、 CS-PICK
は dest を pick することしかできず、 かつ、
CS-DROP
は dest を drop することしかできません。
一部の標準の制御構造ワードは、 以下のワード群から構築されます:
ELSE
( compilation orig1 – orig2 ; run-time – ) core “ELSE”
実行時、 (コンパイル時に) orig を消費する THEN
の直後から実行が続行されます。 orig1 をプッシュした
IF
または AHEAD
または ELSE
または WHILE
は、 ELSE
の直後にジャンプします(see Selection)。
WHILE
( compilation dest – orig dest ; run-time f – ) core “WHILE”
実行時、 f=0 の場合、 (コンパイル時の) orig を消費する REPEAT
(または THEN
または ELSE
) の直後から実行が継続されます。 それ以外の場合は、 WHILE
の直後から実行されます(see Simple Loops)。
REPEAT
( compilation orig dest – ; run-time – ) core “REPEAT”
実行時、 (コンパイル時に) dest をプッシュした BEGIN
の直後から実行が続行されます。 orig
をプッシュした WHILE
または IF
または AHEAD
または ELSE
は、
REPEAT
の直後にジャンプします(see Simple Loops)。
Gforth は、さらにいくつかの制御構造ワードを追加します:
ENDIF
( compilation orig – ; run-time – ) gforth-0.2 “ENDIF”
THEN
と同一です。
?dup-IF
( compilation – orig ; run-time n – n| ) gforth-0.2 “question-dupe-if”
これは、スタック・チェッカー(stack checker)などのツールでより適切に処理できるため、 イディオム「?DUP
IF
」の代替として推奨されます。 しかも、 ?DUP IF
より速いです。
?DUP-0=-IF
( compilation – orig ; run-time n – n| ) gforth-0.2 “question-dupe-zero-equals-if”
制御構造ワードのもう一つのグループ:
case
( compilation – case-sys ; run-time – ) core-ext “case”
case
構造の開始。
endcase
( compilation case-sys – ; run-time x – ) core-ext “end-case”
case
構造を終わらせます。 x を drop して、endcase
の後ろへ進みます。 x の drop は、
元の(of
のみの)case
構造では便利ですが、 他の場合(特に ?of
を使用する場合)では(drop
する為の) x を明示的に指定する必要がある場合があります。
next-case
( compilation case-sys – ; run-time – ) gforth-1.0 “next-case”
一致する case
にジャンプして、 case
ループを再開します。 endcase
とは異なり、
next-case
はセルを drop しないことに注意してください。
of
( compilation – of-sys ; run-time x1 x2 – |x1 ) core-ext “of”
x1=x2 の場合は続行します(両方を drop します)。 それ以外の場合は、 x1 をスタック上に残し、 endof
または
contof
の後ろにジャンプします。
?of
( compilation – of-sys ; run-time f – ) gforth-1.0 “question-of”
f が true の場合は続行します。 それ以外の場合は、 endof
または contof
の後ろにジャンプします。
endof
( compilation case-sys1 of-sys – case-sys2 ; run-time – ) core-ext “end-of”
endcase
/next-case
の後ろにジャンプして、 囲んでいる case
構造を終了(exit)します。
contof
( compilation case-sys1 of-sys – case-sys2 ; run-time – ) gforth-1.0 “cont-of”
囲んでいる case
にジャンプして、 case
ループを再開します。
内部的には、 of-sys は orig
で、 case-sys はセルとスタック深さ情報と、0 個以上の
orig
と、 dest
です。
読みやすさを確保するために、 任意の制御構造を直接作成せず、 必要な制御構造に対して新しい制御構造ワードを定義し、 プログラム内でこれらのワードを使用することをお勧めします。たとえば、 以下のように書く代わりに:
BEGIN ... IF [ 1 CS-ROLL ] ... AGAIN THEN
以下のように制御構造のワードを定義することをお勧めします。 例:
: WHILE ( DEST -- ORIG DEST ) POSTPONE IF 1 CS-ROLL ; immediate : REPEAT ( orig dest -- ) POSTPONE AGAIN POSTPONE THEN ; immediate
そして、 次に、 これらを使用して制御構造を作成します:
BEGIN ... WHILE ... REPEAT
このほうがずっと読みやすいですよね。 もちろん、 REPEAT
と WHILE
は定義済みなので、
この例を見て改めて定義する必要はありません。