Forthを作るのじゃ(2)ワード定義できるようにする

こんにちは、鈴木です。

前回作成した Forth インタプリタを改良して、ワードの定義などができるようにしたいと思います。

 

元になるコード

元になるコードはこちらです。前回作成したものに定義済みワードを追加したものです。(@words にいくつか追加しました。)

 

ワードを定義できるようにしたい

ワードの定義は次のような書き方をするのでした。

例えば値を 3 乗する cube というワードは次のように定義できます。

これを実現するにはどうしたら良いでしょう。

以下のような処理を追加すれば良さそうですね。

  1. 「:」が入力された場合、それ以降の「;」以外の入力は評価せずにスタックに積む。
  2. 「;」が入力された場合、「:」から「;」までを実行する Proc を作成し、@words に登録する。

現状の eval_word(word) メソッドをそのまま使うとワードがすぐに評価されてしまうので、1. を実現することができません。

そのため eval_word(word) の処理内容を「ワード定義外」と「ワード定義内」で処理を分けるようにする必要があります。

それに伴い、ワード定義外とワード定義内で使用可能なワードも切り替える必要があります(例えばワード定義外では「;」は使えないが、ワード定義内では「;」を使えるなど)。

修正方針を整理すると、

  • eval_word(word) の処理内容を「ワード定義外」の場合と「ワード定義内」の場合で切り替える。
  • 使えるワードも「ワード定義外」の場合と「ワード定義内」の場合で切り替える(@words を分割する)。
  • 「;」が入力されたら「:」から「;」までの内容を実行する Proc を作成し、定義済みワードとして登録する。

となります。

 

修正版のコード

上記の方針で修正したコードは以下の通りです。

色々変更していますが、ポイントをコメントで記載しています。

まずは (1) で元のコードにおける @words を @words_in_default(ワード定義外で使用可能なワード)と @word_in_definition(ワード定義内で使用可能なワード)に分割し、それぞれ「:」と「;」の処理を行う Proc を追加しています。

次に (2) の部分を見てください。eval_word(word) メソッドを eval_word_in_default(word) と eval_word_in_definition(word) に分割し、呼び出すべきメソッドを @eval_word に保持するように変更しました。

(3) の begin_word_definition メソッドは「:」が入力されたときに呼び出されます。内部ではスタックに「:」を push してから、以降の eval_word(word) 呼び出しで eval_word_in_definition(word) が呼び出されるように @eval_word の値を更新しています。

(4) の end_word_definition メソッドは、ワード定義の本体です。「:」の後に入力された値が @stack に積まれているので、それを分解して Proc オブジェクトに変換し、登録済みのワードに追加(@word_in_default に追加)しています。最後に @eval_word の値を元に戻しています。

 

動かしてみる

それでは動かしてみましょう。

前回同様、次のような足場を作って実行すれば REPL を始めることができます。

やろうとしていた、値を 3 乗するワード cube を定義してみます。

3 の 3 乗を求めてみると・・、

無事に 27 と表示されました。

 

まとめ

何とか独自のワードを定義できるようになりました。次は条件分岐も実装してみたいと思います。

 

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