簡単な答えは、語彙素の抽出はコンテキスト内で実行する必要があるということです。言語の語彙素と見なされるものは、言語のさまざまな部分でかなり異なる場合があります。たとえば、COBOLでは、データ宣言セクションに「PIC」文字列と場所に依存するレベル番号01〜99があり、これらはプロシージャセクションには表示されません。
したがって、レクサーは、言語のどの部分が処理されているかを何らかの形で知り、収集する語彙素を知ることができます。これは多くの場合、語彙素の言語セット全体のサブセットを処理する字句解析状態を持つことによって処理されます(サブセット内でかなりの重複があることがよくあります。たとえば、識別子は私の経験ではかなり似ている傾向があります)。これらの状態は、高レベルの有限状態マシンを形成し、相変化語彙素が検出されたときにそれらの間で遷移します。たとえば、COBOLプログラムのデータ宣言またはプロシージャセクションへのエントリを示すキーワードです。JavaやC#のような現代言語はこれの必要性を最小限に抑えますが、私が遭遇した他のほとんどの言語は本当にレクサーでこの種の助けを必要としています。
いわゆる「スキャナーレス」パーサー(「GLR」と考えている)は、レクサーを完全に取り除くことで機能します。これで、レクサーが語彙素を生成する必要がなくなり、字句状態を追跡する必要もなくなります。:-}このようなパーサーは、個々の文字のレベルに文法を書き込むだけで機能します。通常、語彙素の説明に書くものとまったく同じ文法規則があります。問題は、なぜそのようなパーサーがどの「語彙素」を生成するかについて混乱しないのかということです。ここでGLR部分が役立ちます。GLRパーサーは、選択が最終的に解決される限り、入力の多くの可能な解釈(「ローカルにあいまいな解析」)を喜んで処理します。したがって、「あいまいなトークン」の場合に実際に発生するのは、両方の「トークン」の文法規則です。
私の会社は言語用のパーサーをたくさん作っています。複雑な言語を処理するのに非常に優れているため、GLRパーサーを使用します。文脈自由文法を書くと、パーサーができます。語彙素ベースの語彙素エクストラクタを使用し、通常の語彙素の正規表現仕様と、特定の語彙素によってトリガーされる語彙素状態遷移を使用します。間違いなくスキャナーレスGLRパーサーを構築することはできますが(レクサーにトークンとして単一の文字を生成させることにより:)、状態ベースのレクサーの効率は余分な手間をかける価値があることがわかります。
実用的な拡張機能として、私たちのレクサーは実際には、単なる有限状態マシンではなく、高レベルの状態マシンにプッシュダウンスタックオートマトンを使用します。これは、サブステートが同一である高レベルのFSAがある場合、およびレクサーがネストされた構造を管理する場合(たとえば、括弧を一致させる場合)、モードスイッチを管理する場合(たとえば、括弧がすべて一致する場合)に役立ちます。
レクサーのユニークな機能:スキャナーレスパーサーが行うことのほんの少しも行います:キーワードが認識されると、レクサーはキーワードと識別子の両方をパーサーに挿入します(スキャナーレスパーサーを文法規則でシミュレートします各)。もちろん、パーサーは「コンテキスト内」で必要なものだけを受け入れ、間違った選択肢を単に破棄します。これにより、多くの言語で発生する「識別子として解釈されるコンテキスト内のキーワード」を簡単に処理できます。