// 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 '<', any '>' replaced with '>', and any '&' replaced with '&' !*/ // 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 { using namespace std; 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 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 << "<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; 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") != 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'>></font>"; recently_seen_paren = false; break; case '<': out << "<font color='#5555FF'><</font>"; recently_seen_paren = false; break; case '&': out << "<font color='#5555FF'>&</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 { 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_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 = " "; 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 = " "; if (count == 100) space = " "; 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 += "<"; else if (str[i] == '>') temp += ">"; else if (str[i] == '&') temp += "&"; else temp += str[i]; } return temp; } // ---------------------------------------------------------------------------------------- } #endif // DLIB_CPP_PRETTY_PRINTER_KERNEl_1_