Forthで文字列を表示するには、「."」を用いる。
." Hello, World."
最後の「"」で、表示する文字の最後を表す。文字列と"の間には空白は必要ない。
ところで、Forthの内部では、文字列は文字数と文字列本体で表現されるらしい。C言語のように0終端ではないのだ。そうすると、."の構文解析というのは、
."コード, 文字数, 文字列本体
となるように仮想マシンコードを作らなければならない。それはまあ良いのだが、どうやって文字列を得るか、頭を悩ませている。構文解析する際、トークンごとに文字列を得ているのだが、それだと文字列中の空白をすべて無視してしまう。困った。
どうすればよいだろうか。以下の様に変更することにする。
- トークンの取り出しにstrtok()を使うのをやめる
- ."が現れたら、トークン単位の読み込みをやめ、"が現れるまで1文字ずつ読み進む
まず、バッファを次の構造体にする。
/** * ソースコード入力バッファ */ typedef struct srccode_buffer { char *buf; /**< バッファ */ int pos; /**< 取り出し位置 */ } SRC_BUFFER;
バッファを宣言する。
int main(int argc, char *argv[]) { char buf[100]; SRC_BUFFER cmd_buf = {buf, 0}; init(); if (argc >= 2) { load_prog(argv[1], &cmd_buf); parse(&cmd_buf); proc_prog(); } else { while (!quit_enabled) { printf(">"); scanf("%100[^\n]%*[^\n]", cmd_buf.buf); getchar(); parse(&cmd_buf); if (!proc_disable_cnt) { proc_prog(); } } } return 0; }
load_prog()を修正する。
/** * Forthで書かれたプログラムをロードする */ void load_prog(char *fname, SRC_BUFFER *cmd) { FILE *fp; unsigned int idx = 0; char c; fp = fopen(fname, "r"); while ((c = fgetc(fp)) != EOF) { cmd->buf[idx++] = c; } cmd->buf[idx] = '\x0'; cmd->pos = 0; fclose(fp); }
parse()を内で使われているstrtok()をget_token()に置き換える。
token = get_token(cmd);
get_token()を作成する。
/** * バッファからトークンを切り出す */ char *get_token(SRC_BUFFER *bufptr) { char *token_ptr; if (bufptr != NULL) { srcbuf = bufptr; srcbuf->pos = 0; } /* 最初が区切り子なら、無視する */ while (is_separator(srcbuf->buf[srcbuf->pos])) { srcbuf->pos++; } /* 先頭位置を得る */ token_ptr = &srcbuf->buf[srcbuf->pos]; /* 区切り子を探す */ while (!is_separator(srcbuf->buf[srcbuf->pos]) && srcbuf->buf[srcbuf->pos] != '\x0') { srcbuf->pos++; } /* 終端する */ srcbuf->buf[srcbuf->pos] = '\x0'; srcbuf->pos++; return token_ptr; }
get_toquote()を作成する。
/** * バッファから'"'終端している文字列を返す */ char *get_toquote(SRC_BUFFER *bufptr) { char *token_ptr; if (bufptr != NULL) { srcbuf = bufptr; srcbuf->pos = 0; } /* 最初が区切り子なら、無視する */ while (is_separator(srcbuf->buf[srcbuf->pos])) { srcbuf->pos++; } /* 先頭位置を得る */ token_ptr = &srcbuf->buf[srcbuf->pos]; /* '"'を探す */ while (srcbuf->buf[srcbuf->pos] != '"' && srcbuf->buf[srcbuf->pos] != '\x0') { srcbuf->pos++; } /* 終端する */ srcbuf->buf[srcbuf->pos] = '\x0'; srcbuf->pos++; return token_ptr; }
parse()に."時の処理を追加する。
} else if (strcmp(token, ".\"") == 0) { int j; prog[parse_idx++] = CODE_DOTQUOTE; token = get_toquote(NULL); *(unsigned short *)(prog + parse_idx) = strlen(token); parse_idx += 2; for (j = 0; token[j] != '\x0'; j++) { prog[parse_idx++] = token[j] } }
実際の処理を行う関数call_dotquote()を追加。
/** * ."処理を行う */ void call_dotquote(void) { unsigned short j; unsigned short len = *(unsigned short *)(prog + prog_cnt); prog_cnt += 2; for (j = 0; j <len ; j++) { printf("%c", prog[prog_cnt++]); } }
テストファイルtestfile/test8の内容
." ABCD abcd"
実行してみた。
$ ./moiforth.exe testfile/test8 ABCD abcd
成功した。
(2009.09.16)
moiforth-0.9.zip