コンソールの複数行入力対応
ふと気がついた。プロンプト上で、以下のようにif以降を1行に書かないで実行すると、
$ ./moiforth.exe >0 if
反応がなくなる。理由が分かった。構文解析関数parse()で、ifワードが現れたときの処理はこうである:
} else if (strcmp(token, "if") == 0) { prog[idx] = CODE_IF; push_branch_stack(idx + 1); idx += 2;
それと、内部インタプリタ関数proc_prog()のif処理部は以下のようになっている:
case CODE_IF: if (pop()) { prog_cnt += 2; } else { prog_cnt = prog[prog_cnt + 1]; } break;
条件が真のときは、prog_cntは次の番地に進み、そのコードが0、CODE_ENDなので、プログラムは終了し、入力待ちプロンプトを表示する。しかし、条件が偽のときは、ジャンプ先がCODE_IFの次のコードとなっているが、プロンプト入力はifで終わっているため、次のコードはCODE_END、つまり0である。ということは、0番地に飛んでしまい、いつまで経っても終了しない。
どうすればよいか。対策として、組となるワード、例えばifに対するelseやthen、が現れるまで内部インタプリタの呼び出しを行わないようにする。
char proc_disable_cnt; /**< 1以上のとき、プロンプトからの実行を禁止する */
この変数を用意し、parse()内でカウントする。ifの場合は+1し、thenの場合は-1する。
} else if (strcmp(token, "if") == 0) { prog[idx] = CODE_IF; push_branch_stack(idx + 1); idx += 2; proc_disable_cnt++; } else if (strcmp(token, "else") == 0) { prog[idx] = CODE_ELSE; prog[pop_branch_stack()] = idx + 2; push_branch_stack(idx + 1); idx += 2; } else if (strcmp(token, "then") == 0) { prog[idx] = CODE_THEN; prog[pop_branch_stack()] = idx + 1; idx++; proc_disable_cnt--;
また、parse()内の変数idxは、いったん関数を抜けると位置を保持できないので、グローバル変数にして、構文解析を続けられるようにする。
if (!proc_disable_cnt) { parse_idx = 0; }
対話環境では、proc_disable_cntが0以下ならば、プログラムを実行する。
while (!quit_enabled) { printf(">"); scanf("%100[^\n]%*[^\n]", cmd_buf); getchar(); parse(cmd_buf); if (!proc_disable_cnt) { proc_prog(); } }
実行してみた。
$ ./moiforth.exe >2 if > 2 3 + . >else > 3 3 + . >then 5>0 if > 1 1 + . >else > 3 3 + . >then 6>
成功。これでif〜thenの複数行の入力に対応した。