使用MegaParsec parse function,我能够运行一个解析器,并获得一个ParseErrorBundle if it fails。
我知道我能够很好地打印ParseErrorBundle,并获得整个解析失败的错误消息,这将包括行号和字符号,使用errorBundlePretty。
我还知道,我可以使用list of ParseError's从ParseErrorBundle获得bundleErrors。我可以用parseErrorPretty或parseErrorTextPretty打印这些东西。
我希望能够运行一个解析器,如果它失败了,获取一个(SourcePos, Text)列表,这样我就可以知道各个错误消息以及每个错误的位置。我想不出一种优雅的方法。虽然理论上我可以从source code to errorBundlePretty中获取相当多的数据,但我觉得自己想要对错误进行折叠,使用reachOffset来推进PosState并不是最简单的方法吗?
发布于 2021-11-27 22:05:49
注意,如果您使用的是megaparsec >= 7.0.0,我认为您应该使用attachSourcePos进行遍历。它返回NonEmpty of (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的源代码。
发布于 2021-11-27 16:29:11
我做到了以下几点:
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一次。然而,在实践中,错误列表可能没有那么大,所以我不太担心。
https://stackoverflow.com/questions/70136516
复制相似问题