我有一个复杂的子 ParserElements 并像这样组合在一起,我计划添加更多。
Group(multi_line ^ macro_parser ^ numeric_assignment ^ assert_parser ^ db ^
charmap_parser ^ comment_parser ^
include_parser ^ label ^ expression ^ macro_call ^
newcharmap_parser ^ popc ^ pushc ^ redef ^ control ^ Literal("\n"))
我遇到了问题,其中一个子 ParserElements 在不应该匹配的情况下会匹配,并且我将得到一个 ParseException,它不告诉我哪个 ParserElement 引发了它。这意味着我必须四处寻找哪些表达式与哪些行匹配,并且需要花费大量时间。
有没有办法在出现异常时获取更多有用的信息?我尝试将 .setDebug() 附加到上面的解析器,但错误消息没有任何帮助。
为了帮助您的最终用户通过使用您的解析器逻辑来了解他们自己的解析失败,有用的事情是通过使用以下内容来增强您自己的 ParserElements 的错误输出:
.setName()
Python方法-
运算符,而不是 +
运算符。.setName()
.setName()
允许您 re 声明您的解析错误输出(用普通的八年级“英语”句子,或者最简单的 BNF,甚至助记符),尽管我经常使用多行 EBNF文本被一对三元组 ("""
) 包围。
.setName()
的默认值,您在Python赋值时定义为ParserElement......到该Python变量,是以文本形式显示其中使用的整个“PyParsing-ese”逻辑的字符串,这通常是对普通用户帮助不大。
每当最终用户遇到该变量中出现的
ParseExceptions
时,将显示该 PyParsing 变量的新的更简单的 .setName()
文本。
用
+
替换 SOME -
可以帮助您的最终用户(和您)更接近该违规角色的“更正确”位置偏移。
对语法错误有“更准确”的偏移量也有助于 parseException 选择“更正确”的 PyParsing 变量/组。使用正确的变量,其(更好选择的)
.setName()
输出将帮助最终用户能够更快地推断出他们(或您的)错误。
然后 parseException 会从包含相关 PyParsing 逻辑 (ParserElement) 的 Python 作业中输出新的
.setName()
文本字符串。
这是在发生
parseException
时输出简化的 EBNF 的示例:
uri = Literal('file:///') | Literal('https://')
uri.setName("""[
https://
| file:///
]"""
)
…
my_filepath = Optional(uri) - Optional(directory) - full_filename
my_filepath.setName('[ <uri> ] [ <directory-path> ] <full-filename>')
ls_command = Suppress(Literal('ls')) - my_filepath
ls_command.setName('ls <filepath>')
而不是在
parseException
时间看到很长的默认输出,但看起来很神秘:
{{{{{{{{{{{{{Suppress('ls') + {Optional( …
新的输出现在是
'ls <filepath>'
或
[ <uri> ] [ <directory-path> ] <full-filename>
或
[
https://
| file:///
]
取决于他们在哪里破坏了你的解析器逻辑。
使用
.setName()
/减号运算符二人组,我很少使用 PyParsing 调试器开销(如果有的话)。
当然,您的逻辑变得越复杂,就是开始向每个受影响的作业添加
.setName()
的好时机。
随着每个 .setName 添加,解决 parseException 的时间会变得越来越短……不仅对您(PyParsing 逻辑的开发人员)来说,而且对您的最终用户来说也是如此。
有一个项目在 PyParsing 中为
named.conf
ISC Bind9 DNS 服务器的 bind9_parser 配置文件编写了 4,000 多个逻辑。 Bind9_parse 很好地利用了这对组合。
bind9_parser 的最终用户更容易破译解析错误非常重要。