Aufbau eines drei Schritte Parsers mit Spirit



  • Liebe Community,

    ich bin noch nicht sehr firm in boost allgemein und taste mich gerade an Spirit ran.

    Das Parsing-System soll folgendes beherrschen:

    Annahmen und Voraussetzungen:

    1.) Es gibt einen String, der völlig beliebig formatiert ist. z. B.

    Ich möchte 3 Nasenbären und 2 Portionen Koks.
    

    2.) Es gibt einen Definitions-String, der folgendermaßen aussehen kann:

    Ich möchte DATA_1 Nasenbären und DATA_2 Portionen Koks.{GetCoati(DATA_1);GetGak(DATA_2);}
    

    3.) Es gibt simpel geskriptete Funktionsaufrufe (kein C/C++), die so aussehen:

    {GetCoati(3);GetGak(2);}
    

    4.) Im 1. String dürfen keine {} vorkommen und das Wort DATA_ darf nicht vorkommen.

    5.) DATA_x Elemente können ein x von 0 bis 9 haben, DATA_x Elemente ohne x sind auch erlaubt, können aber in den in {} gesetzten Funktionsaufrufen nicht verwendet werden. DATA_/DATA_x Elemente dürfen nicht direkt aufeinander folgen.

    Mein Ansatz für den Aufbau mit Spirit:

    Parser I: Soll einen Definitions-String wie in 2. parsen, und einen neuen Parser (Parser II) zurückgeben.

    Parser II: Soll einen String wie in 1. Parsen, und daraus einen String mit Funktionsaufrufen wie in 3. generieren.

    Parser III: Soll einen String wie in 3. parsen, und programmintern dann die korrekten Funktionen mit den korrekten Parametern aufrufen.

    Ist das ein solider Ansatz oder völliger Blödsinn? Ich muss sagen, ich habe das ganze schon realisiert (ohne Spirit oder Regex), allerdings mit übelstem String-Gefrickel. Es läuft einwandfrei, nur fühle ich mich, seitdem ich in der Spirit Doku gewühlt habe, unglaublich gefordert das auch hinsichtlich der späteren Ausbaufähigkeit nochmal ordentlich zu machen.

    Allerdings kommt mir Spirit wie eine neue Programmiersprache vor, was auch ganz cool ist, und ich habe bereits mit der Umsetzung begonnen, und denke mir manchmal: Das muss doch auch einfacher gehen... Oder nicht?



  • Ich wär ehrlich gesagt geneigt, das alles als Blödsinn zu bezeichnen, aber ich halt mich zurück 🙂

    Ich würd meinen, da reicht Regex. Ich weiß nicht, was man von boost::spirit halten soll, habs noch nie verwendet. Hattest du eine formale Sprachen und Compilerbau Vorlesung? Ist dir das generelle Konzept von Parsern klar? Dann sollte spirit nicht so schwer sein, wenn du das lernen willst. Ich wollte das nie. Lohnt sich nicht, finde ich, und ich finds auch schlecht lesbar/nachvollziehbar. Für größere Parser nehm ich ANTRL, für kleinere Sachen schreib ich meist einen Lexer und einen Recursive Descent Parser von Hand, das bin ich gewohnt, das kann ich schnell schreiben und lesen.



  • Für die Frage, ob Regexes ausreichen, ist entscheidend, ob Sätze wie

    Ich möchte DATA_1 Nasenbären und DATA_1 Portionen Koks, also für jeden eine.{foo(DATA_1)}

    erlaubt sein sollen, also ob DATA_-Bezeichner wiederverwendet werden dürfen - in dem Fall wäre das keine reguläre Sprache mehr.

    Ansonsten stellt sich die Frage, ob man irgendwie eingrenzen kann, was als Wert für DATA_x erlaubt ist. Kompliziert werden Fälle wie

    Ich möchte Ich möchte 3 Nasenbären und 2 Portionen Koks. und 2 Portionen Koks. Portionen Koks.

    D.h. mit DATA_1 = "Ich möchte 3 Nasenbären und 2 Portionen Koks." und DATA_2 = "2 Portionen Koks.", also Fälle, in denen mehrere Substrings gematcht werden können.



  • Hi,
    vielen Dank für die Antworten.

    Was hier programmiert werden soll, ist vielleicht noch nicht ganz klar geworden. Dass es auch ohne Spirit geht, ist natürlich klar, schließlich habe ich das, wie gesagt, schon realisiert.

    Stellt Euch vor, es gibt verschiedene Arten von Webservern, die alle das gleiche machen, aber es verschieden loggen.

    Ein String wie String 1 wäre ein Beispiel für eine Zeile so eines Logs.

    Da die Server alle das gleiche machen, und unser Programm diese Daten in einer einheitlichen Tabelle speichern sollen, ihre Logs aber verschiedenartig sind, gibt es das DATA_ System. Das ist so gedacht, dass der User das Programm auch auf einen Server anwenden kann, den das Programm nicht kennt. Mithilfe einer Datei aus Strings wie String 2 kann es sich dann den nötigen Parser für die Logfile generieren, indem es sie in die Sprache des String 3 übersetzt, die es beherrscht.

    Prinzipiell werden alle DATA_ Begriffe als String geparsed und als solche auch in die in Sprache 3 benannten Teile eingefügt.

    Das ist aber hier auch nicht relevant.

    Was ich vielmehr wissen will ist, ob das ein sinnvoller Aufbau mit Spirit ist, auch nicht ob das ohne Spirit geht. Sondern ob ich hier nicht mit Karma oder Lexer in einzelnen Teilen besser bedient wäre.

    Man findet ja leider wenig zu Spirit im Internet, vermutlich auch, weil kaum jemand die Zeit hat, sich damit zu befassen und sich eben lieber schnell selbst was bastelt.



  • So, ich lass erstmal die Pfoten von Spirit. Dazu drei Begründungen, die jeder, der das zu benutzen gedenkt, vorher einmal überdenken sollte:

    1. So genial der modulare Aufbau auch sein mag, das Verhalten des zusammensetzens einzelner Objekte und ihre Rückgabewerte, sind meiner Meinung nach nicht ausreichend mit Beispielen dokumentiert. An sich ist der Aufbau der Dokumentation super, gerade im Einführungsbereich, viele Librarydokus können sich da scheibenweise was von abschneiden, aber je weiter es in die Materie geht, wird es dünner. Ich habe mir wirklich Mühe gemacht, aber z. B. noch nicht herausgefunden, warum eine Rule, die aus zwei Rules, die jeweils einen Vektor zurückgeben, nicht in ein pair<vector, vector> passen mag, wohl aber in eine mit BOOST_FUSION_ADAPT_STRUCT gebaute struct. Wenn man Phoenix, Lambda und Fusion King ist, hat man es sicher etwas leichter.

    2. Das Kompilieren einer simplen Grammar dauert auf meinem High-End Rechner in Visual-Studio 2010 knapp eine halbe Minute. Schnell mal eine Zeile abändern und experimientieren ist nicht.

    3. Dadurch, dass Spirit mit komplex verschachtelten Templates funktioniert, sind Compiler-Errors idR höchst kryptisch. Mit der Zeit kriegt man zwar ein Gefühl dafür, worans liegen kann, da sich die Meldungen aber nie auf Stellen im eigenen Code beziehen, sollte man vor dem Kompilieren nicht zuviele Änderungen machen. Letzteres dauert dann wieder 30 sec.

    4. Debugging in Runtime muss man gewissermaßen mit ein paar Hilfsfunktionen für jede Regel selbst implementieren.

    Wenn man etwas simples wie im Beispiel oben machen will, geht das mit Regex einfach fixer.

    Ich hab trotzdem nach wie vor Bock auf Spirit, denn ich kann mir vorstellen, dass es ab einem gewissen Level an Erfahrung darin einfach wahnsinnig Spaß macht. Man sollte die Library und Phoenix aber mehr oder weniger auswendig können.


Log in to reply