2016-04-10 24 views
2

Ich baue einen Parser für das IBM Rhapsody sbs Dateiformat. Aber leider funktioniert der Rekursionsteil nicht wie erwartet. Die Regel pp.Word(pp.printables + " ") ist wahrscheinlich das Problem, da es auch ; und {} übereinstimmt. Aber mindestens ; kann auch Teil der Werte sein.pyparsing Rekursion der Werteliste (ibm Rhapsody)

import pyparsing as pp 
import pprint 


TEST = r"""{ foo 
    - key = bla; 
    - value = 1243; 1233; 1235; 
    - _hans = "hammer 
    time"; 
    - HaMer = 765; 786; 890; 
    - value = " 
    #pragma LINK_INFO DERIVATIVE \"mc9s12xs256\" 
     "; 
    - _mText = 12.11.2015::13:20:0; 
    - value = "war"; "fist"; 
    - _obacht = "fish,car,button"; 
    - _id = gibml c0d8-4535-898f-968362779e07; 
    - bam = { boing 
     - key = bla; 
    } 
    { boing 
     - key = bla; 
    } 
} 
""" 


def flat(loc, toks): 
    if len(toks[0]) == 1: 
     return toks[0][0] 

assignment = pp.Suppress("-") + pp.Word(pp.alphanums + "_") + pp.Suppress("=") 

value = pp.OneOrMore(
    pp.Group(assignment + (
     pp.Group(pp.OneOrMore(
      pp.QuotedString('"', escChar="\\", multiline=True) + 
      pp.Suppress(";"))).setParseAction(flat) | 
     pp.Word(pp.alphas) + pp.Suppress(";") | 
     pp.Word(pp.printables + " ") 
    )) 
) 

expr = pp.Forward() 

expr = pp.Suppress("{") + pp.Word(pp.alphas) + (
    value | (assignment + expr) | expr 
) + pp.Suppress("}") 
expr = expr.ignore(pp.pythonStyleComment) 


print TEST 
pprint.pprint(expr.parseString(TEST).asList()) 

Ausgang:

% python prase.py              
{ foo 
    - key = bla; 
    - value = 1243; 1233; 1235; 
    - _hans = "hammer 
    time"; 
    - HaMer = 765; 786; 890; 
    - value = " 
    #pragma LINK_INFO DERIVATIVE \"mc9s12xs256\" 
     "; 
    - _mText = 12.11.2015::13:20:0; 
    - value = "war"; "fist"; 
    - _obacht = "fish,car,button"; 
    - _id = gibml c0d8-4535-898f-968362779e07; 
    - bam = { boing 
     - key = bla; 
    } 
    { boing 
     - key = bla; 
    } 
} 

['foo', 
['key', 'bla'], 
['value', '1243; 1233; 1235;'], 
['_hans', 'hammer\n time'], 
['HaMer', '765; 786; 890;'], 
['value', '\n #pragma LINK_INFO DERIVATIVE "mc9s12xs256"\n  '], 
['_mText', '12.11.2015::13:20:0;'], 
['value', ['war', 'fist']], 
['_obacht', 'fish,car,button'], 
['_id', 'gibml c0d8-4535-898f-968362779e07;'], 
['bam', '{ boing'], 
['key', 'bla']] 
+0

Gibt es einen Tippfehler in TEST? Sollte die letzte Gruppe nach '- bam {boing usw.}' '' 'etwas = {boing \ n- key = bla; } '? Es ist schwer zu sehen, was dieses Format sein soll, du hast hier und da verschiedene OneOrMore. Ich denke, wenn Sie aufhörten und zuerst ein BNF schrieben, würden die Dinge klarer sein. – PaulMcG

+0

Auch ich rate dringend von Ausdrücken, die zu viel passen, wie 'pp.Word (printables + '')' - lesen Sie auf die neueste Version von Pyparssing Word-Klasse, die das 'excludeChars' Argument enthält, so dass, wenn Sie wirklich brauchen etwas wie "Wort (alles außer ';' ')", dann schreibe' Word (Ausdrucke, excludeChars = ';') '. – PaulMcG

