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

标签 parsing haskell megaparsec

每个 megaparsec 文档,"Since version 8, reporting multiple parse errors at once has become much easier."我一直无法找到一个这样做的例子。我找到的唯一一个是 this .然而,它只显示了如何解析换行符分隔的玩具语言,也没有显示如何 combine multiple errors进入 ParseErrorBundle。此 SO discussion不是决定性的。

最佳答案

您要使用 withRecoveryregisterParseError 一起从 Megaparsec 生成的错误中恢复(或 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 ','

在良好的输入:
> 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成功申请numbercomma解析器以交替顺序解析 "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将完成。

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

关于parsing - 如何使用megaparsec报告多个错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59640023/

相关文章:

haskell - 可以在 Haskell 中以纯代码读取文件吗?

javascript - ECMA-262 和 ECMA-357 之间有语法差异吗?

python - 在 python 中解析 C++

haskell - LFSR 实现中的高效位摆弄

haskell - 递归到 Haskell 中尚不存在的函数

haskell - 如何使用 Parsec 解析三元表达式?

php - 解析包含分号的 SQL(在 PHP 中)

解析GF(2)中的代数表达式

haskell - 闭包是否违反了函数式编程范式?

performance - 按广度优先顺序列出目录的所有内容会导致效率低下