// Copyright (C) 2005 Davis E. King (davis@dlib.net) // License: Boost Software License See LICENSE.txt for the full license. #ifndef DLIB_CPP_PRETTY_PRINTER_KERNEl_2_ #define DLIB_CPP_PRETTY_PRINTER_KERNEl_2_ #include #include #include #include "cpp_pretty_printer_kernel_abstract.h" #include "../algs.h" namespace dlib { template < typename stack, typename tok > class cpp_pretty_printer_kernel_2 { /*! REQUIREMENTS ON stack must be an implementation of stack/stack_kernel_abstract.h and stack::type == unsigned long REQUIREMENTS ON tok must be an implementation of tokenizer/tokenizer_kernel_abstract.h INFO This implementation applies a black and white color scheme suitable for printing on a black and white printer. It also places the document title prominently at the top of the pretty printed source file. !*/ public: cpp_pretty_printer_kernel_2 ( ); virtual ~cpp_pretty_printer_kernel_2 ( ); void print ( std::istream& in, std::ostream& out, const std::string& title ) const; void print_and_number ( std::istream& in, std::ostream& out, const std::string& title ) const; private: // data members mutable tok t; const std::string htmlify ( const std::string& str ) const; /*! ensures - str == str but with any '<' replaced with '<', any '>' replaced with '>', and any '&' replaced with '&' !*/ void number ( std::istream& in, std::ostream& out ) const; /*! ensures - prints in to out and adds line numbers !*/ // restricted functions cpp_pretty_printer_kernel_2(const cpp_pretty_printer_kernel_2&); // copy constructor cpp_pretty_printer_kernel_2& operator=(const cpp_pretty_printer_kernel_2&); // assignment operator }; // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // member function definitions // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- template < typename stack, typename tok > cpp_pretty_printer_kernel_2:: cpp_pretty_printer_kernel_2 ( ) { } // ---------------------------------------------------------------------------------------- template < typename stack, typename tok > cpp_pretty_printer_kernel_2:: ~cpp_pretty_printer_kernel_2 ( ) { } // ---------------------------------------------------------------------------------------- template < typename stack, typename tok > void cpp_pretty_printer_kernel_2:: print ( std::istream& in, std::ostream& out, const std::string& title ) const { using namespace std; if (!out) throw std::ios::failure("error occurred in cpp_pretty_printer_kernel_2::print"); t.set_stream(in); out << "" << "" << title << "" << "

" << title << "

