22-01.仕切り直し
Bison3.8.1の壁
前章ではBison3.8.1を使って開発を進めようと考えていたのですが、実際にカスタマイズしようとすると、いくつかの問題点(というか理解不足による壁)が出てきました。ようは
%define api.value.type variantを使うことによって、例えばスキャナーscanner.lにおいて状態を管理する<INITIAL>や<COMMENT>を使うことが難しいようです。
何か方法があるのは間違いないのですが、そこが壁になってしまいました。
なので、初心に立ち返って、13-01.構造の修正に紹介した記述方法で開発を進めBison3.8.1への対応はぼちぼち考えたいと思います。
%unionを使った型管理
13-01.構造の修正では%unionを使って%typeの管理をしています。内容的には12-02.FlexとBisonで計算機(VS2019版)を記述してそれを13-01.構造の修正する形になります。以下にソースを紹介します。scanner.l
まずflexのファイルです。scanner.lという名前で作成します。%{ #include <stdio.h> #include "parser.h" #define YY_SKIP_YYWRAP 1 int yywrap(void) { return 1; } %} %% "+" return ADD; "-" return SUB; "*" return MUL; "/" return DIV; "\n" return CR; [1-9][0-9]* { double temp; sscanf(yytext, "%lf", &temp); yylval.double_value = temp; return DOUBLE_LITERAL; } [0-9]*\.[0-9]* { double temp; sscanf(yytext, "%lf", &temp); yylval.double_value = temp; return DOUBLE_LITERAL; } [ \t] ; %%
parser.y
続いて、parser.yを記述します。bisonのファイルですね。%{ #include <stdio.h> #include <stdlib.h> #include <iostream> #define YYDEBUG 1 extern int yylex(void); int yyerror(char const *str) { extern char *yytext; fprintf(stderr, "parser error near %s\n", yytext); return 0; } %} %union { int int_value; double double_value; } %token <double_value> DOUBLE_LITERAL %token ADD SUB MUL DIV CR %type <double_value> expression term primary_expression %% line_list : line | line_list line ; line : expression CR { std::cout << ">>" << $1 << std::endl; } expression : term | expression ADD term { $$ = $1 + $3; } | expression SUB term { $$ = $1 - $3; } ; term : primary_expression | term MUL primary_expression { $$ = $1 * $3; } | term DIV primary_expression { $$ = $1 / $3; } ; primary_expression : DOUBLE_LITERAL ; %%
main.cpp
続いてmain.cppです。main()関数が入ります。#include <stdio.h> #include <stdlib.h> #include <iostream> #include <fstream> #include <vector> #include <string> int main (int argc, char *argv[]) { extern int yyparse(void); extern FILE* yyin; for (int i = 1; i < argc; ++i){ if ((yyin = fopen(argv[i], "r")) == NULL) { std::cerr << "ファイル読み込みに失敗しました" << std::endl; return 1; } if (yyparse()) { std::cout << "プログラム終了" << std::endl; } else{ return 0; } } return 0; }
test.txt
このサンプルは、スクリプトをテキストファイルから読み込むのでtest.txtを作成します。10 + 3 10 * 3
Makefile
最後にMakefileです。calcCpp : scanner.cpp parser.h main.cpp g++ -o calcCpp scanner.cpp main.cpp scanner.cpp : scanner.l flex -o scanner.cpp scanner.l parser.h : parser.y bison -o parser.h parser.y
コンパイルと実行
記述が終わったらコンパイルしましょう$ make flex -o scanner.cpp scanner.l bison -o parser.h parser.y g++ -o calcCpp scanner.cpp main.cppこれでcalcCppが出来上がっています。実行してみましょう。
$ ./calcCpp test.txt >>13 >>30これで、開発するひな形ができました。