请解释这个Parsec置换解析器的行为

Rob*_*ell 1 parsing haskell parsec

为什么这个Parsec排列解析器不解析b

p :: Parser (String, String)
p = permute (pair
              <$?> ("", pa)
              <|?> ("", pb))
  where pair a b = (a, b)

pa :: Parser String
pa = do
  char 'x'
  many1 (char 'a')

pb :: Parser String
pb = do
  many1 (char 'b')

?> parseTest p "xaabb"
("aa","bb")               -- expected result, good
?> parseTest p "aabb"
("","")                   -- why "" for b?
Run Code Online (Sandbox Code Playgroud)

解析器pa配置为可选的,<$?>因此我不明白为什么它的失败影响了b的解析.我可以将其更改optional (char 'x')为获得预期的行为,但我不明白为什么.

pa :: Parser String
pa = do
  optional (char 'x')
  many1 (char 'a')

pb :: Parser String
pb = do
  optional (char 'x')
  many1 (char 'b')

?> parseTest p "xaaxbb"
parse error at (line 1, column 2):
unexpected "a"
expecting "b"
?> parseTest p "xbbxaa"
("aa","bb")
Run Code Online (Sandbox Code Playgroud)

当我们有相同的共享前缀"x"时,如何支持两种输入顺序?

我也不明白可选"x"的消耗对解析行为的影响:

pb :: Parser String
pb = do
  try px -- with this try x remains unconsumed and "aa" gets parsed
         -- without this try x is consumed, but "aa" isn't parsed even though "x" is optional anyway
  many1 (char 'b')

px :: Parser Char
px = do
  optional (char 'x')
  char 'x' <?> "second x"

?> parseTest p "xaaxbb"            -- without try on px
parse error at (line 1, column 2):
unexpected "a"
expecting second x
?> parseTest p "xaaxbb"            -- with try on px
("aa","")
Run Code Online (Sandbox Code Playgroud)

Ale*_*lec 5

为什么parseTest p "aabb"("","")

置换解析器试图剥离,可以通过其组成解析器(被解析给定的字符串的前缀的前papb在这种情况下).在这里,它会试图同时适用pa,并pb"aabb"和失败两种情况-它甚至从来没有得到周围,以试图解析"bb".

为什么不能同时papb与开始optional (char 'x')

看着permute,你会看到它的用途choice,而这又依赖于它(<|>).如文件所述(<|>),

该组合器实现了选择.解析器p <|> q首先应用p.如果成功,p则返回值.如果p在不消耗任何输入的情况下失败,q则尝试解析器.此组合子被定义等于mplus所述的构件MonadPlus类和(<|>)的构件Alternative.

解析器被称为预测,因为q仅在解析器p不消耗任何输入时尝试(即,前瞻为1).这种非回溯行为允许解析器组合器的有效实现和良好错误消息的生成.

因此,当你做类似的事情时parseTest p "xbb",pa不会立即失败(它消耗和'x')然后整个事情失败,因为它无法回溯.

如何使共享前缀工作?

正如丹尼尔所建议的那样,最好将你的语法分解出来.或者,您可以使用try:

解析器的try p行为类似于解析器p,除了它假装它在发生错误时没有消耗任何输入

根据我们之前谈到的内容(<|>),你应该把它放在try两者之前optional (char 'x').