1 | /****************************************************************************
|
---|
2 | **
|
---|
3 | ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
---|
4 | ** All rights reserved.
|
---|
5 | ** Contact: Nokia Corporation ([email protected])
|
---|
6 | **
|
---|
7 | ** This file is part of the tools applications of the Qt Toolkit.
|
---|
8 | **
|
---|
9 | ** $QT_BEGIN_LICENSE:LGPL$
|
---|
10 | ** Commercial Usage
|
---|
11 | ** Licensees holding valid Qt Commercial licenses may use this file in
|
---|
12 | ** accordance with the Qt Commercial License Agreement provided with the
|
---|
13 | ** Software or, alternatively, in accordance with the terms contained in
|
---|
14 | ** a written agreement between you and Nokia.
|
---|
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 | ** GNU General Public License Usage
|
---|
29 | ** Alternatively, this file may be used under the terms of the GNU
|
---|
30 | ** General Public License version 3.0 as published by the Free Software
|
---|
31 | ** Foundation and appearing in the file LICENSE.GPL included in the
|
---|
32 | ** packaging of this file. Please review the following information to
|
---|
33 | ** ensure the GNU General Public License version 3.0 requirements will be
|
---|
34 | ** met: http://www.gnu.org/copyleft/gpl.html.
|
---|
35 | **
|
---|
36 | ** If you have questions regarding the use of this file, please contact
|
---|
37 | ** Nokia at [email protected].
|
---|
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
|
---|