Pyparsing DOT记录标签(递归)
Pyparsing DOT record label (recursion)
我正在尝试使用 pyparsing 来解析用于 DOT 记录的标签。语法是:
rlabel → '{' field ( '|' field )* '}'
field → boxLabel | rlabel
boxLabel → [ ’<’ string ’>’ ] [ string ]
我有两个问题。第一,我不确定如何处理 boxLabel,因为它必须是可选项。第二,我想我有一个左递归但是因为没有 "operator" 不知道如何处理它。
到目前为止我做了什么:
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 解析没有出现在结果中。
恭喜您在 pyparsing 项目上取得了如此大的进步,递归语法在您第一次使用它们时很难理解。
首先,一个小错别字,但可能会让您感到困惑,因为它创建了一个有效的解析器,但却是一个奇怪的有限的解析器。
string = pyparsing.Word('alphanums')
应该是
string = pyparsing.Word(pyparsing.alphanums)
当您将字符串传递给 Word
时,它定义了解析此类字符组时要使用的有效字符列表。所以 Word("0123456789")
将解析一个整数,Word("AB")
将解析由字母 "A" 和 "B" 组成的任何单词。 Word('alphanums')
会解析"alpha"、"nums"、"plums"、"haphalump"等很多词,只要是由[=58=中的字母组成的].我很确定您想要 pyparsing 定义的字符串 pyparsing.alphanums
,其中包括字符 'A'-'Z'、'a'-'z' 和“0” -'9'.
让我们先解决 box_label
问题。通常,您可以将 "A and B or just A or just 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'
我正在尝试使用 pyparsing 来解析用于 DOT 记录的标签。语法是:
rlabel → '{' field ( '|' field )* '}'
field → boxLabel | rlabel
boxLabel → [ ’<’ string ’>’ ] [ string ]
我有两个问题。第一,我不确定如何处理 boxLabel,因为它必须是可选项。第二,我想我有一个左递归但是因为没有 "operator" 不知道如何处理它。 到目前为止我做了什么:
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 解析没有出现在结果中。
恭喜您在 pyparsing 项目上取得了如此大的进步,递归语法在您第一次使用它们时很难理解。
首先,一个小错别字,但可能会让您感到困惑,因为它创建了一个有效的解析器,但却是一个奇怪的有限的解析器。
string = pyparsing.Word('alphanums')
应该是
string = pyparsing.Word(pyparsing.alphanums)
当您将字符串传递给 Word
时,它定义了解析此类字符组时要使用的有效字符列表。所以 Word("0123456789")
将解析一个整数,Word("AB")
将解析由字母 "A" 和 "B" 组成的任何单词。 Word('alphanums')
会解析"alpha"、"nums"、"plums"、"haphalump"等很多词,只要是由[=58=中的字母组成的].我很确定您想要 pyparsing 定义的字符串 pyparsing.alphanums
,其中包括字符 'A'-'Z'、'a'-'z' 和“0” -'9'.
让我们先解决 box_label
问题。通常,您可以将 "A and B or just A or just 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'