+0

Leider ist das Format richtig. Ein echtes Beispiel https://github.com/mansam/exploring-rhapsody/blob/master/LightSwitch/LightSwitch.rpy – delijati

Antwort

2

Wow, das ist ein unordentlich Format! Ich denke, das wird dich nahe bringen. Ich begann damit zu charakterisieren, was ein gültiger Wertausdruck sein könnte. Ich sah, dass jede Gruppierung ";" - abgeschlossene Attributdefinitionen oder "{}" - eingeschlossene verschachtelte Objekte enthalten kann. Jedes Objekt enthielt eine führende Kennung, die den Objekttyp angibt.

Das schwierige Problem war das sehr allgemeine Token, das ich 'value_word' nannte, was so ziemlich jede Gruppierung von Zeichen ist, solange es kein '-', '{' oder '}' ist. Die negativen Lookaheads in der Definition von 'value_word' kümmern sich darum. Ich denke, ein Schlüsselproblem hier ist, dass ich nicht '' als ein gültiges Zeichen in einem 'value_word' enthalten konnte, sondern stattdessen pyparsing seine Standard-Whitespace-Skipping mit einem Potenzial für eine oder mehrere 'value_word's make-up machen lassen ein 'attr_value'.

Der letzte Kicker (nicht im Testfall gefunden, aber in dem Beispiel verknüpfen Du) war diese Zeile für ein Attribut ‚Zuordnung‘:

  - m_pParent = ; 

So hatte die attr_value für eine leere Zeichenfolge zu ermöglichen ebenfalls.

from pyparsing import * 

LBRACE,RBRACE,SEMI,EQ,DASH = map(Suppress,"{};=-") 

ident = Word(alphas + '_', alphanums+'_').setName("ident") 
guid = Group('GUID' + Combine(Word(hexnums)+('-'+Word(hexnums))*4)) 
qs = QuotedString('"', escChar="\\", multiline=True) 
character_literal = Combine("'" + oneOf(list(printables+' ')) + "'") 
value_word = ~DASH + ~LBRACE + ~RBRACE + Word(printables, excludeChars=';').setName("value_word") 

value_atom = guid | qs | character_literal | value_word 

object_ = Forward() 

attr_value = OneOrMore(object_) | Optional(delimitedList(Group(value_atom+OneOrMore(value_atom))|value_atom, ';')) + SEMI 
attr_value.setName("attr_value") 
attr_defn = Group(DASH + ident("name") + EQ + Group(attr_value)("value")) 
attr_defn.setName("attr_defn") 

object_ <<= Group(
    LBRACE + ident("type") + 
    Group(ZeroOrMore(attr_defn | object_))("attributes") + 
    RBRACE 
    ) 

object_.parseString(TEST).pprint() 

Für Ihre Test-String gibt es:

[['foo', 
    [['key', ['bla']], 
    ['value', ['1243', '1233', '1235']], 
    ['_hans', ['hammer\n time']], 
    ['HaMer', ['765', '786', '890']], 
    ['value', ['\n #pragma LINK_INFO DERIVATIVE "mc9s12xs256"\n  ']], 
    ['_mText', ['12.11.2015::13:20:0']], 
    ['value', ['war', 'fist']], 
    ['_obacht', ['fish,car,button']], 
    ['_id', [['gibml', 'c0d8-4535-898f-968362779e07']]], 
    ['bam', [['boing', [['key', ['bla']]]], ['boing', [['key', ['bla']]]]]]]]] 

I Ergebnisse Namen hinzugefügt, die bei der Verarbeitung dieser Strukturen helfen könnte. object_.parseString(TEST).dump() Mit gibt diese Ausgabe:

