Parser Combinatorを使用する場合 パーサージェネレーターを使用する場合
最近、パーサーの世界を深く掘り下げて、独自のプログラミング言語を作成したいと考えています。 しかし、パーサーを作成するには、パーサージェネレーターとパーサーコンビネーターという2つの異なるアプローチが存在することがわかりました。 興味深いことに、どのケースでどのアプローチが優れているかを説明したリソースを見つけることができませんでした。むしろ、資源(および人)の多くは、私は、他のアプローチを知っているだけで説明していない主題について質問彼らのようなアプローチをアプローチし、他のすべてのに言及していません。 有名なドラゴンブックは字句/スキャンに入ると、(f)LEXに言及しているが、すべてのパーサコンビネータを言及していません。 言語実装パターンは、Javaで構築されたANTLR Parser Generatorに大きく依存しており、Parser Combinatorsについては一切言及していません。 HaskellのParser CombinatorであるParsecのParsec入門チュートリアルでは、Parser Generatorsについてはまったく言及していません。 Boost :: spirit、最も有名なC ++ Parser Combinatorは、Parser Generatorsについてまったく言及していません。 パーサーコンビネーターを発明した可能性のある優れた説明ブログ投稿では、パーサージェネレーターについては一切言及していません。 簡単な概要: パーサージェネレーター パーサージェネレーターは、Extended Backus-Naur形式の方言であるDSLで記述されたファイルを受け取り、それを(コンパイル時に)このDSLで記述された入力言語のパーサーにできるソースコードに変換します。 これは、コンパイルプロセスが2つの別々のステップで実行されることを意味します。興味深いことに、パーサージェネレーター自体もコンパイラーです(そしてそれらの多くは実際に自己ホスト型です)。 パーサーコンビネーター パーサーコンビネーターは、すべてがパラメーターとして入力を受け取るパーサーと呼ばれる単純な関数を記述し、一致する場合、この入力の最初の文字を抜き取ります。パーサーがこの入力から何も解析できなかった場合は、タプルを返します。タプル(result, rest_of_input)はresult空(nilまたはNothing)の場合があります。例はdigitパーサーです。もちろん、他のパーサーは、パーサーを最初の引数(最後の引数は入力文字列のまま)として、それらを結合many1できます。 言うまでもなく、新しいパーサーを作成するために、もちろん(compose)digitとを組み合わせることができmany1ますinteger。 また、choiceパーサーのリストを取得し、それぞれを順に試す、より高レベルのパーサーを作成することもできます。 このようにして、非常に複雑なレクサー/パーサーを構築できます。演算子のオーバーロードをサポートする言語では、ターゲット言語で直接記述されていても、EBNFに非常によく似ています(ターゲット言語のすべての機能を使用できます)。 単純な違い 言語: パーサージェネレーターは、EBNFのようなDSLと、これらのステートメントが一致したときに生成するコードの組み合わせで記述されます。 パーサーコンビネーターは、ターゲット言語で直接記述されています。 字句解析/解析: パーサージェネレーターは、「レクサー」(文字列をタグ付けされたトークンに分割して、処理している値の種類を示す)と「パーサー」(レクサーからトークンの出力リストを取得する)そしてそれらを組み合わせて、抽象構文ツリーを形成しようとします)。 パーサーコンビネーターには、この区別はありません/必要ありません。通常、単純なパーサーは「レクサー」の作業を実行し、より高レベルのパーサーはこれらの単純なパーサーを呼び出して、作成するASTノードの種類を決定します。 質問 しかし、これらの違いを考えると(そしてこれはおそらく完全なものではありません!)、いつどれを使用するかについて知識のある選択をすることはできません。これらの違いの意味/結果が何であるかはわかりません。 パーサージェネレーターを使用すると、問題をより適切に解決できることを示す問題のプロパティは何ですか?Parser Combinatorを使用して問題を解決する方が適切であることを示す問題のプロパティは何ですか?