問題文
2 つのパーサーがp
ありq
、それらを次のように連結するとします。
r = try p *> q
Parsec では、これの動作は次のとおりです。
p
入力を消費せずに失敗した場合、入力を消費せr
ずに失敗します。p
入力を消費した後に失敗した場合、入力r
を消費せずに失敗します。q
入力を消費せずに失敗した場合は、 をr
消費した後に失敗しますp
。q
入力を消費した後に失敗した場合は、 と の一部をr
消費した後に失敗します。p
q
ただし、私が探している動作は少し変わっています。
p
入力を消費せずに失敗した場合、入力を消費r
せずに失敗する必要があります。p
入力を消費した後に失敗した場合は、入力r
を消費せずに失敗する必要があります。q
入力を消費せずに失敗する場合は、入力を消費r
せずに失敗する必要があります。q
入力を消費した後に失敗した場合は、r
何らかの入力を消費した後に失敗するはずです。
これを行うためのきれいな方法を考えることができないようです。
根拠
その理由は、次のようなパーサーがあるためです。
s = (:) <$> q <*> many r
q
パーサー内に埋め込まれたパーサーr
には、無効な入力 (入力をq
消費するが失敗した場合に発生) またはmany
ループの終わり (q
何も消費せずに失敗した場合に発生) のいずれかを通知する方法が必要です。入力が無効な場合は、パーサーを完全に失敗させ、問題をユーザーに報告する必要があります。消費する入力がなくなった場合は、many
ループを終了する必要があります (ユーザーにパーサー エラーを報告することはありません)。問題は、入力が で終わる可能性がありますが、これ以上消費するp
有効な がない場合です。その場合、失敗しますが、入力を消費することはありません。q
q
だから、誰かがこの問題を解決するエレガントな方法を持っているかどうか疑問に思っていましたか? ありがとう。
補遺: 例
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
. (もしかしたら一般解は存在しない?)