ワード定義のテスト
2年くらい放っておいた。開発環境はNetBSDからCygwinに変更した。エディタはnvi-m17nからvimに変更した。これからは少しだけ頑張る。
testfileディレクトリ下にtest7というファイルがあった。
: test7 2 3 . cr ; test7
そう、ワードを定義して、それを実行できるようにしようとしてた。これを実行したが、何の反応もない。そこで、prog[0]を表示させてみると0。0はCODE_ENDなので、実行した途端に終了していた。
はて、":"を構文解析したときは、なにをprog_cnt[]になにを入れてあったか?parse()を調べてみる。
} else if (strcmp(token, ":") == 0) { /* :の次の単語をワード名とする */ get_token(fp, token); assoc_word_name(token); idx++;
次の単語をワード名として登録し、idxをインクリメントしているが、prog[idx] = prog[0]には何もデータを入れてない。つまり、ここでidx++してはいけなかった。削除して再make、実行。
$ ./moiforth.exe testfile/test7 3 undefined code: -12
今度は3を表示した後、proc_prog()内でエラーが発生した。-12は、CODE_RET。リターン用コードだが、proc_prog()で処理していなかった。3を表示しているのは、、実行するときに、必ずprog[0]から開始するので、ユーザ定義ワードが格納してある0番地から実行し、スタックトップの3を表示したようだ。
まず、CODE_RETを処理しよう。実行中にこのコードを見つけたら、ユーザ定義ワードからの復帰である。プッシュした番地をプログラムカウンタprog_cntに戻す。prog_cntを記録する場所、スタックが必要である。call_stack[]とでもしよう。と思ったのだが、条件分岐用のbranch_stack[]と繰り返し用のloop_stack[]が既にある。もしかして、分岐とループとコールのスタックは一緒でもいいのではないか?と考えた。と思ったが、コールのスタックと分岐のスタックを一緒にし、ループのスタックは分けておくことにした。
case CODE_RET: prog_cnt = pop_branch_stack(); prog_cnt++; break;
CODE_CALLの時の処理も書いていなかった。このコードが来たら、現在の番地を分岐スタックにプッシュし、prog_cntをユーザ定義ワードの番地に書き換える。
case CODE_CALL: push_branch_stack(prog_cnt); break;
実行してみた。
$ ./moiforth.exe testfile/test7 3 undefined cnt: 1, code: 2
スタックトップの3を表示し、改行しているので、ここまでは希望の動作だが、未定義ワードを実行しようとしてエラーになった。そこで、proc_prog()の実行内容を逐一表示させてみた。
$ ./moiforth.exe testfile/test7 cnt: 0, code: -1 cnt: 2, code: -1 cnt: 4, code: -3 cnt: 5, code: -10 cnt: 6, code: -12 cnt: 1, code: 2 undefined cnt: 1, code: 2
コード12はリターンである。0番地は定義されたワードの開始番地で、ワード呼び出しはリターンコードの直後、7番地に存在しているはずである。ワードを呼び出していないで、0番地から実行していた。prog_cntを初期化時に0にしているため、本当の開始番地の7番地からプログラムを実行していない。これを修正する。
ワードの定義を行ってプログラムの開始番地がずれたのを修正するため、以下の修正を行った:
} else if (strcmp(token, ";") == 0) { /* ワード終端コードを追加する */ prog[idx] = CODE_RET; idx++; prog_cnt = idx;
これで実行してみた。
$ make && ./moiforth.exe testfile/test7 cnt: 7, code: -11 cnt: 0, code: -1 cnt: 2, code: -1 cnt: 4, code: -3 3cnt: 5, code: -10 cnt: 6, code: -12 cnt: 8, code: 0
- 7番地: 開始。ユーザ定義ワードtest7がある0番地にジャンプ
- 0番地: 2をスタックにプッシュ
- 2番地: 3をスタックにプッシュ
- 4番地: 3を表示
- 5番地: 改行表示
- 6番地: リターンコードを実行
- 8番地: 終了コードで実行完了
ということで、ユーザ定義ワードの実装は完了した。