- 追加された行はこのように表示されます。
- 削除された行は
このように表示されます。
!!! 対話環境を作る
2年ぶりにワード定義部分を実装し、いっそうプログラミング言語らしくなった。
しかし、まだ何かが足りない。
そう、私の作ったForthは対話的に使うことが出来ない。
ファイルを指定して実行するだけでなく、コンソール上での入力を受け付けるようにしたい。
ファイル指定の場合は、
+ファイルを読み込んで構文解析し、内部コードに変換する
+内部コードを実行する
+終了
だったのだが、対話環境の場合は、
+ファイル名を指定せずに起動すると、入力待ちのプロンプトを表示する
+入力した文字列を構文解析し、内部コードに変換する
+内部コードを実行する
+再度プロンプトを表示し、入力を待つ
となる。
まず変更するところは、構文解析関数parse()である。
これまでparse()は、ファイル名を引数としていたが、これからはForthのプログラムを引数としなければならない。
そのため、main()を以下のように変更した。
int main(int argc, char *argv[])
{
char cmd_buf[100];
init();
if (argc >= 2) {
load_prog(argv[1], cmd_buf);
parse(cmd_buf);
proc_prog();
} else {
while (!quit_enabled) {
scanf("%100[^\n]%*[^\n]", &cmd_buf);
parse(cmd_buf);
proc_prog();
}
}
return 0;
}
cmd_buf[]にファイルから読み込んだプログラムか、または、コンソールで入力されたワードを保存し、それをparse()に渡すようにした。
また、parse()で行っていたファイル読み込みは、load_prog()で行う。
/**
* Forthで書かれたプログラムをロードする
*/
void load_prog(char *fname, char *cmd)
{
FILE *fp;
unsigned int idx = 0;
fp = fopen(fname, "r");
while ((c = fgetc(fp)) != EOF) {
cmd[idx++] = c;
}
cmd[idx] = '\x0';
}
parse()から呼び出される、get_token()は、対象をファイルから文字列にする。
/**
* トークンを切り出す
*/
void get_token(char **cmd, char *token)
{
int c;
int j = 0;
token[0] = '\x0';
/* 英数記号が来るまで読み込む */
c = **cmd;
*cmd = *cmd + 1;
while (is_separator(c) && **cmd != '\x0') {
c = **cmd;
*cmd = *cmd + 1;
}
/* 区切り記号までを読み込む */
while (!is_separator(c) && **cmd != '\x0') {
token[j] = c;
j++;
c = **cmd;
*cmd = *cmd + 1;
}
token[j] = '\x0';
}
なんか複雑になった。これはstrtok()使えばいいような気がする。
試してみたが、strtok()は、区切り子が連続して2つ続けてくると、2つ目の区切り子を取り除いてくれない。
というのは勘違いで、区切り子の入れ忘れが2箇所あった。
get_token()は必要なくなった。
これで、parse()の引数をファイル名からプログラムに変更できた。
次は、対話環境である。
ファイル指定なしでForthを起動してみる。
プロンプトが表示されない。後で表示させよう。
そして、"2 3 + ."と入力し、Enterキーを押すが、反応なし。
gdbで調べてみた。
*なぜかscanf()で処理が止まらない
*コマンドラインからの入力を保存したcmd_bufをクリアしていない
*コマンドラインのワードを処理した後、prog_cntをクリアしていない
scanf()が止まらないのは、バッファに'\n'が残っているかららしい。scanf()したあとにgetchar()を入れると、無限ループは止まった。
"2 3 + ."と入れて、"5"が表示された。
しかし、次に"6 2 + ."と入れたが、反応はなく、プロンプトが帰ってくるだけだ。
これは、
*"2 3 + ."を実行、prog_cntが6の位置になる( = prog[6] = CODE_END)
*"6 2 + ."を実行、prog_cntがCODE_ENDの位置にあるので、即終了となる
が原因だった。
これを修正するには、prog_cntの初期位置を記憶して、入力されたワードを実行後、元に戻すようにする。
0にしてしまえれば一番簡単だが、ワード定義がある場合、その分prog_cntがずれてしまう。
そこで、プログラムカウンタの初期位置を記録する変数を用意する。
int base_prog_cnt; /**< プログラムカウンタの初期位置 */
proc_prog()を呼び出すときは、必ずbase_prog_cntの値に戻す。
void proc_prog(void)
{
prog_cnt = base_prog_cnt;
while (prog[prog_cnt] != CODE_END) {
ワード定義があったときは、その分だけ位置をずらす。
} else if (strcmp(token, ";") == 0) {
/* ワード終端コードを追加する */
prog[idx] = CODE_RET;
idx++;
base_prog_cnt = idx;
上記の変更をして、実行してみた。
$ ./moiforth.exe
>2 3 + . cr
5
>6 2 + . cr
8
>
うまくいった。これで対話環境が完成した。
[ソースコード|http://moi2.sakura.ne.jp/fswiki/wiki.cgi?page=ImplementInteractiveEnvironment&file=moiforth%2D0%2E6%2Ezip&action=ATTACH]
[[戻る|Forthを作ってみる]] [[前へ|Forthを作ってみる / ワード定義のテスト]] [[次へ|Make32bitForth]]
[[戻る|Forthを作ってみる]] [[前へ|Forthを作ってみる / ワード定義のテスト]] [[次へ|Forthを作ってみる / Forthを32ビット化する]]
{{adsence}}