在pyparsing中嵌套结构的技巧

cyr*_*yrf 8 python nested pyparsing

我正在努力用PyParsing解析嵌套结构.我已经搜索了许多PyParsing'嵌套'示例用法,但我没有看到如何解决我的问题.

这是我的内部结构:

texture_unit optionalName
{
    texture required_val
    prop_name1 prop_val1
    prop_name2 prop_val1
}
Run Code Online (Sandbox Code Playgroud)

这是我的外部结构的样子,但它可以包含零个或多个内部结构.

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
    }
}
Run Code Online (Sandbox Code Playgroud)

我成功地解析了内部结构.这是我的代码.

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()
Run Code Online (Sandbox Code Playgroud)

这是我尝试解析外部结构,

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()
Run Code Online (Sandbox Code Playgroud)

当我说:pass_.parseString(testPassStr)

我在控制台中看到"}"出现错误.

我认为这与C struct示例非常相似,但我不确定缺少什么魔法.我也很好奇如何在使用nestedExpr时控制结果数据结构.

cyr*_*yrf 1

我正在寻找的答案与“Forward”解析器的使用有关,如 Cstruct 示例中所示(OP 中链接)。

定义嵌套结构语法的难点是定义该结构的所有可能的成员类型,这需要包括结构本身,而结构本身尚未定义。

为嵌套结构定义 pyparsing 语法的“技巧”是延迟结构的定义,但在定义结构成员时包含结构的“前向声明”版本,因此成员也可以包含结构。然后将结构语法完成为成员列表。

struct = Forward()
member = blah | blah2 | struct
struct << ZeroOrMore( Group(member) )
Run Code Online (Sandbox Code Playgroud)

这也在这里讨论: Pyparsing: Parsing semi-JSONnested plaintext data to a list

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))
Run Code Online (Sandbox Code Playgroud)