JFlexについて


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

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


例 1

おそらく一番簡単な例 (ファイル中の helloを Bon Jourに書き換える。)
/* import文はここに書く */
import java.io.IOException;

%%

// 生成するクラスの名前
%class Lexer0
// yylexメソッドの戻り値型
%int
%unicode
%line
%column

%{
  /* フィールドやメソッドはここに書く */
    public static void main(String[] args) throws IOException {
        new Lexer0(System.in).yylex();
    }
%}
   /* この例ではここには何も書かない */
%%

   /* ここに動作記述を書く。*/
   /* yytext()はマッチした文字列を返すメソッド */
[hH]ello        { System.out.print("Bon Jour"); }
.|\n            { System.out.print(yytext()); } /* その他の文字はそのまま出力 */

例 2

少しだけ複雑な例 (標準入力から文字を読み込み、 連続した空白文字をひとつのアンダースコア(_)に置き換え、 数と識別子のまわりにそれぞれ、 <b>〜</b>, <i>〜</i>というタグを挿入する。)
/* import文はここに書く */
import java.io.IOException;

%%

// 生成するクラスの名前
%class MyLexer
// yylexメソッドの戻り値型
%int
%unicode
%line
%column

%{
   /* フィールドやメソッドはここに書く */
    public static void main(String[] args) throws IOException {
        new MyLexer(System.in).yylex();
    }
%}
   /* この例ではここには何も書かない */
%%

   /* ここに動作記述を書く。*/
   /* yytext()はマッチした文字列を返すメソッド */
[ \t]+                              { System.out.print('_'); }
[0-9]+(\.[0-9]+)?(E[+\-]?[0-9]+)?   { System.out.print("<b>");
                                      System.out.print(yytext()); 
                                      System.out.print("</b>");   }
[A-Za-z]([A-Za-z0-9])*              { System.out.print("<i>"); 
                                      System.out.print(yytext());
                                      System.out.print("</i>"); }
"."                                 { System.out.print(yytext());
                                      System.exit(1); }          
                                     // その他の文字はそのまま出力
.|\n                                { System.out.printf(yytext()); } 
   /* 上の動作記述では値を返していないが、動作記述の中で
      return文を書くと、yylexメソッドの戻り値になる。(これが本来の使い方)*/
同じ動作だがよく使う正規表現に名前をつけた例
/* import宣言はここに書く */
import java.io.IOException;

%%

// 生成するクラスの名前
%class MyLexer1
// yylexメソッドの戻り値型
%int
%unicode
%line
%column

%{
  /* フィールドやメソッドはここに書く */
    public static void main(String[] args) throws IOException {
        new MyLexer1(System.in).yylex();
    }
%}

 /* ここは正規表現の定義(良く使う正規表現に名前をつける) */
delim  = [ \t]
ws     = {delim}+
letter = [A-Za-z]
digit  = [0-9]
ident  = {letter}({letter}|{digit})*
number = {digit}+(\.{digit}+)?(E[+\-]?{digit})?

%%

   /* ここに動作記述を書く。*/
   /* yytext()はマッチした文字列を返すメソッド */
{ws}      { System.out.print('_'); }
{number}  { System.out.print("<b>");
            System.out.print(yytext()); 
            System.out.print("</b>");   }
{ident}   { System.out.print("<i>"); 
            System.out.print(yytext());
            System.out.print("</i>"); }
"."       { System.out.print(yytext());
            System.exit(1); }          
          // その他の文字はそのまま出力
.|\n      { System.out.printf(yytext()); } 
           
   /* 上の動作記述では値を返していないが、動作記述の中で
      return文を書くと、yylexメソッドの戻り値になる。(これが本来の使い方)*/

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


説明

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

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

意味
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.jflexとする)から Javaソースファイルを生成するには

  java -jar JFlex.jar MyLexer.jflex

というコマンドを実行します。

これで MyLexer.javaという名前の Javaソースファイルが生成されます。
(JFlexソース中の %classオプションで指定したクラス名の Javaソースファイルが生成されます。)

この Javaソースファイル(MyLexer.java)を コンパイルすると、クラスファイルが生成されます。

  javac MyLexer.java

FAQ(よくある質問)

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

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

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

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

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

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

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


Koji Kagawa