元の から始まるほとんどのレクサーは、lex
次のように代替と一致します。
最長一致を使用します。
最長一致に一致する代替が 2 つ以上ある場合は、レクサー定義で最初の代替を使用します。
これにより、次のスタイルが可能になります。
"case" { return CASE; }
[[:alpha:]][[:alnum:]]* { return ID; }
入力パターンがcaser
の場合、2 番目の選択肢が使用されます。これが最長の一致であるためです。入力パターンが の場合、case r
両方が に一致するため、最初の選択肢が使用され、case
上記のルール (2) により、最初の選択肢が勝ちます。
これは少し恣意的に思えるかもしれませんが、ほとんどの場合、DFA のアプローチと一致しています。まず第一に、DFA は最初に受け入れ状態に達したときに停止しません。もしそうなら[[:alpha:]][[:alnum:]]*
、最初の文字で受け入れ状態に入る (アルファベットだと仮定して) ようなパターンは役に立たないでしょう。代わりに、DFA ベースのレクサーは、現在の状態から可能な遷移がなくなるまで続行し、その後、最後の受け入れ状態までバックアップします。(下記参照。)
特定の DFA 状態が 2 つの異なるルールのために受け入れられる場合がありますが、それも問題ではありません。最初の承認ルールのみが記録されます。
公平を期すために、これは DFA の数学的モデルとは少し異なります。DFA は、すべての状態のすべてのシンボルに遷移があり (ただし、それらの多くは「シンク」状態への遷移である可能性があります)、状況に応じて完全な入力に一致します。入力の最後のシンボルが読み取られたときに、オートマトンが受け入れ状態にあるかどうか。lexer モデルは少し異なりますが、同様に簡単に形式化できます。
理論モデルの唯一の難点は、「最後の受容状態に戻ること」です。実際には、これは通常、受け入れ状態に到達するたびに状態と入力位置を記憶することによって行われます。これは、おそらく任意の量だけ、入力ストリームを巻き戻す必要があることを意味します。
ほとんどの言語はバックアップを頻繁に必要とするわけではなく、無期限のバックアップを必要とする言語はほとんどありません。一部のレクサー ジェネレーターは、バックアップ状態がない場合、より高速なコードを生成できます。(またはflex
を使用すると、これが行われます。)-Cf
-CF
無期限のバックアップにつながる一般的なケースの 1 つは、文字列リテラルに対して適切なエラーを返さないことです。
["][^"\n]*["] { return STRING; }
/* ... */
. { return INVALID; }
ここで、同じ行に"
一致するものがあれば、最初のパターンは で始まる文字列リテラルと一致します。"
(\
簡単にするために -escapes を省略しました。) 文字列リテラルが終了していない場合、最後のパターンは一致しますが、入力を . に巻き戻す必要があります"
。ほとんどの場合、一致しない を無視して字句解析を続けようとしても無意味"
です。行の残りの部分をすべて無視する方が理にかなっています。そのため、バックアップは非効率的であるだけでなく、誤ったエラー メッセージが急増する可能性もあります。より良い解決策は次のとおりです。
["][^"\n]*["] { return STRING; }
["][^"\n]* { return INVALID_STRING; }
ここで、2 番目の選択肢は、文字列が終了していない場合にのみ成功します。これは、文字列が終了している場合、最初の選択肢がもう 1 文字一致するためです。したがって、これらの選択肢がどの順序で表示されるかは問題ではありません。