7

我正在转换一些使用 Parsec 的功能性 Haskell 代码,而不是使用 Attoparsec,以期获得更好的性能。我进行了更改,一切都编译了,但我的解析器无法正常工作。

我正在解析一个包含各种记录类型的文件,每行一个。我用于解析记录或注释的每个单独的函数都可以正常工作,但是当我尝试编写一个函数来编译一系列记录时,解析器总是返回部分结果,因为它需要更多的输入。

这是我尝试过的两个主要变体。两者都有同样的问题。

items :: Parser [Item]
items = sepBy (comment <|> recordType1 <|> recordType2) endOfLine

对于第二个,我更改了记录/注释解析器以使用行尾字符。

items :: Parser [Item]
items = manyTill (comment <|> recordType1 <|> recordType2) endOfInput

我的方法有什么问题吗?还有其他方法可以实现我的尝试吗?

4

3 回答 3

6

如果您编写的 attoparsec 解析器在失败之前消耗尽可能多的输入,那么当您到达输入的末尾时,您必须告诉部分结果继续。

于 2010-06-07T21:01:56.517 回答
3

我之前遇到过这个问题,我的理解是它是由<|>以下定义中的工作方式引起的sepBy

sepBy1 :: Alternative f => f a -> f s -> f [a]
sepBy1 p s = scan
    where scan = liftA2 (:) p ((s *> scan) <|> pure [])

这只会移动到pure []一旦(s *> scan)失败,这不会仅仅因为您处于输入的末尾而发生。

我的解决方案只是在返回feedemptyByteString 上调用。这可能是一种 hack,但它似乎也是处理这个问题的方式:Resultparseattoparsec-iteratee

f k (EOF Nothing)  = finalChunk $ feed (k S.empty) S.empty

据我所知,这是在attoparsec-iteratee这里有效的唯一原因,而普通旧的parse则无效。

于 2010-06-07T14:08:31.490 回答
0

你提供的信息很少,这就是为什么我认为很难给你很好的帮助。但是,我想发表一些评论:

  • 也许解析器没有意识到输入已经完成,它取决于获得 EOL 或获得另一条记录。因此它要求部分结果。试着给它喂食相当于 EOL 的东西,希望它能强迫它。
  • 我不记得代码了,但是使用 Alternative 实例可能不利于解析性能。如果是这种情况,您可能需要对评论和记录类型进行区分。
  • 我使用谷物进行大量二进制解析,而且速度也非常快。attoparsec 作为文本解析器似乎更好。您绝对应该考虑该选项。
  • 另一种选择是长期使用基于迭代的 IO。John Lato 在最新的 monad 阅读器(我相信第 16 期)中写了一篇关于迭代的优秀文章。行尾条件是要发出信号的迭代器。请注意,虽然迭代类型非常令人生畏,并且需要一些时间来适应。
于 2010-06-07T13:36:19.290 回答