単純なレクサーとパーサーを作成するたびに、同じ質問に出くわします: レクサーとパーサーはどのように通信する必要があるのでしょうか? 私は4つの異なるアプローチを見ています:
lexer は、入力文字列全体を積極的にトークンのベクトルに変換します。これが完了すると、ベクターはパーサーに渡され、パーサーがツリーに変換します。これは実装する最も簡単なソリューションですが、すべてのトークンがメモリに格納されるため、多くのスペースが無駄になります。
レクサーはトークンを見つけるたびに、パーサーで関数を呼び出し、現在のトークンを渡します。私の経験では、これは、パーサーが LALR パーサーのようなステート マシンとして自然に実装できる場合にのみ機能します。対照的に、再帰降下パーサーではまったく機能しないと思います。
パーサーがトークンを必要とするたびに、レクサーに次のトークンを要求します。これは、キーワードにより C# で実装するのは非常に簡単ですが
yield
、それを持たない C++ では非常に困難です。レクサーとパーサーは、非同期キューを介して通信します。これは一般に「プロデューサー/コンシューマー」というタイトルで知られており、レクサーとパーサーの間の通信を大幅に簡素化する必要があります。また、マルチコアで他のソリューションよりも優れていますか? それとも、レキシングはあまりにも些細なことですか?
私の分析は正しいですか?私が考えていない他のアプローチはありますか?実際のコンパイラでは何が使用されていますか? Eric Lippert のようなコンパイラ ライターがこの問題に光を当てることができれば、本当に素晴らしいことです。