問題文
2 つのパーサーがpありq、それらを次のように連結するとします。
r = try p *> q
Parsec では、これの動作は次のとおりです。
p入力を消費せずに失敗した場合、入力を消費せrずに失敗します。p入力を消費した後に失敗した場合、入力rを消費せずに失敗します。q入力を消費せずに失敗した場合は、 をr消費した後に失敗しますp。q入力を消費した後に失敗した場合は、 と の一部をr消費した後に失敗します。pq
ただし、私が探している動作は少し変わっています。
p入力を消費せずに失敗した場合、入力を消費rせずに失敗する必要があります。p入力を消費した後に失敗した場合は、入力rを消費せずに失敗する必要があります。q入力を消費せずに失敗する場合は、入力を消費rせずに失敗する必要があります。q入力を消費した後に失敗した場合は、r何らかの入力を消費した後に失敗するはずです。
これを行うためのきれいな方法を考えることができないようです。
根拠
その理由は、次のようなパーサーがあるためです。
s = (:) <$> q <*> many r
qパーサー内に埋め込まれたパーサーrには、無効な入力 (入力をq消費するが失敗した場合に発生) またはmanyループの終わり (q何も消費せずに失敗した場合に発生) のいずれかを通知する方法が必要です。入力が無効な場合は、パーサーを完全に失敗させ、問題をユーザーに報告する必要があります。消費する入力がなくなった場合は、manyループを終了する必要があります (ユーザーにパーサー エラーを報告することはありません)。問題は、入力が で終わる可能性がありますが、これ以上消費するp有効な がない場合です。その場合、失敗しますが、入力を消費することはありません。qq
だから、誰かがこの問題を解決するエレガントな方法を持っているかどうか疑問に思っていましたか? ありがとう。
補遺: 例
p = string "P"
q = (++) <$> try (string "xy") <*> string "z"
(仮説の)パーサーsで入力をテストし、それが私が望むように機能したか:
xyz(受け入れる)xyzP(受け入れる;P未解析のまま)xyzPx(受け入れます;Px解析されないままです;q失敗しましたが、入力を消費しませんでした)xyzPxy(拒否; パーサーqは消費さxyれましたが失敗しました)xyzPxyz(受け入れる)
フォームr = try p *> qでは、sは上記のケース #2 とケース #3 の両方を実際に拒否します。もちろん、次のように記述して上記の動作を実現することは可能です。
r = (++) <$> try (string "P" *> string "xy") <*> string "z"
しかし、これは任意のパーサーpおよびq. (もしかしたら一般解は存在しない?)