トップ 一覧 Farm 検索 ヘルプ RSS ログイン

Forthを作ってみる / 繰り返し構造を実装するの変更点

  • 追加された行はこのように表示されます。
  • 削除された行はこのように表示されます。
!!!繰り返し構造を実装する

Forthの繰り返し構造は色々あるが、DO〜LOOPを実装する。

 10 0 DO
     I .
 LOOP

10がループカウンタの終値、0が初期値で、このループはカウンタが0から9まで繰り返したあと、終了する。 
ちなみに'I'は、ループカウンタの現在値をスタックに積む。 
上記の処理結果は、

 0 1 2 3 4 5 6 7 8 9

となる。 
ところで、DOワードは終値<初期値でもループ内を1回は実行する。 
C言語のdo〜whileみたいなもの。 
ちなみにpForthでは、終値<初期値だと止まらない。 
もしかして終了条件が「カウンタ = 終値」になってるんだろうか?

コード化されたプログラムは、次のようになる:

 doコード, ループ内処理, loopコード, ループ内先頭番地, ループ外処理

構文解析時の動作は、こんな感じ。

"do"を見つけたら、
*doコードを現在番地に保存する
*doの次の番地(ループ内処理の先頭番地)をloopの飛び先として保存する

"loop"を見つけたら、
*loopコードを現在番地に保存する
*先に保存した、ループ内処理の先頭番地を保存する

do-loopもネスト可能にするため、loopの飛び先を保存する場所は、if-else-thenで使用した分岐スタックif_stackを用いる。

実行時の動作は、

"do"を見つけたら、
*スタックに積まれた初期値と終値を取り出し、それぞれ、ループ制御領域のカウンタ初期値と終値として保存する

"loop"を見つけたら、
*ループ制御領域のカウンタをインクリメントし、終値と比較する
カウント値が終値に達していたら、ループを抜ける。達していない時、ループ内の先頭の命令にジャンプする

!!コード

構文解析用に、parse()に以下のコードを追加する。

doの処理:
 } else If (strcmp(token, "do") == 0) {
      prog[idx] = CODE_DO;   /* doコードを現在番地に保存する */
      push_branch_stack(idx + 1);    /* doの次の番地(ループ内処理の先頭番地)をloopの飛び先として保存する */
      idx++;
 
loopの処理:
 } else if (strcmp(token, "loop") == 0) {
      prog[idx] = CODE_LOOP; /* loopコードを現在番地に保存する */
      prog[idx + 1] = pop_branch_stack();    /* 先に保存した、ループ内処理の先頭番地を保存する */
      idx += 2;

push_branch_stack(), pop_branch_stack()は以前はpush_if_stack(), pop_if_stack()から名前を変更した。

実行時用に、proc_prog()に以下のコードを追加する。

doの処理:
 case CODE_DO:
     call_do();
     prog_cnt++;
     break;
 
 /* ワード'do'を実行する */
 void call_do(void)
 {
     loop_pos--;
 
     /* スタックの内容をループスタックに入れる */
     loop_cnt_stack[loop_pos] = pop();
     loop_end_stack[loop_pos] = pop();
 }


loopの処理:
 case CODE_LOOP:
     call_loop();
     break;
 
 /* ワード'loop'を実行する */
 void call_loop(void)
 {
     loop_cnt_stack[loop_pos]++;
     if (loop_cnt_stack[loop_pos] >= loop_end_stack[loop_pos]) {
         /* ループを抜ける */
         loop_pos++;
         prog_cnt += 2;
 
     } else {
         /* ループ先頭に戻る */
         prog_cnt = prog[prog_cnt + 1];
 
     }
 }

同様にiとcr(改行する)を追加した。

iの処理:
 /* ワード'i'を実行する */
 void call_i(void)
 {
     /* スタックに一番内側のループカウンタをpushする */
     push(loop_cnt_stack[loop_pos]);
     prog_cnt++;
 }
 
 
crの処理:
 /* ワード'cr'を実行する */
 void call_cr(void)
 {
     printf("\n");
     prog_cnt++;
 }


以下のコード(testfile/test5)を試してみる。

 5 0 do
 	i . cr
 loop

 結果:
 $ moiforth.exe testfile/test5
 0
 1
 2
 3
 4

できた。 
test6はdoがネストしている。

[ソースコード|http://moi2.sakura.ne.jp/fswiki/wiki.cgi?page=ImplementingRepeat&file=moiforth%2D0%2E4%2Etar%2Egz&action=ATTACH]

[[戻る|ForthImplementation]] [[前へ|ImplementingBranch]] [[次へ|ImplementingDefineWord]]
[[戻る|Forthを作ってみる]] [[前へ|Forthを作ってみる / 条件分岐を実装する]] [[次へ|Forthを作ってみる / ワード定義を実装する]]

{{adsence}}