如何使用megaparsec报告多个错误?

sin*_*ity 5 parsing haskell megaparsec

根据 megaparsec 文档,“自第 8 版以来,一次报告多个解析错误变得更加容易。” 我一直无法找到一个这样做的例子。我找到的唯一一个就是这个。然而,它只展示了如何解析换行符分隔的玩具语言,也没有展示如何将多个错误组合到 ParseErrorBundle 中。这个SO 讨论不是结论性的。

K. *_*uhr 6

您希望结合使用withRecovery从 Megaparsec 生成的错误中恢复registerParseError(或registerFailureregisterFancyFailure) 来“注册”这些错误(或您自己生成的错误)以进行延迟处理。

解析结束时,如果没有注册任何解析错误,则解析成功,而如果注册了一个或多个解析错误,则一次全部打印出来。如果您注册了解析错误,然后又触发了无法恢复的错误,则解析将立即终止,并且将打印注册的错误和最终的无法恢复的错误。

这是一个非常简单的示例,用于解析以逗号分隔的数字列表:

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 ','
Run Code Online (Sandbox Code Playgroud)

良好的输入:

> parseTest numbers "1,2,3,4,5"
[1,2,3,4,5]
Run Code Online (Sandbox Code Playgroud)

并在输入时出现多个错误:

> 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
Run Code Online (Sandbox Code Playgroud)

这里有一些微妙之处。对于以下内容,仅处理第一个解析错误:

> parseTest numbers "1,2,e,4,5x"
1:5:
  |
1 | 1,2,e,4,5x
  |     ^
unexpected 'e'
expecting digit
Run Code Online (Sandbox Code Playgroud)

你必须仔细研究解析器才能明白为什么。成功地以交替顺序sepBy应用numbercomma解析器来解析"1,2,"。当它到达 时e,它应用number失败的解析器(因为some digitChar需要至少一位数字字符)。这是一个不可恢复的错误,因此解析立即结束,没有注册其他错误,因此仅打印一个错误。

另外,如果您<* eof从 的定义中删除了numbers(例如,使其成为更大的解析器的一部分),您会发现:

> parseTest numbers "1,2,3.4,5"
Run Code Online (Sandbox Code Playgroud)

给出了句点的解析错误,但是:

> parseTest numbers "1,2,3.4"
Run Code Online (Sandbox Code Playgroud)

解析得很好。另一方面:

> parseTest numbers "1,2,3.4\n hundreds of lines without commas\nfinal line, with comma"
Run Code Online (Sandbox Code Playgroud)

给出文件末尾的句点和逗号的解析错误。

问题在于comma解析器用于sepBy确定逗号分隔的数字列表何时结束。如果解析器成功(它可以通过恢复来完成,吞噬数百行到下一个逗号),sepBy将尝试继续运行;如果解析器失败(最初失败,并且因为恢复代码在扫描整个文件后找不到逗号),sepBy则将完成。

最终,编写可恢复的解析器有点棘手。