yaccというものに興味を持った学生がコチラの記事を実行するまでの環境設定での苦難をまとめました。
はじめに
yacc/lexなるものでコンパイラが作れるらしい……のですが、これはUNIX用のソフトウェアだそうで、Windowsでは互換ソフトが必要とのことでした。
そこで、bison/flexというものを使ってみることにしました。
bisonとflexをインストールし、環境パスを設定し、プログラムを入力し、実行すると、待ち受けていたのは訳の分からないエラーでした。
実行方法 (コチラのサイトからほぼ引用)
適当なディレクトリにYファイル(bison用のファイル)とLファイル(flex用のソースファイルと)を置き、内容を記述する。
%{
# 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;
}
%{
# 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インストールディレクトリに空白が混ざっているとバグが発生するらしいというのを見つけた。
というわけでProgram FilesではなくCドライブ直下を指定し再インストール。
それでも消えない bison: m4: Invalid argument 。
結局、このサイトを参考に、.yや.lを置いているディレクトリにm4.exeをコピーしたら動きました。
根本的解決ではないと思うので、有識者の方々、コメントをお願いします。
エラーⅡ (lpthreadが見つからない)
上記エラーを解決した時点で、bison・flex両方のコマンドがエラー無く通った。
さて、コンパイル。と思ったところでまたもやエラー。
ld.exe: cannot find -lpthread
なにやら ld.exe: cannot find -lpthread というエラー、lpthreadというものが足りないらしい。
似たようなエラーについて書かれたサイトを見かけたので読んでみる。
libpthread.aと言うGNU glibcの静的ライブラリが足りないときに起きるエラーの様だ。
Linux環境ならyumで一発インストールらしいが、Windowsでは無理そうだ。
結局このサイトの手順でインストールを行うことが出来た。
まず、pthreadのサイトにある
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がないと動かないらしいです。
ただ、私は初心者ですから、自動生成されたコードの仕様など知りません。
結局、質問サイトの回答を参考に、mini-lang.tab.c(bisonで生成されたファイル)の適当な箇所にyywrap関数を定義しました。
int yywrap (void)
{
return 1;
}
これで、コンパイルが通りました。
終わりに
これで皆さんもやっとbison/flexの実行が出来たと思います。
これからのコンパイラライフに幸あれ。