我可以轻松地将 attoparsec 包装在变压器中吗?

Can I easily wrap attoparsec in transformer?

我想编写代码来执行 C 预处理之类的操作。所以我找了图书馆,找到了两个候选人,attoparsecmegaparsec.

我需要报告错误位置的功能,megaparsec 已经有了。但是 attoparsec 对性能来说是可取的。

如果我将错误位置功能添加到 attoparsecParser monad,那么我是否必须将其包装在 StateT 转换器中并在我执行时提升所有库的功能使用它们?我认为这是一项令人厌烦的工作。有没有更好的方法?

编辑

我会采用适合这种情况的megaparsec。但我仍然想知道如何包装 attoparsecParser monad。有没有人可以告诉我我上面提到的方法是否是最好的方法?

我只想知道 monad 包装方法。换句话说,提升所有内部monad功能是否是唯一的解决方案。

您可以从 attoparsec 获取当前解析位置,而无需转换器。但是没有导出函数可以做到这一点;你必须自己定义它:

import qualified Data.Attoparsec.Internal.Types as T

offset :: T.Parser i T.Pos
offset = T.Parser $ \t pos more lose succ -> succ t pos more pos

用法示例:

λ> parseOnly (many' (skipMany (word8 46) *> offset <* anyWord8)) ".a..a...a....a"
Right [Pos {fromPos = 1},Pos {fromPos = 4},Pos {fromPos = 8},Pos {fromPos = 13}]

这也适用于增量输入。它只给你 输入的偏移量,不是 (line, column),而是偏移量 足以满足许多应用。

使用fromPosPos得到Int:

λ> T.fromPos <$> parseOnly offset ""
Right 0

现在,我们可以使用 offset 创建一个解析器来报告当前的 失败时偏移。

reportOffsetOnError :: T.Parser i a -> T.Parser i a
reportOffsetOnError p =
  p <|> (offset >>= \pos ->
    fail ("failed at offset: " ++ show (T.fromPos pos)))

用法示例:

λ> parseOnly (word8 46 *> word8 46 *> reportOffsetOnError (word8 97)) "..a"
Right 97
λ> parseOnly (word8 46 *> word8 46 *> reportOffsetOnError (word8 97)) "..b"
Left "Failed reading: failed at offset: 2"

最后一点:如果你真的需要一个转换器并且想继续使用 attoparsec 包,Data.Attoparsec.Zepto 确实提供了 ZeptoT 转换器,但是这个是与 attoparsec.

中的主解析器不同的解析器类型