2014-05-24 55 views
7

我很努力用PyParsing分析嵌套結構。我搜索了很多'nested' example uses of 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 struct example非常相似,但我不確定什麼是缺少的魔法。我也很好奇如何在使用nestedExpr時控制結果數據結構。

+0

這裏是另一個支持嵌套結構。它看起來像使用'pyparsing.Dict'。所有這些例子都顯示了實現嵌套解析的不同方式,通用性是什麼? http://pyparsing.wikispaces.com/share/view/40834661 – cyrf

回答

1

我一直在尋找的答案有關使用'Forward'分析器,如Cstruct示例所示(在OP中鏈接)。

爲嵌套結構定義語法的難點在於定義結構的所有可能的成員類型,該類型需要包含結構本身,但仍未定義。

爲嵌套結構定義pyparsing語法的「訣竅」是在定義結構成員時延遲結構的定義,但包括結構的「前向聲明」版本,因此成員還可以包含一個結構。然後將結構語法作爲成員列表來完成。

struct = Forward() 
member = blah | blah2 | struct 
struct << ZeroOrMore(Group(member)) 

這也是在這裏討論: Pyparsing: Parsing semi-JSON nested plaintext data to a list

的OP(礦)中描述的測試數據和語法是不夠具體,當它竟然沒有匹配。 @NorthCat正確地在文法中發現了不需要的匹配。但是,定義許多「負面預測」的建議似乎難以管理。

而不是定義什麼不應該匹配,我的解決方案,而是明確列出可能的匹配。這些匹配是成員關鍵字,使用'oneOf('用空格分隔的單詞列表')。一旦我指定了所有可能的匹配,我意識到我的結構不是嵌套結構,但實際上深度有限和不同語法的結構描述了每個深度。所以,我的成員定義不需要前向聲明技巧。

我的成員定義的終止符與Cstruct示例中的不同。而不是終止於';' (分號)就像在C++中一樣,我的成員定義需要在行尾結束。在pyparsing中,可以使用'LineEnd'分析器指定行的末尾。因此,我將我的成員定義爲不包含'LineEnd'的值列表,請注意在最後定義中使用「Not」(〜)運算符:

EOL = LineEnd().suppress() 
ident = Word(alphas+"_", alphanums+"[email protected]#.") 
integer = Word(nums) 
real = Combine(Optional(oneOf('+ -')) + Word(nums) + '.' + Optional(Word(nums))) 
propVal = real | integer | ident 
propList = Group(OneOrMore(~EOL + propVal)) 
3

有兩個問題:

  1. 在你的語法您標記texturetexture_unit塊所需的文字,但沒有texture在你的第二個例子。
  2. 在第二個示例中,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字面匹配。我們可以regexnegative lookahead做到這一點:

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']]] 
+0

1.你是對的,我的嵌套示例缺少所需的'紋理'屬性。發佈時這是一個錯字。我會在帖子中糾正它。 – cyrf

+0

@cyrf第二項和他的解決方案呢?關於#2的 – NorthCat

+0

,謝謝你的好建議。我仍在測試它。我試圖理解爲什麼在C Struct Parser示例中不需要'負向預測',它支持嵌套的C結構(在我的原始文章中鏈接)。 – cyrf