如何编写一个 PEG 解析器,它完全使用任何和所有文本,同时仍然匹配其他给定规则?

问题描述

我正在开发一个应用程序,让没有经验的人更容易上手和用户友好地编写 (PEG) 解析器。是的,以前做过,但对我来说,这是一个很好的 GUI 学习经验。

使其平易近人的部分原因是用户无需担心他们的语法必须与整个文本匹配,他们应该能够提取有意义的数据而无需所有“样板”。

如何做到这一点?请看我下面的回答。或提供您自己的。

解决方法

这个问题困扰了我一个晚上的大部分时间,并且在网上找不到答案,所以我想分享一下。

我使用 parsimonious 库所拥有的 MRE。之所以有效,是因为 match 将匹配任何顶级用户定义的表达式,并且有一种回退可以匹配任何其他内容,遗憾的是一次只能匹配一个字符。

from parsimonious.grammar import Grammar

grammar = Grammar("""
root = (match / any)*
match = foo / bar # must include all top level user defined rules,but not their children (if any)
any = ~"."
foo = "foo expression" # user defined
bar = "bar expression" # user defined
""")

print(grammar.match("1 foo expression 2 bar expression 3"))

打印出来是正确的。

<Node called "root" matching "1 foo expression 2 bar expression 3">
    <Node matching "1">
        <RegexNode called "any" matching "1">
    <Node matching " ">
        <RegexNode called "any" matching " ">
    <Node matching "foo expression">
        <Node called "match" matching "foo expression">
            <Node called "foo" matching "foo expression">
    <Node matching " ">
        <RegexNode called "any" matching " ">
    <Node matching "2">
        <RegexNode called "any" matching "2">
    <Node matching " ">
        <RegexNode called "any" matching " ">
    <Node matching "bar expression">
        <Node called "match" matching "bar expression">
            <Node called "bar" matching "bar expression">
    <Node matching " ">
        <RegexNode called "any" matching " ">
    <Node matching "3">
        <RegexNode called "any" matching "3">

老实说,我觉得这不是很优雅,尤其是“any”和“root”如何匹配单个字符(我更喜欢将它们放在一起或完全省略),但这是我能做到的最好的做,如果它对任何人有用,那才是最重要的!


在 Parsimonious 自述文件中有这样的例子。

my_grammar = Grammar(r"""
    styled_text = bold_text / italic_text
    bold_text   = "((" text "))"
    italic_text = "''" text "''"
    text        = ~"[A-Z 0-9]*"i
    """)

对我来说,这表明有一种方法可以在我不知道的更大的文本主体(其中包含既不是粗体也不是斜体的文本)上使用它。除了在文档的每个位置上使用可选的“pos”(位置)参数进行解析/匹配之外,这也不优雅。

我从自述文件中看不到如何,如果有人知道“正确”的方式,请分享。