如何将 MegaParsec "ParseErrorBundle" 转换为 "SourcePos" 列表和错误消息
How do I convert a MegaParsec "ParseErrorBundle" into a list of "SourcePos" and error messages
使用 MegaParsec parse
function, I'm able to run a parser, and get a ParseErrorBundle
if it fails.
我知道我能够漂亮地打印 ParseErrorBundle
,并得到一条针对整个解析失败的错误消息,其中将包括行号和字符号,使用 errorBundlePretty
。
我也知道我可以使用 bundleErrors
从 ParseErrorBundle
得到 list of ParseError
's。而且我可以用 parseErrorPretty
或 parseErrorTextPretty
.
漂亮地打印这些
我希望能够 运行 一个解析器,如果它失败了,得到一个 (SourcePos, Text)
的列表,这样我就可以知道各个错误消息以及每个错误的位置.
我想不出一个优雅的方法来做到这一点。虽然理论上我可以从 source code to errorBundlePretty
中大量抄袭,但我觉得要克服错误并使用 reachOffset
推进 PosState
不是解决这个问题的最简单方法吗? .
我能够按如下方式让它工作:
import qualified Text.Megaparsec as MP
import Data.List.NonEmpty (NonEmpty (..))
import qualified Data.Text as T
annotateErrorBundle :: MP.ParseErrorBundle Text Void -> NonEmpty (MP.SourcePos, Text)
annotateErrorBundle bundle = (\e -> (errorSrcPos e, T.pack $ MP.parseErrorTextPretty e)) <$> MP.bundleErrors bundle
where
initialPosState = MP.bundlePosState bundle
errors = MP.bundleErrors bundle
errorSrcPos e = MP.pstateSourcePos . snd $ MP.reachOffset (MP.errorOffset e) initialPosState
我怀疑这可能效率不高,因为我每个错误都会调用一次 reachOffset
。但是,实际上,错误列表可能并没有那么多,所以我不太担心。
请注意,如果您使用 megaparsec >= 7.0.0
,我认为您应该使用 attachSourcePos
进行遍历。它 returns NonEmpty
对 (ParseError, SourcePos)
对。我认为它看起来像:
import qualified Text.Megaparsec as MP
import qualified Data.Text as T
import Data.List.NonEmpty (NonEmpty (..))
import Data.Void
annotateErrorBundle :: MP.ParseErrorBundle T.Text Void -> NonEmpty (MP.SourcePos, T.Text)
annotateErrorBundle bundle
= fmap (\(err, pos) -> (pos, T.pack . MP.parseErrorTextPretty $ err)) . fst $
MP.attachSourcePos MP.errorOffset
(MP.bundleErrors bundle)
(MP.bundlePosState bundle)
请注意,与您建议的答案不同,attachSourcePos
通过错误包的遍历正确地将 PosState
线程化,而不是在每次 reachOffset
调用后丢弃更新的状态。因此,我相信对于大量的错误,它会更有效率。 (它还使用 reachOffsetNoLine
而不是 reachOffset
,这对于某些流类型可能更有效。
如果您使用的是 megaparsec < 7.0.0
,您可能想尝试从更高版本中调整 attachSourcePos
的源代码。
使用 MegaParsec parse
function, I'm able to run a parser, and get a ParseErrorBundle
if it fails.
我知道我能够漂亮地打印 ParseErrorBundle
,并得到一条针对整个解析失败的错误消息,其中将包括行号和字符号,使用 errorBundlePretty
。
我也知道我可以使用 bundleErrors
从 ParseErrorBundle
得到 list of ParseError
's。而且我可以用 parseErrorPretty
或 parseErrorTextPretty
.
我希望能够 运行 一个解析器,如果它失败了,得到一个 (SourcePos, Text)
的列表,这样我就可以知道各个错误消息以及每个错误的位置.
我想不出一个优雅的方法来做到这一点。虽然理论上我可以从 source code to errorBundlePretty
中大量抄袭,但我觉得要克服错误并使用 reachOffset
推进 PosState
不是解决这个问题的最简单方法吗? .
我能够按如下方式让它工作:
import qualified Text.Megaparsec as MP
import Data.List.NonEmpty (NonEmpty (..))
import qualified Data.Text as T
annotateErrorBundle :: MP.ParseErrorBundle Text Void -> NonEmpty (MP.SourcePos, Text)
annotateErrorBundle bundle = (\e -> (errorSrcPos e, T.pack $ MP.parseErrorTextPretty e)) <$> MP.bundleErrors bundle
where
initialPosState = MP.bundlePosState bundle
errors = MP.bundleErrors bundle
errorSrcPos e = MP.pstateSourcePos . snd $ MP.reachOffset (MP.errorOffset e) initialPosState
我怀疑这可能效率不高,因为我每个错误都会调用一次 reachOffset
。但是,实际上,错误列表可能并没有那么多,所以我不太担心。
请注意,如果您使用 megaparsec >= 7.0.0
,我认为您应该使用 attachSourcePos
进行遍历。它 returns NonEmpty
对 (ParseError, SourcePos)
对。我认为它看起来像:
import qualified Text.Megaparsec as MP
import qualified Data.Text as T
import Data.List.NonEmpty (NonEmpty (..))
import Data.Void
annotateErrorBundle :: MP.ParseErrorBundle T.Text Void -> NonEmpty (MP.SourcePos, T.Text)
annotateErrorBundle bundle
= fmap (\(err, pos) -> (pos, T.pack . MP.parseErrorTextPretty $ err)) . fst $
MP.attachSourcePos MP.errorOffset
(MP.bundleErrors bundle)
(MP.bundlePosState bundle)
请注意,与您建议的答案不同,attachSourcePos
通过错误包的遍历正确地将 PosState
线程化,而不是在每次 reachOffset
调用后丢弃更新的状态。因此,我相信对于大量的错误,它会更有效率。 (它还使用 reachOffsetNoLine
而不是 reachOffset
,这对于某些流类型可能更有效。
如果您使用的是 megaparsec < 7.0.0
,您可能想尝试从更高版本中调整 attachSourcePos
的源代码。