dlib C++ Library - cpp_pretty_printer_kernel_1.h

    // 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_1_
    #define DLIB_CPP_PRETTY_PRINTER_KERNEl_1_

    #include <string>
    #include <iostream>
    #include <sstream>
    #include "cpp_pretty_printer_kernel_abstract.h"
    #include "../algs.h"

    namespace dlib
    {

        template <
            typename stack,
            typename tok
            >
        class cpp_pretty_printer_kernel_1
        {
            /*!
                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 color scheme, turns include directives
                    such as #include "file.h" into links to file.h.html, and it also puts
                    HTML anchor points on function and class declarations.
            !*/

        public:

            cpp_pretty_printer_kernel_1 (
            );

            virtual ~cpp_pretty_printer_kernel_1 (
            );

            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:

            const std::string htmlify (
                const std::string& str
            ) const;
            /*!
                ensures
                    - str == str but with any '<' replaced with '&lt;', any '>' replaced
                      with '&gt;', and any '&' replaced with '&amp;'
            !*/

            // data members
            mutable tok t;

            void number (
                std::istream& in,
                std::ostream& out
            ) const;
            /*!
                ensures
                    - prints in to out and adds line numbers
            !*/

            // restricted functions
            cpp_pretty_printer_kernel_1(const cpp_pretty_printer_kernel_1&);        // copy constructor
            cpp_pretty_printer_kernel_1& operator=(const cpp_pretty_printer_kernel_1&);    // assignment operator

        };

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------
        // member function definitions
    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

        template <
            typename stack,
            typename tok
            >
        cpp_pretty_printer_kernel_1<stack,tok>::
        cpp_pretty_printer_kernel_1 (
        )
        {
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename stack,
            typename tok
            >
        cpp_pretty_printer_kernel_1<stack,tok>::
        ~cpp_pretty_printer_kernel_1 (
        )
        {
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename stack,
            typename tok
            >
        void cpp_pretty_printer_kernel_1<stack,tok>::
        print (
            std::istream& in,
            std::ostream& out,
            const std::string& title
        ) const
        {
            if (!out)
                throw std::ios::failure("error occurred in cpp_pretty_printer_kernel_1::print");

            t.set_stream(in);

            out << "<html><!-- "
                << "Created using the cpp_pretty_printer from the dlib C++ library.  See http://dlib.net for updates."
                << " --><head><title>" << title << "</title></head><body bgcolor='white'><pre>\n";
            if (!out)
                throw std::ios::failure("error occurred in cpp_pretty_printer_kernel_1::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, struct, or enum 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 keywords and have
                // not seen an end of line.

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

            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
            std::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") != std::string::npos)
                                recently_seen_preprocessor = false;
                        }
                        if (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 << "<b><a name='" << token << "'></a>" << token << "</b>" << temp;
                        }
                        else
                        {
                            out << token << temp;
                        }
                    }
                    else if ( !in_initialization_list &&
                         !recently_seen_preprocessor )
                    {
                        // 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() == "(")
                        {
                            if (scope == 0 && paren_count == 0)
                            {
                                // this is a function definition or prototype
                                out << "<b><a name='" << token << "'></a>" << token << "</b>" << temp;
                            }
                            else
                            {
                                // this is a function call (probably)
                                out << "<font color='#BB00BB'>" << token << "</font>" << 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 << "<b><a name='" << token << "'></a>" << token << "</b>";
                    }
                    // this isn't a keyword if it is something like #include <new>
                    else if ( token == "true" || token == "false")
                    {
                        // color 'true' and 'false' the same way we color numbers
                        out << "<font color='#979000'>" << token << "</font>";
                    }
                    else if (!recently_seen_include)
                    {
                        // This is a normal keyword
                        if (token == "char" || token == "unsigned" || token == "signed" ||
                            token == "short" || token == "int" || token == "long" ||
                            token == "float" || token == "double" || token == "bool" ||
                            token == "void" || token == "size_t" || token == "wchar_t")
                        {
                            out << "<font color='#0000FF'><u>" << token << "</u></font>";
                        }
                        else
                        {
                            out << "<font color='#0000FF'>" << token << "</font>";
                        }
                    }
                    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 == "enum")
                    {
                        recently_seen_class_keyword = true;
                    }
                    else if (token == "struct")
                    {
                        recently_seen_new_scope = true;
                        recently_seen_class_keyword = true;
                    }
                    else if (token == "#pragma" || token == "#if" || token == "#define" || token == "#elif")
                    {
                        recently_seen_preprocessor = true;
                    }
                    else if (token == "extern")
                    {
                        recently_seen_extern = true;
                    }
                    recently_seen_paren = false;
                    break;

                case tok::COMMENT: // ---------------------------------------------
                    {
                        // if this is a special anchor comment
                        if (token.size() > 4 &&
                            token[0] == '/' &&
                            token[1] == '*' &&
                            token[2] == '!' &&
                            token[3] == 'A' &&
                            token[4] == ' '
                        )
                        {
                            temp = token;
                            std::istringstream sin(token);
                            sin >> temp;
                            sin >> temp;
                            sin.get();
                            // if there was still more stuff in the token then we are ok.
                            if (sin)
                                out << "<a name='" << temp << "'/>";
                        }
                        out << "<font color='#009900'>" << htmlify(token) << "</font>";
                    }
                    break;

                case tok::SINGLE_QUOTED_TEXT: // ----------------------------------
                    {
                        out << "<font color='#FF0000'>" << htmlify(token) << "</font>";
                        recently_seen_paren = false;
                    }
                    break;

                case tok::NUMBER: // -----------------------------------------
                    {
                        out << "<font color='#979000'>" << token << "</font>";
                        recently_seen_include = false;
                    }
                    break;

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

                case tok::DOUBLE_QUOTED_TEXT: // ----------------------------------
                    {
                        if (recently_seen_include)
                        {
                            // this is the name of an included file
                            recently_seen_include = false;
                            out << "<a style='text-decoration:none' href='" << htmlify(token) << ".html'>" << htmlify(token) << "</a>";
                        }
                        else
                        {
                            // this is just a normal quoted string
                            out << "<font color='#CC0000'>" << htmlify(token) << "</font>";
                        }
                        recently_seen_paren = false;
                    }
                    break;

                case tok::OTHER: // -----------------------------------------------
                    switch (token[0])
                    {
                    case '{':
                        out << "<b>{</b>";
                        // 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 << "<b>}</b>";
                        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 << "<font face='Lucida Console'>)</font>";
                        recently_seen_paren = true;
                        recently_seen_new_scope = false;
                        --paren_count;
                        break;

                    case '(':
                        out << "<font face='Lucida Console'>(</font>";
                        recently_seen_paren = false;
                        ++paren_count;
                        break;

                    case '>':
                        recently_seen_include = false;
                        out << "<font color='#5555FF'>&gt;</font>";
                        recently_seen_paren = false;
                        break;

                    case '<':
                        out << "<font color='#5555FF'>&lt;</font>";
                        recently_seen_paren = false;
                        break;

                    case '&':
                        out << "<font color='#5555FF'>&amp;</font>";
                        recently_seen_paren = false;
                        break;

                    case '=':
                    case '+':
                    case '-':
                    case '/':
                    case '*':
                    case '!':
                    case '|':
                    case '%':
                        out << "<font color='#5555FF'>" << token << "</font>";
                        recently_seen_paren = false;
                        break;

                    default:
                        out << token;
                        recently_seen_paren = false;
                        break;

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

                } // switch (type)

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

            out << "\n</pre></body></html>";
            if (!out)
                throw std::ios::failure("error occurred in cpp_pretty_printer_kernel_1::print");
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename stack,
            typename tok
            >
        void cpp_pretty_printer_kernel_1<stack,tok>::
        print_and_number (
            std::istream& in,
            std::ostream& out,
            const std::string& title
        ) const
        {
            std::ostringstream sout;
            print(in,sout,title);
            std::istringstream sin(sout.str());
            number(sin,out);
        }

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------
        // private member function definitions
    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

        template <
            typename stack,
            typename tok
            >
        void cpp_pretty_printer_kernel_1<stack,tok>::
        number (
            std::istream& in,
            std::ostream& out
        ) const
        {
            if (!out)
                throw std::ios::failure("error occurred in cpp_pretty_printer_kernel_1::number");

            std::string space = "&nbsp;&nbsp;&nbsp;";
            std::ios::int_type ch;
            unsigned long count = 1;
            while ((ch=in.get()) != EOF)
            {
                if (ch != '\n')
                {
                    out << (char)ch;
                }
                else
                {
                    out << "\n<font color='555555'>" << count << " </font> " + space;
                    ++count;
                    if (count == 10)
                        space = "&nbsp;&nbsp;";
                    if (count == 100)
                        space = "&nbsp;";
                    if (count == 1000)
                        space = "";
                }
            }
            if (!out)
                throw std::ios::failure("error occurred in cpp_pretty_printer_kernel_1::number");
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename stack,
            typename tok
            >
        const std::string cpp_pretty_printer_kernel_1<stack,tok>::
        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 += "&lt;";
                else if (str[i] == '>')
                    temp += "&gt;";
                else if (str[i] == '&')
                    temp += "&amp;";
                else
                    temp += str[i];
            }
            return temp;
        }

    // ----------------------------------------------------------------------------------------

    }

    #endif // DLIB_CPP_PRETTY_PRINTER_KERNEl_1_