[Contents]   [Back]   [Prev]   [Up]   [Next]   [Forward]  


Flex と Lex

ここで非常に簡単にではありますが、 FlexとLexの両方を概観してみます。 Flex、Lexそれぞれの性能と、 Lexのようなユーティリティに関するPOSIX標準への準拠度についても、 いくつか一般的なコメントを示します。

Flex

Flexは、 Lexのより優れた再実装であり、 Lexと同様、 パターンとアクションの記述情報を入力として受け取って、 そのパターンにマッチする能力を持つCのスキャナに変換するものです。 しかしながら、 Flexはより少ない時間でテーブルを生成しますし、 Flexにより生成されるテーブルは、 Lexにより生成されるテーブルと比較して、 はるかに効率的なものです。 (Flexが正確には何を生成するのかという説明については、 このマニュアルの冒頭で言及した書籍を参照してください。)

Flexは、 LexおよびPOSIXと十分に互換性があり、 それ独自の特別な機能もいくつか追加しています。

FlexとPOSIX

Flexは、 大体のところLexおよびPOSIXの両方と互換性があります。 将来は (Flex、POSIXのどちらかが変わることによって)、 さらにPOSIXとの互換性を高めていくでしょう。 しかし、 Flex、Lex、POSIXには、 異なる部分もいくつかあります。 それを以下に示します。

