Flexと Bisonを使う場合には、決まった書き方があります。
lexer.lには次のような #include文を入れておきます。
%{ void yyerror(char*); #define YY_SKIP_YYWRAP int yywrap(void) { return 1; } #include "parser.h" /* parser.hは bisonが生成するファイル */ %} delim [ \t] ws {delim}+ letter [A-Za-z] digit [0-9] id {letter}{{letter}|{digit}}* number {digit}+(\.{digit}+)?(E[+\-]?{digit}+)? %% {ws} { /* ここでは何もしない */ } {number} { sscanf(yytext, "%lf", &yylval); return NUMBER; } /* NUMBERという終端記号の種類を返す。その値(“属性”)は yylvalという大域変数に代入する。 */ [+\-\*\/\(\)] { return yytext[0]; } /* + - * / ( )の場合は、マッチした文字をそのまま返す。*/ /* マッチした文字は一般に yytext[0] 〜 yytext[yyleng-1]。*/ "\n" { return EOL; } . { yyerror("不正な文字です。"); return EOL; } %% /* なにもなし */
動作の中に return文を入れておくと、その式が、 yylexという関数の戻り値になります。
終端記号の ``種類''(NUMBER, EOLなど)を yylex関数の値として返し、値(“属性”)を yylvalという 大域変数に代入していることに注意します。
一般に正規表現にマッチした文字は、 yytextという配列に保持されています。また yylengという変数にマッチした文字の数が保持されています。 だからマッチした文字は一般に yytext[0] 〜 yytext[yyleng-1]ということになります。(通常の文字列とは異なり、 最後にヌル文字 '\0'は入っていないので注意が必要です。)
parser.yの方は、 あまり変わりませんが、 yylex関数は flexの方で用意するので宣言だけしておきます。
%{ #define YYSTYPE double /* トークンの属性の型を宣言 */ #include <stdio.h> #include <stdlib.h> /* exit関数を使うため */ void yyerror(char* s) { printf("%s\n", s); } int yylex(void); /* yylexのプロトタイプ宣言 */ %} %token NUMBER %token EOL %left '+' '-' %left '*' '/' %% input : /* 空 */ | input line {} ; line : EOL { exit (0); } /* 空行だったら終了 */ | expr EOL { printf ("\t%g\n", $1); } ; expr : NUMBER { $$ = $1; } | expr '+' expr { $$ = $1 + $3; } | expr '-' expr { $$ = $1 - $3; } | expr '*' expr { $$ = $1 * $3; } | expr '/' expr { $$ = $1 / $3; } | '(' expr ')' { $$ = $2; } ; %% int main(void) { printf("Ctrl-cで終了します。\n"); yyparse(); return 0; }
bison -oparser.c -d parser.y flex -olexer.c -I lexer.lbisonの生成するヘッダーファイルが flexに必要なので、 必ず -dオプションをつけて bisonを先に実行します。 このとき-oオプションで、 Cファイル名(この場合 parser.c)を指定しておきます。 すると、拡張子を除いて同じ名前のヘッダーファイル(この場合 parser.h)ができます。 -oオプションをつけないと、 parser.tab.cと parser.tab.hというファイルができます。 あとはこの 2つの Cソースファイルをコンパイルします。
bcc32 -ecalc lexer.c parser.c-e は実行ファイルの名前を指定するオプションです。 calc.exeという実行可能ファイルができます。