はじめに
いやどこに需要があるねーーんという気持ちで書いています。
ここに辿り着いている時点でlexとかyaccについて大体理解してる人が多いと思うので
説明は省略しますが、
「最初から説明しろこのタコ!!」
という方は参考リンク載せておくので各自見に行ってください。
そこは今回の本題じゃないので。
環境
Ubuntu 20.04/22.04
使用教材
このテキストで使用するコンパイル用プログラムを基に行います。
手順
.lファイルと.yファイルは予め手元にあるものとします。
今回使用したUbuntu環境にはlexとyaccは標準搭載されていなかったので、インストールしておきます。
$ sudo apt install flex
$ sudo apt install bison
sudoは必要ならつけてください。lexとyaccをインストールしても良いのですが、Ubuntu環境だとflexのインストールを推奨されるので大人しく従う方が吉です。
bisonはyaccの親みたいなものなのでどちらでも構いません。動く方を採用しましょう。
今回使用するプログラムファイルにはMakefileがあり、以下のコマンドを一気に実行してくれます。
Makefileについてはこちら。
$ lex cmm.l
$ yacc cmm.y
$ gcc -c code.c
$ gcc -c env.c
$ gcc y.tab.c code.o env.o -ly -ll -o cmm
1行目: cmm.lの中の情報を読み込んで、lex.yy.cというファイルに書き込みます。これは自動で生成されます。
2行目: cmm.yを読み込んでy.tab.cというファイルを生成します。これはプログラムの文法をチェックするものという認識でおk
3,4行目: .oのファイルを作ってます
5行目: y.tab.cを実行します。その際、code.oとenv.o内の関数を利用します。
-ly,-llはライブラリ関数から必要な関数は適宜探してこいという命令を付加します。
それらをくっつけて実行ファイルとしてはcmmで出力せよ、という意味合いになっています。
オブジェクトファイル(.o)についての説明は以下参照。
-ly,-llはliby.a,libl.aからそれぞれ関数を持ってくるコマンドの引数となる。
実行ファイルはa.outとかが一番馴染み深いと思われるが、名前を変更する際は5行目のようにコマンドの中に入れることで、cmmが実行ファイルになる。
大枠の流れは以下の画像を参考にされたい。
困ったこと
実はこのままだと5行目でエラーが出てしまう。
y.tab.c: In function 'yyparse':
y.tab.c:1218:16: warning: implicit declaration of function 'yylex' [-Wimplicit-function-declaration]
1218 | yychar = yylex ();
| ^~~~~
y.tab.c:1952:7: warning: implicit declaration of function 'yyerror'; did you mean 'yyerrok'? [-Wimplicit-function-declaration]
1952 | yyerror (YY_("syntax error"));
| ^~~~~~~
| yyerrok
cmm.y: At top level:
cmm.y:494:1: warning: return type defaults to 'int' [-Wimplicit-int]
494 | main(){
| ^~~~
/usr/bin/ld: cannot find -ly: No such file or directory
collect2: error: ld returned 1 exit status
warningは一旦無視するとして、その後の行に
cannot find -ly: No such file or directory
と書かれている。
-lyはliby.aから必要な関数を適宜探してきて使うものなので、大元のliby.a
に何かしらの問題が起きてそう
ファイルの状況を探るために、まずliby.aがどこにあるのか知りたいので調べてると
apt-file search liby.a
で検索できそうなことが判明
そもそもapt-fileがインストールされてない可能性もあるのでsudo apt install apt-file
は先に実行してもろて
はいというわけで実行しました
Ubuntu20.04環境では
libbison-dev: /usr/lib/x86_64-linux-gnu/liby.a
のように出力されるはず(人によって違うかも)
libbison-devという所にファイルがありそうなので、sudo apt install libbison-dev
を実行してみることに。
インストール後、もう一度5行目のコマンドを実行してみると
y.tab.c: In function 'yyparse':
y.tab.c:1218:16: warning: implicit declaration of function 'yylex' [-Wimplicit-function-declaration]
1218 | yychar = yylex ();
| ^~~~~
y.tab.c:1952:7: warning: implicit declaration of function 'yyerror'; did you mean 'yyerrok'? [-Wimplicit-function-declaration]
1952 | yyerror (YY_("syntax error"));
| ^~~~~~~
| yyerrok
cmm.y: At top level:
cmm.y:494:1: warning: return type defaults to 'int' [-Wimplicit-int]
494 | main(){
| ^~~~
となり、warningしか残らなかったので一応コンパイルが通ったことになります。めでたしめでたし。
コンパイルできた後は、
$ ./cmm < (ファイル名)
で実行することが出来ます。cmmが具体的に何をするのかと言うと、厳密にはちょっと違うのですが、ざっくり言うと、.cに書かれているコードからPL/0コードを生成するものです。
PL/0についてはこちら。
後記
今回この問題を解決するにあたって、様々なサイトを巡っていると、-lyでエラーが起きた場合は、-lyを削除すればうまくいくというものがあったのですが、今回使用しているy.tab.cの中には、yyerrorという関数があり、この関数を使用する際にはliby.aを経由する必要があったため、どうしてもliby.aが必要でした。
しかし、自作で.yファイルを作ったり、他のプログラムファイルによってはyyerrorを使用せずとも.yファイルを構成できるため、今回がレアケースだったという可能性もあります。
自分でyyerrorという関数を作るという手もありますが、その場合はコンパイラ側から怒られるようです。(参考サイト参照)
また、すべてのエラーがこのパターンに当てはまるわけではないので、これ以外で何かしらエラーが起きて困っている場合は、参考サイト様を載せておきますので、是非活用していただければと思います。
参考サイト
- CENT OS 6.5でのyacc及びlexの環境構築について
- 簡易電卓プログラムとその解説
- Install LEX,YACC
- yacc & lex 質問
- Where can I get liby.a?
- lexとyaccの記述方法の説明
- flexとbisonを使って簡単な電卓を作る
- lexとyaccの併用(p67~)
- yacc -wikipedia
- Undefined reference to yylval and yyerror
Thanks. (1) yyerror is defined in fb1-5.y. (2) cc -o fb-1.5 fb1-5.tab.c lex.yy.c -lfl -ly doesn't work, because cc: error: fb1-5.tab.c: No such file or directory – Tim CommentedFeb 7, 2019 at 21:26
@tim: the fact that yyerror is defined in the parser does not make its declaration available in the lexer. The compiler is complaining that yyerror is undeclared, which is different from the linker complaining that it is undefined. – rici CommentedFeb 7, 2019 at 21:29