トップ 差分 一覧 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

成功した。

ソースコード

(2009.09.16)

戻る 前へ 次へ

moiforth-0.9.zip