问题陈述
假设我有两个解析器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
循环(不向用户报告解析器错误)。问题是输入可能以 a 结尾p
但没有任何更有效的q
的 's 可以使用,在这种情况下q
会失败但不消耗任何输入。
所以我想知道是否有人有一个优雅的方法来解决这个问题?谢谢。
附录:示例
p = string "P"
q = (++) <$> try (string "xy") <*> string "z"
(假设的) parser 上的测试输入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
. (也许不存在通用解决方案?)