Haskell 如何强制Parsec返回错误?
我正在用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
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"
这不是我想要的
我考虑过使用basicerror
函数停止解析器的执行,但我希望解析函数返回一个“干净”错误,如下所示:
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的特性(通过transformerParsecT
)和除了
其次,让我们定义一些助手:
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