【问题标题】:How can I force Parsec to return an error?如何强制 Parsec 返回错误?
【发布时间】:2019-01-07 23:50:48
【问题描述】:

我正在使用 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

我想在第一个try $ do 序列中执行一些测试并停止解析并返回特定的错误消息。 当我不使用fail 时,我得到:

ghci>  parse parseA  "" "aaaaaa\nbbbb\n* ccccc\n- ddd"
Right "ccccc\n- ddd"

当我使用failunexpected 时,我的解析器不会停止(由于try 函数)并执行下一个do 序列:

ghci>  parse parseA  "" "aaaaaa\nbbbb\n* ccccc\n- ddd"
Right "ddd"

这不是我想要的!

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

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

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

【问题讨论】:

  • 如果seq1 失败,它会打印出错误信息并尝试seq2?还是在seq1 失败时停止解析?

标签: haskell error-handling text-parsing parsec


【解决方案1】:

如果您希望 monad 表现不同,那么也许您应该构建一个不同的 monad。 (注意,我并不完全清楚你想要什么,但无论如何都要继续前进)。

解决方案:使用 Monad Transformer 堆栈

例如,要获得一个不会被 Parsec 的 try 捕获和忽略的类似 fail 的函数,您可以使用 Except monadExcept 允许您像异常一样抛出错误,但它们是单子探测的,而不是使用需要 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(通过转换器 ParsecT)和 Except 的特性。

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

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。您可能想要做一些事情来避免嵌套的 Either,但这是一个简单的外观更改。 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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-11-21
    • 1970-01-01
    • 2018-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-11
    • 1970-01-01
    相关资源
    最近更新 更多