2014-05-24 14 views
7

Sto lottando per analizzare le strutture annidate con PyParsing. Ho cercato molti degli 'nested' example uses of PyParsing, ma non vedo come risolvere il mio problema.il trucco per nidificare le strutture nel pyparsing

Ecco ciò che la mia struttura interna si presenta come:

texture_unit optionalName 
{ 
    texture required_val 
    prop_name1 prop_val1 
    prop_name2 prop_val1 
} 

e qui è quello che la mia struttura esterna sembra, ma può contenere zero o più delle strutture interne.

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 
    } 
} 

Sto analizzando con successo la struttura interna. Ecco il mio codice per questo.

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() 

Ecco il mio tentativo di analizzare la struttura esterna,

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() 

quando dico: pass_.parseString (testPassStr)

vedo errori nella console che "}" è stato previsto.

Lo vedo molto simile allo C struct example, ma non sono sicuro di quale sia la magia mancante. Sono anche curioso di sapere come controllare la struttura dati risultante quando si utilizza lo nestedExpr.

+0

Ecco un altro esempio che supporta annidato strutture. Sembra che usi un 'pyparsing.Dict'. Tutti questi esempi mostrano un modo diverso per ottenere l'analisi annidata, qual è la comunanza? http://pyparsing.wikispaces.com/share/view/40834661 – cyrf

risposta

1

La risposta che cercavo è legato alla l'uso del parser 'Avanti', mostrato nell'esempio Cstruct (collegato in OP).

La parte difficile della definizione della grammatica per la struttura nidificata consiste nel definire tutti i possibili tipi di membro della struttura, che deve includere la struttura stessa, che non è ancora definita.

Il "trucco" per definire la grammatica pyparsing per una struttura annidata è quello di ritardare la definizione della struttura, ma includere un "in avanti ha dichiarato" versione della struttura al momento di definire i membri della struttura, in modo che i membri possono anche includere una struttura Quindi completa la grammatica della struttura come un elenco di membri.

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

Questo è anche discusso qui: Pyparsing: Parsing semi-JSON nested plaintext data to a list

Il PO (la mia) descrissero dati di prova e la grammatica che non era abbastanza specifico e abbinati quando dovrebbe hanno fallito. @NorthCat ha individuato correttamente le corrispondenze indesiderate nella grammatica. Tuttavia, il suggerimento di definire molti "aspetti negativi" sembravano ingestibili.

Invece di definire ciò che non dovrebbe corrispondere, la mia soluzione ha elencato esplicitamente le possibili corrispondenze. Le corrispondenze erano parole chiave membro, usando 'oneOf (' elenco di parole separate da spazio '). Una volta specificate tutte le possibili corrispondenze, mi sono reso conto che la mia struttura non era una struttura annidata, ma in realtà una struttura con profondità finita e grammatiche diverse descriveva ogni profondità. Quindi, la definizione del mio membro non richiedeva il trucco delle dichiarazioni in avanti.

Il terminatore delle definizioni del mio membro era diverso dall'esempio Cstruct. Invece di terminare con un ';' (punto e virgola) come in C++, le definizioni dei membri dovevano terminare alla fine della riga. In pyparsing, puoi specificare la fine della riga con parser 'LineEnd'. Così, ho definito mie membra come un elenco di valori non è compreso il 'LineEnd', come questo, si noti l'uso dell'operatore "Not" (~) nell'ultimo definizione:

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

ci sono due problemi:

  1. Nella tua grammatica hai segnato texture letterale come richiesto in texture_unit blocco, ma non c'è texture nel secondo esempio.
  2. Nel secondo esempio, pass_props_ coincide con texture_unit optionalName. Dopo di ciò, pp.Literal('}') si aspetta }, ma fornisce {. Questa è la ragione dell'errore.

Possiamo controllare cambiando il pass_ regola in questo modo:

pass_ << pp.Literal('pass').suppress() + pp.Optional(pp.Word(pp.alphanums+'_'+'.')).suppress() + \ 
      pp.Literal('{').suppress() + pass_props_ 

print pass_.parseString(s2) 

Dà seguiteci uscita:

[['prop_name', ['prop_val', 'prop_name', 'prop_val', 'texture_unit', 'optionalName']]] 

Possiamo vedere che pass_props_ coincide con texture_unit optionalName.
Quindi, cosa vogliamo fare: prop_ può contenere alphanums, _ e ., ma non può corrispondere con letterale texture_unit. Siamo in grado di farlo con regex e negative lookahead:

prop_ = pp.Group( pp.Regex(r'(?!texture_unit)[a-z0-9_]+')+ pp.Group(pp.OneOrMore(pp.Regex(r'(?!texture_unit)[a-z0-9_.]+')))) 

Infine, esempio di lavoro sarà simile a questa:

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) 

uscita:

[['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. Sei corretto nel mio esempio annidato mancava la proprietà 'texture' richiesta. Questo è stato un refuso durante la pubblicazione. Lo correggerò nel post. – cyrf

+0

@cyrf E il secondo oggetto e la soluzione per lui? – NorthCat

+0

circa # 2, grazie per l'ottimo suggerimento. Lo sto ancora testando. Sto cercando di capire perché il 'lookahead negativo' non era necessario nell'esempio di C Struct Parser, che supporta le strutture C annidate (collegate nel mio post originale). – cyrf