Haskell 如何强制Parsec返回错误?

Haskell 如何强制Parsec返回错误?,haskell,error-handling,text-parsing,parsec,Haskell,Error Handling,Text Parsing,Parsec,我正在用Parsec创建一个解析器,并试图在解析过程中返回一个特定的错误 这是一个暴露我的问题的最小解析器示例: parseA = try seq1 <|> seq2 seq1 = do manyTill anyChar (try $ string "\n* ") many1 anyChar fail "My error message" seq2 = do manyTill anyC

我正在用Parsec创建一个解析器,并试图在解析过程中返回一个特定的错误

这是一个暴露我的问题的最小解析器示例:

parseA = try seq1
      <|>  seq2

seq1 = do
          manyTill anyChar (try $ string "\n* ")
          many1 anyChar
          fail "My error message" 

seq2 = do
          manyTill anyChar (try $ string "\n- ")
          many1 anyChar
当我使用
fail
unexpected
时,我的解析器不会停止(由于
try
函数)并执行下一个
do
序列:

ghci>  parse parseA  "" "aaaaaa\nbbbb\n* ccccc\n- ddd"
Right "ddd"
这不是我想要的

我考虑过使用basic
error
函数停止解析器的执行,但我希望解析函数返回一个“干净”错误,如下所示:

ghci>  parse parseA  "" "aaaaaa\nbbbb\n* ccccc\n- ddd"
Left "My error message"

您知道如何正确停止解析器并返回自定义错误吗?

如果您希望monad的行为有所不同,那么您可能应该构建一个不同的monad。(注:我不完全清楚你想要什么,但还是继续前进)

解决方案:使用Monad转换器堆栈

例如,要获得一个类似于
fail
的函数,它不会被Parsec的
try捕获和忽略,您可以使用一个
exception
允许您抛出与异常类似的错误,但它们是一元一元地检查的,而不是使用要求IO捕获的实际异常机制

首先,让我们定义monad:

import Text.Parsec
import Text.Parsec.Combinator
import Text.Parsec.Char
import Control.Monad.Trans.Except
import Control.Monad.Trans

type EscParse a = ParsecT String () (Except String) a
因此,monad是
EscParse
,它结合了Parsec的特性(通过transformer
ParsecT
)和
除了

其次,让我们定义一些助手:

run :: EscParse a -> SourceName -> String -> Either String (Either ParseError a)
run op sn input = runExcept (runPT op () sn input)

escFail :: String -> EscParse a
escFail = lift. throwE
我们的
run
类似于
runParse
,但也运行except monad。您可能想做些什么来避免嵌套,但这是一个简单的外观变化<如果不希望忽略错误,则使用代码>escFail

第三,我们需要使用这个新的monad实现您的解析器:

parseA :: EscParse String
parseA = try seq1 <|>  seq2

seq1 :: EscParse String
seq1 = do manyTill anyChar (try $ string "\n* ")
          many1 anyChar
          escFail "My error message"

seq2 :: EscParse String
seq2 = do manyTill anyChar (try $ string "\n- ")
          many1 anyChar
parseA::EscParse字符串
parseA=尝试按顺序1按顺序2
seq1::EscParse字符串
seq1=do manyTill anyChar(尝试$string“\n*”)
many1 anyChar
escFail“我的错误消息”
seq2::EscParse字符串
seq2=do manyTill anyChar(尝试$string“\n-”)
many1 anyChar

除了间距和类型签名之外,上述内容与您所拥有的内容相匹配,但使用的是
escFail
而不是
fail

如果
seq1
失败,它会打印出错误消息并尝试
seq2
?或者在
seq1
失败时停止解析?
parseA :: EscParse String
parseA = try seq1 <|>  seq2

seq1 :: EscParse String
seq1 = do manyTill anyChar (try $ string "\n* ")
          many1 anyChar
          escFail "My error message"

seq2 :: EscParse String
seq2 = do manyTill anyChar (try $ string "\n- ")
          many1 anyChar