Forthを作るのじゃ(3)条件分岐できるようにする

こんにちは、鈴木です。

 

興味本位で作り始めた Forth インタプリタの続きです。

前回はワードを定義できるようにしたので、今度は条件分岐をサポートしようと思います。

 

条件分岐の書き方(おさらい)

条件分岐の書き方ですが、

または、

と書くのでした。

また、条件分岐はワードの定義内(「:」から「;」までの間)でしか書けない、ということもポイントです。

例えば(スタックの一番上にある)値が負数の場合は 0 にする、というワードは次のように定義できます。

if ~ then をネストすることもできるので、値の符号を求めるワード(負数なら -1、正数なら 1、0 なら 0 とするワード)は次のように定義できます。

 

前回書いたコード

前回書いたコードを再掲します。

前回は単純な計算しかできない状態からワード定義ができるように改良しました。

実装面では、ワードを評価するメソッド eval_word(word) 内部で、ワード定義内にいる時は eval_word_in_definition(word) を呼ぶ、ワード定義の外側にいるときは eval_word_in_default(word) を呼ぶように変更しました(どちらを呼び出すかは @eval_word に Method オブジェクトを代入しておきました)。

 

今回の修正方針

今回は条件分岐をサポートします。

条件分岐はワード定義内(「:」から「;」までの間)でしか書けないため、eval_word_in_definition(word) メソッドでうまいこと対応したいところです。

ということで修正版のコードです。

コメント中の (1) を見てください。条件分岐は if で始まり then で終わるので、@words_in_definition に if と then を追加しました。else は追加していませんが、それは then を見つけた段階で if から then の中を Proc オブジェクトに変換するため、@words_in_definition に追加して特別に処理する必要は無いからです。

次に (2) の部分ですが、@eval_word を配列に変更しました。というのも、if ~ else ~ then はネストする可能性があるため、単純に「then が来たら @eval_word に method(:eval_word_in_default) を代入」してしまうと正しく動かないためです。呼び出す側も「@eval_word.call(word)」から「@eval_word.last.call(word)」に変更しています。

(3) では if ~ then の部分を処理しています。begin_if_definition メソッドでは @stack に 'if' を積んでから @eval_word に method(:eval_word_in_definition) を追加しています。

end_if_definition メソッドでは、@stack から 'if' の定義を definitions に取り出し、条件が真の場合(true_case)と偽の場合(false_case)に分割しています。あとは Proc オブジェクトに変換してから @words_in_default に追加するだけです。

 

動かしてみる

それでは動かしてみます。

最初に出てきた、値の符号を求めるワード sign を定義してみます。

適当な値で試してみます。

プラスの値だと 1 になる。

0 だと 0 になる。

マイナスの値だと -1 になる。

きちんと動きました。

 

次回はループをサポートしようと思います。

 

Comments are closed, but you can leave a trackback: Trackback URL.