トップ 差分 一覧 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がネストしている。

ソースコード

戻る 前へ 次へ

moiforth-0.4.tar.gz moiforth-0.4.zip