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

ImplementingBranchの変更点

  • 追加された行はこのように表示されます。
  • 削除された行はこのように表示されます。
!!!条件分岐を実装する

今まで作ったForthには、以下の機能が実装されている 

*値をスタックに積む
*足し算する
*表示する

条件分岐や繰り返し構造がない。せめてifくらいないとカッコつかない。 
で、ifワードを実装する。ちなみにワードとはForthの機能単位だ。 

文法は、こうなる。 

 4 5 <
 if
     Trueの時の処理
 else
     Falseの時の処理
 then

ちょっと変わっている。 
ifワードは直前の計算結果、つまりスタックトップの値を取り出し真偽を判定し、処理を分岐させる。 
elseはなくても良い。 

コード化されたプログラムはこんな並びにする。 

 ifコード, False処理開始位置(または、ifブロック脱出位置), Trueの時の処理... , elseコード, ifブロック脱出位置 , Falseの時の処理... , thenコード, 以降の処理(ifブロックの外),,, 

ifコードとelseコードの直後にジャンプ先を入れたのは、 

*実行時にelseの位置やthenの位置を探すのは効率悪い
*コード化されているelseコードとthenコードを構文解析しながら探すのは面倒だ

と考えたからである。 

構文解析は、こんな感じでやっていこう。 
まず、分岐スタック'if_stack'を用意する。これは、「未確定のジャンプ先を保存するプログラム位置」を保存するためのスタックである。 
ややこしいが、要はFalse処理開始位置とifブロック脱出位置を一時保存するためのスタックである。 

"if"を見つけたら、 

*prog[現在位置]にifコードを埋める
*prog[現在位置 + 1](False処理開始位置の格納番地)の値はまだ決まっていないのでそのまま空けておく
*「現在位置+1」を分岐スタックにpushする

書いてて分からなくなる。 

"else"を見つけたら、 

*prog[現在位置]にelseコードを埋め込む

*else位置が確定したので、分岐スタックからpopした場所(False処理開始位置の格納番地)に「現在位置 + 2」(False処理開始番地)を代入する
*prog[現在位置 + 1](ifブロック脱出位置の格納番地)の値はまだ決まっていないのでそのまま空けておく
「現在位置 + 1」を分岐スタックにpushする

"then"を見つけたら、 

*prog[現在位置]にthenコードを埋め込む
*then位置が確定したので、分岐スタックからpopした場所(ifブロック脱出位置の格納番地)に「現在位置 + 1」(ifブロック脱出位置)を代入する


この例ではif - else - thenだが、else処理のないif - then形式も処理できる。 
その場合は、ifコードの次にifブロック脱出位置が格納される。 

また、ifがネストする場合は、分岐スタックに格納番地が積み上がっていき、その時点の最も内側のifコードの解析を行う。 



コード


まず、分岐スタックだが、 

 char if_stack[10];              /* 分岐スタック */
 int if_pos;                     /* 分岐スタックの位置ポインタ */
 
 /* 分岐スタックに番地を積む */
 void push_if_stack(char adrs)
 {
     if_stack[if_pos] = adrs;
     if_pos--;
 }
 
 
 /* 分岐スタックから番地を取り出す */
 char pop_if_stack(void)
 {
     if_pos++;
     return if_stack[if_pos];
 }

構文解析関数parse()に以下の処理を加えた。 

        } else if (strcmp(token, "if") == 0) {
            prog[idx] = CODE_IF;
            push_if_stack(idx + 1);
            idx += 2;
        } else if (strcmp(token, "else") == 0) {
            prog[idx] = CODE_ELSE;
            prog[pop_if_stack()] = idx + 2;
            push_if_stack(idx + 1);
            idx += 2;
        } else if (strcmp(token, "then") == 0) {
            prog[idx] = CODE_THEN;
            prog[pop_if_stack()] = idx + 1;
            idx++;
        }


プログラム処理関数proc_prog()の追加処理。 

        case CODE_IF:
            if (pop()) {
                prog_cnt += 2;
            } else {
                prog_cnt = prog[prog_cnt + 1];
            }
            break;
        case CODE_ELSE:
            prog_cnt = prog[prog_cnt + 1];
            break;
        case CODE_THEN:
            prog_cnt++;
            break;

testfile/test2を試してみる。 
 3
 if
 	4 5 + .
 else
 	1 2 + .
 then

結果 
 $./a.out testfile/test2
 9

test3はif - then形式、test4はifのネストとなっている。 

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

[[戻る|ForthImplementation]] [[前へ|LoadingSourceCode]] [[次へ|ImplementingRepeat]]

{{adsence}}
[[Forthを作ってみる / 条件分岐を実装する]]に移動しました。