问题陈述
假设我有两个解析器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. (也许不存在通用解决方案?)