根据 megaparsec 文档,“从版本 8 开始,一次报告多个解析错误变得更加容易。” 我还没有找到一个这样做的例子。我唯一找到的是这个。然而,它只展示了如何解析一个换行符分隔的玩具语言,也没有展示如何将多个错误组合到 ParseErrorBundle 中。这个SO 讨论不是结论性的。
1 回答
您想使用(or或)withRecovery
从 Megaparsec 生成的错误中恢复,以“注册”这些错误(或您自己生成的错误)以进行延迟处理。registerParseError
registerFailure
registerFancyFailure
在解析结束时,如果没有注册解析错误,则解析成功,而如果注册了一个或多个解析错误,则将它们全部打印出来。如果你注册了解析错误,然后还触发了一个未恢复的错误,解析将立即终止,注册的错误和最终的未恢复错误都将被打印出来。
这是一个非常简单的示例,它解析以逗号分隔的数字列表:
import Data.Void
import Text.Megaparsec
import Text.Megaparsec.Char
type Parser = Parsec Void String
numbers :: Parser [Int]
numbers = sepBy number comma <* eof
where number = read <$> some digitChar
comma = recover $ char ','
-- recover to next comma
recover = withRecovery $ \e -> do
registerParseError e
some (anySingleBut ',')
char ','
良好的输入:
> parseTest numbers "1,2,3,4,5"
[1,2,3,4,5]
并在输入有多个错误时:
> parseTest numbers "1.2,3e5,4,5x"
1:2:
|
1 | 1.2,3e5,4,5x
| ^
unexpected '.'
expecting ','
1:6:
|
1 | 1.2,3e5,4,5x
| ^
unexpected 'e'
expecting ','
1:12:
|
1 | 1.2,3e5,4,5x
| ^
unexpected 'x'
expecting ',', digit, or end of input
这里有一些微妙之处。对于以下情况,仅处理第一个解析错误:
> parseTest numbers "1,2,e,4,5x"
1:5:
|
1 | 1,2,e,4,5x
| ^
unexpected 'e'
expecting digit
您必须仔细研究解析器才能了解原因。sepBy
成功地将andnumber
解析器comma
以交替顺序应用到 parse "1,2,"
。当它到达时e
,它会应用number
失败的解析器(因为some digitChar
至少需要一位数字字符)。这是一个未恢复的错误,因此解析立即结束,没有注册其他错误,因此只打印一个错误。
此外,如果您<* eof
从定义中删除numbers
(例如,使其成为更大解析器的一部分),您会发现:
> parseTest numbers "1,2,3.4,5"
给出期间的解析错误,但是:
> parseTest numbers "1,2,3.4"
解析得很好。另一方面:
> parseTest numbers "1,2,3.4\n hundreds of lines without commas\nfinal line, with comma"
在文件末尾的句点和逗号上给出解析错误。
问题是comma
解析器用于sepBy
确定逗号分隔的数字列表何时结束。如果解析器成功(它可以通过恢复来完成,吞噬数百行到下一个逗号),sepBy
将尝试继续运行;如果解析器失败(无论是最初还是因为扫描整个文件后恢复代码找不到逗号),sepBy
将完成。
最终,编写可恢复的解析器有点棘手。