#1 Visual C++でbisonおよび、flexを使うには

注意!:この文章およびここからダウンロードできるファイルが含む 間違いや不具合によって、引き起こされる問題について、私は責任を負いません。 ご自身の責任においてご利用して下さい。

Step1. bisonおよびflexの入手
 
bisonおよびflexのオリジナルのソースコードはGNUのホームページ内のGNU Softwareのノードからダウンロードできます。 しかし、オリジナルのソースコードはUNIX向けに作られているため、Windows用の実行形式を作るためには、ソースコードの変更が必要となります。 Visual C++でコンパイルする場合、コンパイルエラーに対する対症療法的な修正と、メモリ関連のライブラリや環境変数の違いの関係で、処理の変更が必要になる部分も存在します。
そこで、これらのソースコードを修正し、Visual C++でコンパイルした実行形式を用意しました。
bisonflex.lzh (515KB)
上記のファイルにはbisonとflexの実行形式及びソースファイルが入っています。
 

Step2. インストール
 
ダウンロードしたファイルを解凍した後、各々のreadme.txtで示されたファイルをVisual C++がビルド時に参照するディレクトリにコピーします。
Visual C++がビルド時に参照するディレクトリは、 Visual C++のメニューから、[ツール]ー[オプション]を選んで、 [ディレクトリ]タブのシートで設定できます。
特にコピー先のディレクトリが決まっていない場合、 \Microsoft Visual Studio\VC98\Binがコピー先として妥当です。

Step3. プロジェクトの作成
 
Visual C++のプロジェクトを作成します。プロジェクトタイプは、Win32 Console Application空のアプリケーションを作成します。

Step4. ファイルの作成
 

以下の3つのファイルを新規作成し、プロジェクトに追加します。
ファイル名:test.y
ファイルの内容:bison用ソースファイル
ソースコード:
%{
#include <stdlib.h>


int yyerror(const char* str);
int yylex();

int sum = 0;

%}

%token RETURN
%token NUMBER

%%
number_list : /* empty */
            | number_list number RETURN 
    ;

number : NUMBER {
                    sum += $1;
                    printf("sum=%d\n",sum);
                }
    ;

%%

int yyerror(const char* str){
	printf("%s",str);
	return 0;
}



解説:
test.yをbisonによってコンパイルすることで、構文解析関数yyparse()の定義されたCのソースファイルが生成されます。
yyparse()の内部では、yyerror()というエラー報告用関数と、 yylex()という字句解析用の関数を呼び出します。 これらの関数はプロトタイプ宣言されていないため、test.yの先頭でこれらの関数を宣言しています。 また、test.yの最後でyyerror()の実装を定義しています。
yylex()はflexが生成する字句解析関数です。
 
ファイル名:test.l
ファイルの内容:flex用ソースファイル
ソースコード:
%{
#include <stdlib.h>
#include "testbison.h"

int yywrap();

%}

%%
\n       return (RETURN);
[0-9]+   {yylval = atoi(yytext); return (NUMBER);}
.        printf("ERROR!!\n");
%%

int yywrap() {
	return 1;
}

解説:
test.lをflexによってコンパイルすることで、字句解析関数yylex()の定義されたCのソースファイルが生成されます。
yylex()の内部では、入力ストリームが終端に達した場合の処理を決定するyywrap()という関数を呼び出します。 この関数はプロトタイプ宣言されていないため、test.lの先頭で宣言し、またtest.lの最後でその実装を定義しています。
yywrap()が0を返すと、yylex()は入力ストリームを最初から読み直します。通常は1を返すようにします。
testbison.hは後にも説明しますが、bisonが構文解析関数のソースコードとともに生成するヘッダファイルで、 test.yで定義されているトークンが、#defineとして書き出されています。
test.lではトークンを返す際に、RETURNやNUMBERという名前を使っていますが、 これらはtest.yで定義されています。これらの定義はtestbison.h内で#defineされているため、 これをインクルードしておくことで、test.l内で、 これらの名前を使用できるようになります。
ファイル名:main.c
ファイルの内容:main関数
ソースコード:
int yyparse();

int main()
{
	return yyparse();

}

解説:
main.cにはmain関数の定義が書かれています。main()関数は単にyyparse()を呼び出しているだけです。
 
注意:yyparse()ととyylex()は、stdlib.hライブラリに依存しています。ところが生成されるソースコード内では stdlib.hはインクルードされません。stdio.hなど、その他に必要なライブラリはインクルードされているようなので、 もしかしたら、私が…(ごにょごにょごにょ)

Step5. カスタムビルドの設定
 

test.yと、test.lのカスタムビルド規則を以下のように設定します。
 
test.y
コマンド:
bison -d -otestbison.c $(InputName).y
出力:
$(ProjDir)
解説:
test.yをbisonによってコンパイルし、構文解析関数yyparse()のソースコードとヘッダファイルを生成します。
生成されるファイル名は-oオプションによって指定されている testbison.cとなります。
-dオプションを指定することで、トークンの#defineや、 yylval(字句の属性値)の型の定義が書かれたヘッダファイル(testbison.h)が同時に生成されます。
test.l
コマンド:
flex -otestflex.c $(InputName).l
出力:
$(ProjDir)
解説:
test.lをflexによってコンパイルし、字句解析関数yylex()のソースコードを生成します。
生成されるファイル名は-oオプションによって指定されているtestflex.cとなります。

Step6. test.yおよびtest.lのコンパイルとファイルの追加
 
test.yのコンテキストメニューから設定画面を表示し、「一般」タブを選択し、 「このファイルをビルドしない」のチェックを外します。
次にtest.yのコンテキストメニューから[コンパイル]を選択しtest.yをコンパイルします。
アウトプットウィンドウにエラーが報告されていないことを確認したら、 もう一度test.yの設定画面を表示し、「このファイルをビルドしない」にチェックを入れます。
次に、test.lに対して同じ操作を行います。
 
実は、カスタムビルド規則を持つ2つ以上のファイルで、「このファイルをビルドしない」のチェックを外している場合、 個別にコンパイルしようとすると、コンパイル対象とは異なるファイルがコンパイルされてしまう場合があるようです。
コンパイルしたいファイル1つだけにチェックを外して、その他のファイルにはチェックを入れた状態で、 コンパイルを試みてください。

 
以上の操作によってtestbison.ctestbison.htestflex.cの3つのファイルがプロジェクトディレクトリ内に生成されます。 FileViewから[ファイルをフォルダに追加]を選択し、これらのファイルをプロジェクトへ追加します。

Step7. コンパイルして実行
 
ここまできたら後は全体をコンパイルして実行するのみです! [ビルド]ー[実行]を選択して下さい。
コンパイルが正常に終了すると、DOS窓が表示されます。何か適当な数値を入力し、[ENTER]を押してみて下さい。
sum=入力した数値
という形式で、入力した数値がフィードバックされます。更に続けて数値を入力すると、それまでに入力した値に加算されて、 フィードバックされます。
数値以外の文字を入力すると、「ERROR!」、および「parser error」という文字列が表示されてプログラムが停止します。
「ERROR!」という文字列は、数値とリターンコード以外の文字が入力された時に表示するようにtest.l内で書かれています。
「parser error」という文字列は構文上のエラーが発生したときに、yyparse()がyyerror()に渡す文字列です。yyerror()ではこれをそのまま printf()しています。

戻る