GNU Bisonの使い方(C言語)
GNU Bisonは人が読んで理解しやすい文法ファイル(*.y)から、C,C++そしてJavaのparserを生成してくれるツールです。それぞれに多少APIが異なると思いますが、ここではC言語で使う場合に定義しないといけない関数、アクセスできるグローバル変数について説明したいと思います。
文法ファイル
%{ #define YYSTYPE double // YYSTYPEはyylvalの型。指定しなければintになる。 int yylex(void); void yyerror(char*); #include <stdlib.h> #include <stdio.h> static double* result; %} %token NUM EOL %% line:EOL | exp EOL { result = (double*)malloc(sizeof(double)); *result = $1; return 1;} ; exp: NUM | exp exp '+' { $$ = $1 + $2;} | exp exp '-' { $$ = $1 - $2;} | exp exp '*' { $$ = $1 * $2;} | exp exp '/' { $$ = $1 / $2;} ; %% static double *result; static const char* input; double* parse(const char* source) { result = NULL; input = source; yyparse(); return result; } int yylex(void) { int c; if(input == NULL || *input == '\0') return EOL; do{ c = *input++; }while(isspace(c) || c == '\n'); if(isdigit(c)) { yylval = c - '0'; return NUM; } return c; } void yyerror(char* s) { fprintf(stderr, "%s\n", s); exit(1); }
文法ファイルは3つの部分に分かれます。上下の部分はC言語になっているので、そこから説明を始めます。bisonの方が生成するparserとしてyyparse(void)がありますが、この関数は中でyylexを繰り返し呼びます。parseする処理を行うには、グローバル変数yylvalの値とyylexが返すトークンの種類に関する情報が必要になります。yylvalの型はYYSTYPE(これ自体はマクロ)になります。YYSTYPEはトークンの型で、デフォルトでint型ですが、#defineなどで定義し直すことができます(上の例ではとりあえずdoubleにしてます)。文法違反した場合はyyparseはyyerror(char*)を呼ぶので、これも定義しておく必要があります。
文法の定義の仕方(directive)
あとで書く
bisonコマンドの使い方
とりあえず、bisonを使ったプログラムを走らせるまでやってみます。main関数(ファイル名はmain.cとしましょう)の方はこんな感じです。
#include "rpn.h" #include <stdio.h> #include <assert.h> #include <stdlib.h> #include <string.h> #include "y.tab.c" void test_parse(void) { double* result = NULL; result = parse("12+"); assert(3 == *result && "exp: exp '+' exp"); free(result); result = parse("32-"); assert(1 == *result && "exp: exp '-' exp"); free(result); result = parse("72*"); if(result != NULL) assert(14 == *result && "exp: exp '-' exp"); free(result); result = parse("32/"); if(result != NULL) assert(1.5 == *result && "exp: exp '-' exp"); free(result); result = parse("1 5 -"); if(result != NULL) assert(-4 == *result && "skip space"); free(result); result = parse("1\n 2 +"); if(result != NULL) assert(3 == *result && "skip '\n'"); free(result); } int main(int argc, char* argv[]) { if(argc == 2 && strcmp(argv[1], "test") == 0) { test_parse(); printf("all tests done\n"); exit(0); } else if(argc >=2) { char exp[80] = ""; int i; for(i = 1; i < argc; i++) strcat(exp, argv[i]); printf("%lf\n",*parse(exp)); exit(0); } return 1; }
"bison -y