Mae向きなブログ

Mae向きな日記のブログ版。ようやくこちらに移行してきました。

mfcalc

http://d.hatena.ne.jp/rahaema/20080611 でつまずいていた
http://www.mi.s.osakafu-u.ac.jp/~kada/course-kitami/j3_03/bison.pdf
の「2.5 練習問題」(3)に再チャレンジ。

(3)初期化されていない変数について、値を書き込むのではなく、値を使おうとするとエラーを報告するようにプログラムを改良しなさい。

calc.hを以下のように変更しました。init_flagは、変数への代入が行われたときに、1をセットするようにします。変数を参照する際、init_flaが0の場合は、初期化処理が行われていないことになります。

struct symrec
{
  char *name;
  int type;
  int init_flag;                /* (3) */
  union {
    double var;
    double (*fnctptr)();
  } value;
  struct symrec *next;
};

mfcalc2.yとの変更点を以下に示します。

  • 文法規則部において、新たに、statementを導入しました。
  • 変数に値を代入する際、init_flagに1をセットしています。
  • 変数を参照するときは、init_flagが0の場合、エラー処理をしています。しかし、まだここは不十分(実行結果参照)。
input:   /* 空 */
        | input line
;

line:
        '\n'
        | statement '\n'
        | error '\n' { yyerrok; }
;

statement: VAR '=' exp
           { 
             $1->value.var = $3; 
             $1->init_flag = 1;
           }            
        | exp                 { printf ("\t%.10g\n", $1); }
;

exp:      NUM                { $$ = $1;                         }
        | VAR                
          { 
            if ($1->init_flag)
                $$ = $1->value.var;
            else {
                yyerror("Not initialized.\n");
            }
          }
        | FNCT '(' exp ')'   { $$ = (*($1->value.fnctptr))($3); }
        | exp '+' exp        { $$ = $1 + $3;                    }
        | exp '-' exp        { $$ = $1 - $3;                    }
        | exp '*' exp        { $$ = $1 * $3;                    }
        | exp '/' exp        { $$ = $1 / $3;                    }
        | '-' exp  %prec NEG { $$ = -$2;                        }
        | exp '^' exp        { $$ = pow($1, $3);                }
        | '(' exp ')'        { $$ = $2;                         }
;

あと、定数をシンボルテーブルにセットするときに、init_flag = 1しています。

void init_table()
{
  int i;
  symrec *ptr;
  for (i = 0; arith_fncts[i].fname != 0; i++) {
    ptr = putsym(arith_fncts[i].fname, FNCT);
    ptr->value.fnctptr = arith_fncts[i].fnct;
  }
  for (i = 0; const_vars[i].vname != 0; i++) { /* (2) */
    ptr = putsym(const_vars[i].vname, VAR);
    ptr->value.var = const_vars[i].var;
    ptr->init_flag = 1;         /* (3) */
  }
}

実行結果

mmasa@debian:~/work/c/bison/mfcalc$ ./mfcalc3
PI
        3.14159
a = 123
a
        123
b
Not initialized.

        123.0000019

初期化されていないbという変数を、参照するとNot initialized.と表示されて、余計な数値を表示せず、次の入力待ち状態になって欲しいのですが…。