トップ 一覧 Farm 検索 ヘルプ RSS ログイン

Forthを作ってみる / 文字列を表示するの変更点

  • 追加された行はこのように表示されます。
  • 削除された行はこのように表示されます。
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

成功した。

[ソースコード|http://www.moi2.sakura.ne.jp/fswiki/wiki.cgi?page=Forth%A4%F2%BA%EE%A4%C3%A4%C6%A4%DF%A4%EB+%2F+%CA%B8%BB%FA%CE%F3%A4%F2%C9%BD%BC%A8%A4%B9%A4%EB&file=moiforth%2D0%2E9%2Ezip&action=ATTACH]

(2009.09.16)

[[戻る|Forthを作ってみる]] [[前へ|Forthを作ってみる / スレッディングの変更]] [[次へ|Forthを作ってみる / 文字列を表示する]]
[[戻る|Forthを作ってみる]] [[前へ|Forthを作ってみる / スレッディングの変更]] [[次へ|Forthを作ってみる / 変数を実装する]]

{{adsence}}