[X] Boost::Spirit - Eine Einführung



  • 1 Grundlegendes
    Dieser Artikel soll Sie in die Welt von Boost::Spirit einführen. Boost::Spirit
    ist ein Parser Gernerator Framework, dass in C++ realisiert ist. Um die
    folgenden Beispiele complieren zu können ist Boost::Spirit ab der Version
    1.6.3 zu verwenden. Desweiteren wird DEV-C++ in der Version 4.9.9.2 empfohlen.

    1.1 Von Datein und anderen zu durchsuchenden Strukturen
    Ein klassisches Beispiel für die Verwendung eines Parsers ist die
    Dateiverarbeitung. Wie oft kommt es vor, dass man ein bestimmtes Format oder
    eine bestimmte Struktur zu durchsuchen hat und keine geeigneten Klassen oder
    Strukturen zur verfügung stehen.

    XML Dateien werden etwa anders verarbeitet als
    INI Dateien. Mathematische Eingaben sollen anders interpretiert werden als
    bestimmte Stringfolgen. All dass kennt man mittlerweile als Programmierer
    zur Genüge.

    Auf diesem Gebiet hat Boost::Spirit seine unverkennbaren Stärken. Durch die
    einfache Definition von Regeln ist in null kommma nichts ein Parser entwicklet,
    der die Arbeit von alleine erledigt. Ob dabei Zeilenweise durch eine Datei
    geparst wird, oder Mathematische Ausdrücke geparst werden spielt dabei
    keine große Rolle. Boost::Spirit kann in diese Richtung fast alles handhaben.

    2 Installation von Boost::Spirit
    Um das Framework von Boost::Spirit nutzen zu können ist es erforderlich, die
    Header Dateien bekannt zu machen. Am einfachsten ist es den Ordner boost
    aus dem Verzeichnis spirit-1.6.3\boost sowie aus dem Verzeichnis
    spirit-1.6.3\boost\miniboost in das Include Verzeichnis des
    Compiliers/der Entwicklungsumgebung zu kopieren.

    In ein Projekt müssen unbedingt folgende Zeilen eingebunden werden.

    // ...
    #include <boost/spirit.hpp>
    /*****************************************************************************/
    /* Die obige Include Zeile ist nur zu verwenden, falls die Includedateien    */
    /* in das Includeverzeichnis des Compiliers kopiert wurden... Falls sie sich */
    /* im Projektverzeichnis befinden bitte                                      */
    /* #include "boost/spirit.hpp"                                               */
    /* einbinden.                                                                */
    /*****************************************************************************/
    // ...
    using namespace boost::spirit
    

    3 Boost::Spirit an Hand eines .ini Datei Parsers
    Zur Erläuterung der Funktionsweise von Boost::Spirit werden wir uns im folgenden
    einen .ini Fileparser zusammenbauen. Dazu definieren wir das Aussehen einer .ini
    Datei

    Definition:

    [Sektionsname{1}]
    variable{1}=wert{1}
    variable{2}=wert{2}
    .
    .
    .
    variable{n}=wert{n}
    [Sektionsname{2}]
    variable{1}=wert{1}
    variable{2}=wert{2}
    .
    .
    .
    variable{n}=wert{n}
    .
    .
    .
    [Sektionsname{n}]
    variable{1}=wert{1}
    variable{2}=wert{2}
    .
    .
    .
    variable{n}=wert{n}
    

    Für diese Definition überlegen wir uns im folgenden Regeln, die wir in Spirit
    abbilden und die uns das Parsen erlauben.

    Es folgen die Regeln abgebildet in Spirit!

    // ...
    int main(int argc, char** argv)
    {
    
        rule<> _VALUE = *(anychar_p - eol_p - end_p);                          //[1]
        rule<> _VAR = +(anychar_p - space_p - chlit<char>('='));               //[2]
        rule<> _VAR_DECLARATION = _VAR >> chlit<char>('=') >> _VALUE >> *eol_p;//[3]
        rule<> _SECTION = chlit<char>('[') >> *(anychar_p - chlit<char>(']'))  
                          >> chlit<char>(']') >> *eol_p;                       
        rule<> _INI_ROW = _SECTION | _VAR_DECLARATION | eol_p;                 //[4]
    
        // ...
    
        if(parse(<zeile aus der .ini Datei>, _INI_ROW).full)                   //[5] 
        {
            cout << "Valid";
        }
    
        // ...
    }
    

    Erklärung:

    rule<> Variablename = Zuweisung
    

    Der rule<> Datentyp
    Der Variablentyp rule<> ist einer der wichtigsten Datentypen, wenn man mit
    Boost::Spirit in Kontakt kommt. Der rule<> Datentyp legt das "Aussehen"
    eines Parseausdruck fest.

    Zu [1]:
    Es wird die Regel _VALUE definiert. Wir legen fest, das _VALUE jedes
    Zeichen außer ein "End of Line" und ein "Eingabe Ende" beinahlten darf. Der
    * vor der Klammer ist der sogenannte "Kleene Star" und besagt, das 0 bis
    beliebig viele Zeichen vorkommen können.

    Zu [2]:
    Die Regel _VAR legt fest, dass for dem '=' mindestens ein Zeichen (+) stehen
    muss, dass kein '=' enthalten darf. Desweiteren dürfen keine Leerzeichen, keine
    Returns, keine Whitespaces und derartige dinge vorkommen(space_p Parser).

    Zu [3]:
    In der Regel _VAR_DECLARATION haben wir unser erstes "gefolgt von"
    Zeichen. Wir definieren das eine Variablen Declaration wie folgt aussieht.
    Die Regel _VAR gefolgt von (>>) einem Character Literal ('=')
    gefolgt von (>>) der Regel _VALUE gefolgt von (>>)
    einem möglichen "End of Line".

    Zu [4]:
    Die Regel _INI_ROW ist unsere sogenannte "Startregel". Sie ist es, die die parse
    Funktion bedient. In Ihr legen wir fest wie eine Zeile in der .ini File
    auszusehen hat. Entweder es kommt eine _SECTION, oder (|) eine
    _VAR_DECLARATION oder (|) ein "End of Line".

    Zu [5]:
    Die Funktion parse liefert ein Parseergebnis, auf dass wir später noch genauer
    eingehen werden. Vorerst ist es nur wichtig zu wissen, das die parse Funktion
    in unserem Beispiel zwei Argumente benötigt. Nämlich den zu parsenden Ausdruck
    und die "Startregel". Das .full zeigt uns, ob wir den Ausdruck komplett
    durchlaufen haben. Ist das geschehen geben wir "Valid" aus.

    Zusammenfassend zu diesem Beispiel:

    Der Datentyp rule<> als einer der wichtigsten Datentypen in Boost::Spirit.
    Es gibt verschiedene vorgefertigte Parser wie eol_p, space_p, anychar_p ...
    Wichtige Operatoren sind: *, +, |, >>
    Die parse Funktion übernimmt die Arbeit und liefert als Ergebnis einen "true"
    oder "false" Wert.

    Wird fortgesetzt...



  • Artchi schrieb:

    Kannst Du bitte die harten Zeilenumbrüche rausnehmen? Ist schlechter Stil, die Formatierung nicht dem User bzw. seinem Browser zu überlassen. Weiterhin wird es nicht einfach das ganze nachher in das PDF-Magazin zu überführen.

    Hallo Artchi,

    beim nächsten Mal lass ich die Zeilenumbrüche weg. 🤡 Ich mach übrigens gleichzeitig ne PDF Version des Artikels... 🙂

    Bis
    denne

    Tobi



  • Ich hab mich gleich mal als unwissender Testleser in diesen Artikel reingeworfen: Gibts einen Grund für die ganzen _ und Großschrebungen bei den Regeln?

    Ansonsten finde ich den Artikel bereits sehr informativ 👍

    MfG SideWinder



  • Hallo SideWinder,

    es gibt keinen Grund für die _Großschreibungen, ich habe mir das als ich mit Spirit begonnen habe so angefangen, um die Regeln von normalen Programmen unterscheiden zu können.

    Gruss
    Tobi

    [edit:]
    muss natürlich Variablen statt Programmen heißen 🤡
    [/edit:]



  • Vielleicht solltest du das in einer Zeile anmerken 🙂

    Edit: Und wenn du schon angibst welche Compiler empfohlen werden, dann entweder mehrere oder keinen. Aber nur den Außenseiter-Compiler von dir anzugeben ist unfair :p

    MfG SideWinder



  • 1 Grundlegendes
    Dieser Artikel soll Sie in die Welt von Boost::Spirit einführen. Boost::Spirit ist ein Parser Gernerator Framework, dass in C++ realisiert ist. Um die folgenden Beispiele complieren zu können ist Boost::Spirit ab der Version 1.6.3 zu verwenden. Desweiteren wird DEV-C++ in der Version 4.9.9.2 empfohlen.

    Anmerkung: Auch andere Compilier werden unterstützt (z.B Visual Studio 2003, das Code::Blocks Packet u.a). Es gibt jedoch auch Compiler die Probleme bereiten können (MS Visual Studio 6.0 und Open Watcom 1.3).

    1.1 Von Datein und anderen zu durchsuchenden Strukturen
    Ein klassisches Beispiel für die Verwendung eines Parsers ist die Dateiverarbeitung. Wie oft kommt es vor, dass man ein bestimmtes Format oder eine bestimmte Struktur zu durchsuchen hat und keine geeigneten Klassen oder Strukturen zur verfügung stehen.
    XML Dateien werden etwa anders verarbeitet als INI Dateien. Mathematische Eingaben sollen anders interpretiert werden als bestimmte Stringfolgen. All dass kennt man mittlerweile als Programmierer zur Genüge.
    Auf diesem Gebiet hat Boost::Spirit seine unverkennbaren Stärken. Durch die einfache Definition von Regeln ist in null kommma nichts ein Parser entwicklet, der die Arbeit von alleine erledigt. Ob dabei Zeilenweise durch eine Datei geparst wird, oder Mathematische Ausdrücke geparst werden spielt dabei keine große Rolle. Boost::Spirit kann in diese Richtung fast alles handhaben.

    2 Installation von Boost::Spirit
    Um das Framework von Boost::Spirit nutzen zu können ist es erforderlich, die Header Dateien bekannt zu machen. Am einfachsten ist es den Ordner boost aus dem Verzeichnis spirit-1.6.3\boost sowie aus dem Verzeichnis spirit-1.6.3\boost\miniboost in das Include Verzeichnis des Compiliers/der Entwicklungsumgebung zu kopieren.
    In ein Projekt müssen unbedingt folgende Zeilen eingebunden werden.

    // ...
    #include <boost/spirit.hpp>
    /*****************************************************************************/
    /* Die obige Include Zeile ist nur zu verwenden, falls die Includedateien    */
    /* in das Includeverzeichnis des Compiliers kopiert wurden... Falls sie sich */
    /* im Projektverzeichnis befinden bitte                                      */
    /* #include "boost/spirit.hpp"                                               */
    /* einbinden.                                                                */
    /*****************************************************************************/
    // ...
    using namespace boost::spirit
    

    2.1 Theoretische Informatik
    Für die folgenden Beispiele ist es vielleicht ganz interessant sich etwas in die Theoretische Informatik einzulesen. Deswegen hab ich ein kleines PDF erstellt: Klick mich

    3 Boost::Spirit an Hand eines .ini Datei Parsers
    Zur Erläuterung der Funktionsweise von Boost::Spirit werden wir uns im folgenden einen .ini Fileparser zusammenbauen. Dazu definieren wir das Aussehen einer .ini Datei.

    Definition:

    [Sektionsname{1}]
    variable{1}=wert{1}
    variable{2}=wert{2}
    .
    .
    .
    variable{n}=wert{n}
    [Sektionsname{2}]
    variable{1}=wert{1}
    variable{2}=wert{2}
    .
    .
    .
    variable{n}=wert{n}
    .
    .
    .
    [Sektionsname{n}]
    variable{1}=wert{1}
    variable{2}=wert{2}
    .
    .
    .
    variable{n}=wert{n}
    

    Für diese Definition überlegen wir uns im folgenden Regeln, die wir in Spirit abbilden und die uns das Parsen erlauben.

    Es folgen die Regeln abgebildet in Spirit!

    // ...
    int main(int argc, char** argv)
    {
    
        rule<> _VALUE = *(anychar_p - eol_p - end_p);                          //[1]
        rule<> _VAR = +(anychar_p - space_p - chlit<char>('='));               //[2]
        rule<> _VAR_DECLARATION = _VAR >> chlit<char>('=') >> _VALUE >> *eol_p;//[3]
        rule<> _SECTION = chlit<char>('[') >> *(anychar_p - chlit<char>(']'))  
                          >> chlit<char>(']') >> *eol_p;                       
        rule<> _INI_ROW = _SECTION | _VAR_DECLARATION | eol_p;                 //[4]
    
        // ...
    
        if(parse(<zeile aus der .ini Datei>, _INI_ROW).full)                   //[5] 
        {
            cout << "Valid";
        }
    
        // ...
    }
    

    Erklärung:

    rule<> Variablename = Zuweisung
    

    Der rule<> Datentyp
    Der Variablentyp rule<> ist einer der wichtigsten Datentypen, wenn man mit Boost::Spirit in Kontakt kommt. Der rule<> Datentyp legt das "Aussehen" eines Parseausdruck fest.

    Zu [1]:
    Es wird die Regel _VALUE definiert. Wir legen fest, das _VALUE jedes Zeichen außer ein "End of Line" und ein "Eingabe Ende" beinahlten darf. Der * vor der Klammer ist der sogenannte "Kleene Star" und besagt, das 0 bis beliebig viele Zeichen vorkommen können.

    Zu [2]:
    Die Regel _VAR legt fest, dass for dem '=' mindestens ein Zeichen (+) stehen muss, dass kein '=' enthalten darf. Desweiteren dürfen keine Leerzeichen, keine Returns, keine Whitespaces und derartige dinge vorkommen(space_p Parser).

    Zu [3]:
    In der Regel _VAR_DECLARATION haben wir unser erstes "gefolgt von" Zeichen. Wir definieren das eine Variablen Declaration wie folgt aussieht. Die Regel _VAR gefolgt von (>>) einem Character Literal ('=') gefolgt von (>>) der Regel _VALUE gefolgt von (>>) einem möglichen "End of Line".

    Zu [4]:
    Die Regel _INI_ROW ist unsere sogenannte "Startregel". Sie ist es, die die parse Funktion bedient. In Ihr legen wir fest wie eine Zeile in der .ini File auszusehen hat. Entweder es kommt eine _SECTION, oder (|) eine _VAR_DECLARATION oder (|) ein "End of Line".

    Zu [5]:
    Die Funktion parse liefert ein Parseergebnis, auf dass wir später noch genauer eingehen werden. Vorerst ist es nur wichtig zu wissen, das die parse Funktion in unserem Beispiel zwei Argumente benötigt. Nämlich den zu parsenden Ausdruck und die "Startregel". Das .full zeigt uns, ob wir den Ausdruck komplett durchlaufen haben. Ist das geschehen geben wir "Valid" aus.

    Zusammenfassend zu diesem Beispiel:

    -> Der Datentyp rule<> als einer der wichtigsten Datentypen in Boost::Spirit.
    -> Es gibt verschiedene vorgefertigte Parser wie eol_p, space_p, anychar_p ...
    -> Wichtige Operatoren sind: *, +, |, >>
    -> Die parse Funktion übernimmt die Arbeit und liefert als Ergebnis einen "true" oder "false" Wert.

    4 Anzeigen von Daten aus dem Parsevorgang mit Funktionen
    Nun ist es ja gut und schön wenn man weiß, dass eine zu parsender Ausdruck den Regeln entspricht. Aber wie an die Daten kommen, die in dem Ausdruck vorhanden sind.

    Dafür gibt es in Boost::Spirit Funktionen und Functoren die im Grundgerüst wie folgt definiert sind.

    template<typename IteratorT>
    void <funktionsname>(IteratorT begin, IteratorT end)
    {
      //Verarbeitung der Iteratoren
    }
    
    // oder falls nur ein Iterator zur Verfügung steht z.B bei Zahlenparsern von Spirit
    <template<typename IteratorT>
    void <funktionsname>(IteratorT begin)
    {
      //Verarbeitung des Iterators
    }
    

    wird vortgesetzt...



  • Hallo,

    Dieser Artikel soll Sie in die Welt von Boost::Spirit einführen. Boost::Spirit ist ein Parser Gernerator Framework, dass in C++ realisiert ist. Um die folgenden Beispiele complieren zu können ist Boost::Spirit ab der Version 1.6.3 zu verwenden. Desweiteren wird DEV-C++ in der Version 4.9.9.2 empfohlen.

    Anmerkung: Auch andere Compilier werden unterstützt (z.B Visual Studio 2003, das Code::Blocks Packet u.a). Es gibt jedoch auch Compiler die Probleme bereiten können (MS Visual Studio 6.0 und Open Watcom 1.3).

    sorry, wenn ich auch noch darauf rumreite, aber das ist sehr schlecht forumliert. Zuerst einmal redest du meistens von IDEs und nicht von Compilern, und zweitens ist Dev-Cpp imho mist, gut, das kann man anders sehen, aber worauf es wirklich ankommt, ist der Compiler der drunterliegt. Und das ist der MinGW. Deshalb solltest du etwas wie folgendes schreiben:

    Dieser Artikel soll Sie in die Welt von Boost::Spirit einführen. Boost::Spirit ist ein Parser Gernerator Framework, dass in C++ realisiert ist. Um die folgenden Beispiele complieren zu können ist Boost::Spirit ab der Version 1.6.3 zu verwenden. Desweiteren wird der MinGW (g++) Compiler ab Version 3.3.6 empfohlen, der unter anderem Code::Blocks, dem MinGW Developer Studio und Dev-Cpp beiliegt. Ebenfalls gut gerüstet ist man mit dem Visual C++ 2003 oder 2005.

    Meiden sollte man allerdings den Visual C++ 6 und den Open Watcom 1.3, da diese Compiler nicht mehr zeitgemäß sind.

    Ist nur ein Vorschlag.

    Mfg

    GPC



  • GPC schrieb:

    Ist nur ein Vorschlag.

    Aber kein sehr guter, wenn du den Fehler, den du bemängelst, selber machst 😉

    Open Watcom kenne ich nicht, aber Visual C++ 6 ist garantiert kein Compiler ...



  • In dem Template-Artikel wurde eine Formulierung bzgl. Compiler eingesetzt, die man auch hier gebrachen sollte.

    VC++ 6.0 ist kein Compiler? Das wäre mir neu! Natürlich ist die gesamte VC++ Reihe auch ein Compiler. Wie willste den sonst nennen? Außerdem ist das jetzt Haarspalterei.

    Wir sollte in unseren Artikeln vielleicht einfach nur schreiben, das ein _aktueller_ und sehr ISO-C++-konformer Compiler nötig ist. Aber wie gesagt, in dem Template-Artikel ist eine Formulierung, die hier vom Team schon damals für i.O. befunden wurde.

    Dann noch eine weitere Anmerkung: warum muß man die Spirit Lib in das Compiler-Verzeichnis kopieren? 😕 Das ist wohl eher eine Geschmackssache und keine Voraussetzung um Libs benutzen zu können.



  • Reyx schrieb:

    GPC schrieb:

    Ist nur ein Vorschlag.

    Aber kein sehr guter, wenn du den Fehler, den du bemängelst, selber machst 😉

    Open Watcom kenne ich nicht, aber Visual C++ 6 ist garantiert kein Compiler ...

    Aha, auf die Begründung warum der Visual C++ 6.0 kein Compiler ist, bin ich sehr gespannt...
    wenn du mir wenigstens angekreidet hättest, dass ich Visual C++ 2003/2005 anstatt Visual C++ 7.1 und Visual C++ 8.0 geschrieben habe.



  • Hallo zusammen,

    hatte mal wieder etwas Zeit und hab weitergemacht zuvor allerdings noch ein paar Anmerkungen meinerseits

    GPC schrieb:

    [...]

    sorry, wenn ich auch noch darauf rumreite, aber das ist sehr schlecht forumliert. Zuerst einmal redest du meistens von IDEs und nicht von Compilern, und zweitens ist Dev-Cpp imho mist, gut, das kann man anders sehen, aber worauf es wirklich ankommt, ist der Compiler der drunterliegt. Und das ist der MinGW.

    [...]

    Mfg

    GPC

    Da geb ich dir recht. Ist wirklich etwas schlecht formuliert. Ich habe das im nachfolgenden auf ISO C++ Compilier geändert und Hinweise auf die zwei mir bekannten Complilier gegeben, die Probleme bereiten.

    Artchi schrieb:

    [...]
    Dann noch eine weitere Anmerkung: warum muß man die Spirit Lib in das Compiler-Verzeichnis kopieren? 😕 Das ist wohl eher eine Geschmackssache und keine Voraussetzung um Libs benutzen zu können.

    Ich hab auch nie geschrieben, dass das Voraussetzung ist...

    Tobias Gerg schrieb:

    [...]
    Am einfachsten ist es den Ordner boost aus dem Verzeichnis spirit-1.6.3\boost sowie aus dem Verzeichnis spirit-1.6.3\boost\miniboost in das Include Verzeichnis des Compiliers/der Entwicklungsumgebung zu kopieren.
    In ein Projekt müssen unbedingt folgende Zeilen eingebunden werden.
    [...]

    Für jemanden der nicht weiß wo er was ändern muss um das Verzeichnis der Headerdateien in seine IDE einzubinden, ist das imho das Einfachste... 🙂

    Gruss
    Tobi



  • 1 Grundlegendes
    Dieser Artikel soll Sie in die Welt von Boost::Spirit einführen. Boost::Spirit ist ein Parser Gernerator Framework, dass in C++ realisiert ist. Um die folgenden Beispiele complieren zu können ist Boost::Spirit ab der Version 1.6.3 zu verwenden. Desweiteren wird ein ISO C++ Compilier (gcc, Visual C++(ab Version 7.0), ...) benötigt

    Anmerkung: Probleme bereiten unteranderem MS Visual C++ 6.0 und Open Watcom 1.3.

    1.1 Von Datein und anderen zu durchsuchenden Strukturen
    Ein klassisches Beispiel für die Verwendung eines Parsers ist die Dateiverarbeitung. Wie oft kommt es vor, dass man ein bestimmtes Format oder eine bestimmte Struktur zu durchsuchen hat und keine geeigneten Klassen oder Strukturen zur verfügung stehen.
    XML Dateien werden etwa anders verarbeitet als INI Dateien. Mathematische Eingaben sollen anders interpretiert werden als bestimmte Stringfolgen. All dass kennt man mittlerweile als Programmierer zur Genüge.
    Auf diesem Gebiet hat Boost::Spirit seine unverkennbaren Stärken. Durch die einfache Definition von Regeln ist in null kommma nichts ein Parser entwicklet, der die Arbeit von alleine erledigt. Ob dabei Zeilenweise durch eine Datei geparst wird, oder Mathematische Ausdrücke geparst werden spielt dabei keine große Rolle. Boost::Spirit kann in diese Richtung fast alles handhaben.

    2 Installation von Boost::Spirit
    Um das Framework von Boost::Spirit nutzen zu können ist es erforderlich, die Header Dateien bekannt zu machen. Am einfachsten ist es den Ordner boost aus dem Verzeichnis spirit-1.6.3\boost sowie aus dem Verzeichnis spirit-1.6.3\boost\miniboost in das Include Verzeichnis des Compiliers/der Entwicklungsumgebung zu kopieren.
    In ein Projekt müssen unbedingt folgende Zeilen eingebunden werden.

    // ...
    #include <boost/spirit.hpp>
    /*****************************************************************************/
    /* Die obige Include Zeile ist nur zu verwenden, falls die Includedateien    */
    /* Systemweit bekannt gemacht wurden...                                      */
    /* Falls sie sich im Projektverzeichnis befinden bitte                       */
    /* #include "boost/spirit.hpp"                                               */
    /* einbinden.                                                                */
    /*****************************************************************************/
    // ...
    using namespace boost::spirit
    

    2.1 Theoretische Informatik
    Für die folgenden Beispiele ist es vielleicht ganz interessant sich etwas in die Theoretische Informatik einzulesen. Deswegen hab ich ein kleines PDF erstellt:
    Klick mich

    3 Boost::Spirit an Hand eines .ini Datei Parsers
    Zur Erläuterung der Funktionsweise von Boost::Spirit werden wir uns im folgenden einen .ini Fileparser zusammenbauen. Dazu definieren wir das Aussehen einer .ini Datei
    Definition:

    [Sektionsname{1}]
    variable{1}=wert{1}
    variable{2}=wert{2}
    .
    .
    .
    variable{n}=wert{n}
    [Sektionsname{2}]
    variable{1}=wert{1}
    variable{2}=wert{2}
    .
    .
    .
    variable{n}=wert{n}
    .
    .
    .
    [Sektionsname{n}]
    variable{1}=wert{1}
    variable{2}=wert{2}
    .
    .
    .
    variable{n}=wert{n}
    

    Für diese Definition überlegen wir uns im folgenden Regeln, die wir in Spirit abbilden und die uns das Parsen erlauben.

    Es folgen die Regeln abgebildet in Spirit!

    // ...
    int main(int argc, char** argv)
    {
    
        rule<> _VALUE = *(anychar_p - eol_p - end_p);                          //[1]
        rule<> _VAR = +(anychar_p - space_p - chlit<char>('='));               //[2]
        rule<> _VAR_DECLARATION = _VAR >> chlit<char>('=') >> _VALUE >> *eol_p;//[3]
        rule<> _SECTION = chlit<char>('[') >> *(anychar_p - chlit<char>(']'))  
                          >> chlit<char>(']') >> *eol_p;                       
        rule<> _INI_ROW = _SECTION | _VAR_DECLARATION | eol_p;                 //[4]
    
        // ...
    
        if(parse(<zeile aus der .ini Datei>, _INI_ROW).full)                   //[5] 
        {
            cout << "Valid";
        }
    
        // ...
    }
    

    Erklärung:

    rule<> Variablename = Zuweisung
    

    Der rule<> Datentyp
    Der Variablentyp rule<> ist einer der wichtigsten Datentypen, wenn man mit Boost::Spirit in Kontakt kommt. Der rule<> Datentyp legt das "Aussehen" eines Parseausdruck fest.

    Zu [1]:
    Es wird die Regel _VALUE definiert. Wir legen fest, das _VALUE jedes Zeichen außer ein "End of Line" und ein "Eingabe Ende" beinahlten darf. Der * vor der Klammer ist der sogenannte "Kleene Star" und besagt, das 0 bis beliebig viele Zeichen vorkommen können.

    Zu [2]:
    Die Regel _VAR legt fest, dass for dem '=' mindestens ein Zeichen (+) stehen muss, dass kein '=' enthalten darf. Desweiteren dürfen keine Leerzeichen, keine Returns, keine Whitespaces und derartige dinge vorkommen(space_p Parser).

    Zu [3]:
    In der Regel _VAR_DECLARATION haben wir unser erstes "gefolgt von" Zeichen. Wir definieren das eine Variablen Declaration wie folgt aussieht. Die Regel _VAR gefolgt von (>>) einem Character Literal ('=') gefolgt von (>>) der Regel _VALUE gefolgt von (>>) einem möglichen "End of Line".

    Zu [4]:
    Die Regel _INI_ROW ist unsere sogenannte "Startregel". Sie ist es, die die parse Funktion bedient. In Ihr legen wir fest wie eine Zeile in der .ini File auszusehen hat. Entweder es kommt eine _SECTION, oder (|) eine _VAR_DECLARATION oder (|) ein "End of Line".

    Zu [5]:
    Die Funktion parse liefert ein Parseergebnis, auf dass wir später noch genauer eingehen werden. Vorerst ist es nur wichtig zu wissen, das die parse Funktion in unserem Beispiel zwei Argumente benötigt. Nämlich den zu parsenden Ausdruck und die "Startregel". Das .full zeigt uns, ob wir den Ausdruck komplett durchlaufen haben. Ist das geschehen geben wir "Valid" aus.

    Zusammenfassend zu diesem Beispiel:

    -> Der Datentyp rule<> als einer der wichtigsten Datentypen in Boost::Spirit.
    -> Es gibt verschiedene vorgefertigte Parser wie eol_p, space_p, anychar_p ...
    -> Wichtige Operatoren sind: *, +, |, >>
    -> Die parse Funktion übernimmt die Arbeit und liefert als Ergebnis einen "true"
    oder "false" Wert.

    4 Die Parse Funktion
    Wie im obigen Beispiel gesehen, ist es die parse Funktion die den eigentlichen Parsevorgang übernimmt. Diese Funktion liefert in Wirklichkeit mehr als nur einen boolsche Wert. Der Rückgabewert ist eine Variable vom Typ parse_info

    Die Struktur enthält folgende Membervariablen:

    -> stop
    -> hit
    -> full
    -> length

    Über diese Variablen lassen sich einige Informationen extrahieren, die Aufschluss über den Parsevorgang geben.

    Über Stop findet man die Position im geparsten Ausdruck wo aufgehört wurde zu parsen.

    Bsp:

    // ...
    
    // Eine Regel, die eine Liste von Zahlen getrennt durch Kommas liest
    rule<> r = int_p >> *(chlit<char>(',') >> int_p);
    
    // Das ParseInfo Objekt
    parse_info<> pI;
    
    // Parsen eines ausdrucks, der nicht nur Zahlen entält
    pI = parse("1,2,3,a,b,0", r);
    
    // Ausgabe der Werte von ParseInfo
    std::cout << pI.stop << std::endl << pI.full << std::endl << pI.hit << std::endl << pI.length << std::endl;
    
    // ...
    

    Die Ausgabe des Programms

    ,a,b,0      // Hier wurde aufgehört zu parsen
    0           // Ausdruck nicht vollständig geparst
    1           // Teilausdruck wurde erkannt
    5           // 5 Zeichen wurden geparst
    

    Mit diesen Angaben kann man unter anderem einschränken, wo der zu parsende Ausdruck "fehlerhaft" war.

    5 Anzeigen von Daten aus dem Parsevorgang mit Funktionen
    Nun ist es ja gut und schön wenn man weiß, dass eine zu parsender Ausdruck den Regeln entspricht. Aber wie an die Daten kommen, die in dem Ausdruck vorhanden sind.

    Dafür gibt es in Boost::Spirit unteranderem Funktionen die im Grundgerüst wie folgt definiert sind.

    template<IteratorT>
    void <funktionsname>(IteratorT begin, IteratorT end)
    {
      //Verarbeitung der Iteratoren
    }
    
    // oder falls nur ein Iterator zur Verfügung steht z.B bei Zahlenparsern von Spirit
    void <funktionsname>(IteratorT begin)
    {
      //Verarbeitung des Iterators
    }
    

    wird vortgesetzt...



  • Ich hab es mir mal durchgelesen. Irgendwie erschlägt einen das erste Beispiel mit den parsen von ini-Dateien sehr. Das kann aber vielleicht daher kommen, das du nicht erklärst, was diese ganze Syntax soll. Also, warum ist sie so kryptisch. Man kann nämlich nicht gerade behaupten, es handel sich um alltäglichen C++ Sourcecode, den man da sieht. Es ist ja schon sehr speziell. Warum ist das so? Das war die erste Frage, die ich mir gestellt habe. Und ich hatte auch keine Lust weiter zu lesen, ich mußte mich zusammen reissen.

    Es wäre also ganz gut, wenn du vorher erläuterst, wie Spirit überhaupt arbeitet und den Parser erzeugt. Der technische Background fehlt.

    Dann nochmal zu den Compilern (ja, es muß sein 😃 ), da ein seeeehr kleiner Fehler noch drin ist: VisualC++ 7.1 ist erst der sehr ISO-konforme Compiler, VC++ 7.0 ist nur ein Update vom 6.0 für ManagedC++. Brauchst nur die 7.0 durch 7.1 ersetzen. 😉



  • Artchi schrieb:

    Ich hab es mir mal durchgelesen. Irgendwie erschlägt einen das erste Beispiel mit den parsen von ini-Dateien sehr. Das kann aber vielleicht daher kommen, das du nicht erklärst, was diese ganze Syntax soll. Also, warum ist sie so kryptisch. Man kann nämlich nicht gerade behaupten, es handel sich um alltäglichen C++ Sourcecode, den man da sieht. Es ist ja schon sehr speziell. Warum ist das so? Das war die erste Frage, die ich mir gestellt habe. Und ich hatte auch keine Lust weiter zu lesen, ich mußte mich zusammen reissen.

    Es wäre also ganz gut, wenn du vorher erläuterst, wie Spirit überhaupt arbeitet und den Parser erzeugt. Der technische Background fehlt.

    Dann nochmal zu den Compilern (ja, es muß sein 😃 ), da ein seeeehr kleiner Fehler noch drin ist: VisualC++ 7.1 ist erst der sehr ISO-konforme Compiler, VC++ 7.0 ist nur ein Update vom 6.0 für ManagedC++. Brauchst nur die 7.0 durch 7.1 ersetzen. 😉

    Hallo Artchi,

    erstmal danke fürs Feedback.

    Das mit dem Compilier mach ich... (Ich hoffe das Ganze hat sich dann erledigt! 🤡 Blödes Compilier wirwarr)

    Zu deinen anderen Anmerkungen...
    Was genau findest du so kryptisch?

    rule<> <Variablenname> = <Zuweisung> ist meiner Ansicht nach einfach zu verstehen.
    Typ (OK mit Template aber was ist daran krytisch) rule<>
    <Variablenname>
    <Zuweisung>

    Die Zuweisungen werden nach dem Beispiel erläutert...

    Etwas genauere Infos was du nicht verstehts würden mir weiterhelfen.

    Danke!

    PS: Hast du das PDF gelesen?
    Meinungen dazu würden mich interessieren!

    Gruss
    Tobi



  • Ist der Artikel noch [A] oder schon [T]? 🙂



  • Ist noch [A]...
    Ich hoffe ich bring Ihn bis spätestens nächste Woche soweit das man ihn auf [T] setzen kann



  • Keine Panik, ich wollte nur mal fragen... 🙂



  • 1 Grundlegendes
    Dieser Artikel soll Sie in die Welt von Boost::Spirit einführen. Boost::Spirit ist ein Parser Gernerator Framework, dass in C++ realisiert ist. Um die folgenden Beispiele complieren zu können ist Boost::Spirit ab der Version 1.6.3 zu verwenden. Desweiteren wird ein ISO C++ Compilier (gcc, Visual C++(ab Version 7.1), ...) benötigt

    Anmerkung: Probleme bereiten unteranderem MS Visual C++ 6.0 und Open Watcom 1.3.

    1.1 Von Datein und anderen zu durchsuchenden Strukturen
    Ein klassisches Beispiel für die Verwendung eines Parsers ist die Dateiverarbeitung. Wie oft kommt es vor, dass man ein bestimmtes Format oder eine bestimmte Struktur zu durchsuchen hat und keine geeigneten Klassen oder Strukturen zur verfügung stehen.
    XML Dateien werden etwa anders verarbeitet als INI Dateien. Mathematische Eingaben sollen anders interpretiert werden als bestimmte Stringfolgen. All dass kennt man mittlerweile als Programmierer zur Genüge.
    Auf diesem Gebiet hat Boost::Spirit seine unverkennbaren Stärken. Durch die einfache Definition von Regeln ist in null kommma nichts ein Parser entwicklet, der die Arbeit von alleine erledigt. Ob dabei Zeilenweise durch eine Datei geparst wird, oder Mathematische Ausdrücke geparst werden spielt dabei keine große Rolle. Boost::Spirit kann in diese Richtung fast alles handhaben.

    2 Installation von Boost::Spirit
    Um das Framework von Boost::Spirit nutzen zu können ist es erforderlich, die Header Dateien bekannt zu machen. Am einfachsten ist es den Ordner boost aus dem Verzeichnis spirit-1.6.3\boost sowie aus dem Verzeichnis spirit-1.6.3\boost\miniboost in das Include Verzeichnis des Compiliers/der Entwicklungsumgebung zu kopieren.
    In ein Projekt müssen unbedingt folgende Zeilen eingebunden werden.

    // ...
    #include <boost/spirit.hpp>
    /*****************************************************************************/
    /* Die obige Include Zeile ist nur zu verwenden, falls die Includedateien    */
    /* Systemweit bekannt gemacht wurden...                                      */
    /* Falls sie sich im Projektverzeichnis befinden bitte                       */
    /* #include "boost/spirit.hpp"                                               */
    /* einbinden.                                                                */
    /*****************************************************************************/
    // ...
    using namespace boost::spirit
    

    2.1 Theoretische Informatik
    Für die folgenden Beispiele ist es vielleicht ganz interessant sich etwas in die Theoretische Informatik einzulesen. Deswegen hab ich ein kleines PDF erstellt:
    Klick mich

    3 Die Grundlagen für Parseaktionen im Framework
    Der Datentyp, der in den nächsten Beispielen immer wieder vorkommen wird, ist der rule<> Datentyp. Mit diesem Datentyp, baut man Parser auf. Spirit stellt von sich aus schon diverse Parser zur Verfügung von denen ich einige in diesem Kapitel
    vorstellen möchte.

    3.1 Die Literale strlit<> und chlit<>

    // ...
    rule<> ch_A = chlit<>('A');
    rule<> str_Hallo = strlit<>("Hallo");
    // ...
    

    Wie schon zu vermuten ist, werden hier ein Character Literal und ein String Literal erstellt. Es ist durchaus möglich, auch Variablen in der Regel anzugeben wie das folgende Beispiel zeigt.

    // ...
    string myString = "Test";
    rule<> str_Variabel = strlit<>(myString.c_str());  //char* übergeben
    // Alternativ
    rule<> str_Variable2 = strlit<string::const_iterator>(myString.begin(), myString.end());
    // ...
    

    3.2 Ranges
    Um einen Bereich abzudecken gibt es den range<> Parser.

    //...
    rule<> rng = range<>('A', 'Z');
    //...
    

    Die Regel rng deckt die Buchstaben von 'A' bis 'Z' ab.

    3.3 Zahlen
    Es gibt in Spirit diverse Zahlenparser. Einer der wichtigeren ist wohl der Integerparser.

    //...
    rule<> integer = int_p;
    //...
    

    Erlaubt eine beliebige Ganzzahl.

    3.4 Operatoren
    Es gibt in Spirit eine Reihe von Operatoren zum Verknüpfen von Regeln. Dazu gehören unteranderem

    <a> | <b>	// <a> oder <b> oder beide werden gematched
    <a> - <b>	// <a> ohne <b>
    <a> >> <b>	// <a> gefolgt von <b>
    !<a>		// 0 oder 1 mal <a>
    *<a>		// 0 oder beliebig oft mal <a>
    +<a>		// 1 oder beliebig oft mal <a>
    ~<a>		// nicht <a>
    

    Weiterführendes Material findet sich unter Spirit Doku

    4 Boost::Spirit an Hand eines .ini Datei Parsers
    Zur Erläuterung der Funktionsweise von Boost::Spirit werden wir uns im folgenden einen .ini Fileparser zusammenbauen. Dazu definieren wir das Aussehen einer .ini Datei
    Definition:

    [Sektionsname{1}]
    variable{1}=wert{1}
    variable{2}=wert{2}
    .
    .
    .
    variable{n}=wert{n}
    [Sektionsname{2}]
    variable{1}=wert{1}
    variable{2}=wert{2}
    .
    .
    .
    variable{n}=wert{n}
    .
    .
    .
    [Sektionsname{n}]
    variable{1}=wert{1}
    variable{2}=wert{2}
    .
    .
    .
    variable{n}=wert{n}
    

    Für diese Definition überlegen wir uns im folgenden Regeln, die wir in Spirit abbilden und die uns das Parsen erlauben.

    Es folgen die Regeln abgebildet in Spirit!

    // ...
    int main(int argc, char** argv)
    {
    
        rule<> _VALUE = *(anychar_p - eol_p - end_p);                          //[1]
        rule<> _VAR = +(anychar_p - space_p - chlit<char>('='));               //[2]
        rule<> _VAR_DECLARATION = _VAR >> chlit<char>('=') >> _VALUE >> *eol_p;//[3]
        rule<> _SECTION = chlit<char>('[') >> *(anychar_p - chlit<char>(']'))  
                          >> chlit<char>(']') >> *eol_p;                       
        rule<> _INI_ROW = _SECTION | _VAR_DECLARATION | eol_p;                 //[4]
    
        // ...
    
        if(parse(<zeile aus der .ini Datei>, _INI_ROW).full)                   //[5] 
        {
            cout << "Valid";
        }
    
        // ...
    }
    

    Erklärung:

    rule<> Variablename = Zuweisung
    

    Der rule<> Datentyp
    Der Variablentyp rule<> ist einer der wichtigsten Datentypen, wenn man mit Boost::Spirit in Kontakt kommt. Der rule<> Datentyp legt das "Aussehen" eines Parseausdruck fest.

    Zu [1]:
    Es wird die Regel _VALUE definiert. Wir legen fest, das _VALUE jedes Zeichen außer ein "End of Line" und ein "Eingabe Ende" beinahlten darf. Der * vor der Klammer ist der sogenannte "Kleene Star" und besagt, das 0 bis beliebig viele Zeichen vorkommen können.

    Zu [2]:
    Die Regel _VAR legt fest, dass for dem '=' mindestens ein Zeichen (+) stehen muss, dass kein '=' enthalten darf. Desweiteren dürfen keine Leerzeichen, keine Returns, keine Whitespaces und derartige dinge vorkommen(space_p Parser).

    Zu [3]:
    In der Regel _VAR_DECLARATION haben wir unser erstes "gefolgt von" Zeichen. Wir definieren das eine Variablen Declaration wie folgt aussieht. Die Regel _VAR gefolgt von (>>) einem Character Literal ('=') gefolgt von (>>) der Regel _VALUE gefolgt von (>>) einem möglichen "End of Line".

    Zu [4]:
    Die Regel _INI_ROW ist unsere sogenannte "Startregel". Sie ist es, die die parse Funktion bedient. In Ihr legen wir fest wie eine Zeile in der .ini File auszusehen hat. Entweder es kommt eine _SECTION, oder (|) eine _VAR_DECLARATION oder (|) ein "End of Line".

    Zu [5]:
    Die Funktion parse liefert ein Parseergebnis, auf dass wir später noch genauer eingehen werden. Vorerst ist es nur wichtig zu wissen, das die parse Funktion in unserem Beispiel zwei Argumente benötigt. Nämlich den zu parsenden Ausdruck und die "Startregel". Das .full zeigt uns, ob wir den Ausdruck komplett durchlaufen haben. Ist das geschehen geben wir "Valid" aus.

    Zusammenfassend zu diesem Beispiel:

    -> Der Datentyp rule<> als einer der wichtigsten Datentypen in Boost::Spirit.
    -> Es gibt verschiedene vorgefertigte Parser wie eol_p, space_p, anychar_p ...
    -> Wichtige Operatoren sind: *, +, |, >>
    -> Die parse Funktion übernimmt die Arbeit und liefert als Ergebnis einen "true"
    oder "false" Wert.

    5 Die Parse Funktion
    Wie im obigen Beispiel gesehen, ist es die parse Funktion die den eigentlichen Parsevorgang übernimmt. Diese Funktion liefert in Wirklichkeit mehr als nur einen boolsche Wert. Der Rückgabewert ist eine Variable vom Typ parse_info
    Die Struktur enthält folgende Membervariablen:

    -> stop
    -> hit
    -> full
    -> length

    Über diese Variablen lassen sich einige Informationen extrahieren, die Aufschluss über den Parsevorgang geben.

    Über Stop findet man die Position im geparsten Ausdruck wo aufgehört wurde zu parsen.

    Bsp:

    // ...
    
    // Eine Regel, die eine Liste von Zahlen getrennt durch Kommas liest
    rule<> r = int_p >> *(chlit<char>(',') >> int_p);
    
    // Das ParseInfo Objekt
    parse_info<> pI;
    
    // Parsen eines ausdrucks, der nicht nur Zahlen entält
    pI = parse("1,2,3,a,b,0", r);
    
    // Ausgabe der Werte von ParseInfo
    std::cout << pI.stop << std::endl << pI.full << std::endl << pI.hit << std::endl << pI.length << std::endl;
    
    // ...
    

    Die Ausgabe des Programms

    ,a,b,0      // Hier wurde aufgehört zu parsen
    0           // Ausdruck nicht vollständig geparst
    1           // Teilausdruck wurde erkannt
    5           // 5 Zeichen wurden geparst
    

    Mit diesen Angaben kann man unter anderem einschränken, wo der zu parsende Ausdruck "fehlerhaft" war.

    6 Anzeigen von Daten aus dem Parsevorgang mit Funktionen
    Nun ist es ja gut und schön wenn man weiß, dass eine zu parsender Ausdruck den Regeln entspricht. Aber wie an die Daten kommen, die in dem Ausdruck vorhanden sind.

    Dafür gibt es in Boost::Spirit unteranderem Funktionen die im Grundgerüst wie folgt definiert sind.

    template<IteratorT>
    void <funktionsname>(IteratorT begin, IteratorT end)
    {
      //Verarbeitung der Iteratoren
    }
    
    // oder falls nur ein Iterator zur Verfügung steht z.B bei Zahlenparsern von Spirit
    void <funktionsname>(IteratorT begin)
    {
      //Verarbeitung des Iterators
    }
    

    wird vortgesetzt...



  • Hallo,

    gefällt mir schon sehr gut, was ich da lese. Hoffe der Rest wird genauso gut. 👍

    Nur eins: Achte darauf, dass Layout einzuhalten (Hauptpunkte müssen noch unterstrichen werden).

    MfG

    GPC



  • Mach ich,

    werde die Hauptpunkte beim nächsten mal unterstreichen 😉

    Gruss
    Tobi


Anmelden zum Antworten