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これで、開発するひな形ができました。