我想编写像C预处理这样的代码。所以我找了两个候选人,attoparsec
,megaparsec
。
我需要功能报告错误位置,megaparsec
已经有了。但attoparsec
对于表现来说是理想的。
如果我将错误位置功能添加到attoparsec
的{{1}} monad,那么我是否应该将其包含在Parser
变压器中,然后解除所有库#39 ;当我使用它们时的功能?我认为这是一项令人厌倦的工作。有没有更好的方法?
我会采用适合这种情况的StateT
。但我仍然想知道如何包装megaparsec
attoparsec
monad。有没有人可以告诉我上面提到的方法是否是最好的方法?
我想知道monad包装方法。换句话说,是否解除所有内部monad函数是唯一的解决方案。
答案 0 :(得分: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)
,而是偏移量
足以满足许多应用。
使用fromPos
从Int
Pos
λ> 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"
最后一点:Data.Attoparsec.Zepto
确实提供了ZeptoT
变换器,如果你真的需要一个变换器并想继续使用 attoparsec 包,但这是一个不同的解析器类型来自 attoparsec 中的主解析器。