Boost Spirit und C++ Repräsentation eines Grammatik Objekts



  • Hallo,

    mein Einstieg in Boost ist erstmal sehr glatt gelaufen vorallem Dank der schönen Einführung, ich habe erstmal aus ganz vielen **rule<>**s meine Grammatik relativ leicht zusammenbekommen und bekomme Matches da wo ich will. Theoretisch könnte ich dann, wenn ein Match für einen Typ erreicht ist, den eben über die die ganzen integralen Actors Stück für Stück abspeichern. Nur eine Grammatik bedeutet ja auch ein größeres abstraktes Konstrukt zu beschreiben und zu erfassen. Ich will eigentlich nicht das komplexe Grammatikkonstrukt nicht nur erkennen, ich will es auch geparst als ganzes Objekt erfassen.

    Ist möglich was ich mir gerade vorstelle, auf eine abstrakte Regel die Parserepräsentation in C++ als komplettes Objekt bzw. Struktur zu bekommen?

    Ich möchte zuzusagen wenn ein Match für einen komplexen Typ gefunden wurde ein komplett ausgefülltes Struct mit den herausgeparsten Informationen bekommen und nur die Regeln beschreiben, wann, wie, welche Daten in einer spezifischen, gewünschten Struktur landen.

    Ist dann mit den Sprachmitteln von Boost möglich oder muß ich mir alle Integral-Matches irgendwie merken und die Informationen zu übergeordneten Typen zu Fuß zusammenbauen?

    danke!! 🙂



  • Vielleicht zeigtst du mal etwas Code. Ich kann dir gedanklich gerade nicht flogen...

    Gruß
    Tobi



  • Also ich parse nach einem "komplexen" Eintrag (example_entry) für das ich eine rule<> habe und eine Struktur die diesem zu parsenden Eintrag an Programmlogik gegenübersteht:

    rule<> seperator = /*..*/
    rule<> i_value = !(int_p >> seperator);
    rule<> name_string = print_p >> seperator;
    rule<> example_entry = i_value >> *name_string;
    
    struct example_s
    {
       int i;
       list<string> names;
    
       example_s( rule<>& transitition_match)
       {
          i = transitition.i_value.int_p;
          // ... 
       }
    }
    

    Das ist nur ein einfaches Beispiel, aber was ich möchte ist nicht einem i_value bzw. int_p fest sagen wo es beim Parsen hingeht, sondern ich möchte eine Struktur/Klasse (hier example_s) angeben und die an example_entry registrieren. Bei einem Match wird dann eine Funktion, Konstruktor, etc. aufgerufen, die die Regeln definiert um das komplett durch example_entry beschriebene Parsing-Set eines Matches auf ein Objekt zu transformieren.

    Also wie in Pseudocode Manier für i im Konstruktor von example_entry geschehen. Man macht eine Art example_entry.registry<example_s>, bei einem Match wird example_s::example_s(transitition_match) aufgerufen und dort ist dann alles drin, was bei diesem Match geparsed wurde und die Regeln beschrieben wie das in ein Objekt transformiert wird.

    Das muß jetzt natürlich nicht exakt so sein, aber ich suche eben nach einem Weg eine direkte Transition zwischen einer komplexen Regeln und einem komplexen C++ Typ herzustellen und nicht jeden Integraltyp während des Parsens durch die Gegend schieben zu müssen. Ist das in irgendeienr Form mit Boost::Spirit möglich?



  • So was ist mir mit den Boardmitteln nicht bekannt. Allerdings kannst du einen complexen typ an eine Grammatik übergeben und so die variablen dieses typs füllen...

    Ich hab dass hier mal an deinem Beispiel gemacht (abgewandelt vom Calculator Beispiel aus der Spirit Doku)

    #include <boost/spirit/include/classic_core.hpp>
    #include <iostream>
    #include <list>
    #include <functional>
    #include <string>
    
    ////////////////////////////////////////////////////////////////////////////
    using namespace std;
    using namespace BOOST_SPIRIT_CLASSIC_NS;
    
    ////////////////////////////////////////////////////////////////////////////
    //
    // Complex data type
    //
    ////////////////////////////////////////////////////////////////////////////
    struct complex_type
    {
    	int i;
    	list<string> names;
    
    	// ausführen wenn komplette Regel geparst wurde
    	void execute()
    	{
    		std::cout << "Do something with the data type after match" << std::endl;
    		cout << i << " ";
    		std::list<string>::iterator it = names.begin();
    		while(it != names.end())
    			std::cout << *it++ << std::endl;
    		names.erase(names.begin());
    	}
    };
    
    ////////////////////////////////////////////////////////////////////////////
    //
    //  Semantic actions
    //
    ////////////////////////////////////////////////////////////////////////////
    
    // zahl holen
    struct getiValue
    {
    	explicit getiValue(complex_type& eval_) : eval(eval_){}
    	template <typename IteratorT>
    	void operator()(IteratorT val) const
    	{
    		eval.i = val;
    	}
    
    	complex_type& eval;
    };
    
    // strings holen
    struct getNames
    {
    	explicit getNames(complex_type& eval_) : eval(eval_){}
    	template <typename IteratorT>
    	void operator()(IteratorT begin, IteratorT end) const
    	{
    		eval.names.push_back(std::string(begin, end));
    	}
    
    	complex_type& eval;
    };
    
    // wenn regel geparst wurde execute aufrufen
    struct operateOn
    {
    	explicit operateOn(complex_type& eval_) : eval(eval_){}
    
    	template <typename IteratorT>
    	void operator()(IteratorT begin, IteratorT end) const
    	{
    		eval.execute();
    	}
    
    	complex_type& eval;
    };
    
    ////////////////////////////////////////////////////////////////////////////
    //
    //  Our calculator grammar
    //
    ////////////////////////////////////////////////////////////////////////////
    
    // grammatik definieren
    struct complex_grammar : public grammar<complex_grammar>
    {
    	complex_grammar(complex_type& eval_)
        : eval(eval_) {}
    
        template <typename ScannerT>
        struct definition
        {
            definition(complex_grammar const& self)
            {
    			// i_value nach eval.i schreiben
    			i_value = !(int_p[getiValue(self.eval)]);
    
    			// namen an liste hängen
    			name = (+(print_p - space_p))[getNames(self.eval)];
    
    			// namens liste
    			string_names = +(space_p >> name) ;
    
    			// beispiel eintrag (operation mit werten ausfürhen nach einem kompletten match
    			sample_entry = (i_value >> string_names)[operateOn(self.eval)];
            }
    
            rule<ScannerT> sample_entry, i_value, string_names, name;
            rule<ScannerT> const&
            start() const { return sample_entry; }
        };
    
        complex_type& eval;
    };
    
    ////////////////////////////////////////////////////////////////////////////
    //
    //  Main program
    //
    ////////////////////////////////////////////////////////////////////////////
    int
    main()
    {
        cout << "/////////////////////////////////////////////////////////\n\n";
        cout << "\t\tThe simplest working calculator...\n\n";
        cout << "/////////////////////////////////////////////////////////\n\n";
        cout << "Type an expression...or [q or Q] to quit\n\n";
    
        complex_type eval;
        complex_grammar calc(eval); //  Our parser
    
        string str;
        while (getline(cin, str))
        {
            if (str.empty() || str[0] == 'q' || str[0] == 'Q')
                break;
    
            parse_info<> info = parse(str.c_str(), calc, space_p);
    
            if (info.full)
            {
                cout << "-------------------------\n";
                cout << "Parsing succeeded\n";
                cout << "-------------------------\n";
    
            }
            else
            {
                cout << "-------------------------\n";
                cout << "Parsing failed\n";
                cout << "stopped at: \": " << info.stop << "\"\n";
                cout << "-------------------------\n";
            }
        }
    
        cout << "Bye... :-) \n\n";
        return 0;
    }
    

    Vielleicht hilft dir dass weiter... 🙂

    Gruß
    Tobi



  • Vielen Dank, daß hat mir wirklich sehr geholfen und dadurch habe ich auch das jetzt im Prinzip hinbekommen, was ich wollte.

    Ich hätte noch eine Frage, auf die ich keine Antwort gefunden habe. Wenn ich nach oben beschriebenen Schema eine Funktion an eine rule<> dranhänge:

    rule<> test = int_p[getInt(self.eval)];
    rule<> final = test;
    

    Dann wird die entsprechende Implementation ausgeführt:

    struct getInt
    {
        template <typename IteratorT>
        void operator()(IteratorT val) const
        {
           //
        }
    }
    

    Entscheidenend ist, daß operator() mit einem Parameter und 'val' aufgerufen wird und darin steckt in jedem Fall bereits der geparste Int.

    Wenn ich aber das mache:

    rule<> test = int_p;
    rule<> final = test[getInt(self.eval)];
    

    Wird nur diese Implementation aufgerufen

    void operator()(IteratorT begin, IteratorT end) const
    {
    }
    

    Die Implementation enthält begin und end von dem geparsten String und liefert keinen Int, sondern eben einen String zurück. Kann ich für eine rule<> irgendwie festlegen, daß das Resultat vom geünschten Typ ist? Also wenn ich verschiedene Funktionen an rule<> test anhänge, daß dies Resultate vom gewollten Typ sind und nicht der grundlegendste Operator für den geparsten String implementiert wird?



  • Probier mal,

    rule<> test = int_p;
    rule<parser_context<int> > final = test[getInt(self.eval)];
    

    Greets
    Tobi



  • ⚠ ⚠

    Man sollte nicht mehr mit spirit classic neue Projekte anfangen, nehm lieber direkt spirit2,
    das ist mittlerweile recht ausgereift, und der 2.1 Zweig lässt sich auch aus dem SVN gut nutzen.

    http://c-plusplus.net/forum/viewtopic-var-t-is-189058.html

    Bei Fragen kannst du dich gerne an die Spiritmailingliste wenden.

    phlox



  • Warum sollte man nicht mehr mit spirit classic arbeiten...
    Ist das schon irgendwo als deprecated markiert?



  • Tobias Gerg schrieb:

    Warum sollte man nicht mehr mit spirit classic arbeiten...
    Ist das schon irgendwo als deprecated markiert?

    Nein, aber neue Projekte sollte man in spirit2 machen, weil es einfach besser ist vom Design und Aufbau her.
    Auch ist es unter Umständen schneller.

    Du kannst dir aber gerne selber ein Bild machen:
    http://www.boost.org/doc/libs/1_39_0/libs/spirit/doc/html/index.html



  • Jetzt seh ich das auch mal... Auf der boost homepage leitet mich ein klick auf spirit immer auf die doku der classic variante um. 👎

    Ich muss mir das mal genauer anschauen... Danke für den Link 👍

    Greets
    Tobi



  • Tobias Gerg schrieb:

    Jetzt seh ich das auch mal... Auf der boost homepage leitet mich ein klick auf spirit immer auf die doku der classic variante um. 👎

    Greets
    Tobi

    Ja das ärgert mich auch, ist irgendwie blöd, aber so wirklich in doku und ähnliche Dinge stecken sie nicht so viel Aufwand, aber die Examples sind recht gut.

    @GmBH:
    Schau dir mal das MiniXML AST Beispiel an:
    http://www.boost.org/doc/libs/1_39_0/libs/spirit/doc/html/spirit/qi_and_karma.html#spirit.qi_and_karma.tutorials.mini_xml___asts_



  • Auf der spirit hompage steht dass Spirit 2.0 noch beta ist. Allerdings ist der Eintrag vom August 17, 2008.

    @phlox81: Weißt du ob die Version schon aus dem beta stadium raus ist?


Anmelden zum Antworten