排他的スタート状態
FlexとPOSIXは排他的スタート状態をサポートしていますが、 Lexはサポートしていません。
定義
LexとFlexでは定義の展開の方法が違います。 Flex(およびPOSIXのドラフト仕様)は、 定義を展開する時に丸括弧( )で囲みますが、 Lexは囲みません。(15) このことは、 Flex定義では演算子`^'`$'`/'`<<EOF>>'、および`<start state>'は使うことができないということを意味しています。 このことがもたらす主要な問題の1つに、 マッチの優先順位に影響を与え、 FlexとLexの間でスキャン処理に微妙な差異が出てくるということがあります。 この問題の例については、 パターン・セクションを参照してください。
input()
FlexおよびPOSIXのドラフト仕様では、 input()は再定義可能ではありません。 Flexで入力を制御するためには、 input()を再定義する代わりに、 YY_INPUTという拡張機能を使います (これは現在のところPOSIXではサポートされていません)。 また、 Lexとは異なり、 Flexのinput()yytextの値を変更するという点に注意してください。
output()
Flexはoutput()ルーチンをサポートしていません。 ECHOの出力はyyout経由で行われます。 このyyoutのデフォルトはstdoutです。 これを使うようにoutput()を書くことも可能ですが、 現在のPOSIXのドラフト仕様は、 output()が正確には何をすべきなのかを示していません。
Ratforスキャナ
Flex、POSIXのどちらも、 LexのRatfor(16)スキャナ・オプション(%r)をサポートしていません。
yylineno
これは、 FlexやPOSIXには存在しない、 ドキュメント化されていないLexの機能です。(17) しかし、 Flexで行数をカウントする機能を実装するのは難しくありません。 定義中に行数カウント機能を組み込む方法の例については、 その他を参照してくださいを参照してください。
yywrap()
現在のところyywrap()はマクロです。 POSIXのドラフト仕様では、 これは関数であるべきとされていますので、 おそらく将来は変更されることになるでしょう。(18)
unput()
現在のところunput()yytextyylengの値を破壊しますが、 次のトークンがマッチされるまでは、 これは不当です。 LexとPOSIXでは、 yytextyylengunput()の影響を受けません。(19)
数値範囲
POSIX によると、 `abc{1,3}'は 「abの後ろに1個、2個、または3個のcが続くもの」 にマッチすべきとなっています。 Flexはこのとおりに動きますが、 Lexはこれを 「1個、2個、または3個のabc」 と解釈します。
yytext
Flexにおいてyytextの正しい定義は`extern char *yytext'ですが、 Lexでは`extern char yytext[]'です。(20) 配列によるアクセス方法は、 性能にかなりの影響を及ぼすので、 Flexでは`extern char *yytext'を使い続けるでしょう。 最新のPOSIXドラフト仕様は、 `%array'`%pointer'を導入することによって、 両方の方法をサポートしています。 これは、 FlexとLexのいずれにもまだ組み込まれていません。(21)
テーブル・サイズ
Lexにはテーブル・サイズ宣言子(%p%a等)がありますが、 Flexでは必要ありません。 互換性のために認識はされますが、 無視されるだけです。
FLEX_SCANNER
スキャナがFlexとLexのどちらにより生成されたかによって、 コードをインクルードしたりしなかったりすることができるように、 FLEX_SCANNER#defineによって定義されています。
アクション
Flexでは、 大括弧の対{...}を使うことなく、 単一行において複数の文を置くことができます。 これに対してLexは、 そのような行を単一文に切り詰めてしまいます。
コメント
Flexではコメントを`#'で始めることができますが、 LexとPOSIXではできません。 ただし、 この形式のコメントを使うことはお勧めできません。
yyterminate()yyrestart()<<EOF>>YY_DECL#line 指示子
これらはいずれもLexではサポートされていませんし、 POSIXにおいて明示的に定義されてもいません。 #line指示子の説明に関しては、 Flex コマンドライン・オプションの要約を参照してください。

FlexとPOSIX(Flex 2.5の補足情報)

Flex 2.5でサポートされている新しい機能のうち、 POSIXの仕様(および、Lex)に存在しないものを以下に列挙します。

C++スキャナ
%option指示子
スタート状態スコープ
スタート状態スタック
yy_scan_string()、yy_scan_bytes()、yy_scan_buffer()
yy_set_interactive()
yy_set_bol()
YY_AT_BOL()
<*>
YY_START

標準Lex

Lexはスキャナを作成するための標準的なUnixユーティリティであり、 長い歴史を持っています。 LexはFlexと非常によく似ていますが、 スキャナを生成するのにより多くの時間がかかりますし、 Lexの生成するスキャナはFlexの生成するスキャナよりも通常は遅いものです。 Lexは、 特に多くのPOSIX機能を提供していないという理由から、 置き換える必要が大いにあります。 FlexはこうしたPOSIX機能を提供しています。 より多くのコンピュータ・システムがPOSIX互換になるにつれて、 Flexの提供する多くの機能をサポートしなければならなくなり、 このために、 おそらくはFlexがLexの代わりにインストールされるようになるでしょう (例えば、 4.4 BSDリリースはFlexを使うことになります)。 しかし、 Lexがインストールされている少数のシステムがあるために、 しばらくの間はLexの存在は確実に維持されるでしょう。

FlexとLexの大きな違いは、 Flexが性能を考慮して書かれたという点にあります。 一般的には、 Flexを持っているのであればそれを使うべきです。 両者の性能差は、 無視するにはあまりにも大きすぎます。 しかし、 移植性が最も重要なのであれば、 スキャナ定義は可能な限りLexのものに近づけるべきです。 というのは、 Lexは事実上すべてのUnixマシンに入っていることが保証されていますが、 Flexは入っていない可能性があるからです (しかし、 Flexのインストールは通常は取るに足りない作業です)。 このような場合に残念なのは、 FlexとPOSIXが持っている排他的スタート状態のような、 より便利な拡張機能を使うことができなくなるということです。

この問題を回避するためのもう1つの方法は、 Flexでスキャナを作成して、 作成されたスキャナを配布することです。 スキャナというものは一度書かれるとほとんど変更されることがないので、 多くの場合この方法は実行可能です。 仮に変更が必要になったとしても、 プログラムの他の部分も相当変更しなければならない可能性があり、 よってプログラムを更新するための努力全体から見れば、 Flexをインストールすることなどはほんの些細なものでしょう。


[Contents]   [Back]   [Prev]   [Up]   [Next]   [Forward]