请解释一下这个Parsec排列解析器的行为

时间:2016-07-06 18:08:34

标签: parsing haskell parsec

为什么这个Parsec permutation parser无法解析 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?

解析器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")

当我们有相同的共享前缀&#34; x&#34;?

时,如何支持两种输入排序?

我也不了解可选&#34; x&#34;正在进行解析行为:

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","")

2 个答案:

答案 0 :(得分:5)

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

排列解析器尝试剥离给定字符串前缀的前面,这些前缀可以由其组成解析器解析(在这种情况下为papb)。在这里,它会尝试将papb同时应用于"aabb",但在两种情况下都失败了 - 它甚至都没有试图解析"bb"

为什么papb都不能以optional (char 'x')

开头

查看permute,您会看到它使用的是choice,而后者依赖于(<|>)。正如(<|>)的文档所述,

  

这个组合器实现了选择。解析器p <|> q首先应用p。如果成功,则返回p的值。如果p在没有消耗任何输入的情况下失败,则会尝试解析器q。此组合定义等于mplus类的MonadPlus成员和(<|>)的{​​{1}}成员。

     

解析器被称为预测,因为只有当解析器Alternative没有消耗任何输入时才会尝试q(即前瞻是1)。这种非回溯行为既可以有效地实现解析器组合器,也可以生成良好的错误消息。

因此当你执行p之类的操作时,parseTest p "xbb"不会立即失败(它消耗并且pa)然后整个事情都会失败,因为它无法回溯。

如何使共享前缀有效?

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

  

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

根据我们之前为p所讨论的内容,您应该将(<|>)放在try的前面。

答案 1 :(得分:2)

  

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

因为'a'不是解析器pa或解析器pb的有效第一个字符。

  

当我们有相同的共享前缀&#34; x&#34;?

时,如何支持两种输入排序?

必须从语法中考虑共享前缀;插入回溯点(使用try),但会牺牲性能。