最初の例では、次のように置き換えます
content = many anyChar
と
content = many digit
コンテンツのパーサーがセパレーターと誤って一致しないようにします。
単に数字以上のものを一致させたいと思うかもしれませんが、そうであっても、 s の間で何が有効であるかを注意深く考え、それを行う<!>パーサーを作成することをお勧めします。
なんで?
コンテンツ用の本当に優れたパーサーを手に入れたら、応答の定義は完璧になります。このようにmystring = "hello<!>mum"して、最上位のパーサーによって切り刻まれることなくコンテンツを含めることができます。低レベルのstringLiteralパーサーは全体を食べて"hello<!>mum"しまい、最上位のパーサーはその<!>中に正しく無邪気に含まれているものを見ることはありません。
一般に、...
ほとんどの解析状況では、コンテンツで何が許可されているかを本当に明確にし、それのみを解析することが最善です。これには次の 3 つの理由があります。
- 再利用性 (これをより大きなパーサー内で使用できます)
- 正しさ
- 通常は効率的です。先読みを避けすぎると、パーサーの実行速度が速くなります。
再利用性は重要です。現時点では、分割し<!>て他のすべてを食べるパーサーを使用すると、入力全体を食べることが保証され、それ以上の解析を行うことができなくなります。
ボトムアップ
パーサーはゼロから機能する必要があります。これについて、コメントで「パーサーを特定のものから一般的なものにスタックする」と非常によく説明しました。
テストを容易にするためにこの順序で記述するのが最も簡単なので、最初に a stringCharthen stringLiteralbefore memberbefore arraybefore before objectbefore jsonbefore contentthenに一致するものを記述しresponseます。途中で相互に再帰的に呼び出すことができます。parseTestその後、一緒に行うように、それぞれの小さなものをテストするために使用できます。parseTest response "1<!>2<!>3"main を書き直してコンパイルするよりも、ghci に入力する方が速いです。
トップダウン?
パーサーをトップダウンで書くのは間違っていませんが、難しいだけです。あなたは書ける
response = many $ content `sepBy` contentSeparator
content = json <|> somethingElse
json = object <|> array
array = ...
しかし、非常に小さなパーサーを作成するまでは、何もテストできません。