tools/qdoc3/cpptoqsconverter.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the tools applications of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 /*
       
    43   cpptoqsconverter.cpp
       
    44 */
       
    45 
       
    46 #include "config.h"
       
    47 #include "cpptoqsconverter.h"
       
    48 
       
    49 QT_BEGIN_NAMESPACE
       
    50 
       
    51 #define CONFIG_QUICK                    "quick"
       
    52 #define CONFIG_INDENTSIZE               "indentsize"
       
    53 
       
    54 void setTabSize( int size );
       
    55 void setIndentSize( int size );
       
    56 int columnForIndex( const QString& t, int index );
       
    57 int indentForBottomLine( const QStringList& program, QChar typedIn );
       
    58 
       
    59 static QString balancedParens = "(?:[^()]+|\\([^()]*\\))*";
       
    60 
       
    61 QRegExp CppToQsConverter::qClassRegExp;
       
    62 QRegExp CppToQsConverter::addressOperatorRegExp;
       
    63 QRegExp CppToQsConverter::gulbrandsenRegExp;
       
    64 int CppToQsConverter::tabSize;
       
    65 
       
    66 ClassNode *CppToQsConverter::findClassNode( Tree *qsTree,
       
    67 					    const QString& qtName )
       
    68 {
       
    69     ClassNode *classe = (ClassNode *) qsTree->findNode( QStringList(qtName), Node::Class );
       
    70     if ( classe == 0 )
       
    71 	classe = (ClassNode *) qsTree->findNode( QStringList(qtName.mid(1)), Node::Class );
       
    72     return classe;
       
    73 }
       
    74 
       
    75 QString CppToQsConverter::convertedDataType( Tree *qsTree,
       
    76 					     const QString& leftType,
       
    77 					     const QString& /* rightType */ )
       
    78 {
       
    79     QString s = leftType;
       
    80 
       
    81     if ( s.startsWith("const ") )
       
    82 	s = s.mid( 6 );
       
    83     while ( s.endsWith("*") || s.endsWith("&") || s.endsWith(" ") )
       
    84 	s.truncate( s.length() - 1 );
       
    85 
       
    86     switch ( s[0].unicode() ) {
       
    87     case 'Q':
       
    88 	if ( s == "QCString" ) {
       
    89 	    return "String";
       
    90 	} else {
       
    91 	    Node *node = findClassNode( qsTree, s );
       
    92 	    if ( node == 0 ) {
       
    93 		return "";
       
    94 	    } else {
       
    95 		return node->name();
       
    96 	    }
       
    97 	}
       
    98 	break;
       
    99     case 'b':
       
   100 	if ( s == "bool" )
       
   101 	    return "Boolean";
       
   102 	break;
       
   103     case 'c':
       
   104 	if ( s == "char" ) {
       
   105 	    if ( leftType == "const char *" ) {
       
   106 		return "String";
       
   107 	    } else {
       
   108 		return "Number";
       
   109 	    }
       
   110 	}
       
   111 	break;
       
   112     case 'd':
       
   113 	if ( s == "double" )
       
   114 	    return "Number";
       
   115 	break;
       
   116     case 'f':
       
   117 	if ( s == "float" )
       
   118 	    return "Number";
       
   119     case 'i':
       
   120 	if ( s == "int" )
       
   121 	    return "Number";
       
   122 	break;
       
   123     case 'l':
       
   124 	if ( s == "long" || s == "long int" || s == "long long" ||
       
   125 	     s == "long long int" || s == "long double" )
       
   126 	    return "Number";
       
   127 	break;
       
   128     case 's':
       
   129 	if ( s == "short" || s == "short int" || s == "signed char" ||
       
   130 	     s == "signed short" || s == "signed short int" || s == "signed" ||
       
   131 	     s == "signed int" || s == "signed long" || s == "signed long int" )
       
   132 	    return "Number";
       
   133 	break;
       
   134     case 'u':
       
   135 	if ( s == "uchar" || s == "unsigned" || s == "unsigned char" ||
       
   136 	     s == "ushort" || s == "unsigned short" ||
       
   137 	     s == "unsigned short int" || s == "uint" || s == "unsigned int" ||
       
   138 	     s == "ulong" || s == "unsigned long" || s == "unsigned long int" )
       
   139 	    return "Number";
       
   140 	break;
       
   141     case 'v':
       
   142 	if ( s == "void" )
       
   143 	    return "";
       
   144     }
       
   145     return s;
       
   146 }
       
   147 
       
   148 QString CppToQsConverter::convertedCode( Tree *qsTree, const QString& code,
       
   149 					 const QSet<QString>& classesWithNoQ )
       
   150 {
       
   151     QString result;
       
   152     QStringList program;
       
   153     QStringList comments;
       
   154     int programWidth = 0;
       
   155 
       
   156     QStringList originalLines = code.split("\n");
       
   157     QStringList::ConstIterator ol = originalLines.begin();
       
   158     while ( ol != originalLines.end() ) {
       
   159 	QString code = (*ol).trimmed();
       
   160 	QString comment;
       
   161 
       
   162 	int slashSlash = code.indexOf( "//" );
       
   163 	if ( slashSlash != -1 ) {
       
   164 	    comment = code.mid( slashSlash );
       
   165 	    code.truncate( slashSlash );
       
   166 	    code = code.trimmed();
       
   167 	}
       
   168 
       
   169 	code = convertCodeLine( qsTree, program, code, classesWithNoQ );
       
   170 	program.append( code );
       
   171 
       
   172 	comment = convertComment( qsTree, comment, classesWithNoQ );
       
   173 	comments.append( comment );
       
   174 
       
   175 	int n = indentForBottomLine( program, QChar::Null );
       
   176 	for ( int i = 0; i < n; i++ )
       
   177 	    program.last().prepend( " " );
       
   178 
       
   179 	int width = columnForIndex( program.last(), program.last().length() );
       
   180 	if ( !comment.isEmpty() && width > programWidth )
       
   181 	    programWidth = width;
       
   182 	++ol;
       
   183     }
       
   184 
       
   185     programWidth = ( (programWidth + (tabSize - 1) + 2) / tabSize ) * tabSize;
       
   186 
       
   187     QStringList::ConstIterator p = program.begin();
       
   188     QStringList::ConstIterator c = comments.begin();
       
   189     while ( c != comments.end() ) {
       
   190 	if ( c != comments.begin() )
       
   191 	    result += "\n";
       
   192 
       
   193 	if ( (*p).trimmed().isEmpty() ) {
       
   194 	    if ( !(*c).isEmpty() )
       
   195 		result += *p;
       
   196 	} else {
       
   197 	    result += *p;
       
   198 	    if ( !(*c).isEmpty() ) {
       
   199 		int i = columnForIndex( *p, (*p).length() );
       
   200 		while ( i++ < programWidth )
       
   201 		    result += " ";
       
   202 	    }
       
   203 	}
       
   204 	result += *c;
       
   205 	++p;
       
   206 	++c;
       
   207     }
       
   208     return result;
       
   209 }
       
   210 
       
   211 void CppToQsConverter::initialize( const Config& config )
       
   212 {
       
   213     qClassRegExp.setPattern( "\\bQ([A-Z][A-Za-z]+)\\b" );
       
   214     addressOperatorRegExp.setPattern( "([(\\s])[*&]([a-zA-Z])" );
       
   215     gulbrandsenRegExp.setPattern( "\\b::\\b|->" );
       
   216 
       
   217     tabSize = config.getInt( CONFIG_TABSIZE );
       
   218     setTabSize( tabSize );
       
   219 
       
   220     int size = config.getInt( CONFIG_QUICK + Config::dot + CONFIG_INDENTSIZE );
       
   221     if ( size > 0 )
       
   222 	setIndentSize( size );
       
   223 }
       
   224 
       
   225 void CppToQsConverter::terminate()
       
   226 {
       
   227 }
       
   228 
       
   229 QString CppToQsConverter::convertCodeLine( Tree *qsTree,
       
   230 					   const QStringList& program,
       
   231 					   const QString& code,
       
   232 					   const QSet<QString>& classesWithNoQ )
       
   233 {
       
   234     static QString dataTypeFmt =
       
   235 	"(?!return)(?:const\\b\\s*)?[A-Za-z_]+(?:\\s*[*&])?";
       
   236     static QRegExp funcPrototypeRegExp(
       
   237 	"(" + dataTypeFmt + ")\\s*\\b([A-Z][a-zA-Z_0-9]*::)?"
       
   238 	"([a-z][a-zA-Z_0-9]*)\\(([^);]*)(\\)?)(?:\\s*const)?" );
       
   239     static QRegExp paramRegExp(
       
   240 	"^\\s*(" + dataTypeFmt + ")\\s*\\b([a-z][a-zA-Z_0-9]*)\\s*(,)?\\s*" );
       
   241     static QRegExp uninitVarRegExp(
       
   242 	"(" + dataTypeFmt + ")\\s*\\b([a-z][a-zA-Z_0-9]*);" );
       
   243     static QRegExp eqVarRegExp(
       
   244 	dataTypeFmt + "\\s*\\b([a-z][a-zA-Z_0-9]*)\\s*=(\\s*)(.*)" );
       
   245     static QRegExp ctorVarRegExp(
       
   246 	"(" + dataTypeFmt + ")\\s*\\b([a-z][a-zA-Z_0-9]*)\\((.*)\\);" );
       
   247     static QRegExp qdebugRegExp(
       
   248 	"q(?:Debug|Warning|Fatal)\\(\\s*(\"(?:\\\\.|[^\"])*\")\\s*"
       
   249 	"(?:,\\s*(\\S(?:[^,]*\\S)?))?\\s*\\);" );
       
   250     static QRegExp coutRegExp( "c(?:out|err)\\b(.*);" );
       
   251     static QRegExp lshiftRegExp( "\\s*<<\\s*" );
       
   252     static QRegExp endlRegExp( "^endl$" );
       
   253 
       
   254     if ( code.isEmpty() || code == "{" || code == "}" )
       
   255 	return code;
       
   256 
       
   257     QString result;
       
   258 
       
   259     if ( funcPrototypeRegExp.exactMatch(code) ) {
       
   260 	QString returnType = funcPrototypeRegExp.cap( 1 );
       
   261 	QString className = funcPrototypeRegExp.cap( 2 );
       
   262 	QString funcName = funcPrototypeRegExp.cap( 3 );
       
   263 	QString params = funcPrototypeRegExp.cap( 4 ).trimmed();
       
   264 	bool toBeContinued = funcPrototypeRegExp.cap( 5 ).isEmpty();
       
   265         // ### unused
       
   266         Q_UNUSED(toBeContinued);
       
   267 
       
   268 	className.replace( "::", "." );
       
   269 
       
   270 	result = "function " + className + funcName + "(";
       
   271 
       
   272 	if ( !params.isEmpty() && params != "void" ) {
       
   273 	    result += " ";
       
   274 	    int i = funcPrototypeRegExp.pos( 4 );
       
   275 	    while ( (i = paramRegExp.indexIn(code, i,
       
   276 					     QRegExp::CaretAtOffset)) != -1 ) {
       
   277 		QString dataType = paramRegExp.cap( 1 );
       
   278 		QString paramName = paramRegExp.cap( 2 );
       
   279 		QString comma = paramRegExp.cap( 3 );
       
   280 
       
   281 		result += paramName + " : " +
       
   282 			  convertedDataType( qsTree, dataType );
       
   283 		if ( comma.isEmpty() )
       
   284 		    break;
       
   285 		result += ", ";
       
   286 		i += paramRegExp.matchedLength();
       
   287 	    }
       
   288 	    result += " ";
       
   289 	}
       
   290 
       
   291 	result += ")";
       
   292 	returnType = convertedDataType( qsTree, returnType );
       
   293 	if ( !returnType.isEmpty() )
       
   294 	    result += " : " + returnType;
       
   295     } else if ( uninitVarRegExp.exactMatch(code) ) {
       
   296 	QString dataType = uninitVarRegExp.cap( 1 );
       
   297 	QString varName = uninitVarRegExp.cap( 2 );
       
   298 
       
   299 	result = "var " + varName;
       
   300 	dataType = convertedDataType( qsTree, dataType );
       
   301 	if ( !dataType.isEmpty() )
       
   302 	    result += " : " + dataType;
       
   303 	result += ";";
       
   304     } else if ( eqVarRegExp.exactMatch(code) ) {
       
   305 	QString varName = eqVarRegExp.cap( 1 );
       
   306 	QString value = eqVarRegExp.cap( 3 );
       
   307 
       
   308 	value = convertExpr( qsTree, value, classesWithNoQ );
       
   309 	result += "var " + varName + " = " + value;
       
   310     } else if ( ctorVarRegExp.exactMatch(code) ) {
       
   311 	QString dataType = ctorVarRegExp.cap( 1 );
       
   312 	QString varName = ctorVarRegExp.cap( 2 );
       
   313 	QString value = ctorVarRegExp.cap( 3 ).trimmed();
       
   314 
       
   315 	result += "var " + varName + " = ";
       
   316 
       
   317 	dataType = convertedDataType( qsTree, dataType );
       
   318 	value = convertExpr( qsTree, value, classesWithNoQ );
       
   319 
       
   320 	if ( dataType.isEmpty() || dataType == "String" ) {
       
   321 	    if ( value.contains(",") ) {
       
   322 		result += "...";
       
   323 	    } else {
       
   324 		result += value;
       
   325 	    }
       
   326 	} else {
       
   327 	    result += "new " + dataType;
       
   328 	    if ( !value.isEmpty() )
       
   329 		result += "( " + value + " )";
       
   330 	}
       
   331 	result += ";";
       
   332     } else if ( qdebugRegExp.exactMatch(code) ) {
       
   333 	QString fmt = qdebugRegExp.cap( 1 );
       
   334 	QString arg1 = qdebugRegExp.cap( 2 );
       
   335 
       
   336 	result += "println ";
       
   337 	int i = 0;
       
   338 	while ( i < (int) fmt.length() ) {
       
   339 	    if ( fmt[i] == '%' ) {
       
   340 		int percent = i;
       
   341 		i++;
       
   342 		while ( i < (int) fmt.length() &&
       
   343 			QString("diouxXeEfFgGaAcsCSpn%\"").indexOf(fmt[i]) == -1 )
       
   344 		    i++;
       
   345 		if ( fmt[i] == '%' ) {
       
   346 		    result += fmt[i++];
       
   347 		} else if ( fmt[i] != '"' ) {
       
   348 		    if ( percent == 1 ) {
       
   349 			result.truncate( result.length() - 1 );
       
   350 		    } else {
       
   351 			result += "\" + ";
       
   352 		    }
       
   353 		    i++;
       
   354 		    if ( arg1.endsWith(".latin1()") )
       
   355 			arg1.truncate( arg1.length() - 9 );
       
   356 		    result += arg1;
       
   357 		    if ( i == (int) fmt.length() - 1 ) {
       
   358 			i++;
       
   359 		    } else {
       
   360 			result += " + \"";
       
   361 		    }
       
   362 		}
       
   363 	    } else {
       
   364 		result += fmt[i++];
       
   365 	    }
       
   366 	}
       
   367 	result += ";";
       
   368     } else if ( coutRegExp.exactMatch(code) &&
       
   369 		program.filter("var cout").isEmpty() ) {
       
   370 	QStringList args = coutRegExp.cap(1).split(lshiftRegExp);
       
   371 	args.replaceInStrings( endlRegExp, "\"\\n\"" );
       
   372 	if ( args.last() == "\"\\n\"" ) {
       
   373 	    args.erase( args.end() - 1 );
       
   374 	    if ( args.isEmpty() )
       
   375 		args << "\"\"";
       
   376 	    result += "println ";
       
   377 	} else {
       
   378 	    result += "print ";
       
   379 	}
       
   380 	result += args.join( " + " ) + ";";
       
   381     } else {
       
   382 	result = convertExpr( qsTree, code, classesWithNoQ );
       
   383     }
       
   384     return result;
       
   385 }
       
   386 
       
   387 QString CppToQsConverter::convertComment( Tree * /* qsTree */,
       
   388 					  const QString& comment,
       
   389 					  const QSet<QString>& classesWithNoQ )
       
   390 
       
   391 {
       
   392     QString result = comment;
       
   393 
       
   394     result.replace( "TRUE", "true" );
       
   395     result.replace( "FALSE", "false" );
       
   396     result.replace( addressOperatorRegExp, "\\1\\2" );
       
   397     result.replace( gulbrandsenRegExp, "." );
       
   398 
       
   399     int i = 0;
       
   400     while ( (i = result.indexOf(qClassRegExp, i)) != -1 ) {
       
   401 	if ( classesWithNoQ.contains(qClassRegExp.cap(1)) )
       
   402 	    result.remove( i, 1 );
       
   403 	i++;
       
   404     }
       
   405     return result;
       
   406 }
       
   407 
       
   408 QString CppToQsConverter::convertExpr( Tree *qsTree, const QString& expr,
       
   409 				       const QSet<QString>& classesWithNoQ )
       
   410 {
       
   411     // suboptimal
       
   412     return convertComment( qsTree, expr, classesWithNoQ );
       
   413 }
       
   414 
       
   415 QT_END_NAMESPACE