0

我想编写代码来做类似 C 预处理的事情。所以我寻找图书馆并得到了两个候选人attoparsec,, megaparsec.

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

如果我将错误位置功能添加到attoparsec's Parsermonad,那么我是否必须将其包装在StateT变压器中并在使用它们时提升所有库的功能?我觉得工作很累。有没有更好的方法?

编辑

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

我只想知道单子包装方法。换句话说,是否解除所有内部 monad 函数是唯一的解决方案。

4

1 回答 1

2

您可以从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),但对于许多应用程序来说,偏移量就足够了。

用于从 afromPos获取:IntPos

λ> 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"

最后一点:如果你真的需要一个转换器并且想继续使用attoparsecData.Attoparsec.Zepto包,它确实提供了转换器,但这是与 attoparsec 中的主解析器不同的解析器类型ZeptoT

于 2017-07-30T10:38:29.133 回答