用 megaparsec 解析后的 Errorbundles

Errorbundles after parsing with megaparsec

我目前在 megaparsec 中有一个可用的解析器,我在其中为我的程序构建了一个 AST。我现在想在我的 AST 上做一些除草操作,同时能够使用与解析器相同类型的漂亮错误。虽然这个阶段是在解析之后,但我想知道是否有 megaparsec 这样做的一般做法。有没有办法提取每一行和注释(在捆绑包中使用)并将其添加到我的 AST 中的每个项目?人们还有其他方法可以解决这个问题吗?

如果这听起来是开放式的,请提前致歉,但我主要想知道是否有比自己获取行号和创建捆绑包更好的想法。我还是 haskell 的新手,所以我无法正确浏览所有源代码。

megaparsec 开发者回答了这个问题here

总而言之,解析器有一个 getOffset 函数可以 returns 当前的字符索引。您可以将它与初始 PosState 一起使用来创建一个错误包,稍后您可以漂亮地打印它。

我有一个示例项目within the github thread,在这里再次粘贴:

module TestParser where

import           Data.List.NonEmpty as NonEmpty
import qualified Data.Maybe         as Maybe
import qualified Data.Set           as Set
import           Data.Void
import           Parser
import           Text.Megaparsec

data Sample
  = Test Int
         String
  | TestBlock [Sample]
  | TestBlank
  deriving (Show, Eq)

sampleParser :: Parser Sample
sampleParser = do
  l <- many testParser
  return $ f l
  where
    f []  = TestBlank
    f [s] = s
    f p   = TestBlock p

testParser :: Parser Sample
testParser = do
  offset <- getOffset
  test <- symbol "test"
  return $ Test offset test

fullTestParser :: Parser Sample
fullTestParser = baseParser testParser

testParse :: String -> Maybe (ParseErrorBundle String Void)
testParse input =
  case parse (baseParser sampleParser) "" input of
    Left e -> Just e
    Right x -> do
      (offset, msg) <- testVerify x
      let initialState =
            PosState
              { pstateInput = input
              , pstateOffset = 0
              , pstateSourcePos = initialPos ""
              , pstateTabWidth = defaultTabWidth
              , pstateLinePrefix = ""
              }
      let errorBundle =
            ParseErrorBundle
              { bundleErrors = NonEmpty.fromList [TrivialError offset Nothing Set.empty]
                            -- ^ A collection of 'ParseError's that is sorted by parse error offsets
              , bundlePosState = initialState
                            -- ^ State that is used for line\/column calculation
              }
      return errorBundle

-- Sample verify; throw an error on the second test key
testVerify :: Sample -> Maybe (Int, String)
testVerify tree =
  case tree of
    TestBlock [_, Test a _, _] -> Just (a, "Bad")
    _                          -> Nothing

testMain :: IO ()
testMain = do
  testExample "test test test"
  putStrLn "Done"

testExample :: String -> IO ()
testExample input =
  case testParse input of
    Just error -> putStrLn (errorBundlePretty error)
    Nothing    -> putStrLn "pass"

有些部分来自其他文件,但重要部分在代码中。