Parsing 如何在Parsec';什么是一元语境?

Parsing 如何在Parsec';什么是一元语境?,parsing,haskell,error-handling,parsec,parse-error,Parsing,Haskell,Error Handling,Parsec,Parse Error,我正在解析的语法正好由两个必需且唯一的逻辑部分组成,Alpha和Beta。这些部件可以按任何顺序定义,Alpha在Beta之前或visa vera。我想为不太懂技术的用户提供可靠的错误消息 在下面的示例中,存在多个解析失败的情况。我将失败消息Strings与函数连接起来,并将结果连接传递给组合器。当在grammarDefinition上调用时,这将创建一个具有单个值的值 示例场景: import Data.Either (partitionEithers) i

我正在解析的语法正好由两个必需且唯一的逻辑部分组成,
Alpha
Beta
。这些部件可以按任何顺序定义,
Alpha
Beta
之前或visa vera。我想为不太懂技术的用户提供可靠的错误消息

在下面的示例中,存在多个解析失败的情况。我将失败消息
String
s与函数连接起来,并将结果连接传递给组合器。当在
grammarDefinition
上调用时,这将创建一个具有单个值的值

示例场景:

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
fails :: [String] -> ParsecT s u m a
fails = undefined -- Not sure how to define this!

如何在Parsec的一元上下文中为结果提供多个值?

fail
在这种情况下等同于
Text.Parsec.Prim
中的定义:

parserFail :: String -> ParsecT s u m a
parserFail msg
    = ParsecT $ \s _ _ _ eerr ->
      eerr $ newErrorMessage (Message msg) (statePos s)
由于
newErrorMessage
addErrorMessage
都创建了
ParseError
parserFail
的这种变体也应该起作用:

parserFail' :: 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)
这会将3条消息推送到错误消息列表中

同样在该模块中,请查看
标签
标签
,它们是 唯一使用
addErrorMessage
的地方<代码>标签只是一条多信息
运算符的版本。注意它是如何使用
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
唯一的gatcha是您需要访问
ParsecT
构造函数,该构造函数 不是通过
Text.Parsec.Prim
导出的。也许你能找到一种使用
标签的方法
或者用另一种方法来解决这个问题。否则,您可以始终包括您的
使用您的代码拥有被黑客攻击的
parsec

我们可以利用的一个实例,将的定义与函数相结合,得出所需的结果:

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

注意:有很多值,而不是很多值…

我建议从转换到更新和更可扩展的库

自版本
4.2.0.0
以来已解决

使用以下函数可以轻松创建多个分析错误:

fails :: MonadParsec m => [String] -> m a
fails = failure . fmap Message

您的回答提供了大量信息,但是没有导出
ParsecT
构造函数。这使得您提出的大多数解决方案在库的公开界面之外无法使用模块的源代码。很高兴知道使用
标签
对您有效。这并不完美,因为我更喜欢
消息
而不是
期望值
值,但它可能是我能得到的最接近的解决方案。加上
失败
定义又短又甜;)