最近、パーサーの世界を深く掘り下げて、独自のプログラミング言語を作成したいと考えています。
ただし、パーサーの作成には、パーサージェネレーターとパーサーコンビネーターという2つの異なるアプローチが存在することがわかりました。
興味深いことに、どのアプローチでより良いアプローチを説明できるリソースを見つけることができませんでした。むしろ、私が対象について問い合わせた多くのリソース(および人)は他のアプローチを知りませんでした。それらのアプローチをtheアプローチし、他の人にはまったく触れない:
パーサージェネレーターは [〜#〜] dsl [〜#〜]Extended Backus-Naur form の一部の方言で記述されたファイルを受け取り、それをソースに変換しますその後、(コンパイル時に)このDSLで記述されている入力言語のパーサーになることができるコード。
つまり、コンパイルプロセスは2つの別々のステップで行われます。興味深いことに、パーサージェネレーター自体もコンパイラーです(そして、それらの多くは確かに self-hosting です)。
Parser Combinatorはparsersと呼ばれる単純な関数を記述します。これらはすべて入力をパラメーターとして受け取り、一致する場合はこの入力の最初の文字を取り出します。パーサーがこの入力から何かを解析できなかった場合、タプル(result, rest_of_input)
を返します。ここで、result
は空の場合があります(例:nil
またはNothing
)。例としては、digit
パーサーがあります。もちろん、他のパーサーはパーサーを最初の引数(最後の引数はまだ入力文字列のままです)として結合することができます。 many1
は、他のパーサーと可能な限り何度も一致させようとします(ただし、少なくとも1回は失敗するか、それ自体が失敗します)。
もちろん、digit
とmany1
を組み合わせて(合成して)、新しいパーサーを作成することもできます(たとえば、integer
)。
また、より高いレベルのchoice
パーサーを記述して、パーサーのリストを取得し、それぞれを順番に試すことができます。
このようにして、非常に複雑なレクサー/パーサーを構築できます。演算子のオーバーロードをサポートする言語では、ターゲット言語で直接記述されているにもかかわらず、これもEBNFに非常によく似ています(必要なターゲット言語のすべての機能を使用できます)。
言語:
語彙/構文解析:
ただし、これらの違いが与えられたとしても(そして、この違いのリストはおそらく完全とはほど遠い!)、whenについて知識のある選択をしてどれを使用するかを決めることはできません。私はこれらの違いの影響/結果が何であるかを理解できません。
問題のプロパティは、問題がパーサージェネレーターを使用してより適切に解決されることを示しますか?とParser Combinatorを使用して問題をより適切に解決できることを示す問題のプロパティは何ですか?
構文エラーがないことが保証されている入力の場合、または構文の正しさの全体的な合格/不合格が問題ない場合、特に関数型プログラミング言語では、パーサーコンビネーターの操作がはるかに簡単です。これらは、パズルのプログラミング、データファイルの読み取りなどの状況です。
パーサージェネレーターの複雑さを追加する機能は、エラーメッセージです。ユーザーに行と列を示すエラーメッセージが必要であり、うまくいけば人間にも理解できるはずです。これを適切に行うには多くのコードが必要であり、antlrのようなより優れたパーサージェネレーターがそれを支援します。
ただし、自動生成ができるのはこれまでのところだけであり、ほとんどの商用で長寿命のオープンソースコンパイラーは、手動でパーサーを作成することになります。私はあなたがこれを行うのに慣れていると思うなら、あなたはこの質問をしなかっただろうと思うので、私はパーサジェネレータを使うことを勧めます。
ANTLRパーサージェネレーターのメンテナーの1人であるSam Harwell氏 最近書いた :
[コンビネーター]が私のニーズを満たしていないことがわかりました。
- ANTLRは、あいまいさなどを管理するためのツールを提供します。開発中、あいまいな解析結果を表示できるツールがあるため、文法のあいまいさを排除できます。実行時にIDEでの不完全な入力から生じるあいまいさを利用して、コード補完などの機能でより正確な結果を生成できます。
- 実際には、パーサーのコンビネーターは私のパフォーマンス目標を満たすのに適していないことがわかりました。これの一部は戻る
- 解析結果をアウトライン、コード補完、スマートインデントなどの機能に使用すると、文法の微妙な変更が結果の精度に影響を与えるのは簡単です。 ANTLRは、タイプがコンパイルされる場合でも、これらの不一致をコンパイルエラーに変換できるツールを提供します。 IDEを形成するすべての追加コードが最初から新しい機能の完全なエクスペリエンスを提供することを知っているため、文法に影響を与える新しい言語機能のプロトタイプを自信を持って作成できます。ANTLR4の私のフォーク(C#ターゲットのベースとなっている)は、この機能を提供しようとしても、私が知っている唯一のツールです。
基本的に、パーサーのコンビネーターは遊ぶのに最適なおもちゃですが、真剣な仕事をするために切り抜かれているわけではありません。
Karlが言及しているように、パーサージェネレーターはより適切なエラー報告を行う傾向があります。加えて:
一方、コンビネータには独自の利点があります。