制御フロー・スタック

  • 更新:2024年12月20日

  • 作成:2024年7月27日

メリット

知れば制御構造がらみで出てくるエラーの理解が進みます。 初見だと制御構造絡みのエラーは訳わからん

あー、 更にガンバレば独自の制御構造が作れます。 但し、 たいていの制御構造は既に用意されています…

解説

参照: (gforth) 6.9.6 Arbitrary control structures https://kuma35.github.io/gforth-docs-ja/docs-ja-0/gforth/Arbitrary-control-structures.html

制御構造の「コンパイル時」に使用されるのが「制御フロー・スタック」(control-flow stack) です。

gforth の制御フロー・スタックは(定義ごとに)コンパイル時にデーター・スタック上に構築され使用されます。

逆に言うと、コンパイル時のみで制御構造の実行時にはの制御フロー・スタックは存在しません。

スタック上に

( stack-state locals-list address type )

cs-roll, cs-pick, cs-drop は この4つ を 1組扱いして操作します。

  • type ( defstart, live-orig, dead-orig, dest, do-dest, scopestart) ( TOS )

  • address (of the branch or the instruction to be branched to) (second)

  • locals-list (valid at address) (third)

  • stack state address for checking (fourth)

6.9.6.1 Programming Style の例に cs-stack を例示してみましょう。 これは true IF の時は IF の ... を実行し、更に AGAINに到達しBEGINに戻り、 false IF のときは THEN に飛んでBEGIN〜AGAINループを抜ける事を意図しています。

BEGIN ( cs: dest_BEGIN )
  \ ...
  IF ( cs: dest_BEGIN orig_IF )
    \ ...
( destを期待 ) AGAIN ( orig を期待) THEN

これをコンパイルすると以下のようにエラーとなります。 AGAINでは dest を期待しているからです。

: hoge  compiled
  BEGIN  compiled
    IF  compiled
  AGAIN THEN ;
*the terminal*:4:3: error: expected dest
>>>AGAIN<<< THEN ;

そこで、 IF の後ろで [ 1 CS-ROLL ] として順番を入れ替えます。 ここで注意してほしいのは「コンパイル時」の操作だということです。 制御構造にかかわらず前から後ろへ上から下へ順番にワードは処理されていきます。 IFブロックの中だからといって true IF の時だけ [ 1 CS-ROLL ] が処理される訳では無いということです。 IFが条件分岐として機能するのはあくまで実行時です。だんだん混乱してきましたね。訳者もよく混乱しています。

gforth (forth) は 自在変幻にコンパイルと実行が入り交じる(コンパイル自体もワードの「実行」によって行われる) という実にわけわからんなんじゃこりゃなので、 そこらへん注意深くじっくり見つめましょう。

BEGIN ( cs: dest_BEGIN )
  \ ...
  IF ( cs: dest_BEGIN orig_IF ) [ 1 CS-ROLL ] ( cs: orig_IF dest_BEGIN )
  \ ...
( destを期待 ) AGAIN ( orig を期待) THEN
: hoge  compiled
  BEGIN  compiled
    IF [ 1 CS-ROLL ]  compiled
  AGAIN THEN ;  ok

正常にコンパイルできました。

ちょっとそれっぽく動くようにしてみましょう。

: hoge  ( 0 ... n -- )
  BEGIN
    dup .
    IF [ 1 CS-ROLL ]
      ." true"
  AGAIN THEN ;
0 1 2 3 4 5 hoge 5 true4 true3 true2 true1 true0  ok
1 2 3 4 5 hoge 5 true4 true3 true2 true1 true
*the terminal*:19:11: error: Stack underflow
1 2 3 4 5 >>>hoge<<<

orig

前方参照のために cs-stack に積まれます。 orig を積む時、当該ワードは手元のアドレスに ダミーの ゼロ を書き込みます。 そのアドレスを orig の address にセットします。

orig を 消費するワードは、 orig address の指し示す場所、 つまり、 ダミーのゼロを orig を 消費するワードの位置のアドレスで書き換えます。

これで、 orig を積んだワードから、 orig を消費するワードへ、実行時にジャンプができるようになりました。

dest

dest を積むワードのアドレスを orig と同様に dest の address にセットしますが、 dest の address がの指し示すのは、 dest を積むワードのアクションへのアドレスをセットします。 これは後から書き換えはされません。

dest を消費するワードは dest から取り出した address を dest を消費するワードのジャンプ先としてセットします。

これで、 dest を消費するワードから dest を積んだワードへ実行時にジャンプできるようになりました。