如何在Parsec的monadic上下文中返回多个解析失败?

时间:2015-09-15 18:06:01

标签: parsing haskell error-handling parsec parse-error

我有一个我正在解析的语法,它包含正好两个必需且唯一的逻辑部分AlphaBeta。这些部分可以按Alpha或签证之前的任何顺序Beta定义。我想为精通技术的用户提供强大的错误消息。

在下面的示例中,存在多个解析失败的情况。我将失败消息Stringunlines函数连接起来,并将结果串联传递给fail组合子。在grammarDefinition上调用ParseError时,这会创建一个Message值,其中包含 parse值。

示例场景:

import Data.Either                   (partitionEithers)
import Data.Set                      (Set)
import Text.Parsec                   (Parsec)
import Text.Parsec.Char
import Text.ParserCombinators.Parsec

data Result = Result Alpha Beta
type Alpha  = Set (Int,Float)
type Beta   = Set String

grammarDefinition :: Parsec String u Result
grammarDefinition = do
    segments <- partitionEithers <$> many segment
    _        <- eof
    case segments of
      (     [],      []) -> fail $ unlines [missingAlpha, missingBeta]
      (      _,      []) -> fail $ missingBeta
      (     [],       _) -> fail $ missingAlpha
      ((_:_:_), (_:_:_)) -> fail $ unlines [multipleAlpha, multipleBeta]
      (      _, (_:_:_)) -> fail $ multipleBeta
      ((_:_:_),       _) -> fail $ multipleAlpha
      (    [x],     [y]) -> pure $ Result x y
    where
      missingAlpha     = message "No" "alpha"
      missingBeta      = message "No" "beta"
      multipleAlpha    = message "Multiple" "alpha"
      multipleBeta     = message "Multiple" "beta"
      message x y      = concat [x," ",y," defined in input, ","exactly one ",y," definition required"]

-- Type signature is important!
segment :: Parsec String u (Either Alpha Beta)
segment = undefined -- implementation irrelevant

我希望ParseError在多次失败的情况下包含多个 Message值。由于存在addErrorMessage函数,这应该是可能的。在通过调用parse实现结果之前,我不确定在Parsec monadic上下文中是否会提供多个失败。

示例功能:

fails :: [String] -> ParsecT s u m a
fails = undefined -- Not sure how to define this!

如何向 {{3>提供多个 Message 值在 Parsec的monadic上下文中结果

2 个答案:

答案 0 :(得分:2)

在这种情况下,

Text.Parsec.Prim相当于parserFail :: String -> ParsecT s u m a parserFail msg = ParsecT $ \s _ _ _ eerr -> eerr $ newErrorMessage (Message msg) (statePos s) 中定义的parserFail

newErrorMessage

由于addErrorMessageParseError都创建了parserFailparserFail' :: String -> ParsecT s u m a parserFail' msg = ParsecT $ \s _ _ _ eerr -> eerr $ theMessages s where theMessages s = addErrorMessage (Message "blah") $ addErrorMessage (Expect "expected this") $ newErrorMessage (Message msg) (statePos s) 的这种变体也应该有效:

label

应该将3条消息推送到错误消息列表中。

同样在该模块中,请查看labelsaddErrorMessage 唯一使用labels的地方。 <?>只是一条多消息 foldr运算符的版本。请注意它如何使用labels :: ParsecT s u m a -> [String] -> ParsecT s u m a labels p msgs = ParsecT $ \s cok cerr eok eerr -> let eok' x s' error = eok x s' $ if errorIsUnknown error then error else setExpectErrors error msgs eerr' err = eerr $ setExpectErrors err msgs in unParser p s cok cerr eok' eerr' where setExpectErrors err [] = setErrorMessage (Expect "") err setExpectErrors err [msg] = setErrorMessage (Expect msg) err setExpectErrors err (msg:msgs) = foldr (\msg' err' -> addErrorMessage (Expect msg') err') (setErrorMessage (Expect msg) err) msgs 来构建化合物 错误讯息:

ParsecT

唯一的问题是你需要访问Text.Parsec.Prim构造函数 labels未导出。也许你可以找到一种方法来使用parsec 或者另一种解决方法。否则你可以随时包括你的 使用您的代码拥有{{1}}的黑客版本。

答案 1 :(得分:0)

我们可以利用ParsecTMonadPlus的一个实例的事实,将mzero的定义与函数labels结合起来,以得到所需的结果:

fails :: [String] -> ParsecT s u m a
fails = labels mzero

注意: ParseError有很多Expect个值,Message个值不多......