- 追加された行はこのように表示されます。
- 削除された行は
このように表示されます。
!!!条件分岐を実装する
今まで作った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を作ってみる / 条件分岐を実装する]]に移動しました。