我正在尝试使用pyparsing来解析用于DOT记录的标签。语法是:
rlabel → '{' field ( '|' field )* '}'
field → boxLabel | rlabel
boxLabel → [ ’<’ string ’>’ ] [ string ]
我有两个问题。一,我不知道如何处理boxLabel因为它有选项。二,我认为我有一个左递归,但因为没有“操作员”不知道如何处理它。 到目前为止我做了什么:
langle = pyparsing.Literal('<')
rangle = pyparsing.Literal('>')
string = pyparsing.Word('alphanums')
port_id = pyparsing.Group(langle + string + rangle)
port_name = pyparsing.Group(string)
box_label = pyparsing.Group(pyparsing.Optional(port_id) + pyparsing.Optional(port_name))
rlabel = pyparsing.Forward()
field = box_label | rlabel
rlabel_content = pyparsing.ZeroOrMore(field + "|") + field
rlabel << pyparsing.nestedExpr(opener='{', content=rlabel_content, closer='}')
解析进入无限递归。
此外,
>> print(box_label.parseString("<h> root"))
[[['<', 'h', '>']]]
我不知道为什么结果中不存在 port_name 解析。
答案 0 :(得分:0)
恭喜你在你的pyparsing项目上取得了这么大的进步,递归语法很难让你第一次做到这一点。
首先,这是一个小错字,但可能会让你感到困惑的是它会创建一个有效的解析器,但却是一个奇怪的解析器。
string = pyparsing.Word('alphanums')
应该是
string = pyparsing.Word(pyparsing.alphanums)
将字符串传递给Word
时,会定义解析此类字符组时要使用的有效字符列表。因此Word("0123456789")
将解析一个整数,Word("AB")
将解析由字母“A”和“B”组成的任何单词。 Word('alphanums')
将解析“alpha”,“nums”,“plums”,“haphalump”等许多单词,只要它们由“alphanums”中的字母组成即可。我很确定你想要pyparsing定义的字符串pyparsing.alphanums
,其中包括字符'A' - 'Z','a' - 'z'和'0' - '9'。
让我们先解决box_label
个问题。通常,您可以将“A和B或仅A或仅B”实现为:
A + Optional(B) | B
或者如果你想更明确,你可以这样做:
A + B | A | B
但是当你必须使用2个以上的表达式时,这开始变得毛茸茸。幸运的是,你只有2,我选择了下面提出的解决方案的第一种格式。
您在递归时遇到的部分问题是您正在使用Forward
和nestedExpr
- 通常只需要一个或另一个就足够了。但在你的情况下,使用Forward
是有利的,所以我将使用该方法。
(我也将pyparsing导入为pp
,以减少所有额外的字符。)
import pyparsing as pp
# suppress these punctuation marks - useful during parsing, but just in the way afterward
langle = pp.Literal('<').suppress()
rangle = pp.Literal('>').suppress()
lbrace = pp.Literal('{').suppress()
rbrace = pp.Literal('}').suppress()
string = pp.Word(pp.alphanums)
port_id = langle + string("port_id") + rangle
port_name = string("port_name")
rlabel = pp.Forward()
box_label = pp.Group(port_id + pp.Optional(port_name) | port_name)
field = box_label | rlabel
# use delimitedList(expr) in place of expr + ZeroOrMore(delim + expr)
rlabel <<= pp.Group(lbrace + pp.delimitedList(field, delim='|') + rbrace)
创建一组测试字符串,并使用它们调用runTests
以查看解析器的行为:
field.runTests("""\
<h> root
<h>
root
{ <h> root | { <h2> sub | <h2>sub2 }}
{ <h> root | { <h2> sub | <h2>sub2 } | sub3 }
""")
runTests
将尝试解析每个给定的行,然后转储结果,或显示解析异常:
<h> root
[['h', 'root']]
[0]:
['h', 'root']
- port_id: 'h'
- port_name: 'root'
<h>
[['h']]
[0]:
['h']
- port_id: 'h'
root
[['root']]
[0]:
['root']
- port_name: 'root'
{ <h> root | { <h2> sub | <h2>sub2 }}
[[['h', 'root'], [['h2', 'sub'], ['h2', 'sub2']]]]
[0]:
[['h', 'root'], [['h2', 'sub'], ['h2', 'sub2']]]
[0]:
['h', 'root']
- port_id: 'h'
- port_name: 'root'
[1]:
[['h2', 'sub'], ['h2', 'sub2']]
[0]:
['h2', 'sub']
- port_id: 'h2'
- port_name: 'sub'
[1]:
['h2', 'sub2']
- port_id: 'h2'
- port_name: 'sub2'
{ <h> root | { <h2> sub | <h2>sub2 } | sub3 }
[[['h', 'root'], [['h2', 'sub'], ['h2', 'sub2']], ['sub3']]]
[0]:
[['h', 'root'], [['h2', 'sub'], ['h2', 'sub2']], ['sub3']]
[0]:
['h', 'root']
- port_id: 'h'
- port_name: 'root'
[1]:
[['h2', 'sub'], ['h2', 'sub2']]
[0]:
['h2', 'sub']
- port_id: 'h2'
- port_name: 'sub'
[1]:
['h2', 'sub2']
- port_id: 'h2'
- port_name: 'sub2'
[2]:
['sub3']
- port_name: 'sub3'