这是我的玩具文件:
import Text.Megaparsec
import Text.Megaparsec.Char
import Data.Void (Void)
type Parser = Parsec Void String
myParser :: Parser String
myParser = do
d <- digitChar
c <- letterChar
return $ replicate (read [d]) c
现在,从 ghci 开始,如果我输入,parseTest (myParser <?> "foo") "3a"
我会得到"aaa"
预期的结果,但如果我输入parseTest (myParser <?> "foo") "33a"
,我会得到:
1:2:
|
1 | 33a
| ^
unexpected '3'
expecting letter
在这个简单的情况下,错误消息是有意义的(我必须输入一个字母而不是另一个数字),但在编写更复杂的解析器时,letterChar
可能会出现在任意数量的复合解析器中,因此不清楚哪个 letterChar
是失败的。由于我为解析器传递了一个标签foo
,因此如果错误消息显示如下内容,我会希望它:
1:2:
|
1 | 33a
| ^
error while parsing foo:
unexpected '3'
expecting letter
更一般地说,只要我使用 给解析器标签<?>
,我就希望显示错误的整个回溯,例如:
error while parsing grandparent:
error while parsing parent:
unexpected '3'
expecting letter
有没有办法在百万秒差距中做到这一点?
Megaparsec 没有内置支持执行此操作,但您可以使用其自定义错误机制。
我们可以定义一个自定义错误类型,将上下文标签添加到现有的
ParseError
,以及ShowErrorComponent
在错误消息中显示它的实例。(这里奇怪的孤立Ord
实例ParseError
满足了一项技术要求。自定义错误需要一个Ord
实例,但ParseError
没有实例,因此如果我们想在自定义错误中包含嵌套的 ParseError ,我们必须派生一个实例。)就其本身而言,这不会执行任何操作,但我们可以修改 的定义
<?>
及其非运算符等效项label
以利用此自定义错误。具体来说,我们可以修改它们,以便它们调用原始的 Megaparsec 定义,该label
定义可以正确处理解析器在不消耗输入的情况下失败的情况(通过将标签显示为“最低”错误),然后还处理解析器在消耗后失败的情况输入(通过用上下文包装错误ErrorWithLabel
):这对于你的例子来说效果很好:
完整的代码示例,带有一些稍微复杂的标签: