トップ 差分 一覧 Farm ソース 検索 ヘルプ RSS ログイン

Forthを作ってみる / 変数を実装する

Forthには、スタックだけでなく、変数も利用できる。文法はこうなる:

variable hoge

ANS Forthの規格には、"6.1.2410 VARIABLE"にその説明がある。それによると、構文解析時には変数となる番地を割り当て、実行時にはその番地をスタックにPUSHする。

、、、すみません。今日は2010年12月28日。一年以上更新をサボっていました。えーと、何をやっていたのか、すっかり忘れていますので、おさらい。

構文解析時:

  • "variable hoge"が現れたら、hogeを変数名テーブルに登録する
  • hogeと実際に保存される番地との関連付けが必要
  • hogeがプログラム中に単独で現れたら、hogeに関連付けられた番地をスタックにPUSHするコードに置き換える

実行時:

  • hogeは番地をPUSHするコードになっているので、ただ実行するだけ

、、、おさらい終わり、、、

作ってみよう。まず、どうすればいいだろう?構文解析時にvariableを見つけたら、次にくる文字列は変数名である。そこで、この時点で文字列と割り当てるメモリ領域の先頭番地を、変数名-アドレス変換テーブルに保存する。これのための構造体を作ろうと思ったが、以前作ったワード名-番地変換テーブルの構造と同じだった。

/**
 * ワード名-処理番地の組
 */
typedef struct word_proc_table {
    char word_pointer;          /**< ワード名が登録されたヒープへの番地 */
    char proc_pointer;          /**< 登録された処理番地 */
} WORD_PROC_TABLE;

そこで、名前を変更し、両方に使えるようにする。

/**
 * 名前-実体番地の組
 */
typedef struct name_instance_pair {
    char name_pointer;          /**< 名前が登録されている位置 */
    char instance_pointer;          /**< 実体が登録されている位置 */
} NAME_INSTANCE_PAIR;

そして、変数領域を宣言する。

NAME_INSTANCE_PAIR var_table[100];  /**< 変数管理テーブル */
unsigned char var_table_head;       /**< 変数管理テーブルの未使用位置の先頭 */

unsigned char var[100];             /**< 変数実体の格納領域 */
int var_pos;                        /**< 変数実体領域の未使用領域の先頭を指す */

char varname_heap[100];            /**< 変数名登録領域 */
unsigned char varname_heap_index;  /**< 変数名登録領域の未使用領域の先頭を指す */

構文解析時の処理。まず、variable。parse()に追加する。

       } else if (strcmp(token, "variable") == 0) {
           /* 続くトークンを変数とする */
           token = get_token(NULL);
           assoc_var_name(token);

assoc_var_name()は、引数として渡されたトークン文字列と自動的に割り当てられた実体番地を、構造体配列var_table[]に登録する。

/**
 * 変数名と実体番地とを関連付ける
 */
void assoc_var_name(unsigned char *name)
{
    /* 変数名をヒープ領域に登録する */
    var_table[var_table_head].name_pointer = reg_var_name(name);

    /* 変数の割り当て番地を登録する */
    var_table[var_table_head].instance_pointer = var_pos;
    var_pos += 2;

    /* 使っていない登録テーブルを先頭にする */
    var_table_head++;
}

ワード内容に変数名が現れたら、変数の値が割り当てられた番地を埋め込む。CODE_VARADRは、動作はCODE_PUSHと同じである。同じくparse()に追加する。get_var_address()は、トークンが登録されていなければ、-1を返す。

       } else if ((adrs = get_var_address(token)) >= 0) {
           /* 変数の番地をスタックにPUSHする */
           prog[parse_idx] = CODE_VARADR;
           prog[parse_idx + 1] = adrs;
           parse_idx += 2;

実行時の処理。

/**
 * 変数の番地をスタックに積む
 */
void call_varadr(void)
{
    push(prog[prog_cnt]);
    prog_cnt++;
}

テストプログラム。コードが正しければ、var_table[ ].instance_pointerの値(変数の番地ではなく、var[]の添字)を表示するはず。

variable x x . cr
variable y y . cr

実行してみる。

$ ./moiforth.exe testfile/test9
0
2

成功した。

ソースコード

次回、変数の参照と代入を行う。

(2011.01.03)

戻る 前へ 次へ

moiforth-0.10.zip