Next: , Previous: Flex and Bison, Up: Flex and Bison


4.4.1 FlexとBisonのインターフェイス

FlexとBisonの間で情報を渡す基本的な方法は、 関数yylex()を使うことです。 これは、 Flexにより生成されるスキャナにおいて、 スキャン処理を実行する関数の名前です。 Flexの入力ファイルのアクション部分においてreturn文を使うことによって、 単なる0や1以外の値を返すことができます。 この方法で、 yylex()は最後に認識されたトークンを表す整数値を返すことができます。

Bisonを‘-d’オプション付きで使うと、 Bisonは.tab.hという拡張子を持つファイルを生成します。 このファイルには、 記述情報中にある正当なトークンの1つ1つに対する一意な定義情報が含まれます。 この出力情報は、 特にスキャナによって使用されることを想定して設計されています。 このファイルをFlexにより生成されたスキャナに含めることで、 2つのプログラムの間に非常に明確なインターフェイスを作ることができます。 例として、 以下にBisonのファイルを示します。 このファイルの名前をexpr.yとしましょう。

     
     
     
     
     /*
      * expr.y : Bisonマニュアル中の例に基づく
      *          Bisonによる簡単な表現式パーサ
      */
     
     %{
     #include <stdio.h>
     #include <math.h>
     
     %}
     %union {
        float val;
     }
     
     %token NUMBER
     %token PLUS MINUS MULT DIV EXPON
     %token EOL
     %token LB RB
     %left  MINUS PLUS
     %left  MULT DIV
     %right EXPON
     
     %type  <val> exp NUMBER
     %%
     input   :
             | input line
             ;
     line    : EOL
             | exp EOL { printf("%g\n",$1);}
     exp     : NUMBER                 { $$ = $1;        }
             | exp PLUS  exp          { $$ = $1 + $3;   }
             | exp MINUS exp          { $$ = $1 - $3;   }
             | exp MULT  exp          { $$ = $1 * $3;   }
             | exp DIV   exp          { $$ = $1 / $3;   }
             | MINUS  exp %prec MINUS { $$ = -$2;       }
             | exp EXPON exp          { $$ = pow($1,$3);}
             | LB exp RB              { $$ = $2;        }
             ;
     
     %%
     void yyerror(char *s)
     {
       printf("%s\n",s);
     }
     int main()
     {
       yyparse();
     }

これは非常に簡単な計算機の文法定義です。

-y -d’オプション付きで呼び出されると、 Bisonはy.tab.hというファイルを生成します。 このファイルには以下のような定義か、 それにきわめてよく似た定義が含まれます。

     typedef union  {
        float val;
     } YYSTYPE;
     extern YYSTYPE yylval;
     #define NUMBER  258
     #define PLUS    259
     #define MINUS   260
     #define MULT    261
     #define DIV     262
     #define EXPON   263
     #define EOL     264
     #define LB      265
     #define RB      266

Flexがトークンの値を正しくBisonに返すことができるように、 (#includeを使って) これをスキャナに含めることができます。 そのコードは以下のようなものになります。

     
     
     
     
     /*
      * expr.lex : 簡単な表現式パーサのためのスキャナ
      */
     
     %{
     #include "y.tab.h"
     %}
     
     %%
     [0-9]+     { yylval.val = atof(yytext);
                  return(NUMBER);
                }
     [0-9]+\.[0-9]+ {
                  sscanf(yytext,"%f",&yylval.val);
                  return(NUMBER);
                }
     "+"        return(PLUS);
     "-"        return(MINUS);
     "*"        return(MULT);
     "/"        return(DIV);
     "^"        return(EXPON);
     "("        return(LB);
     ")"        return(RB);
     \n         return(EOL);
     .          { yyerror("Illegal character");
                  return(EOL);
                }
     %%

上記のファイルは、 以下のようにしてコンパイルすることができます。

     bison -d -y expr.y
     flex -I  expr.lex
     cc -o expr y.tab.c lex.yy.c alloca.c

また、 この例のソースが手元にあれば、 examplesサブディレクトリにおいて‘make expr’を実行するだけでコンパイルできます。 どちらの方法でも、 exprという名前の簡単な計算機が生成されます。 これは以下のような表現式をパースして、 その結果を出力します。

     1 + 2 * (199*2)

これを見てお分かりのように、 この種のインターフェイスは非常に柔軟であり、 かつ、 保守も非常に容易です。 (トークンを定義する名前が変わらない限り) BisonとFlexの間のインターフェイスを変更することなく、 Flex、 Bisonいずれの入力情報においても、 機能の追加や削除、 定義やコードの変更を行うことが可能です。

この例では、 FlexとBisonの間で情報を渡すための別の方法を導入していることに注意してください。 この例では、 数字の値をBisonに返すのにyylvalを使っています。 これについては次の節でより詳細に説明します。 ここではとりあえず、 return文の使い方を学んでおいてください。

注:これは単純な例です。 表現式のパース処理についてより詳しく知りたい人は、 Bisonのマニュアルを参照してください。