内存不足使用Attoparsec

时间:2014-05-17 01:03:51

标签: parsing haskell attoparsec

我试图使用attoparsec创建一个简单的解析器。制作规则如下:

block:  ?token> [inline]
inline: <?token>foo<?> | anyText

所以,我试图得到的是,一个块以文字?开头,后跟一个标记,后跟一个&gt;,然后是一系列内联。

内联可以是foo形式的序列,也可以是任何纯文本。

我正在使用爆炸性内存,但我不确定如何将解析器用于避免它。我写作的解析器的意思是提取那些“令牌”#39;的东西。这是我的实施:

import Control.Applicative
import Control.Monad
import Data.Attoparsec.Text as Text
import Data.Text

blockLine :: Parser [Text]
blockLine = do
  block   <- hiddenBlock                       -- the block token
  inlines <- many (hiddenInline <|> inline)    -- followed by inlines, which might have tokens
  return $ block : inlines

inline = manyTill anyChar (hiddenInline <|> (endOfInput >> return Text.empty)) 

hiddenInline = Text.pack <$> do
  char '<'   -- opening "tag"
  char '?'   -- opening "tag" still
  token <- manyTill anyChar (char '>')  -- the token
  manyTill anyChar (string "<?>") -- close the "tag"
  return token

hiddenBlock = Text.pack <$> do
  char '?'
  manyTill anyChar (char '>')

对我而言,这看起来是将生产规则非常简单地翻译成LL解析器。我认为困难在于我不确定如何表达内联的制作。它应该是随意的&#34;文本,但解析应在找到hiddenInline后立即停止。

1 个答案:

答案 0 :(得分:2)

问题在于您在使用中嵌套了对manyTill的调用 many。 由于inline的终止条件为endOfFilemanyTill anyChar 将乐于消耗你所有的输入,然后成功。 inline的后续使用也将成功,因为manyTill可以运行它 第一个解析器或更多次。 因此,在many解析器上使用inline只会导致many成功 在生成无限的空字符串列表时永远循环。 使用此示例

时,此行为更为明显
 parseOnly (many (manyTill anyChar endOfInput)) $ Text.pack ""

大量的分配可能是由attoparsec积累造成的 继续管理回溯。 作为一般规则,您提供给many的任何解析器都不应该 平凡地成功(即没有消耗任何输入流)。 因此,您需要重写inline或以其他方式重组您的 解析器以避免这种情况。