hakeの日記

Windows環境でプログラミングの勉強をしています。

bison & flexメモ その1

環境は、Windows XP上のMinGWで、flexのバージョンは2.5.35、bisonのバージョンは2.4.2
いきなり両者を使うとよく理解できないので、まずはC言語flexを使う方法のメモ。

基本的なフォーマットは以下

%{
初期Cコード
%}
%%
パターン アクション
%%
他のCコード

初期Cコード部分にヘッダファイルや定義等を記述して、他のCコード部分にはmain()等の関数を記述。パターンには適合させたい文字列を正規表現で、アクションには適合するパターンが見つかった場合に実行させたい内容を記述する。

main()からyylex()が呼ばれると、入力文字列を頭からスキャンしていき、適合するパターンが見つかるとそのパターンに応じたアクションを実行する。アクションの中でreturn int値を記述すると、それがyylex()の戻り値になる。全てのスキャンが終了した場合には、0を返す。
下のサンプルでは、数字列と英小字列を見つけると、それぞれNUMBER(=1)とSTRING(=2)を返す。この時に変数yytextには適合した文字列(の先頭アドレス)が、yylengには文字数が入っている。これらの変数の値は次回yylex()が呼ばれるまで保持されている(と思う)
数字列に適合した場合は、アクションの中で用意されているnumという変数に対応する数値を代入している。空白や改行の場合はアクションで何もしない。その他の文字の場合はエラーとしてyyerror()が呼ばれる。

test_flex.l

%{
#include <stdio.h>

/* 字句(トークン)の種類を定義 */
enum {NUMBER = 1, STRING};

int num; /* 数字列を変換した数値の格納用 */

/* 読み込みファイルが複数ある場合に使用、今は1を返す様にしておく */
int yywrap(void){return 1; }
%}

%%
[0-9]+                  {sscanf(yytext,"%d",&num); /* パターンに適合すると{ }内のアクションを実行 */
                         return NUMBER;} /* yylex()の戻り値としてNUMBERを返す */

[a-z]+                  {return STRING;} /* yylex()の戻り値としてSTRINGを返す */

" "|"\n"                {}               /* スペース・改行は何もしない */

.                       {yyerror("Illegal character");}/* その他の文字はエラー処理 */
%%

void yyerror(char *msg){ printf("%s : '%s'\n", msg, yytext); }

int main(void){
   int t;
   while((t = yylex()) != 0){
      if(t == NUMBER)
         printf("kind = %d, string = '%s' length = %d value = %d\n", t, yytext, yyleng, num);
      else if(t == STRING)
         printf("kind = %d, string = '%s' length = %d\n", t, yytext, yyleng);
      else
         printf("other TOKEN\n");
   }
}

Makefile

TARGET = test_flex
LEX_SRC = $(TARGET).l
LEX_C   = lex.yy.c
C_FILES = $(LEX_C)


all : $(TARGET)

$(TARGET) : $(LEX_C)
	gcc -o $(TARGET) $(C_FILES)

$(LEX_C) : $(LEX_SRC)
	flex $(LEX_SRC)

clean :
	rm -f *.exe
	rm -f *.c

コンパイル時にyyerror()について以下の警告がでるけど、関数yyerror()をコメントアウトするとエラーになるので無視

test_flex.l:25:6: warning: conflicting types for 'yyerror'
test_flex.l:21:2: note: previous implicit declaration of 'yyerror' was here

実行結果

bash-3.1$ ./test_flex.exe
123                           ←入力文字
kind = 1, string = '123' length = 3 value = 123
abc 456                       ←入力文字
kind = 2, string = 'abc' length = 3
kind = 1, string = '456' length = 3 value = 456
ABC                           ←入力文字
Illegal character : 'A'
Illegal character : 'B'
Illegal character : 'C'