Flexについて


Flexは正規表現を記述したソースファイルから、 C言語の字句解析プログラム(スキャナ)を自動生成するプログラムです。 Flexのソースファイル(通常 .l.lexという拡張子をつける)は、次のようなものです。

(構文解析系(パーサ)から利用するための関数 yylexを生成します。 yylex関数は、与えられたファイルを最初から順に読み、 呼ばれるたびに次のトークンをリターンするのが本来の使用法ですが、 この例では標準出力に結果を出力しています。)


例 1

おそらく一番簡単な例 (ファイル中の helloを Bonjourに書き換える。)
%{
/* 動作記述のなかで用いる関数の定義や宣言をここに書く。 */

/* 次の 2行は決まり文句 */
#define YY_SKIP_YYWRAP
int yywrap(void) { return 1; }
%}
   /* この例ではここには何も書かない */
%%
   /* ここに動作記述を書く。*/
   /* ECHOはマッチした文字列をそのまま出力するマクロ */
[hH]ello    { printf("Bonjour"); }
.|\n        { ECHO; } /* その他の文字はそのまま出力 */
%%
/* その他の関数の定義などをここに書く。*/
int main (void) {
  return yylex();
}

例 2

少しだけ複雑な例 (標準入力から文字を読み込み、 連続した空白文字をひとつのアンダースコア(_)に置き換え、 数と識別子のまわりにそれぞれ、 <b>〜</b>, <i>〜</i>というタグを挿入する。)
%{
/* 動作記述のなかで用いる関数の定義や宣言をここに書く。 */

/* 次の 2行は決まり文句 */
#define YY_SKIP_YYWRAP
int yywrap(void) { return 1; }
%}
%%
   /* ここに動作記述を書く。*/
   /* ECHOはマッチした文字列をそのまま出力するマクロ */
[ \t]+                              { putchar('_'); }
[0-9]+(\.[0-9]+)?(E[+\-]?[0-9]+)?   { printf("<b>"); ECHO; printf("</b>"); }
[A-Za-z]([A-Za-z0-9])*              { printf("<i>"); ECHO; printf("</i>"); }
"."                                 { ECHO; exit(1); }          
.|\n                                { ECHO; }
   /* 上の動作記述では値を返していないが、動作記述の中で
      return文を書くと、yylex関数の戻り値になる。(これが本来の使い方)*/

%%
/* その他の関数の定義などをここに書く。*/
int main (void) {
  return yylex();
}
/* この例では lexer単独で動作させるので main関数を定義し、
   その中で yylex関数を呼んでいる。*/
同じ動作だがよく使う正規表現に名前をつけた例
%{
#define YY_SKIP_YYWRAP
int yywrap(void) { return 1; }
%}
   /* ここは正規表現の定義(良く使う正規表現に名前をつける) */
   /* 行頭に空白を入れないようにしてください。 */
delim   [ \t]
ws      {delim}+
letter  [A-Za-z]
digit   [0-9]
ident   {letter}({letter}|{digit})*
number  {digit}+(\.{digit}+)?(E[+\-]?{digit}+)?
%%
   /* ここに動作記述を書く。*/
{ws}        { putchar('_'); }
{number}    { printf("<b>"); ECHO; printf("</b>"); }
{ident}     { printf("<i>"); ECHO; printf("</i>"); }
"."         { ECHO; exit(1); }
.|\n        { ECHO; }
%%
int main (void) {
  return yylex();
}

bison(構文解析部生成系)で生成した構文解析部といっしょに動作させる例は、 ここにあります。


説明

Flexのソース中では次の文字は特別な意味を持ち、 正規表現のために使います。 (教科書や授業で説明した記法と微妙に違う場合があるので注意してください。)


   \ " . [ ] * + ? { } | ( ) - < > ^ % / $

意味
c 上記の特殊文字以外の文字はその文字そのもの a
\c \n, \tなどはC言語と同じ意味、 それ以外の文字 cc そのもの \*
"str" 文字列 str そのもの "**"
. 改行以外の任意の文字 a.*b
[str] 文字列 str正規表現ではありません!) 中の任意の文字 [abc]
[c1-c2] 文字 c1から文字c2 の範囲の任意の文字 [a-zA-Z]
[^str] 文字列 str に含まれない任意の文字 [^abc]
r* 正規表現 rの 0回以上の繰り返し a*
r+ 正規表現 rの 1回以上の繰り返し a+
r? 正規表現 rの 0回か 1回の出現 a?
r1r2 正規表現 r1の後に 正規表現 r2 ab
r1|r2 正規表現 r1または 正規表現 r2 a|b
{name} nameという名前のついた正規表現の定義の展開 {ident}

この他、()がグループ化のために用いられます。 例えば、 (ab|cd)* abまたはcdの 0回以上の繰り返しを表わします。


生成

このようなファイル(mylexer.lとする)から Cソースファイルを生成するには

  flex -I mylexer.l

eというコマンドを実行します。
注: -l(小文字のエル)ではなくて -I(大文字のアイ)です。
これで lexyy.cという名前の Cソースファイルが生成されます。また、

  flex -ofoo.c -I mylexer.l
というように-oの後にファイル名を書くと、 その名前(この場合 foo.c)の Cソースファイルが生成されます。

この例の場合は、この Cソースファイル(foo.c)を bcc32でコンパイル(
  bcc32 foo.c
または
  bcc32 -ebar foo.c
)すると、実行可能ファイルが生成されます。 (いくつか、Warning(警告)がでますが、気にする必要はありません。) (-eは、 実行可能ファイルの名前を指定するためのオプションです。 後者のように -ebarというように使うと bar.exeという実行可能ファイルが生成されます。)

FAQ(よくある質問)

  1. (例1について) 作成したプログラムを実行したとき、どうやって終了すれば良いですか?

    Ctrl-cまたは Ctrl-zで終了できます。

  2. (例2について)exit(1)って何ですか?

    exitはプログラムを終了させるための関数です。 例2では 「.」を入力するとプログラムを終了するようになっています。

  3. (例2について) \.[+\-]ってどういう意味ですか?

    \.はピリオドそのものを表わします。 「.」(ピリオド)は flexの規則中では特別な意味を持つので、 ピリオドそのものを表わすには\でエスケープしてやります。 同様に「-」も[]の中では特別な意味を持つので、 エスケープが必要で、 [+\-]は 「+または -」の意味になります。

コンパイラのホームページ


Koji Kagawa