22.Ubuntuでひな形を作る2

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