584 lines
20 KiB
C++
584 lines
20 KiB
C++
// 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_
|
|
|