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