0

假设我有一个文档,其文本由 Jade 式括号分隔,例如{{foo}}. 我编写了一个似乎可以foo正确提取的 Attoparsec 解析器:

findFoos :: Parser [T.Text]
findFoos = many $ do
  manyTill anyChar (string "{{")
  manyTill letter (string "}}")

测试它表明它有效:

> parseOnly findFoos "{{foo}}"
Right ["foo"]
> parseOnly findFoos "{{foo}} "
Right ["foo"]

现在,使用 中的Data.Conduit.Attoparsec模块conduit-extra,我似乎遇到了奇怪的行为:

> yield "{{foo}}" $= (mapOutput snd $ CA.conduitParser findFoos) $$ CL.mapM_ print
["foo"]
> yield "{{foo}} " $= (mapOutput snd $ CA.conduitParser findFoos) $$ CL.mapM_ print
-- floods stdout with empty lists

这是期望的行为吗?我应该在这里使用导管实用程序吗?对此的任何帮助都将是巨大的!

4

1 回答 1

1

因为它使用many,将在没有找到任何分隔文本时findFoos返回而不消耗输入。[]

另一方面,在流上重复conduitParser应用解析器,返回每个解析的值,直到耗尽流。

问题"{{foo}} "在于解析器将消耗{{foo}},但流中的空白空间仍未消耗,因此解析器的进一步调用总是返回[]

如果您重新定义findFoos一次使用一个引用元素,包括尾随空格,它应该可以工作:

findFoos' :: Parser String
findFoos' = do
   manyTill anyChar (string "{{")
   manyTill letter (string "}}") <* skipSpace

真实世界的示例在括号文本之间会有其他字符,因此在每次解析后跳过“额外内容”(下一次解析不消耗任何{{左大括号)会涉及更多。

也许像下面这样的东西会起作用:

findFoos'' :: Parser String
findFoos'' = do
    manyTill anyChar (string "{{")
    manyTill letter (string "}}") <* skipMany everythingExceptOpeningBraces
  where 
    -- is there a simpler / more efficient way of doing this?
    everythingExceptOpeningBraces =
        -- skip one or more non-braces
        (skip (/='{') *> skipWhile (/='{'))
        <|> 
        -- skip single brace followed by non-brace character
        (skip (=='{') *> skip (/='{'))
        <|>
        -- skip a brace at the very end 
        (skip (=='{') *> endOfInput)

(但是,如果流中没有任何带括号的文本,则此解析器将失败。也许您可以构建一个在这种情况下Parser (Maybe Text)返回Nothing的。)

于 2015-05-23T08:19:20.137 回答