Defekt eigenes (Spaß) Präprozessors.



  • Ich habe zum verstehen aller Regeln des Präprozessors einen eigenen geschrieben. Ich scheine noch nicht alle Regeln der Makroexpansion erfasst zu haben. Es fehlt bei einem speziellen Fall ein letzter Scan.
    Deshalb wollte ich hier nach den Regeln der Makroexpansion nachfragen.

    Ich habe mir das hier durchgelesen:
    https://www.mirbsd.org/htman/i386/manINFO/cppinternals.html#Top

    und das hier mehrmals:
    https://gcc.gnu.org/onlinedocs/cpp/

    Hier ein paar Auszüge aus meinen Unit Tests:

    (Korrekte Fälle)
    ###################################################################################

    #define x (4 + y)
    #define y (2 * x)
    
    x
    y
    

    wird zu:

    (4 + (2 * x))
    (2 * (4 + y))
    

    ###################################################################################

    #define A_1 b
    #define B(param) param ## 1
    B(A_)
    

    wird zu

    b
    

    ###################################################################################

    #include <boost/preprocessor/control/if.hpp>
    
    BOOST_PP_IF(10, a, b) // expands to a
    BOOST_PP_IF(0, a, b) // expands to b
    

    wird zu

    a
    b
    

    ###################################################################################

    #define EMPTY()
    #define DEFER(id) id EMPTY()
    #define OBSTRUCT(id) id DEFER(EMPTY)()
    #define EXPAND(...) __VA_ARGS__
    
    #define A() 123
    A() // Expands to 123
    DEFER(A)() // Expands to A () because it requires one more scan to fully expand
    EXPAND(DEFER(A)()) // Expands to 123, because the EXPAND macro forces another scan
    

    wird zu:

    123
    A()
    123
    

    ###################################################################################
    Problemfälle
    ###################################################################################

    #include <boost/preprocessor/control/if.hpp>
    #include <boost/preprocessor/facilities/empty.hpp>
    #include <boost/preprocessor/punctuation/comma.hpp>
    
    BOOST_PP_IF(1, BOOST_PP_COMMA, BOOST_PP_EMPTY)() // expands to ,
    

    wird zu:

    BOOST_PP_COMMA()
    

    ###################################################################################

    #include <boost/preprocessor/repetition/enum.hpp>
    
    #define TEXT(z, n, text) text
    
    BOOST_PP_ENUM(4, TEXT, class) // expands to class, class, class, class
    

    wird zu:

    BOOST_PP_ENUM_1(4, TEXT, class)
    

    ###################################################################################

    Der bleibt bei sowas immer kleben.

    Ich vermute, dass man mir hier so nicht antworten werden können wird, deshalb versuche ich zuletzt noch den (momentan noch sehr langsamen - Ich will es noch beschleunigen optimieren wenn ich fertig bin) zugrunde liegenden Code zu kürzen:

    // [...] bedeutet dass ich code entfernt habe

    void Preprocessor::expandTokensFully(token_iterator line,
    									 std::vector <std::unique_ptr <Tokenizer::Token>>& tokens,
    									 std::vector <std::string> alreadyExpanded)
    {
        std::vector <std::unique_ptr <Tokenizer::Token>> newTokens; // this will hold all results
        bool stringizeNext = false;
    
        // [...] some self explanatory lambdas here
    
        for (; i != std::end(tokens); ++i)
        {
            // token pasting / token concatenation
    		if (test_next("##", i))
    		{
                // [...] does some token pasting here
            }
    
            if ((*i)->toString() == "#")
    		{
    			ERROR_ADD(StrayPPOP, LINE_NUMBER, {"#"});
    		}
    
            if ((*i)->getID() == Tokenizer::TokenID::IDENTIFIER)
            {    
                // is identifier a macro?
    			auto def = pImpl->definitions_.find((*i)->toString());
    			if (def == pImpl->definitions_.end())
    			{
    				// the identifier is not a macro, so just add the identifier to the token list
    				addToken(i); // is a lambda - adds token to "newTokens"
    				continue;
    			}
            }
    
            // self referential macro breaker
            //####################################################################################
            if (std::find(std::begin(alreadyExpanded), std::end(alreadyExpanded), def->first) 
            	!= std::end(alreadyExpanded))
            {
                addToken(i);
    			continue;
            }
            //####################################################################################
    
            // amount of expected parameters:
    		auto expectedCount = def->second.parameters.size();
    
            // if expectedCount > 0 -> functionMacro
    		if (def->second.hasArgumentList)
    		{
    			if (!test_next("(", i))
    			{
    				// is not this macro! -> therefor just an ordinary identifier
    				addToken(i);
    			}
                else
    			{
    				++i;
    
    				// get macro arguments
    				std::vector <std::vector <std::unique_ptr<Tokenizer::Token>>> actualParameters;
    				gatherArguments(i, std::end(tokens), actualParameters, line);
    
                    // [...] Error Handling Here
    
                    if (!def->second.parameters.empty())
    				{
    					// do argument prescan
    					argumentPrescan (actualParameters, line);
    
                        // [...] Error Handling Here
                    }
    
                    // DO EXPANSION
    				std::vector <std::unique_ptr <Tokenizer::Token> > temporaryBranch;
    				expand(line, def->second, actualParameters, stringizeNext, newTokens, temporaryBranch);
    
    				// I suspect this part to be very slow, but it was the "easiest" solution I could come up with for now
    				auto alt_state = alreadyExpanded;
    				alreadyExpanded.push_back(def->first);
    				expandTokensFully(temporaryBranch, line, alreadyExpanded); // recursive call creates branches that kill self referential macros
    				moveTokensOver(newTokens, temporaryBranch);
    				alreadyExpanded = std::move(alt_state);
                }
            }
            else // no parameters expected
            {
                // [...] just copies tokens
            }
        } // end token loop
        tokens = std::move(newTokens);
    }
    

    Ich komme nicht drauf. Es sind immer diese Fälle in denen durch die Ersetzung ein Makroname entsteht und eine Argumentliste folgt. Aber wenn ich weitere scans hinzufüge zerstöre ich immer die Regelung für selbstreferenzierenden Makros oder das Beispiel mit DEFER.

    EDIT: expandTokensFully wird immer auf "eine Zeile" voller Tokens angewendet.


  • Mod

    Wie lang wäre denn ein vollständiges Beispiel? Das Problem klingt interessant, aber sich durch den auch noch gekürzten Teil eines Abschnitts eines fremden Programms zu denken ist immer sehr schwer.

    PS: Folgendes Dokument kennst du?
    https://gcc.gnu.org/onlinedocs/cppinternals/



  • Ich habs in ein Git gepushed.
    Ich erwarte nicht, dass da jemand raufschaut, aber Fragen kostet ja nichts.

    https://bitbucket.org/5cript/preprocessor/src

    getestet mit MinGW (g++ 4.9 aber 4.8 müsste auch reichen) und Visual Studio.

    Abhängigkeiten:
    - boost system
    - boost filesystem
    - boost regex

    der gepostete Ausschnitt stammt aus "Preprocessor.cpp"

    Der Code enthielt "zum Glück" schon Kommentare (könnte aber vielleicht ein paar mehr vertragen :/). Außerdem sind die Javadoc like Kommentare unvollständig -.-

    Zuletzt: Ich gehe hier und da "den einfachen Weg". Trotzdem nehme ich gerne Kritik am Code entgegen. Vielleicht kommt da ja etwas wozu ich nicht einfach sagen würde: "war ich zu faul zu" 😉

    PS: Schon witzig, was Visual Studio alles kompiliert...

    EDIT: Kann es sein, dass VS die tabs nicht in Leerzeichen umwandelt? *seufz*


Log in to reply