Frage zu boost::spirit



  • Hi,

    ich habe ein (hoffentlich) kleines Problem mit Spirit und wusste nicht, ob das eher nach "Rund um..." oder nach "C++" gehört, deswegen steht es nun hier.

    Mein Ziel ist, eine hierarchisch gegliederte Struktur aus einer Textdatei einlesen. Im Moment übe ich noch und versuche einzelne Objekte (die als Attribute bislang nur Farben kennen) einzulesen, doch ich steige noch nicht ganz dahinter wie ich an die geparsten Informationen kommen soll. Habe boost::spirit bislang immer nur für Syntaxchecks eingesetzt. Kennt sich da jemand aus und kann mir sagen wie ich das bewerkstelligen kann (bzw. auf die Stelle in der Doku verweisen an der das behandelt wird [finde ich nicht])?

    // LayoutParser.hpp
    #ifndef LAYOUT_PARSER_HPP
    #define LAYOUT_PARSER_HPP
    #include <boost/spirit.hpp>
    
    class LayoutParser
    {
    	struct Syntax : public boost::spirit::grammar<Syntax>
    	{
    		template<typename ScannerType>
    		struct definition
    		{
    			definition(const Syntax& self)
    			{
    				using namespace boost::spirit;
    
    				colors = *(str_p("Color")) >> '(' >> +xdigit_p >> ')';
    				variables = *alpha_p >> '=' >> colors >> eol_p;
    				objects = *alpha_p >> '(' >> *alnum_p >> ')' >> '{' >> *variables >> '}';
    				baseExpression = *objects;
    			}
    
    			const boost::spirit::rule<ScannerType>& start() const
    			{
    				return baseExpression;
    			}
    
    			boost::spirit::rule<ScannerType> colors, variables, objects, baseExpression;
    		};
    		friend struct definition;
    	};
    
    public:
    	void parse(const std::string& str);
    };
    #endif
    
    // LayoutParser.cpp
    #include "LayoutParser.hpp"
    
    using namespace boost::spirit;
    
    void LayoutParser::parse(const std::string& str)
    {
    	try
    	{
    		parse_info<> info;
    		Syntax syntax;
    		info = boost::spirit::parse(str.c_str(), syntax, space_p);
    
    		// hier die gelesenen Objekte verarbeiten
    	}
    	catch(exception& e)
    	{
    		// TODO: Fehler richtig behandeln
    		TRACE(e.what());
    		TRACE("\n");
    	}	
    }
    

    Ich rufe das so auf:

    LayoutParser parser;
    	parser.parse(
    						"TextObject(varname1) {"
    							"FGCOLOR = Color(92AB2F)"
    							"BGCOLOR = Color(C0C0C0)"
    						"}"
    						"TextObject(varname2) {"
    							"FGCOLOR = Color(FF00FF)"
    							"BGCOLOR = Color(FFFFFF)"
    						"}"
    					);
    

    (Eventuelle Stilkrücken im Code mag man mir verzeihen. Ich überarbeite immer erst, wenn es prinzipiell funktioniert 😉 .)

    Gruß,
    Christian



  • das ist die goße schwäche von spirit, da man das nur so machen kann:

    am ende jedes parsers,sei es ein einzelner parser oder eine parser gruppe(die ja imemr wieder einen einzelnen parser ergeben), hast du die möglichkeit via op[] einen funktor zu übergeben, dem dann bei erfolgreichem parsen die von dem jeweiligen parser komplett geparste datenmenge übergeben wird.

    dh:

    parser_p[functor]>>parser2_p[functor2];
    //oder
    (parser_p>>parser2_p)[functor3];
    

    functor 3 bekommt dann alel daten die functor 1 oder 2 bekämen als einen string,leider nicht als token,was bedeuted,dass man das ganze dann selbst wieder auseinanderfriemeln muss 😞

    der link:
    http://www.boost.org/libs/spirit/doc/semantic_actions.html



  • Danke! Hab wie blöd in der Doku rumgeblättert und den Abschnitt wahrscheinlich übersehen. Mhpf das wird Arbeit 😞 ; bis eben hielt ich Spirit noch für eine praktische Sache 😉 .



  • sagenw irs mal so: solange mans nur braucht, um daten auf richtige eingabe zu prüfen, ist es brauchbar 😃



  • otze schrieb:

    sagenw irs mal so: solange mans nur braucht, um daten auf richtige eingabe zu prüfen, ist es brauchbar 😃

    Dafür habe ich es bislang auch nur gebraucht (und als sehr praktisch empfunden). Naja, ich denke das werde ich schon irgendwie mit vertretbarem Aufwand hinbekommen.



  • ich würde persönlich spirit nurnoch als bessren tokenizer verwenden, der gleichzeitig die syntax prüft, und die token umstellt. alles andre wird zu komplex.



  • So ein Blödsinn. Du kannst sehr wohl auch komplexere Datentypen als result type benutzen. Die einfachste Möglichkeit dafür ist sicher functor_parser. Das sieht dann zum Beispiel so aus:

    namespace bs = boost::spirit;
    
    struct vektorparsefunctor {
      typedef std::vector<int> result_t; // Typ definieren
    
      template<typename ScannerT>
      int operator(const ScannerT &scan, result_t &result) const { //...und den entsprechenden Operator
        if(scan.at_end()) return -1;
        result.clear();
    
        bs::parse_info<typename ScannerT::iterator_t> info =
        bs::parse(scan.first, scan.last,
                  bs::ch_p('(') >>
                  bs::int_p[bs::push_back(result)] >> ',' >>
                  bs::int_p[bs::push_back(result)] >> ',' >>
                  bs::int_p[bs::push_back(result)] >> ')',
                  bs::space_p);
        if(info.hit) {
          scan.first = info.stop;
          return info.length;
        }
    
        return -1;
      }
    };
    
    bs::functor_parser<vektorparsefunctor> vektor_p; // dann in den Funktor-Parser zwängen
    

    ...und voila, du hast nen Parser, der std::vector<int> als Ergebnis hat und Ausdrücke der Form (1, 2, 3) parst. Nichts mehr mit auseinanderfummeln.



  • dead? was hat dein post mit dem problem zu tun? 😕

    und wie du auf diese aussage kommst, frag ich mich schon seit nunmehr 5 minuten...

    So ein Blödsinn. Du kannst sehr wohl auch komplexere Datentypen als result type benutzen

    vortallem, weil in diesem bezug nichts gesagt wurde. und was nicht gesagt wurde kann nicht blödsinns ein.



  • otze schrieb:

    functor 3 bekommt dann alel daten die functor 1 oder 2 bekämen als einen string,leider nicht als token,was bedeuted,dass man das ganze dann selbst wieder auseinanderfriemeln muss 😞

    Darauf bezog ich mich.



  • functor 3 bekommt die daten auch nur als string. als nichts anderes, weil bei compounded parsern nur strings bzw stringiteratoren übergeben werden.

    es gibt nur ganz wenig parser, die andere functoren annehmen.



  • @mastah

    ganz vergessen: schau dir das an:
    http://www.boost.org/libs/spirit/doc/phoenix.html

    vielleicht machts dir sie sache einfacher.

    und hier wird etwas ähnliches gemacht, wie dus machen willst(naja, fast^^):
    http://www.boost.org/libs/spirit/doc/closures.html

    //edit2 spirit isn monster,mehr sag ich dazu nichmehr. vielleicht kann spirit jede aufgabe erledigen, vielleicht auch nicht. vielleicht gehts wirklich in verbindung mit phönix super einfach, aber wer weis? spirit scheint mehr als ein parser zu sein, der schlicht irgendwo daten speichert,das ding ist eine touringmaschine. die stärke spielt spirit aber leider erst in den letzten kapiteln aus, und bis dahin ist es ein weiter weg



  • otze schrieb:

    functor 3 bekommt die daten auch nur als string. als nichts anderes, weil bei compounded parsern nur strings bzw stringiteratoren übergeben werden.

    es gibt nur ganz wenig parser, die andere functoren annehmen.

    Deswegen macht es ja auch Sinn, sich eigene Parser für eigene Datentypen und patterns zu schreiben.

    Was spirit ansonsten angeht, es ist nicht sonderlich einfach zu lernen, das ist richtig. Das liegt aber in der Natur der Sache, Sprachen und Parser sind halt nicht die einfachste Materie. Wenn man spirit aber einmal begriffen hat, ist es geradezu unglaublich einfach, darin sehr komplizierte Parser zu schreiben, und darum gehts ja.



  • Deswegen macht es ja auch Sinn, sich eigene Parser für eigene Datentypen und patterns zu schreiben.

    phönix machts besser.jede wette.



  • Auch auf die Gefahr hin, das ich damit den Thread aus dem C++ Forum nach "Rund um katapultiere" 🙂 :

    Ich schreibe derzeit auch zig parser selber (lesen von Dateien, Nutzereingaben u.s.w.). Ich hab mich jetzt mal ein bisschen mit boost::spirit und lex/yacc beschäftigt.
    Auch wenn die Frage an sich blöd ist : Ab welcher Komplexität lohnt sich der Einsatz von Spirit? Es nervt einfach, jedesmal das Rad neu zu erfinden. Ich bin von den Möglichkeiten die Spirit u. ähnliche Tools bieten fasziniert, aber im Moment bin ich bei einfacheren Parsingaufgaben einfach schneller es selbst zu machen und bei größeren (z.b. XML Parser) nehme ich lieber eine spezielles Tool.



  • wenn man spirit komplett beherrscht, so kann es jede kleine bis mittelgroße parsinggeschichte machen. mit spirit ist man schneller.

    weitere spiritanwendungen sind auch jene, wo ein string direkt geparst und berechnet werden können muss,spirit kann dir alles in einem rutsch machen.

    yacc etc sind bei sehr großen parsern interessant, da sie ideale maschienen produzieren.



  • Vermutlich richtig. Man (bzw. Ich) muß vermutlich der Versuchung wiederstehen bei einfachen Parsingaufgaben erstmal schnell was selbst zu zimmern.

    Vermutlich wie bei den meisten Tools: In der Lernphase braucht man erstmal ein vielfaches an Zeit, als man ohne sie gebraucht hätte :).


Anmelden zum Antworten