\n"
            << "\n";
        if (!out)
            throw std::ios::failure("error occurred in cpp_pretty_printer_kernel_2::print");

        unsigned long scope = 0; // counts the number of new scopes we have entered 
                        // since we were at a scope where functions can be declared

        bool recently_seen_class_keyword = false;
            // true if we have seen the keywords class or struct and
            // we have not seen any identifiers or { characters

        bool recently_seen_include = false;
            // true if we have seen the #include keyword and have not seen double
            // quoted text or >

        bool recently_seen_new_scope = false;  
            // true if we have seen the keywords class, namespace, or struct and
            // we have not seen the characters {, ), or ; since then

        bool recently_seen_paren = false;
            // true if we have seen a ) and we have only seen white_space or comments since

        bool in_initialization_list = false;
            // true if we have seen a ) followed by any white space or comments and then
            // followed by a : (in scope==0 with recently_seen_preprocessor==false) and we 
            // have not yet seen the character { or ;

        bool recently_seen_preprocessor = false;
            // true if we have seen the #pragma or #if or #define or #elif keyword and 
            // have not seen an identifier.


        bool recently_seen_extern = false;
            // true if we have seen the extern keyword and haven't yet seen a 
            // { or ; character.

        unsigned long paren_count = 0; 
            // this is the number of ( we have seen minus the number of ) we have
            // seen.
            

        int type;
        stack scopes; // a stack to hold old scopes
        string token, temp;
        t.get_token(type,token);
        while (type != tok::END_OF_FILE)
        {
            switch (type)
            {
            case tok::IDENTIFIER: // ------------------------------------------
                if ( recently_seen_class_keyword)
                {
                    // this might be a class name so check if there is a 
                    // ; or identifier or * or & coming up.
                    type = t.peek_type();
                    temp.clear();
                    if (type == tok::WHITE_SPACE)
                    {
                        t.get_token(type,temp);
                        if (temp.find_first_of("\n\r") != string::npos)
                            recently_seen_preprocessor = false;
                    }
                    if (t.peek_token() != ";" && t.peek_type() != tok::IDENTIFIER &&
                        t.peek_token() != "*" && t.peek_token() != "&")
                    {
                        // this is the name of a class or struct in a class or
                        // struct declaration.
                        out << "" << token << "" << temp;
                    }
                    else
                    {
                        out << token << temp;
                    }
                }
                else if ( !in_initialization_list &&
                     !recently_seen_preprocessor &&
                     scope == 0 &&
                     paren_count == 0)
                {
                    // this might be a function name so check if there is a 
                    // ( coming up.
                    type = t.peek_type();
                    temp.clear();
                    if (type == tok::WHITE_SPACE)
                    {
                        t.get_token(type,temp);
                        type = t.peek_type();
                    }
                    if (type == tok::OTHER && t.peek_token() == "(")
                    {
                        // this is a function definition or prototype
                        out << "" << token << "" << temp;
                    }
                    else
                    {
                        out << token << temp;
                    }
                }
                else
                {
                    out << token;
                }
                


                recently_seen_class_keyword = false;
                recently_seen_paren = false;
                break;

            case tok::KEYWORD: // ---------------------------------------------
                if (scope == 0 && token == "operator")
                {
                    // Doing this is sort of weird since operator is really a keyword
                    // but I just like how this looks.
                    out << "" << token << "";
                }
                // this isn't a keyword if it is something like #include 
                else if (!recently_seen_include) 
                {
                    // This is a normal keyword
                    out << "" << token << "";
                }
                else
                {
                    out << token;
                }

                if (token == "#include") 
                {
                    recently_seen_include = true;
                }
                else if (token == "class")
                {
                    recently_seen_new_scope = true;
                    recently_seen_class_keyword = true;
                }
                else if (token == "namespace")
                {
                    recently_seen_new_scope = true;
                }
                else if (token == "struct")
                {
                    recently_seen_new_scope = true;
                    recently_seen_class_keyword = true;
                }
                else if (token == "#pragma" || token == "#define" || token == "#elif" || token == "#if")
                {
                    recently_seen_preprocessor = true;
                }
                else if (token == "extern")
                {
                    recently_seen_extern = true;
                }
                recently_seen_paren = false;
                break;

            case tok::COMMENT: // ---------------------------------------------
                {
                    out << "" << htmlify(token) << "";
                }
                break;

            case tok::SINGLE_QUOTED_TEXT: // ----------------------------------
                {
                    out << htmlify(token);
                    recently_seen_paren = false;
                }
                break;

            case tok::WHITE_SPACE: // -----------------------------------------
                {
                    out << token;
                    if (token.find_first_of("\n\r") != string::npos)
                        recently_seen_preprocessor = false;
                }
                break;

            case tok::DOUBLE_QUOTED_TEXT: // ----------------------------------
                {                    
                    out << htmlify(token);
                    recently_seen_paren = false;
                    recently_seen_include = false;
                }
                break;

            case tok::NUMBER:
            case tok::OTHER: // -----------------------------------------------               
                switch (token[0])
                {
                case '{':
                    out << "{";  
                    // if we are entering a new scope
                    if (recently_seen_new_scope || recently_seen_extern)
                    {
                        recently_seen_new_scope = false;
                        scopes.push(scope);
                        scope = 0;
                    }
                    else
                    {
                        ++scope;
                    }
                    in_initialization_list = false;
                    recently_seen_paren = false;
                    recently_seen_class_keyword = false;
                    recently_seen_extern = false;
                    break;
                case '}':
                    out << "}";
                    if (scope > 0)
                    {
                        --scope;
                    }
                    else if (scopes.size())
                    {
                        scopes.pop(scope);
                    }
                    recently_seen_paren = false;
                    break;

                case ':':
                    out << ':';
                    if (recently_seen_paren && scope == 0 &&
                        recently_seen_preprocessor == false)
                    {
                        in_initialization_list = true;
                    }
                    recently_seen_paren = false;
                    break;

                case ';': 
                    out << ';';
                    recently_seen_new_scope = false;
                    recently_seen_paren = false;
                    recently_seen_extern = false;
                    break;

                case ')':
                    out << ')';
                    recently_seen_paren = true;
                    recently_seen_new_scope = false;
                    --paren_count;
                    break;

                case '(':
                    out << '(';
                    recently_seen_paren = false;
                    ++paren_count;
                    break;

                case '>':
                    recently_seen_include = false;
                    out << ">";
                    recently_seen_paren = true;
                    break;

                case '<':
                    out << "<";
                    recently_seen_paren = true;
                    break;

                case '&':
                    out << "&";
                    recently_seen_paren = true;
                    break;

                default:
                    out << token;
                    recently_seen_paren = false;
                    if (token == ">")
                        recently_seen_include = false;
                    break;

                } // switch (token[0])
                break;

            } // switch (type)

            t.get_token(type,token);
        } // while (type != tok::END_OF_FILE)


        out << "
"; if (!out) throw std::ios::failure("error occurred in cpp_pretty_printer_kernel_2::print"); } // ---------------------------------------------------------------------------------------- template < typename stack, typename tok > void cpp_pretty_printer_kernel_2:: print_and_number ( std::istream& in, std::ostream& out, const std::string& title ) const { using namespace std; ostringstream sout; print(in,sout,title); istringstream sin(sout.str()); number(sin,out); } // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // private member function definitions // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- template < typename stack, typename tok > void cpp_pretty_printer_kernel_2:: number ( std::istream& in, std::ostream& out ) const { if (!out) throw std::ios::failure("error occurred in cpp_pretty_printer_kernel_2::number"); std::string space = "   "; std::ios::int_type ch; unsigned long count = 1; while ((ch=in.get()) != EOF) { if (ch != '\n') { out << (char)ch; } else { out << "\n" << count << " " + space; ++count; if (count == 10) space = "  "; if (count == 100) space = " "; if (count == 1000) space = ""; } } if (!out) throw std::ios::failure("error occurred in cpp_pretty_printer_kernel_2::number"); } // ---------------------------------------------------------------------------------------- template < typename stack, typename tok > const std::string cpp_pretty_printer_kernel_2:: htmlify ( const std::string& str ) const { std::string::size_type i; std::string temp; for (i = 0; i < str.size(); ++i) { if (str[i] == '<') temp += "<"; else if (str[i] == '>') temp += ">"; else if (str[i] == '&') temp += "&"; else temp += str[i]; } return temp; } // ---------------------------------------------------------------------------------------- } #endif // DLIB_CPP_PRETTY_PRINTER_KERNEl_2_