hakeの日記

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

bison & flexメモ その3

C言語flexを使う方法のメモ その3
スタート状態について


%% 〜 %%間のどのルールを有効にするかを指定する。

%x state_name
%s state_name

%sまたは%xに続いて使用する状態名を記述する。
%x(排他的スタート)はその状態名が書かれたルールのみ有効になり、%s(包含的スタート)はその状態名で書かれたルールと状態名が書かれていないルールが有効になる。ルールセクションでの記述は以下のとおり。

%%
<state_name> pattern  action
<state_name> pattern  action
%%

または

%%
<state_name> {
pattern  action
pattern  action
}
%%

デフォルトの状態はINITIALであり、別のスタート状態に移行するにはアクションの中で、BEGIN(state_name);を記述する。


下の例では、C言語タイプのコメント/* 〜 */を除去している。"/*"を検出すると状態に移行する。状態で"*/"を検出すると状態に戻る。
改行(\n)に適合した場合は、行番号を保存する変数lineを+1するが、全ての状態で使用するので状態名は記述していない。

test_flex.l

%{
#include <stdio.h>

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

int num; /* 数字列を変換した数値の格納用 */
int line = 1; /* ソースの行番号表示 */

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

%s COMMENT

%%
<INITIAL>{
"/*"                    {BEGIN(COMMENT);}/* COMMENT状態に移行 */
[0-9]+                  {sscanf(yytext,"%d",&num); /* パターンに適合すると{ }内のアクションを実行 */
                         return NUMBER;} /* yylex()の戻り値としてNUMBERを返す */
[a-z]+                  {return STRING;} /* yylex()の戻り値としてSTRINGを返す */
" "                     {}               /* スペースは何もしない*/
.                       {yyerror("Illegal character");}/* その他の文字はエラー処理 */
}

<COMMENT>{
"*/"                    {BEGIN(INITIAL);}/* INITIAL状態に移行 */
" "                     {}               /* スペースは何もしない*/
.                       {}               /* コメント文は無視 */
}

"\n"                    {line++;}        /* 改行がきたら行番号を+1する
                                            <INITIAL> <COMMENT>双方で使用 */

%%

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

int main(int argc, char ** argv){
   int t;

   if ( argc > 1 )
      /* yyinをファイル入力に切り替え */
      if((yyin = fopen( argv[1], "r")) == NULL){
         fprintf(stderr,"Can't open a input file!\n");
         return 1;
      }


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

   return 0;
}

sample.txt

int i;  /* foo */
/* bar */ int j;
/* comment line 1
   comment line 2 */
i = 123;

実行結果

bash-3.1$ ./test_flex.exe sample.txt
line 1 : kind = 2, string = 'int' length = 3
line 1 : kind = 2, string = 'i' length = 1
line 1 : Illegal character : ';'
line 2 : kind = 2, string = 'int' length = 3
line 2 : kind = 2, string = 'j' length = 1
line 2 : Illegal character : ';'
line 5 : kind = 2, string = 'i' length = 1
line 5 : Illegal character : '='
line 5 : kind = 1, string = '123' length = 3 value = 123
line 5 : Illegal character : ';'