[['foo', [['key', ['bla']], ['value', ['1243', '1233', '1235']], ['_hans', ['hammer\n time']], ... 
[0]: 
    ['foo', [['key', ['bla']], ['value', ['1243', '1233', '1235']], ['_hans', ['hammer\n time']], ... 
    - attributes: [['key', ['bla']], ['value', ['1243', '1233', '1235']], ['_hans', ['hammer... 
    [0]: 
     ['key', ['bla']] 
     - name: key 
     - value: ['bla'] 
    [1]: 
     ['value', ['1243', '1233', '1235']] 
     - name: value 
     - value: ['1243', '1233', '1235'] 
    [2]: 
     ['_hans', ['hammer\n time']] 
     - name: _hans 
     - value: ['hammer\n time'] 
    [3]: 
     ['HaMer', ['765', '786', '890']] 
     - name: HaMer 
     - value: ['765', '786', '890'] 
    [4]: 
     ['value', ['\n #pragma LINK_INFO DERIVATIVE "mc9s12xs256"\n  ']] 
     - name: value 
     - value: ['\n #pragma LINK_INFO DERIVATIVE "mc9s12xs256"\n  '] 
    [5]: 
     ['_mText', ['12.11.2015::13:20:0']] 
     - name: _mText 
     - value: ['12.11.2015::13:20:0'] 
    [6]: 
     ['value', ['war', 'fist']] 
     - name: value 
     - value: ['war', 'fist'] 
    [7]: 
     ['_obacht', ['fish,car,button']] 
     - name: _obacht 
     - value: ['fish,car,button'] 
    [8]: 
     ['_id', [['gibml', 'c0d8-4535-898f-968362779e07']]] 
     - name: _id 
     - value: [['gibml', 'c0d8-4535-898f-968362779e07']] 
     [0]: 
      ['gibml', 'c0d8-4535-898f-968362779e07'] 
    [9]: 
     ['bam', [['boing', [['key', ['bla']]]], ['boing', [['key', ['bla']]]]]] 
     - name: bam 
     - value: [['boing', [['key', ['bla']]]], ['boing', [['key', ['bla']]]]] 
     [0]: 
      ['boing', [['key', ['bla']]]] 
      - attributes: [['key', ['bla']]] 
      [0]: 
       ['key', ['bla']] 
       - name: key 
       - value: ['bla'] 
      - type: boing 
     [1]: 
      ['boing', [['key', ['bla']]]] 
      - attributes: [['key', ['bla']]] 
      [0]: 
       ['key', ['bla']] 
       - name: key 
       - value: ['bla'] 
      - type: boing 
    - type: foo 

Es ist auch erfolgreich das verknüpfte Beispiel parst, sobald die führende Versionslinie entfernt wird.

+0

Nizza. Ich fügte ein Handling für 'negative Nummer',' Datum' und den führenden 'Header' hinzu, aber es funktionierte großartig. Ich war mir des "NotAny (~)" - Operators nicht bewusst, aber um es zusammenzufassen, es ist alles eine "abgegrenzte Liste" oder ein "Objekt". Ich habe es auf einem größeren (700kB) Modell versucht und es dauerte 25s, um es zu analysieren. Ich habe deinen Repo gegabelt und ich spiele mit Cython herum. Für jetzt habe ich es auf 18s runter. Ich poste mein Ergebnis in diesem Repo https://github.com/delijati/pyparsing – delijati

+0

Bevor Sie zu weit in einem Forking oder Cython Straße gehen, versuchen Sie, Packat Parsing zu aktivieren. Es hilft nicht immer, aber für einige rekursive Grammatiken können die Ergebnisse 10-100X betragen. – PaulMcG

+0

Versucht pp.enablePackrat() 'aber der Speicherverbrauch ging \t durch das Dach bis zu 4x. Ich habe gelesen, dass das Aufrufen von 'resetCache' hilft. http://stackoverflow.com/questions/26591485/incremental-but-complete-parsing-with-pyparsing. Vielleicht könnte es auch funktionieren, den Cache durch 'lru_cache' zu ​​ersetzen, der eine' maxsize' hat. – delijati