49

HaskellのParsecのようなパーサーコンビネーターライブラリでパーサーを作成する場合、通常は2つの選択肢があります。

  • String入力をトークンに分割するレクサーを作成してから、解析を実行します[Token]
  • パーサーコンビネータを直接書き込むString

多くの解析入力は空白で区切られたトークンとして理解できることを考えると、最初の方法はしばしば理にかなっているように思われます。

他の場所では、トークン化(またはスキャン字句解析、一部の人はそれをどのように呼んでいるのか)に反対することを人々が推奨しているのを見てきました。主な理由は単純さです。

字句解析と字句解析を行わないことの間の一般的なトレードオフは何ですか?

4

1 に答える 1

62

最も重要な違いは、字句解析によって入力ドメインが変換されることです。

これの良い結果はそれです

  • もう空白について考える必要はありません。直接(非字句)パーサーではspace、空白が許可されているすべての場所にパーサーを振りかける必要があります。これは忘れがちで、空白がすべてのトークンを分離する必要がある場合はコードが乱雑になります。

  • 人間にとって簡単な、1つずつ入力を考えることができます。

ただし、字句解析を実行すると、次のような問題が発生します。

  • で一般的なパーサーを使用することはできなくなりましStringた。たとえば、ライブラリ関数(文字列入力ストリームで動作する)を使用して数値を解析する場合は、解析結果を検査するために、パーサーのような操作を行う必要があります(通常はparseFloat :: Parsec String s Float)。これは書くのが面倒で、構成可能性を制限します。takeNextToken :: TokenParser StringexecuteparseFloatEither ErrorMessage a

  • すべてのエラーメッセージを調整しました。トークンのパーサーが20番目のトークンで失敗した場合、入力文字列のどこにありますか?エラーの場所を手動で入力文字列にマップする必要がありますが、これは面倒です(Parsecでは、これはすべてのSourcePos値を調整することを意味します)。

  • エラー報告は一般的に悪いです。string "hello" *> space *> floatのような間違った入力で実行すると、"hello4"の後に空白が欠落していることが予想されますがhello、レクサーは。を見つけたと主張します"invalid token"

  • アトミックユニットであり、レクサーによって分離されると予想される多くのことは、実際には、レクサーが識別するにはかなり「難しすぎる」ものです。たとえば、文字列リテラルは、突然"hello world"2つのトークン"helloではなくなりworld"ます(ただし、もちろん、引用符がエスケープされていない場合のみ\")。これはパーサーにとっては非常に自然なことですが、レクサーにとっては複雑なルールと特殊なケースを意味します。

  • トークンでパーサーをうまく再利用することはできません。のdoubleを解析する方法を定義する場合はString、それをエクスポートすると、世界中で使用できます。(特殊な)トークナイザーを最初に実行することはできません。

  • あなたはそれに固執しています。解析する言語を開発しているとき、レクサーを使用すると、早期の決定を下し、後で変更したいものを修正することにつながる可能性があります。たとえば、Floatトークンを含む言語を定義したとします。ある時点で、負のリテラル(-3.4および- 3.4)を導入する必要があります。これは、レクサーが空白をトークン区切り文字として解釈するために不可能な場合があります。パーサーのみのアプローチを使用すると、柔軟性を維持して、言語の変更を容易にすることができます。パーサーは本質的にルールをエンコードするより複雑なツールであるため、これはそれほど驚くべきことではありません。

要約すると、ほとんどの場合、レクサーフリーのパーサーを作成することをお勧めします。

結局のところ、レクサーは単なる「ダミングダウン」*パーサーです。とにかくパーサーが必要な場合は、それらを1つに結合します。


*コンピューティング理論から、すべての正規言語も文脈自由言語であることがわかります。レクサーは通常、通常のパーセクであり、コンテキストフリーまたはコンテキストセンシティブです(Parsecのようなモナディックパーサーはコンテキストセンシティブを表現できます)。

于 2013-03-05T05:02:52.860 に答える