Boost Spirit Qi Parser



  • Vielleicht hat jemand eine Idee mir zu helfen 🙂

    Ich hab eine Qi Parser aus dem Boost Spirit 2.3.

    start = (string_literal >> bool_literal) [bind(zombie_test)];
    

    string_literal ist eine Zeichenkette in der From: 'ab' (mind 2 Stellig).
    bool_literal ist true und false.

    Beide Literale werden werden vom dem Spirit Lex an dem Parser weiter gereicht ( ich sag's weil der Artikel im Magazin kein Scanner verwendet).

    Die Zombie-Funktion schreibt einfach in die Ausgabe, was sie erkannt hat.

    Ledenfalls wird die Funktion nicht ausgelöst in der Form.

    Wie auch immer wenn ich die >>(Konkatenation) durch |(Oder) ersetzte, werden Bools und Strings ohne Probleme erkannt.

    Danke.



  • Minimalbeispiel:

    #include <boost/spirit/include/lex_lexertl.hpp>
    #include <boost/spirit/include/qi.hpp>
    #include <boost/spirit/include/phoenix_operator.hpp>
    #include <boost/spirit/include/phoenix_statement.hpp>
    #include <boost/spirit/include/phoenix_container.hpp>
    
    #include <iostream>
    #include <string>
    #include <boost/bind.hpp>
    #include <boost/ref.hpp>
    
    using namespace boost::spirit::lex;
    using namespace boost::spirit;
    using namespace boost::spirit::ascii;
    
    template<class Lexer> struct rules : lexer<Lexer> {
    	rules() {												
    		this->self.add_pattern("STRING_LITERAL", "\\'([A-Za-z]|[0-9])([A-Za-z]|[0-9])+\\'");
    		this->self.add_pattern("TRUE_LITERAL", "true");
    		this->self.add_pattern("FALSE_LITERAL", "false");
    
    		string_literal = "{STRING_LITERAL}";
    		true_literal = "{TRUE_LITERAL}";
    		false_literal = "{FALSE_LITERAL}";
    
    		this->self.add(string_literal);				
    		this->self.add(true_literal);
    		this->self.add(false_literal);	
    
    	}
    	token_def<std::string> string_literal, true_literal, false_literal;
    };
    
    void zombie_string() { std::cout << "string" << std::endl; }
    void zombie_bool() { std::cout << "bool" << std::endl; }
    void zombie_end() { std::cout << "end" << std::endl; }
    
    template<class Iterator> struct grammar : qi::grammar<Iterator>
    {
    	template<class TokenDefinition>
    	grammar(const TokenDefinition& td) : grammar::base_type(start)
    	{
    		using boost::bind;
    
    		string_literal				=	td.string_literal						[bind(zombie_string)]		;
    		true_literal				=	td.true_literal									;
    		false_literal				=	td.false_literal								;
    		bool_literal				=	(false_literal | true_literal)			[bind(zombie_bool)]			;
    
    		start						=	(string_literal >> bool_literal)		[bind(zombie_end)]			;
    	}			
    
    	qi::rule<Iterator> string_literal, bool_literal, true_literal, false_literal;
    	qi::rule<Iterator> start;
    };
    
    int main() {
    	typedef lexertl::token<char const*, boost::mpl::vector<std::string> > token_type;
    	typedef lexertl::lexer<token_type> lexer_type;
    	typedef rules<lexer_type>::iterator_type iterator_type;
    
    	rules<lexer_type> lexer;
    	grammar<iterator_type> parser(lexer);
    
    	while(true) {
    		std::string input;
    		std::cin >> input;
    
    		char const* first = input.c_str();
    		char const* last = &first[input.size()];
    
    		tokenize_and_parse(first, last, lexer, parser);
    	}
    }
    


  • Spirit Regeln (rules) verhalten sich anders in Abhängigkeit davon, ob der Ausdruck auf der rechten Seite semantische Aktionen hat oder nicht. Wenn keine semantischen Aktionen vorhanden sind, verhält sich die Regel so, als ob die rechte Seite mit operator%= zugewiesen worden wäre, also das Attribute der linken Seite automatisch an die rechte Seite 'weitergereicht' wird. Sobald semantische Aktionen ins Spiel kommen, wird dieser 'Auto-Mechanismus' ausgeschaltet.

    Aus diesem Grund scheint der Ausdruck

    qi::rule<Iterator> start = (string_literal >> bool_literal) [...]
    

    kein Resultat zu liefern.

    Weiterhin mußt Du den Instanzen der Regeln sagen, welchen Attribut-Typ diese 'liefern'. Also z.B.

    qi::rule<Iterator, std::string()> string_literal = td.string_literal;
    

    Wenn Du trotz semantischer Aktionen das Attribut durchreichen willst, müssen alle Zuweisungen mittels %= erfolgen.

    Regards Hartmut



  • Danke für die Hinweise. Ich werd's mit wohl an ein kalteren Tagen nochmal ansehen.

    Erstaml geht es mir um die richtige und tatsächliche Reihenfolge der semantischen Aktionen. Es macht mich nur bisschen stutzig, weil ein anderes Programm genauso aufgebaut ist, allerdings verhält es sich korrekt, wie erwartet.

    #include <iostream>
    #include <string>
    
    #include <boost/spirit/include/lex_lexertl.hpp>
    #include <boost/spirit/include/qi.hpp>
    #include <boost/spirit/include/phoenix_operator.hpp>
    #include <boost/spirit/include/phoenix_statement.hpp>
    #include <boost/spirit/include/phoenix_container.hpp>
    #include <boost/bind.hpp>
    #include <boost/ref.hpp> 
    
    using namespace boost::spirit;
    using namespace boost::spirit::lex;
    using namespace boost::spirit::ascii;
    
    template<class Lexer> struct rules : lexer<Lexer> {
    	rules() {
    		this->self.add_pattern("DIGIT", "[0-9]");
    		this->self.add_pattern("PREFIX", "[-|+]");
    
    		digit	= "{DIGIT}";
    		prefix = "{PREFIX}";
    
    		this->self.add(digit);
    		this->self.add(prefix);
    
    	}
    
    	lex::token_def<std::string> digit, prefix;
    };
    
    void m_digit() { std::cout << "digit" << std::endl; }
    void m_prefix() {std::cout << "prefix" << std::endl;}
    void m_pair() {std::cout << "pair" << std::endl;}
    void m_end() {std::cout << "end" << std::endl;}
    
    template<class Iterator> struct grammar : qi::grammar<Iterator> {
    
    	template<class TokenDefinition>
    	grammar(const TokenDefinition& td) : grammar::base_type(start) {
    		using boost::bind;				
    
    		prefix		=		td.prefix								[bind(m_prefix)]	;
    		digit		=		td.digit								[bind(m_digit)]		;
    		pair		=		(prefix >> digit)						[bind(m_pair)]		;
    		start		=		pair									[bind(m_end)]		;
    	}			
    	qi::rule<Iterator> start, pair ,digit, prefix;
    };
    
    int main() {
    	typedef lexertl::token<char const*, boost::mpl::vector<std::string> > token_type;
    	typedef lexertl::lexer<token_type> lexer_type;
    	typedef rules<lexer_type>::iterator_type iterator_type;
    
    	rules<lexer_type> my_lexer;
    	grammar<iterator_type> my_parser(my_lexer);
    
    	while(true) {
    		std::string input;
    		std::cin >> input;
    
    		char const* first = input.c_str();
    		char const* last = &first[input.size()];
    
    		lex::tokenize_and_parse(first, last, my_lexer, my_parser);
    
    	}
    	std::cin.get();
    }
    


  • Kannst Du bitte an Hand einer konkreten Eingabe-Zeichenkette zeigen, welches Ergebnis Du siehst und was Du eigentlich sehen wolltest?

    Regards Hartmut



  • Ja gern 🙂

    1. Version
    Eingabe:
    'Hallo' true

    Ausgabe:
    string

    Erwartet:
    string
    bool
    end

    2. Version
    Eingabe:
    +1

    Ausgabe:
    prefix
    digit
    pair
    end

    Erwartet:
    prefix
    digit
    pair
    end



  • Zeus schrieb:

    Ja gern 🙂

    1. Version
    Eingabe:
    'Hallo' true

    Ausgabe:
    string

    Erwartet:
    string
    bool
    end

    Wenn Du das Leerzeichen zwischen 'Hallo' und true wegläßt, dann bekommst Du die erwartete Ausgabe. Weder der Lexer, noch der Parser wissen etwas über ein einzelnes (oder auch mehrere) Leerzeichen, Deine Eingabe kann also nicht 'gematched' werden. Der Rückgabewert von lex::tokenize_and_parse is false (der Parser liefert diesen Fehler), zusätzlich ist nach dem Aufruf von tokenize_and_parse first != last, was darauf hinweist, daß die Eingabe nicht vollständig verarbeitet wurde.

    Zeus schrieb:

    2. Version
    Eingabe:
    +1

    Ausgabe:
    prefix
    digit
    pair
    end

    Erwartet:
    prefix
    digit
    pair
    end

    Hier stimmt ja alles 😛

    Regards Hartmut



  • Hmm ok,... *peinlich wegrennen*

    In der Tat hab ich die Whitespaces nicht mehr gedacht. Ich weiß, wie ich die Sachen im Lexergenerator Flex einstellen, einfach eine leere Regeln ohne Aktion. Wie ist ein Spirit.Lex, ich hab ein wenig probiert und in die Dokumentation nachgesehen erstmal ohne Erfolg. Geht das nur mit eine Skip Grammar?



  • Zeus schrieb:

    In der Tat hab ich die Whitespaces nicht mehr gedacht. Ich weiß, wie ich die Sachen im Lexergenerator Flex einstellen, einfach eine leere Regeln ohne Aktion. Wie ist ein Spirit.Lex, ich hab ein wenig probiert und in die Dokumentation nachgesehen erstmal ohne Erfolg. Geht das nur mit eine Skip Grammar?

    Am einfachsten ist es wohl, dem Lexer zu sagen bestimmte Tokens zu ignorieren:

    template<class Lexer> struct rules : lexer<Lexer> 
    {
        rules() {
            using lex::_pass;
            using lex::pass_flags;
    
            this->self.add_pattern("DIGIT", "[0-9]");
            this->self.add_pattern("PREFIX", "[-|+]");
    
            digit    = "{DIGIT}";
            prefix = "{PREFIX}";
            ws = "[ \t\n]";
    
            this->self.add(digit);
            this->self.add(prefix);
            this->self += ws [_pass = pass_flags::pass_ignore];
        }
    
        lex::token_def<std::string> digit, prefix;
        lex::token_def<> ws;
    };
    

    Die einzige Besonderheit ist, daß der verwendete Lexer vom Typ

    lex::lexertl::actor_lexer<token_type>
    

    sein muß (ansonsten bekommst Du auf Grund der semantischen Aktion mehrere Compiler-Fehler).

    Regards Hartmut





  • Zeus schrieb:

    Bitte stelle mir das vollständige Programm zur Verfügung, ich schau es mir dann an.

    Regards Hartmut



  • Vielen Danke, dass du dir so viele Mühe mit mir machst 🙂

    Eingabe sind:
    +1 Geht
    + 1 Geht nie

    #include <iostream> 
    #include <string> 
    
    #include <boost/spirit/include/lex_lexertl.hpp> 
    #include <boost/spirit/include/qi.hpp> 
    #include <boost/spirit/include/phoenix_operator.hpp> 
    #include <boost/spirit/include/phoenix_statement.hpp> 
    #include <boost/spirit/include/phoenix_container.hpp> 
    #include <boost/bind.hpp> 
    #include <boost/ref.hpp> 
    
    using namespace boost::spirit; 
    using namespace boost::spirit::lex; 
    using namespace boost::spirit::ascii; 
    
    template<class Lexer> struct rules : lexer<Lexer> 
    { 
    	rules() { 
    		using lex::_pass; 
    		using lex::pass_flags; 
    
    		this->self.add_pattern("DIGIT", "[0-9]"); 
    		this->self.add_pattern("PREFIX", "[-|+]"); 
    
    		digit    = "{DIGIT}"; 
    		prefix = "{PREFIX}"; 
    
    		//ws = "[ ]"; // Geht nicht
    		//ws = "[\\ ]"; // Geht nicht
    		//ws = "[\\\x20]"; // Geht nicht
    		//ws = "[\x20]"; // Geht nicht 
    
    		ws = "[ \t\n]"; // Geht nicht
    		//ws = "[x]";  // Geht
    
    		this->self.add(digit); 
    		this->self.add(prefix); 
    		this->self += ws [_pass = pass_flags::pass_ignore]; 
    	} 
    
    	lex::token_def<std::string> digit, prefix; 
    	lex::token_def<> ws; 
    };
    
    void m_digit() { std::cout << "digit" << std::endl; } 
    void m_prefix() {std::cout << "prefix" << std::endl;} 
    void m_pair() {std::cout << "pair" << std::endl;} 
    void m_end() {std::cout << "end" << std::endl;} 
    
    template<class Iterator> struct grammar : qi::grammar<Iterator> { 
    
    	template<class TokenDefinition> 
    	grammar(const TokenDefinition& td) : grammar::base_type(start) { 
    		using boost::bind;                
    
    		prefix        =        td.prefix                                [bind(m_prefix)]    ; 
    		digit        =        td.digit                                [bind(m_digit)]        ; 
    		pair        =        (prefix >> digit)                        [bind(m_pair)]        ; 
    		start        =        pair                                    [bind(m_end)]        ; 
    	}            
    	qi::rule<Iterator> start, pair ,digit, prefix; 
    }; 
    
    int main() { 
    	typedef lexertl::token<char const*, boost::mpl::vector<std::string> > token_type; 
    	typedef lexertl::actor_lexer<token_type> lexer_type; 
    	typedef rules<lexer_type>::iterator_type iterator_type; 
    
    	rules<lexer_type> my_lexer; 
    	grammar<iterator_type> my_parser(my_lexer); 
    
    	while(true) { 
    		std::string input; 
    		std::cin >> input; 
    
    		char const* first = input.c_str(); 
    		char const* last = &first[input.size()]; 
    
    		bool b = lex::tokenize_and_parse(first, last, my_lexer, my_parser); 
    		std::cout << "Parser says: " << b << std::endl;
    
    	} 
    	std::cin.get(); 
    }
    


  • Dein Problem liegt hier:

    Zeus schrieb:

    while(true) { 
            std::string input; 
            std::cin >> input; 
            // ...
        }
    

    Es muß so aussehen:

    std::string input; 
        std::cin.unsetf(std::ios::skipws);
        while(std::getline(std::cin, input)) 
        { 
            // ...
        }
    

    Ansonsten spielt Dir der iostream einen Streich und der Lexer/Parser sieht immer nur die Eingabe bis zum nächsten Leerzeichen.

    HTH
    Regards Hartmut



  • Endlich kann ich Spaß mit Spirit haben :>

    Danke ^^


Anmelden zum Antworten