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.と表示されて、余計な数値を表示せず、次の入力待ち状態になって欲しいのですが…。