Windows10へのBison/Flexのインストール→実行

yaccというものに興味を持った学生が[コチラ](https://qiita.com/toru0408/items/483c77cb76198d4c6ceb)の記事を実行するまでの環境設定での苦難をまとめました。

## はじめに
yacc/lexなるものでコンパイラが作れるらしい……のですが、これはUNIX用のソフトウェアだそうで、Windowsでは互換ソフトが必要とのことでした。
そこで、bison/flexというものを使ってみることにしました。
[bison](http://gnuwin32.sourceforge.net/packages/bison.htm)と[flex](http://gnuwin32.sourceforge.net/packages/flex.htm)をインストールし、環境パスを設定し、プログラムを入力し、実行すると、待ち受けていたのは訳の分からないエラーでした。

## 実行方法 ([コチラ](https://qiita.com/toru0408/items/483c77cb76198d4c6ceb)のサイトからほぼ引用)
適当なディレクトリにYファイル(bison用のファイル)とLファイル(flex用のソースファイルと)を置き、内容を記述する。

```mini-lang.y
%{
#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include <string.h>

#define outofrange(n) fprintf(stderr, "memory #%d out of range\n", (n));
#define IDENTSIZE 16
typedef struct _identmap{
    char name;
    double value;
} identmap;
identmap imap[IDENTSIZE];
double getvalue(char name);
int identregister(char name,double value);
double it;
%}

%union {
    int ival;
    double dval;
    char cval;
}

%token <ival> INTNUM MEMLOAD MEMSTORE FOR IN TO STEP IF THEN ELSE END EXIT
%token <dval> DOUBLENUM
%token <cval> IDENTIFIER
%type <dval> program block expr term factor ifstmt

%start program

%%
program :                       { $$ = it; }
        | block ';'             { $$ = it = $1; printf("program return = %lf\n", it); }
        | block '\n'            { $$ = it = $1; printf("program return = %lf\n", it); }
        | program block '\n'    { $$ = it = $2; printf("program return = %lf\n", it); }
        | program EXIT '\n'     { $$ = it; printf("program return = %lf\n", it); return 0; }
        ;

block   : expr                  { $$ = $1; }
        | ifstmt                { $$ = $1; }

ifstmt  : IF expr THEN expr END { if ( $2 == 1.0) $$ = $4;
                                  else $$ = -1; }
        ;   

expr    : term                  { $$ = $1; }
        | IDENTIFIER '=' expr   { identregister($1, $3);
                                  $$ = $3; }
        | expr '+' term         { $$ = $1 + $3; }
        | expr '-' term         { $$ = $1 - $3; }
        ;

term    : factor                { $$ = $1; }
        | term '*' factor       { $$ = $1 * $3; }
        | term '/' factor       { $$ = $1 / $3; }
        | factor '^' term       { $$ = pow($1, $3); }
        ;

factor  : INTNUM                { $$ = (double) $1; }
        | DOUBLENUM             { $$ = $1; }
        | IDENTIFIER            { $$ = getvalue($1); }
        | factor IDENTIFIER     { $$ = (double) $1 * getvalue($2); }
        | '(' expr ')'          { $$ = $2; }
        ;

%%
int main(void)
{
    yyparse();
    printf("See you!!");
    return 0;
}

int yyerror(char *msg)
{
    fprintf(stderr, "mini-lang: %s\n", msg);
    return 0;
}

int indexident(char name){
    int i;
    for( i = 0; i < IDENTSIZE; i++){
        if( imap[i].name == name )
            return i;
    }
    return -1;
}

int identregister(char name,double value){
    int i = indexident(name);
    if ( i == -1){
        static int usedi = 0;
        imap[usedi].name = name;
        imap[usedi].value = value;
        usedi++;
    }else{
        imap[i].value = value;
    }
    return 0;
}

double getvalue(char name){
    int i = indexident(name);
    if (i != -1)
        return imap[i].value;
    else
        printf("mini-lang: \'%c\' is not defined\n",name);
    return 0.0;
}
```

```mini-lang.l
%{
#include "mini-lang.tab.h"
#include <string.h>
%}

white       [ \t]
floating    [0-9]+\.[0-9]+
integer     [0-9]+
symbol      [=+\-\^*/();\n]
letter      [a-zA-Z]
other       .

%%
{white}+
{symbol}                { return(yytext[0]); }
{integer}               { sscanf(yytext, "%d", &yylval.ival);
                          return(INTNUM); }
{floating}              { sscanf(yytext, "%lf", &yylval.dval);
                          return(DOUBLENUM); }
{letter}                { yylval.cval = yytext[0];
                          return(IDENTIFIER); }
for                     { return(FOR); }
in                      { return(IN); }
to                      { return(TO); }
step                    { return(STEP); }
if                      { return(IF); }
then                    { return(THEN); }
else                    { return(ELSE); }
end                     { return(END); }
exit                    { return(EXIT); }                     
{other}                 { fprintf(stderr, "Illegal charcter %c, ignored\n", yytext[0]); }

%%
```

あとはコマンドプロンプトからコマンドを入力するだけ。

```
bison -d mini-lang.y ::Yファイルをbisonにかける
flex mini-lang.l ::Lファイルをflexにかける
gcc mini-lang.tab.c lex.yy.c ::できたファイルをコンパイル
```

これでEXEファイルが生成された方は終了です。

## エラーⅠ (m4がみつからない)
まず、1行目のコマンド(bison)を実行した時点で、以下のようなエラーが発生しました。

```
bison: m4: Invalid argument
```

このエラーは、bison.exeと同じディレクトリ(bin)にあるべきm4.exeが見当たらないというものらしいです。
しかし、binを覗いてみると......ある。
ggってみると、[bisonインストールディレクトリに空白が混ざっているとバグが発生する](http://www.chriscalender.com/resolving-the-bison-exe-m4-invalid-argument-error-when-building-mysqlmariadbxtradb-on-windows/)らしいというのを見つけた。
というわけでProgram FilesではなくCドライブ直下を指定し再インストール。
それでも消えない bison: m4: Invalid argument 。

結局、[このサイト](http://venublog.com/2010/02/07/changing-mysql-parser-code-on-windows-build-breaks-due-to-bison/)を参考に、.yや.lを置いているディレクトリにm4.exeをコピーしたら動きました。

根本的解決ではないと思うので、有識者の方々、コメントをお願いします。

## エラーⅡ (lpthreadが見つからない)
上記エラーを解決した時点で、bison・flex両方のコマンドがエラー無く通った。
さて、コンパイル。と思ったところでまたもやエラー。

```
ld.exe: cannot find -lpthread
```

なにやら ld.exe: cannot find -lpthread というエラー、lpthreadというものが足りないらしい。
[似たようなエラーについて書かれたサイト](https://www.softek.co.jp/SPG/Pgi/FAQ/faq_link.html#005)を見かけたので読んでみる。
libpthread.aと言うGNU glibcの静的ライブラリが足りないときに起きるエラーの様だ。
Linux環境ならyumで一発インストールらしいが、Windowsでは無理そうだ。

結局[このサイト](http://demura.net/misc/7168.html)の手順でインストールを行うことが出来た。
まず、[pthread](https://sourceforge.net/projects/mingw/files/MinGW/Base/pthreads-w32/pthreads-w32-2.8.0-3/)のサイトにある
libpthread-2.8.0-3-mingw32-dll-2.tar.lzma
pthreads-w32-2.8.0-3-mingw32-dev.tar.lzma
の2つをダウンロードして、解凍。
GCCのインストールフォルダ内に以下のようにファイルを配置する。

- bin
    - libpthread-2.dll　　　(libpthread-2.8.0-3-mingw32-dll-2.tar.lzma内)
- lib
    - libpthread.dll.a　　　　　(pthreads-w32-2.8.0-3-mingw32-dev内)
- include
    - pthread.h　　　　　　　　(pthreads-w32-2.8.0-3-mingw32-dev内) 
    - sched.h　　　　　　　　　(pthreads-w32-2.8.0-3-mingw32-dev内) 
    - semaphore.h　　　　　　　(pthreads-w32-2.8.0-3-mingw32-dev内) 

これでGCCへのPthreadのインストールが完了する。

## エラーⅢ (_yywrapが見つからない)
今度は実行環境ではなくソースコードにエラー。
しかも、自動生成したコードに対して......。

```
undefined reference to `yywrap'
```

試しにBCCで実行してみると、

```
外部シンボル '_yywrap' が未解決
```

undefined reference to `yywrap' と 外部シンボル '_yywrap' が未解決 は多分同じエラーだと思います。

これ、何かしらの関数？である_yywrapがないと動かないらしいです。
ただ、私は初心者ですから、自動生成されたコードの仕様など知りません。

結局、[質問サイト](https://stackoverflow.com/questions/14042903/flex-undefined-reference-to-yywrap-only-when-compiling-linking-separately/14069799)の回答を参考に、mini-lang.tab.c(bisonで生成されたファイル)の適当な箇所にyywrap関数を定義しました。

```
int yywrap (void)
{
    return 1;
}
```

これで、コンパイルが通りました。

## 終わりに
これで皆さんもやっとbison/flexの実行が出来たと思います。
これからのコンパイラライフに幸あれ。