在pyparsing中嵌套结构的技巧
我正在努力用PyParsing解析嵌套结构。 我搜索了很多PyParsing的'嵌套'示例用法,但我没有看到如何解决我的问题。
以下是我的内部结构:
texture_unit optionalName
{
texture required_val
prop_name1 prop_val1
prop_name2 prop_val1
}
这里是我的外部结构看起来像,但它可以包含零个或更多的内部结构。
pass optionalName
{
prop_name1 prop_val1
prop_name2 prop_val1
texture_unit optionalName
{
// edit 2: showing use of '.' character in value
texture required_val.file.name optional_val // edit 1: forgot this line in initial post.
// edit 2: showing potentially multiple values
prop_name3 prop_val1 prop_val2
prop_name4 prop_val1
}
}
我正在成功解析内部结构。 这是我的代码。
prop_ = pp.Group(pp.Word(pp.alphanums+'_')+pp.Group(pp.OneOrMore(pp.Word(pp.alphanums+'_'+'.'))))
texture_props_ = pp.Group(pp.Literal('texture') + pp.Word(pp.alphanums+'_'+'.')) + pp.ZeroOrMore(prop_)
texture_ = pp.Forward()
texture_ << pp.Literal('texture_unit').suppress() + pp.Optional(pp.Word(pp.alphanums+'_')).suppress() + pp.Literal('{').suppress() + texture_props_ + pp.Literal('}').suppress()
这是我尝试解析外部结构,
pass_props_ = pp.ZeroOrMore(prop_)
pass_ = pp.Forward()
pass_ << pp.Literal('pass').suppress() + pp.Optional(pp.Word(pp.alphanums+'_'+'.')).suppress() + pp.Literal('{').suppress() + pass_props_ + pp.ZeroOrMore(texture_) + pp.Literal('}').suppress()
当我说:pass_.parseString(testPassStr)
我在控制台中看到“}”预期的错误。
我认为这与C结构示例非常相似,但我不确定什么是缺少的魔法。 我也很好奇如何在使用nestedExpr时控制结果数据结构。
有两个问题:
texture_unit
块的要求标记了texture
文字,但在第二个例子中没有texture
。 pass_props_
与texture_unit optionalName
一致。 在它之后, pp.Literal('}')
期望}
,但给出{
。 这是错误的原因。 我们可以通过像这样更改pass_
规则来检查它:
pass_ << pp.Literal('pass').suppress() + pp.Optional(pp.Word(pp.alphanums+'_'+'.')).suppress() +
pp.Literal('{').suppress() + pass_props_
print pass_.parseString(s2)
它让我们跟随输出:
[['prop_name', ['prop_val', 'prop_name', 'prop_val', 'texture_unit', 'optionalName']]]
我们可以看到pass_props_
与texture_unit optionalName
一致。
所以,我们想要做的是: prop_
可以包含alphanums
, _
和.
,但不能与texture_unit
文字匹配。 我们可以用regex
和负面看法来做到这一点:
prop_ = pp.Group( pp.Regex(r'(?!texture_unit)[a-z0-9_]+')+ pp.Group(pp.OneOrMore(pp.Regex(r'(?!texture_unit)[a-z0-9_.]+'))) )
最后,工作示例如下所示:
import pyparsing as pp
s1 = '''texture_unit optionalName
{
texture required_val
prop_name prop_val
prop_name prop_val
}'''
prop_ = pp.Group( pp.Regex(r'(?!texture_unit)[a-z0-9_]+')+ pp.Group(pp.OneOrMore(pp.Regex(r'(?!texture_unit)[a-z0-9_.]+'))) )
texture_props_ = pp.Group(pp.Literal('texture') + pp.Word(pp.alphanums+'_'+'.')) + pp.ZeroOrMore(prop_)
texture_ = pp.Forward()
texture_ = pp.Literal('texture_unit').suppress() + pp.Word(pp.alphanums+'_').suppress() +
pp.Literal('{').suppress() + pp.Optional(texture_props_) + pp.Literal('}').suppress()
print texture_.parseString(s1)
s2 = '''pass optionalName
{
prop_name1 prop_val1.name
texture_unit optionalName1
{
texture required_val1
prop_name2 prop_val12
prop_name3 prop_val13
}
texture_unit optionalName2
{
texture required_va2l
prop_name2 prop_val22
prop_name3 prop_val23
}
}'''
pass_props_ = pp.ZeroOrMore(prop_ )
pass_ = pp.Forward()
pass_ = pp.Literal('pass').suppress() + pp.Optional(pp.Word(pp.alphanums+'_'+'.')).suppress() +
pp.Literal('{').suppress() + pass_props_ + pp.ZeroOrMore(texture_ ) + pp.Literal('}').suppress()
print pass_.parseString(s2)
输出:
[['texture', 'required_val'], ['prop_name', ['prop_val', 'prop_name', 'prop_val']]]
[['prop_name1', ['prop_val1.name']], ['texture', 'required_val1'], ['prop_name2', ['prop_val12', 'prop_name3', 'prop_val13']], ['texture', 'required_va2l'], ['prop_name2', ['prop_val22', 'prop_name3', 'prop_val23']]]
我正在寻找的答案与使用“Forward”分析器相关,如Cstruct示例中所示(在OP中链接)。
为嵌套结构定义语法的难点在于定义结构的所有可能的成员类型,它需要包含结构本身,但仍未定义。
为嵌套结构定义pyparsing语法的“诀窍”是延迟结构的定义,但在定义结构成员时包含结构的“前向声明”版本,因此成员还可以包含结构。 然后将结构语法作为成员列表来完成。
struct = Forward()
member = blah | blah2 | struct
struct << ZeroOrMore( Group(member) )
这也在这里讨论:Pyparsing:将半JSON嵌套明文数据解析为列表
OP(我的)描述的测试数据和语法不够具体,并且在应该失败时匹配。 @NorthCat正确地在文法中发现了不需要的匹配。 但是,定义许多“负面预测”的建议似乎难以管理。
我的解决方案并没有明确列出可能的匹配,而是定义了不匹配的内容。 这些匹配是成员关键字,使用'oneOf('用空格分隔的单词列表')。 一旦我指定了所有可能的匹配,我意识到我的结构不是嵌套结构,但实际上深度有限和不同语法的结构描述了每个深度。 所以,我的成员定义不需要前向声明技巧。
我的成员定义的终止符与Cstruct示例中的不同。 而不是终止于';' (分号)就像在C ++中一样,我的成员定义需要在行尾结束。 在pyparsing中,可以使用'LineEnd'分析器指定行的末尾。 因此,我将我的成员定义为不包含'LineEnd'的值列表,如下所示,请注意在最后定义中使用“Not”(〜)运算符:
EOL = LineEnd().suppress()
ident = Word( alphas+"_", alphanums+"_$@#." )
integer = Word(nums)
real = Combine(Optional(oneOf('+ -')) + Word(nums) + '.' + Optional(Word(nums)))
propVal = real | integer | ident
propList = Group(OneOrMore(~EOL + propVal))
链接地址: http://www.djcxy.com/p/79281.html
上一篇: the trick to nested structures in pyparsing
下一篇: how to search json