我不知道如何将表达式与 comma_separated_list 结合起来以匹配括号中的列表。以下不起作用,因为 csv 表达式吃掉了最后一个括号:
import pyparsing as pp
rule = "{" + pp.common.comma_separated_list + "}"
rule.parse_string("{something}")
# >>> ParseException: Expected '}', found end of text
列表中的值也可以包含括号,因此规则必须仅匹配外部值。我猜 [1:-1] 解决了它,但这不是我正在寻找的解决方案。
您需要引用以下字符串:
import pyparsing as pp
rule = "{" + pp.common.comma_separated_list + "}"
result = rule.parse_string("{\"something\",\"anything\"}")
print(result)
Pyparsing 的
comma_separated_list
是此类列表常见情况的便利助手,但它硬编码了它希望看到的列表元素。还有,你的这条评论:
列表中的值也可以包含括号
告诉我你实际上需要一个递归语法来解析 {} 中的 {}。
因此,与其使用
comma_separated_list
帮助程序(或其类似的帮助程序 nested_expr
),我将分步介绍这个表达式的定义。 (我们将使用delimited_list
,因为它允许我们为列表的元素定义一个表达式。)
编写新语法时我采取的第一步是为将要解析的标点符号定义常量。通常,标点符号在解析时很重要,但通常可以从实际解析结果中删除。
import pyparsing as pp
LBRACE, RBRACE = map(pp.Suppress, "{}")
# in pyparsing 3.1.0, you will be able to write this as
# LBRACE, RBRACE = Suppress.using_each("{}")
由于
rule
将是递归的,我们首先将其定义为 pyparsing Forward
。也就是说,我们是 forward-declaring 一个表达式,我们稍后会定义它的内容。这允许我们使用 rule
作为它自己定义的一部分。 (我还将对大多数这些表达式调用 set_name
,以便我们可以生成一些漂亮的铁路图。)
rule = pp.Forward().set_name("rule")
将成为规则元素的单词将是任何可打印(即非空格)字符,不包括我们需要用于包含规则和分隔列表的“{”、“}”和“,”字符。
wd_chars = pp.printables
wd = pp.Word(wd_chars, exclude_chars='{},').set_name("word")
要将
delimited_list
与自定义内容一起使用,我们需要一个 pyparsing 表达式来表示列表中的项目可能是什么。到目前为止,它们要么是 wd
表达式,要么是 rule
表达式。
content = wd | rule
所以现在我们准备好定义
rule
是什么了。我们不能使用普通的“=”运算符,因为那会取代我们用来定义 rule
的 content
。相反,我们使用“<<=" operator, as a way of "injecting" the grammar for the existing rule
表达式:
rule <<= pp.Group(LBRACE + pp.delimited_list(content) + RBRACE)
测试解析器的一种简单方法是使用
run_tests
。 run_tests
回显输入的测试字符串,然后列出已解析的项,或凸起的 ParseException
。事实上,如果你正在关注测试驱动开发,你可以从编写测试代码开始:
rule.run_tests("""
a simple test
a slightly more complicated test
... etc. ...
""")
然后在您要添加解析器的新方面时继续添加新测试。
run_tests
也接受评论,因此您可以在定义新测试字符串时标记您的测试。以下是按复杂程度递增顺序排列的一些测试:
rule.run_tests("""
# a rule with just one item
{something}
# a rule with multiple items
{thing1, thing2}
# a rule containing a simple embedded rule
{thing1, {thing2a}, thing3}
# a rule containing a rule with multiple items
{thing1, {thing2a, thing2b}, thing3}
# an empty rule
{}
""", full_dump=False)
打印:
# a rule with just one item
{something}
[['something']]
# a rule with multiple items
{thing1, thing2}
[['thing1', 'thing2']]
# a rule containing a simple embedded rule
{thing1, {thing2a}, thing3}
[['thing1', ['thing2a'], 'thing3']]
# a rule containing a rule with multiple items
{thing1, {thing2a, thing2b}, thing3}
[['thing1', ['thing2a', 'thing2b'], 'thing3']]
# an empty rule
{}
{}
^
ParseException: Expected rule, found '}' (at char 1), (line:1, col:2)
FAIL: Expected rule, found '}' (at char 1), (line:1, col:2)
有趣!如果不支持空规则,则只需删除最后一个测试。但是如果您希望能够解析这样的规则,那么我们需要稍微修改我们的解析器,以表明 {} 中的内容项列表是可选的:
rule <<= pp.Group(LBRACE + pp.Opt(pp.delimited_list(content)) + RBRACE)
# in pyparsing 3.1.0, you will be able to write this as
# rule <<= pp.Group(LBRACE + (pp.delimited_list(content) | "") + RBRACE)
这是 pyparsing 将为此解析器创建的铁路图,使用以下语句:
rule.create_diagram("rule_parser.html")
最后,如果我们需要创建一个包含逗号或大括号的规则项怎么办?或者可能是多个单词?期望这样的项目应该用引号括起来并不是不合理的。 Pyparsing 包括一些内置表达式,例如
dbl_quoted_string
表示包含在 "
字符中的字符串。如果我们需要,我们可以扩展content
的定义:
content = pp.dbl_quoted_string | wd | rule
修改了
content
的图表说明:
(一个读者学习的问题:为什么
dbl_quoted_string
表达式必须在wd
之前?)
并将此测试添加到我们对
run_tests
的调用中:
# a rule with embedded , and brace characters
{thing1, "tricky rule with characters like , { and }", thing2}
这增加了输出:
# a rule with embedded , and brace characters
{thing1, "tricky rule with characters like , { and }", thing2}
[['thing1', '"tricky rule with characters like , { and }"', 'thing2']]
我可能对你试图用你的解析器做什么做了一些不正确的假设,但希望从这个例子中你可以继续使用你自己的规则解析器版本。