author | Eckhart Koeppen <eckhart.koppen@nokia.com> |
Thu, 08 Apr 2010 14:19:33 +0300 | |
branch | RCL_3 |
changeset 7 | 3f74d0d4af4c |
parent 4 | 3b1da2848fc7 |
child 13 | c0432d11811c |
permissions | -rw-r--r-- |
0 | 1 |
/**************************************************************************** |
2 |
** |
|
4
3b1da2848fc7
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
3
diff
changeset
|
3 |
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
0 | 4 |
** All rights reserved. |
5 |
** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 |
** |
|
7 |
** This file is part of the Qt Linguist 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 |
#include "lupdate.h" |
|
43 |
||
44 |
#include <translator.h> |
|
45 |
||
46 |
#include <QtCore/QBitArray> |
|
47 |
#include <QtCore/QDebug> |
|
48 |
#include <QtCore/QFileInfo> |
|
49 |
#include <QtCore/QStack> |
|
50 |
#include <QtCore/QString> |
|
51 |
#include <QtCore/QTextCodec> |
|
52 |
#include <QtCore/QTextStream> |
|
53 |
||
54 |
#include <ctype.h> // for isXXX() |
|
55 |
||
56 |
QT_BEGIN_NAMESPACE |
|
57 |
||
58 |
/* qmake ignore Q_OBJECT */ |
|
59 |
||
60 |
static QString MagicComment(QLatin1String("TRANSLATOR")); |
|
61 |
||
62 |
#define STRING(s) static QString str##s(QLatin1String(#s)) |
|
63 |
||
64 |
//#define DIAGNOSE_RETRANSLATABILITY // FIXME: should make a runtime option of this |
|
65 |
||
66 |
class HashString { |
|
67 |
public: |
|
68 |
HashString() : m_hashed(false) {} |
|
69 |
explicit HashString(const QString &str) : m_str(str), m_hashed(false) {} |
|
70 |
void setValue(const QString &str) { m_str = str; m_hashed = false; } |
|
71 |
const QString &value() const { return m_str; } |
|
72 |
bool operator==(const HashString &other) const { return m_str == other.m_str; } |
|
73 |
private: |
|
74 |
QString m_str; |
|
75 |
mutable uint m_hash; |
|
76 |
mutable bool m_hashed; |
|
77 |
friend uint qHash(const HashString &str); |
|
78 |
}; |
|
79 |
||
80 |
uint qHash(const HashString &str) |
|
81 |
{ |
|
82 |
if (!str.m_hashed) { |
|
83 |
str.m_hashed = true; |
|
84 |
str.m_hash = qHash(str.m_str); |
|
85 |
} |
|
86 |
return str.m_hash; |
|
87 |
} |
|
88 |
||
89 |
class HashStringList { |
|
90 |
public: |
|
91 |
explicit HashStringList(const QList<HashString> &list) : m_list(list), m_hashed(false) {} |
|
92 |
const QList<HashString> &value() const { return m_list; } |
|
93 |
bool operator==(const HashStringList &other) const { return m_list == other.m_list; } |
|
94 |
private: |
|
95 |
QList<HashString> m_list; |
|
96 |
mutable uint m_hash; |
|
97 |
mutable bool m_hashed; |
|
98 |
friend uint qHash(const HashStringList &list); |
|
99 |
}; |
|
100 |
||
101 |
uint qHash(const HashStringList &list) |
|
102 |
{ |
|
103 |
if (!list.m_hashed) { |
|
104 |
list.m_hashed = true; |
|
105 |
uint hash = 0; |
|
106 |
foreach (const HashString &qs, list.m_list) { |
|
107 |
hash ^= qHash(qs) ^ 0xa09df22f; |
|
108 |
hash = (hash << 13) | (hash >> 19); |
|
109 |
} |
|
110 |
list.m_hash = hash; |
|
111 |
} |
|
112 |
return list.m_hash; |
|
113 |
} |
|
114 |
||
115 |
typedef QList<HashString> NamespaceList; |
|
116 |
||
117 |
struct Namespace { |
|
118 |
||
119 |
Namespace() : |
|
120 |
classDef(this), |
|
121 |
hasTrFunctions(false), complained(false) |
|
122 |
{} |
|
123 |
~Namespace() |
|
124 |
{ |
|
125 |
qDeleteAll(children); |
|
126 |
} |
|
127 |
||
128 |
QHash<HashString, Namespace *> children; |
|
129 |
QHash<HashString, NamespaceList> aliases; |
|
130 |
QList<HashStringList> usings; |
|
131 |
||
132 |
// Class declarations set no flags and create no namespaces, so they are ignored. |
|
133 |
// Class definitions may appear multiple times - but only because we are trying to |
|
134 |
// "compile" all sources irrespective of build configuration. |
|
135 |
// Nested classes may be forward-declared inside a definition, and defined in another file. |
|
136 |
// The latter will detach the class' child list, so clones need a backlink to the original |
|
137 |
// definition (either one in case of multiple definitions). |
|
3
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
138 |
// Namespaces can have tr() functions as well, so we need to track parent definitions for |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
139 |
// them as well. The complication is that we may have to deal with a forrest instead of |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
140 |
// a tree - in that case the parent will be arbitrary. However, it seem likely that |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
141 |
// Q_DECLARE_TR_FUNCTIONS would be used either in "class-like" namespaces with a central |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
142 |
// header or only locally in a file. |
0 | 143 |
Namespace *classDef; |
144 |
||
145 |
QString trQualification; |
|
146 |
||
147 |
bool hasTrFunctions; |
|
148 |
bool complained; // ... that tr functions are missing. |
|
149 |
}; |
|
150 |
||
151 |
static int nextFileId; |
|
152 |
||
153 |
class VisitRecorder { |
|
154 |
public: |
|
155 |
VisitRecorder() |
|
156 |
{ |
|
157 |
m_ba.resize(nextFileId); |
|
158 |
} |
|
159 |
bool tryVisit(int fileId) |
|
160 |
{ |
|
161 |
if (m_ba.at(fileId)) |
|
162 |
return false; |
|
163 |
m_ba[fileId] = true; |
|
164 |
return true; |
|
165 |
} |
|
166 |
private: |
|
167 |
QBitArray m_ba; |
|
168 |
}; |
|
169 |
||
170 |
struct ParseResults { |
|
171 |
int fileId; |
|
172 |
Namespace rootNamespace; |
|
173 |
QSet<const ParseResults *> includes; |
|
174 |
}; |
|
175 |
||
176 |
typedef QHash<QString, const ParseResults *> ParseResultHash; |
|
177 |
typedef QHash<QString, const Translator *> TranslatorHash; |
|
178 |
||
179 |
class CppFiles { |
|
180 |
||
181 |
public: |
|
182 |
static const ParseResults *getResults(const QString &cleanFile); |
|
183 |
static void setResults(const QString &cleanFile, const ParseResults *results); |
|
184 |
static const Translator *getTranslator(const QString &cleanFile); |
|
185 |
static void setTranslator(const QString &cleanFile, const Translator *results); |
|
186 |
static bool isBlacklisted(const QString &cleanFile); |
|
187 |
static void setBlacklisted(const QString &cleanFile); |
|
188 |
||
189 |
private: |
|
190 |
static ParseResultHash &parsedFiles(); |
|
191 |
static TranslatorHash &translatedFiles(); |
|
192 |
static QSet<QString> &blacklistedFiles(); |
|
193 |
}; |
|
194 |
||
195 |
class CppParser { |
|
196 |
||
197 |
public: |
|
198 |
CppParser(ParseResults *results = 0); |
|
199 |
void setInput(const QString &in); |
|
200 |
void setInput(QTextStream &ts, const QString &fileName); |
|
201 |
void setTranslator(Translator *_tor) { tor = _tor; } |
|
202 |
void parse(const QString &initialContext, ConversionData &cd, QSet<QString> &inclusions); |
|
203 |
void parseInternal(ConversionData &cd, QSet<QString> &inclusions); |
|
204 |
const ParseResults *recordResults(bool isHeader); |
|
205 |
void deleteResults() { delete results; } |
|
206 |
||
207 |
struct SavedState { |
|
208 |
NamespaceList namespaces; |
|
209 |
QStack<int> namespaceDepths; |
|
210 |
NamespaceList functionContext; |
|
211 |
QString functionContextUnresolved; |
|
212 |
QString pendingContext; |
|
213 |
}; |
|
214 |
||
215 |
private: |
|
216 |
struct IfdefState { |
|
217 |
IfdefState() {} |
|
218 |
IfdefState(int _braceDepth, int _parenDepth) : |
|
219 |
braceDepth(_braceDepth), |
|
220 |
parenDepth(_parenDepth), |
|
221 |
elseLine(-1) |
|
222 |
{} |
|
223 |
||
224 |
SavedState state; |
|
225 |
int braceDepth, braceDepth1st; |
|
226 |
int parenDepth, parenDepth1st; |
|
227 |
int elseLine; |
|
228 |
}; |
|
229 |
||
230 |
uint getChar(); |
|
231 |
uint getToken(); |
|
232 |
bool getMacroArgs(); |
|
233 |
bool match(uint t); |
|
234 |
bool matchString(QString *s); |
|
235 |
bool matchEncoding(bool *utf8); |
|
236 |
bool matchStringOrNull(QString *s); |
|
237 |
bool matchExpression(); |
|
238 |
||
239 |
QString transcode(const QString &str, bool utf8); |
|
240 |
void recordMessage( |
|
241 |
int line, const QString &context, const QString &text, const QString &comment, |
|
242 |
const QString &extracomment, const QString &msgid, const TranslatorMessage::ExtraData &extra, |
|
243 |
bool utf8, bool plural); |
|
244 |
||
245 |
void processInclude(const QString &file, ConversionData &cd, |
|
246 |
QSet<QString> &inclusions); |
|
247 |
||
248 |
void saveState(SavedState *state); |
|
249 |
void loadState(const SavedState *state); |
|
250 |
||
251 |
static QString stringifyNamespace(const NamespaceList &namespaces); |
|
252 |
static QStringList stringListifyNamespace(const NamespaceList &namespaces); |
|
253 |
typedef bool (CppParser::*VisitNamespaceCallback)(const Namespace *ns, void *context) const; |
|
254 |
bool visitNamespace(const NamespaceList &namespaces, int nsCount, |
|
255 |
VisitNamespaceCallback callback, void *context, |
|
256 |
VisitRecorder &vr, const ParseResults *rslt) const; |
|
257 |
bool visitNamespace(const NamespaceList &namespaces, int nsCount, |
|
258 |
VisitNamespaceCallback callback, void *context) const; |
|
259 |
static QStringList stringListifySegments(const QList<HashString> &namespaces); |
|
260 |
bool qualifyOneCallbackOwn(const Namespace *ns, void *context) const; |
|
261 |
bool qualifyOneCallbackUsing(const Namespace *ns, void *context) const; |
|
262 |
bool qualifyOne(const NamespaceList &namespaces, int nsCnt, const HashString &segment, |
|
7
3f74d0d4af4c
qt:70947f0f93d948bc89b3b43d00da758a51f1ef84
Eckhart Koeppen <eckhart.koppen@nokia.com>
parents:
4
diff
changeset
|
263 |
NamespaceList *resolved, QSet<HashStringList> *visitedUsings) const; |
3f74d0d4af4c
qt:70947f0f93d948bc89b3b43d00da758a51f1ef84
Eckhart Koeppen <eckhart.koppen@nokia.com>
parents:
4
diff
changeset
|
264 |
bool qualifyOne(const NamespaceList &namespaces, int nsCnt, const HashString &segment, |
0 | 265 |
NamespaceList *resolved) const; |
3
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
266 |
bool fullyQualify(const NamespaceList &namespaces, int nsCnt, |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
267 |
const QList<HashString> &segments, bool isDeclaration, |
0 | 268 |
NamespaceList *resolved, QStringList *unresolved) const; |
3
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
269 |
bool fullyQualify(const NamespaceList &namespaces, |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
270 |
const QList<HashString> &segments, bool isDeclaration, |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
271 |
NamespaceList *resolved, QStringList *unresolved) const; |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
272 |
bool fullyQualify(const NamespaceList &namespaces, |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
273 |
const QString &segments, bool isDeclaration, |
0 | 274 |
NamespaceList *resolved, QStringList *unresolved) const; |
275 |
bool findNamespaceCallback(const Namespace *ns, void *context) const; |
|
276 |
const Namespace *findNamespace(const NamespaceList &namespaces, int nsCount = -1) const; |
|
277 |
void enterNamespace(NamespaceList *namespaces, const HashString &name); |
|
278 |
void truncateNamespaces(NamespaceList *namespaces, int lenght); |
|
3
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
279 |
Namespace *modifyNamespace(NamespaceList *namespaces, bool haveLast = true); |
0 | 280 |
|
281 |
enum { |
|
282 |
Tok_Eof, Tok_class, Tok_friend, Tok_namespace, Tok_using, Tok_return, |
|
283 |
Tok_tr = 10, Tok_trUtf8, Tok_translate, Tok_translateUtf8, Tok_trid, |
|
284 |
Tok_Q_OBJECT = 20, Tok_Q_DECLARE_TR_FUNCTIONS, |
|
285 |
Tok_Ident, Tok_Comment, Tok_String, Tok_Arrow, Tok_Colon, Tok_ColonColon, |
|
286 |
Tok_Equals, |
|
287 |
Tok_LeftBrace = 30, Tok_RightBrace, Tok_LeftParen, Tok_RightParen, Tok_Comma, Tok_Semicolon, |
|
288 |
Tok_Null = 40, Tok_Integer, |
|
289 |
Tok_QuotedInclude = 50, Tok_AngledInclude, |
|
290 |
Tok_Other = 99 |
|
291 |
}; |
|
292 |
||
293 |
// Tokenizer state |
|
294 |
QString yyFileName; |
|
295 |
int yyCh; |
|
296 |
bool yyAtNewline; |
|
297 |
bool yyCodecIsUtf8; |
|
298 |
bool yyForceUtf8; |
|
299 |
QString yyWord; |
|
300 |
qlonglong yyInteger; |
|
301 |
QStack<IfdefState> yyIfdefStack; |
|
302 |
int yyBraceDepth; |
|
303 |
int yyParenDepth; |
|
304 |
int yyLineNo; |
|
305 |
int yyCurLineNo; |
|
306 |
int yyBraceLineNo; |
|
307 |
int yyParenLineNo; |
|
308 |
||
309 |
// the string to read from and current position in the string |
|
310 |
QTextCodec *yySourceCodec; |
|
311 |
QString yyInStr; |
|
312 |
const ushort *yyInPtr; |
|
313 |
||
314 |
// Parser state |
|
315 |
uint yyTok; |
|
316 |
||
317 |
NamespaceList namespaces; |
|
318 |
QStack<int> namespaceDepths; |
|
319 |
NamespaceList functionContext; |
|
320 |
QString functionContextUnresolved; |
|
321 |
QString prospectiveContext; |
|
322 |
QString pendingContext; |
|
323 |
ParseResults *results; |
|
324 |
Translator *tor; |
|
325 |
bool directInclude; |
|
326 |
||
327 |
SavedState savedState; |
|
328 |
int yyMinBraceDepth; |
|
329 |
bool inDefine; |
|
330 |
}; |
|
331 |
||
332 |
CppParser::CppParser(ParseResults *_results) |
|
333 |
{ |
|
334 |
tor = 0; |
|
335 |
if (_results) { |
|
336 |
results = _results; |
|
337 |
directInclude = true; |
|
338 |
} else { |
|
339 |
results = new ParseResults; |
|
340 |
directInclude = false; |
|
341 |
} |
|
342 |
yyBraceDepth = 0; |
|
343 |
yyParenDepth = 0; |
|
344 |
yyCurLineNo = 1; |
|
345 |
yyBraceLineNo = 1; |
|
346 |
yyParenLineNo = 1; |
|
347 |
yyAtNewline = true; |
|
348 |
yyMinBraceDepth = 0; |
|
349 |
inDefine = false; |
|
350 |
} |
|
351 |
||
352 |
void CppParser::setInput(const QString &in) |
|
353 |
{ |
|
354 |
yyInStr = in; |
|
355 |
yyFileName = QString(); |
|
356 |
yySourceCodec = 0; |
|
357 |
yyForceUtf8 = true; |
|
358 |
} |
|
359 |
||
360 |
void CppParser::setInput(QTextStream &ts, const QString &fileName) |
|
361 |
{ |
|
362 |
yyInStr = ts.readAll(); |
|
363 |
yyFileName = fileName; |
|
364 |
yySourceCodec = ts.codec(); |
|
365 |
yyForceUtf8 = false; |
|
366 |
} |
|
367 |
||
368 |
/* |
|
369 |
The first part of this source file is the C++ tokenizer. We skip |
|
370 |
most of C++; the only tokens that interest us are defined here. |
|
371 |
Thus, the code fragment |
|
372 |
||
373 |
int main() |
|
374 |
{ |
|
375 |
printf("Hello, world!\n"); |
|
376 |
return 0; |
|
377 |
} |
|
378 |
||
379 |
is broken down into the following tokens (Tok_ omitted): |
|
380 |
||
381 |
Ident Ident LeftParen RightParen |
|
382 |
LeftBrace |
|
383 |
Ident LeftParen String RightParen Semicolon |
|
384 |
return Semicolon |
|
385 |
RightBrace. |
|
386 |
||
387 |
The 0 doesn't produce any token. |
|
388 |
*/ |
|
389 |
||
390 |
uint CppParser::getChar() |
|
391 |
{ |
|
392 |
const ushort *uc = yyInPtr; |
|
393 |
forever { |
|
394 |
ushort c = *uc; |
|
395 |
if (!c) { |
|
396 |
yyInPtr = uc; |
|
397 |
return EOF; |
|
398 |
} |
|
399 |
++uc; |
|
400 |
if (c == '\\') { |
|
401 |
ushort cc = *uc; |
|
402 |
if (cc == '\n') { |
|
403 |
++yyCurLineNo; |
|
404 |
++uc; |
|
405 |
continue; |
|
406 |
} |
|
407 |
if (cc == '\r') { |
|
408 |
++yyCurLineNo; |
|
409 |
++uc; |
|
410 |
if (*uc == '\n') |
|
411 |
++uc; |
|
412 |
continue; |
|
413 |
} |
|
414 |
} |
|
415 |
if (c == '\r') { |
|
416 |
if (*uc == '\n') |
|
417 |
++uc; |
|
418 |
c = '\n'; |
|
419 |
++yyCurLineNo; |
|
420 |
yyAtNewline = true; |
|
421 |
} else if (c == '\n') { |
|
422 |
++yyCurLineNo; |
|
423 |
yyAtNewline = true; |
|
424 |
} else if (c != ' ' && c != '\t' && c != '#') { |
|
425 |
yyAtNewline = false; |
|
426 |
} |
|
427 |
yyInPtr = uc; |
|
428 |
return c; |
|
429 |
} |
|
430 |
} |
|
431 |
||
432 |
// This ignores commas, parens and comments. |
|
433 |
// IOW, it understands only a single, simple argument. |
|
434 |
bool CppParser::getMacroArgs() |
|
435 |
{ |
|
436 |
// Failing this assertion would mean losing the preallocated buffer. |
|
437 |
Q_ASSERT(yyWord.isDetached()); |
|
438 |
yyWord.resize(0); |
|
439 |
||
440 |
while (isspace(yyCh)) |
|
441 |
yyCh = getChar(); |
|
442 |
if (yyCh != '(') |
|
443 |
return false; |
|
444 |
do { |
|
445 |
yyCh = getChar(); |
|
446 |
} while (isspace(yyCh)); |
|
447 |
ushort *ptr = (ushort *)yyWord.unicode(); |
|
448 |
while (yyCh != ')') { |
|
449 |
if (yyCh == EOF) |
|
450 |
return false; |
|
451 |
*ptr++ = yyCh; |
|
452 |
yyCh = getChar(); |
|
453 |
} |
|
454 |
yyCh = getChar(); |
|
455 |
for (; ptr != (ushort *)yyWord.unicode() && isspace(*(ptr - 1)); --ptr) ; |
|
456 |
yyWord.resize(ptr - (ushort *)yyWord.unicode()); |
|
457 |
return true; |
|
458 |
} |
|
459 |
||
460 |
STRING(Q_OBJECT); |
|
461 |
STRING(Q_DECLARE_TR_FUNCTIONS); |
|
462 |
STRING(QT_TR_NOOP); |
|
463 |
STRING(QT_TRID_NOOP); |
|
464 |
STRING(QT_TRANSLATE_NOOP); |
|
465 |
STRING(QT_TRANSLATE_NOOP3); |
|
466 |
STRING(QT_TR_NOOP_UTF8); |
|
467 |
STRING(QT_TRANSLATE_NOOP_UTF8); |
|
468 |
STRING(QT_TRANSLATE_NOOP3_UTF8); |
|
469 |
STRING(class); |
|
470 |
// QTranslator::findMessage() has the same parameters as QApplication::translate() |
|
471 |
STRING(findMessage); |
|
472 |
STRING(friend); |
|
473 |
STRING(namespace); |
|
474 |
STRING(qtTrId); |
|
475 |
STRING(return); |
|
476 |
STRING(struct); |
|
477 |
STRING(TR); |
|
478 |
STRING(Tr); |
|
479 |
STRING(tr); |
|
480 |
STRING(trUtf8); |
|
481 |
STRING(translate); |
|
482 |
STRING(using); |
|
483 |
||
484 |
uint CppParser::getToken() |
|
485 |
{ |
|
486 |
restart: |
|
487 |
// Failing this assertion would mean losing the preallocated buffer. |
|
488 |
Q_ASSERT(yyWord.isDetached()); |
|
489 |
yyWord.resize(0); |
|
490 |
||
491 |
while (yyCh != EOF) { |
|
492 |
yyLineNo = yyCurLineNo; |
|
493 |
||
494 |
if (yyCh == '#' && yyAtNewline) { |
|
495 |
/* |
|
496 |
Early versions of lupdate complained about |
|
497 |
unbalanced braces in the following code: |
|
498 |
||
499 |
#ifdef ALPHA |
|
500 |
while (beta) { |
|
501 |
#else |
|
502 |
while (gamma) { |
|
503 |
#endif |
|
504 |
delta; |
|
505 |
} |
|
506 |
||
507 |
The code contains, indeed, two opening braces for |
|
508 |
one closing brace; yet there's no reason to panic. |
|
509 |
||
510 |
The solution is to remember yyBraceDepth as it was |
|
511 |
when #if, #ifdef or #ifndef was met, and to set |
|
512 |
yyBraceDepth to that value when meeting #elif or |
|
513 |
#else. |
|
514 |
*/ |
|
515 |
do { |
|
516 |
yyCh = getChar(); |
|
517 |
} while (isspace(yyCh) && yyCh != '\n'); |
|
518 |
||
519 |
switch (yyCh) { |
|
520 |
case 'd': // define |
|
521 |
// Skip over the name of the define to avoid it being interpreted as c++ code |
|
522 |
do { // Rest of "define" |
|
523 |
yyCh = getChar(); |
|
524 |
if (yyCh == EOF) |
|
525 |
return Tok_Eof; |
|
526 |
if (yyCh == '\n') |
|
527 |
goto restart; |
|
528 |
} while (!isspace(yyCh)); |
|
529 |
do { // Space beween "define" and macro name |
|
530 |
yyCh = getChar(); |
|
531 |
if (yyCh == EOF) |
|
532 |
return Tok_Eof; |
|
533 |
if (yyCh == '\n') |
|
534 |
goto restart; |
|
535 |
} while (isspace(yyCh)); |
|
536 |
do { // Macro name |
|
537 |
if (yyCh == '(') { |
|
538 |
// Argument list. Follows the name without a space, and no |
|
539 |
// paren nesting is possible. |
|
540 |
do { |
|
541 |
yyCh = getChar(); |
|
542 |
if (yyCh == EOF) |
|
543 |
return Tok_Eof; |
|
544 |
if (yyCh == '\n') |
|
545 |
goto restart; |
|
546 |
} while (yyCh != ')'); |
|
547 |
break; |
|
548 |
} |
|
549 |
yyCh = getChar(); |
|
550 |
if (yyCh == EOF) |
|
551 |
return Tok_Eof; |
|
552 |
if (yyCh == '\n') |
|
553 |
goto restart; |
|
554 |
} while (!isspace(yyCh)); |
|
555 |
do { // Shortcut the immediate newline case if no comments follow. |
|
556 |
yyCh = getChar(); |
|
557 |
if (yyCh == EOF) |
|
558 |
return Tok_Eof; |
|
559 |
if (yyCh == '\n') |
|
560 |
goto restart; |
|
561 |
} while (isspace(yyCh)); |
|
562 |
||
563 |
saveState(&savedState); |
|
564 |
yyMinBraceDepth = yyBraceDepth; |
|
565 |
inDefine = true; |
|
566 |
goto restart; |
|
567 |
case 'i': |
|
568 |
yyCh = getChar(); |
|
569 |
if (yyCh == 'f') { |
|
570 |
// if, ifdef, ifndef |
|
571 |
yyIfdefStack.push(IfdefState(yyBraceDepth, yyParenDepth)); |
|
572 |
yyCh = getChar(); |
|
573 |
} else if (yyCh == 'n') { |
|
574 |
// include |
|
575 |
do { |
|
576 |
yyCh = getChar(); |
|
577 |
} while (yyCh != EOF && !isspace(yyCh)); |
|
578 |
do { |
|
579 |
yyCh = getChar(); |
|
580 |
} while (isspace(yyCh)); |
|
581 |
int tChar; |
|
582 |
if (yyCh == '"') |
|
583 |
tChar = '"'; |
|
584 |
else if (yyCh == '<') |
|
585 |
tChar = '>'; |
|
586 |
else |
|
587 |
break; |
|
588 |
ushort *ptr = (ushort *)yyWord.unicode(); |
|
589 |
forever { |
|
590 |
yyCh = getChar(); |
|
591 |
if (yyCh == EOF || yyCh == '\n') |
|
592 |
break; |
|
593 |
if (yyCh == tChar) { |
|
594 |
yyCh = getChar(); |
|
595 |
break; |
|
596 |
} |
|
597 |
*ptr++ = yyCh; |
|
598 |
} |
|
599 |
yyWord.resize(ptr - (ushort *)yyWord.unicode()); |
|
600 |
return (tChar == '"') ? Tok_QuotedInclude : Tok_AngledInclude; |
|
601 |
} |
|
602 |
break; |
|
603 |
case 'e': |
|
604 |
yyCh = getChar(); |
|
605 |
if (yyCh == 'l') { |
|
606 |
// elif, else |
|
607 |
if (!yyIfdefStack.isEmpty()) { |
|
608 |
IfdefState &is = yyIfdefStack.top(); |
|
609 |
if (is.elseLine != -1) { |
|
610 |
if (yyBraceDepth != is.braceDepth1st || yyParenDepth != is.parenDepth1st) |
|
611 |
qWarning("%s:%d: Parenthesis/brace mismatch between " |
|
612 |
"#if and #else branches; using #if branch\n", |
|
613 |
qPrintable(yyFileName), is.elseLine); |
|
614 |
} else { |
|
615 |
is.braceDepth1st = yyBraceDepth; |
|
616 |
is.parenDepth1st = yyParenDepth; |
|
617 |
saveState(&is.state); |
|
618 |
} |
|
619 |
is.elseLine = yyLineNo; |
|
620 |
yyBraceDepth = is.braceDepth; |
|
621 |
yyParenDepth = is.parenDepth; |
|
622 |
} |
|
623 |
yyCh = getChar(); |
|
624 |
} else if (yyCh == 'n') { |
|
625 |
// endif |
|
626 |
if (!yyIfdefStack.isEmpty()) { |
|
627 |
IfdefState is = yyIfdefStack.pop(); |
|
628 |
if (is.elseLine != -1) { |
|
629 |
if (yyBraceDepth != is.braceDepth1st || yyParenDepth != is.parenDepth1st) |
|
630 |
qWarning("%s:%d: Parenthesis/brace mismatch between " |
|
631 |
"#if and #else branches; using #if branch\n", |
|
632 |
qPrintable(yyFileName), is.elseLine); |
|
633 |
yyBraceDepth = is.braceDepth1st; |
|
634 |
yyParenDepth = is.parenDepth1st; |
|
635 |
loadState(&is.state); |
|
636 |
} |
|
637 |
} |
|
638 |
yyCh = getChar(); |
|
639 |
} |
|
640 |
break; |
|
641 |
} |
|
642 |
// Optimization: skip over rest of preprocessor directive |
|
643 |
do { |
|
644 |
if (yyCh == '/') { |
|
645 |
yyCh = getChar(); |
|
646 |
if (yyCh == '/') { |
|
647 |
do { |
|
648 |
yyCh = getChar(); |
|
649 |
} while (yyCh != EOF && yyCh != '\n'); |
|
650 |
break; |
|
651 |
} else if (yyCh == '*') { |
|
652 |
bool metAster = false; |
|
653 |
||
654 |
forever { |
|
655 |
yyCh = getChar(); |
|
656 |
if (yyCh == EOF) { |
|
657 |
qWarning("%s:%d: Unterminated C++ comment\n", |
|
658 |
qPrintable(yyFileName), yyLineNo); |
|
659 |
break; |
|
660 |
} |
|
661 |
||
662 |
if (yyCh == '*') { |
|
663 |
metAster = true; |
|
664 |
} else if (metAster && yyCh == '/') { |
|
665 |
yyCh = getChar(); |
|
666 |
break; |
|
667 |
} else { |
|
668 |
metAster = false; |
|
669 |
} |
|
670 |
} |
|
671 |
} |
|
672 |
} else { |
|
673 |
yyCh = getChar(); |
|
674 |
} |
|
675 |
} while (yyCh != '\n' && yyCh != EOF); |
|
676 |
yyCh = getChar(); |
|
677 |
} else if ((yyCh >= 'A' && yyCh <= 'Z') || (yyCh >= 'a' && yyCh <= 'z') || yyCh == '_') { |
|
678 |
ushort *ptr = (ushort *)yyWord.unicode(); |
|
679 |
do { |
|
680 |
*ptr++ = yyCh; |
|
681 |
yyCh = getChar(); |
|
682 |
} while ((yyCh >= 'A' && yyCh <= 'Z') || (yyCh >= 'a' && yyCh <= 'z') |
|
683 |
|| (yyCh >= '0' && yyCh <= '9') || yyCh == '_'); |
|
684 |
yyWord.resize(ptr - (ushort *)yyWord.unicode()); |
|
685 |
||
686 |
//qDebug() << "IDENT: " << yyWord; |
|
687 |
||
688 |
switch (yyWord.unicode()[0].unicode()) { |
|
689 |
case 'Q': |
|
690 |
if (yyWord == strQ_OBJECT) |
|
691 |
return Tok_Q_OBJECT; |
|
692 |
if (yyWord == strQ_DECLARE_TR_FUNCTIONS) |
|
693 |
return Tok_Q_DECLARE_TR_FUNCTIONS; |
|
694 |
if (yyWord == strQT_TR_NOOP) |
|
695 |
return Tok_tr; |
|
696 |
if (yyWord == strQT_TRID_NOOP) |
|
697 |
return Tok_trid; |
|
698 |
if (yyWord == strQT_TRANSLATE_NOOP) |
|
699 |
return Tok_translate; |
|
700 |
if (yyWord == strQT_TRANSLATE_NOOP3) |
|
701 |
return Tok_translate; |
|
702 |
if (yyWord == strQT_TR_NOOP_UTF8) |
|
703 |
return Tok_trUtf8; |
|
704 |
if (yyWord == strQT_TRANSLATE_NOOP_UTF8) |
|
705 |
return Tok_translateUtf8; |
|
706 |
if (yyWord == strQT_TRANSLATE_NOOP3_UTF8) |
|
707 |
return Tok_translateUtf8; |
|
708 |
break; |
|
709 |
case 'T': |
|
710 |
// TR() for when all else fails |
|
711 |
if (yyWord == strTR || yyWord == strTr) |
|
712 |
return Tok_tr; |
|
713 |
break; |
|
714 |
case 'c': |
|
715 |
if (yyWord == strclass) |
|
716 |
return Tok_class; |
|
717 |
break; |
|
718 |
case 'f': |
|
719 |
/* |
|
720 |
QTranslator::findMessage() has the same parameters as |
|
721 |
QApplication::translate(). |
|
722 |
*/ |
|
723 |
if (yyWord == strfindMessage) |
|
724 |
return Tok_translate; |
|
725 |
if (yyWord == strfriend) |
|
726 |
return Tok_friend; |
|
727 |
break; |
|
728 |
case 'n': |
|
729 |
if (yyWord == strnamespace) |
|
730 |
return Tok_namespace; |
|
731 |
break; |
|
732 |
case 'q': |
|
733 |
if (yyWord == strqtTrId) |
|
734 |
return Tok_trid; |
|
735 |
break; |
|
736 |
case 'r': |
|
737 |
if (yyWord == strreturn) |
|
738 |
return Tok_return; |
|
739 |
break; |
|
740 |
case 's': |
|
741 |
if (yyWord == strstruct) |
|
742 |
return Tok_class; |
|
743 |
break; |
|
744 |
case 't': |
|
745 |
if (yyWord == strtr) |
|
746 |
return Tok_tr; |
|
747 |
if (yyWord == strtrUtf8) |
|
748 |
return Tok_trUtf8; |
|
749 |
if (yyWord == strtranslate) |
|
750 |
return Tok_translate; |
|
751 |
break; |
|
752 |
case 'u': |
|
753 |
if (yyWord == strusing) |
|
754 |
return Tok_using; |
|
755 |
break; |
|
756 |
} |
|
757 |
return Tok_Ident; |
|
758 |
} else { |
|
759 |
switch (yyCh) { |
|
760 |
case '\n': |
|
761 |
if (inDefine) { |
|
762 |
loadState(&savedState); |
|
763 |
prospectiveContext.clear(); |
|
764 |
yyBraceDepth = yyMinBraceDepth; |
|
765 |
yyMinBraceDepth = 0; |
|
766 |
inDefine = false; |
|
767 |
} |
|
768 |
yyCh = getChar(); |
|
769 |
break; |
|
770 |
case '/': |
|
771 |
yyCh = getChar(); |
|
772 |
if (yyCh == '/') { |
|
773 |
ushort *ptr = (ushort *)yyWord.unicode() + yyWord.length(); |
|
774 |
do { |
|
775 |
yyCh = getChar(); |
|
776 |
if (yyCh == EOF) |
|
777 |
break; |
|
778 |
*ptr++ = yyCh; |
|
779 |
} while (yyCh != '\n'); |
|
780 |
yyWord.resize(ptr - (ushort *)yyWord.unicode()); |
|
781 |
} else if (yyCh == '*') { |
|
782 |
bool metAster = false; |
|
783 |
ushort *ptr = (ushort *)yyWord.unicode() + yyWord.length(); |
|
784 |
||
785 |
forever { |
|
786 |
yyCh = getChar(); |
|
787 |
if (yyCh == EOF) { |
|
788 |
qWarning("%s:%d: Unterminated C++ comment\n", |
|
789 |
qPrintable(yyFileName), yyLineNo); |
|
3
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
790 |
break; |
0 | 791 |
} |
792 |
*ptr++ = yyCh; |
|
793 |
||
794 |
if (yyCh == '*') |
|
795 |
metAster = true; |
|
796 |
else if (metAster && yyCh == '/') |
|
797 |
break; |
|
798 |
else |
|
799 |
metAster = false; |
|
800 |
} |
|
801 |
yyWord.resize(ptr - (ushort *)yyWord.unicode() - 2); |
|
802 |
||
803 |
yyCh = getChar(); |
|
804 |
} |
|
805 |
return Tok_Comment; |
|
806 |
case '"': { |
|
807 |
ushort *ptr = (ushort *)yyWord.unicode() + yyWord.length(); |
|
808 |
yyCh = getChar(); |
|
809 |
while (yyCh != EOF && yyCh != '\n' && yyCh != '"') { |
|
810 |
if (yyCh == '\\') { |
|
811 |
yyCh = getChar(); |
|
812 |
if (yyCh == EOF || yyCh == '\n') |
|
813 |
break; |
|
814 |
*ptr++ = '\\'; |
|
815 |
} |
|
816 |
*ptr++ = yyCh; |
|
817 |
yyCh = getChar(); |
|
818 |
} |
|
819 |
yyWord.resize(ptr - (ushort *)yyWord.unicode()); |
|
820 |
||
821 |
if (yyCh != '"') |
|
822 |
qWarning("%s:%d: Unterminated C++ string\n", |
|
823 |
qPrintable(yyFileName), yyLineNo); |
|
824 |
else |
|
825 |
yyCh = getChar(); |
|
826 |
return Tok_String; |
|
827 |
} |
|
828 |
case '-': |
|
829 |
yyCh = getChar(); |
|
830 |
if (yyCh == '>') { |
|
831 |
yyCh = getChar(); |
|
832 |
return Tok_Arrow; |
|
833 |
} |
|
834 |
break; |
|
835 |
case ':': |
|
836 |
yyCh = getChar(); |
|
837 |
if (yyCh == ':') { |
|
838 |
yyCh = getChar(); |
|
839 |
return Tok_ColonColon; |
|
840 |
} |
|
841 |
return Tok_Colon; |
|
842 |
// Incomplete: '<' might be part of '<=' or of template syntax. |
|
843 |
// The main intent of not completely ignoring it is to break |
|
844 |
// parsing of things like std::cout << QObject::tr() as |
|
845 |
// context std::cout::QObject (see Task 161106) |
|
846 |
case '=': |
|
847 |
yyCh = getChar(); |
|
848 |
return Tok_Equals; |
|
849 |
case '>': |
|
850 |
case '<': |
|
851 |
yyCh = getChar(); |
|
852 |
return Tok_Other; |
|
853 |
case '\'': |
|
854 |
yyCh = getChar(); |
|
855 |
if (yyCh == '\\') |
|
856 |
yyCh = getChar(); |
|
857 |
||
858 |
forever { |
|
859 |
if (yyCh == EOF || yyCh == '\n') { |
|
860 |
qWarning("%s:%d: Unterminated C++ character\n", |
|
861 |
qPrintable(yyFileName), yyLineNo); |
|
862 |
break; |
|
863 |
} |
|
864 |
yyCh = getChar(); |
|
865 |
if (yyCh == '\'') { |
|
866 |
yyCh = getChar(); |
|
867 |
break; |
|
868 |
} |
|
869 |
} |
|
870 |
break; |
|
871 |
case '{': |
|
872 |
if (yyBraceDepth == 0) |
|
873 |
yyBraceLineNo = yyCurLineNo; |
|
874 |
yyBraceDepth++; |
|
875 |
yyCh = getChar(); |
|
876 |
return Tok_LeftBrace; |
|
877 |
case '}': |
|
878 |
if (yyBraceDepth == yyMinBraceDepth) { |
|
879 |
if (!inDefine) |
|
880 |
qWarning("%s:%d: Excess closing brace in C++ code" |
|
881 |
" (or abuse of the C++ preprocessor)\n", |
|
882 |
qPrintable(yyFileName), yyCurLineNo); |
|
883 |
// Avoid things getting messed up even more |
|
884 |
yyCh = getChar(); |
|
885 |
return Tok_Semicolon; |
|
886 |
} |
|
887 |
yyBraceDepth--; |
|
888 |
yyCh = getChar(); |
|
889 |
return Tok_RightBrace; |
|
890 |
case '(': |
|
891 |
if (yyParenDepth == 0) |
|
892 |
yyParenLineNo = yyCurLineNo; |
|
893 |
yyParenDepth++; |
|
894 |
yyCh = getChar(); |
|
895 |
return Tok_LeftParen; |
|
896 |
case ')': |
|
897 |
if (yyParenDepth == 0) |
|
898 |
qWarning("%s:%d: Excess closing parenthesis in C++ code" |
|
899 |
" (or abuse of the C++ preprocessor)\n", |
|
900 |
qPrintable(yyFileName), yyCurLineNo); |
|
901 |
else |
|
902 |
yyParenDepth--; |
|
903 |
yyCh = getChar(); |
|
904 |
return Tok_RightParen; |
|
905 |
case ',': |
|
906 |
yyCh = getChar(); |
|
907 |
return Tok_Comma; |
|
908 |
case ';': |
|
909 |
yyCh = getChar(); |
|
910 |
return Tok_Semicolon; |
|
911 |
case '0': |
|
912 |
yyCh = getChar(); |
|
913 |
if (yyCh == 'x') { |
|
914 |
do { |
|
915 |
yyCh = getChar(); |
|
916 |
} while ((yyCh >= '0' && yyCh <= '9') |
|
917 |
|| (yyCh >= 'a' && yyCh <= 'f') || (yyCh >= 'A' && yyCh <= 'F')); |
|
918 |
return Tok_Integer; |
|
919 |
} |
|
920 |
if (yyCh < '0' || yyCh > '9') |
|
921 |
return Tok_Null; |
|
922 |
// Fallthrough |
|
923 |
case '1': |
|
924 |
case '2': |
|
925 |
case '3': |
|
926 |
case '4': |
|
927 |
case '5': |
|
928 |
case '6': |
|
929 |
case '7': |
|
930 |
case '8': |
|
931 |
case '9': |
|
932 |
do { |
|
933 |
yyCh = getChar(); |
|
934 |
} while (yyCh >= '0' && yyCh <= '9'); |
|
935 |
return Tok_Integer; |
|
936 |
default: |
|
937 |
yyCh = getChar(); |
|
938 |
break; |
|
939 |
} |
|
940 |
} |
|
941 |
} |
|
942 |
return Tok_Eof; |
|
943 |
} |
|
944 |
||
945 |
/* |
|
946 |
The second part of this source file are namespace/class related |
|
947 |
utilities for the third part. |
|
948 |
*/ |
|
949 |
||
950 |
void CppParser::saveState(SavedState *state) |
|
951 |
{ |
|
952 |
state->namespaces = namespaces; |
|
953 |
state->namespaceDepths = namespaceDepths; |
|
954 |
state->functionContext = functionContext; |
|
955 |
state->functionContextUnresolved = functionContextUnresolved; |
|
956 |
state->pendingContext = pendingContext; |
|
957 |
} |
|
958 |
||
959 |
void CppParser::loadState(const SavedState *state) |
|
960 |
{ |
|
961 |
namespaces = state->namespaces; |
|
962 |
namespaceDepths = state->namespaceDepths; |
|
963 |
functionContext = state->functionContext; |
|
964 |
functionContextUnresolved = state->functionContextUnresolved; |
|
965 |
pendingContext = state->pendingContext; |
|
966 |
} |
|
967 |
||
3
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
968 |
Namespace *CppParser::modifyNamespace(NamespaceList *namespaces, bool haveLast) |
0 | 969 |
{ |
970 |
Namespace *pns, *ns = &results->rootNamespace; |
|
971 |
for (int i = 1; i < namespaces->count(); ++i) { |
|
972 |
pns = ns; |
|
973 |
if (!(ns = pns->children.value(namespaces->at(i)))) { |
|
974 |
do { |
|
975 |
ns = new Namespace; |
|
3
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
976 |
if (haveLast || i < namespaces->count() - 1) |
0 | 977 |
if (const Namespace *ons = findNamespace(*namespaces, i + 1)) |
978 |
ns->classDef = ons->classDef; |
|
979 |
pns->children.insert(namespaces->at(i), ns); |
|
980 |
pns = ns; |
|
981 |
} while (++i < namespaces->count()); |
|
982 |
break; |
|
983 |
} |
|
984 |
} |
|
985 |
return ns; |
|
986 |
} |
|
987 |
||
988 |
QString CppParser::stringifyNamespace(const NamespaceList &namespaces) |
|
989 |
{ |
|
990 |
QString ret; |
|
991 |
for (int i = 1; i < namespaces.count(); ++i) { |
|
992 |
if (i > 1) |
|
993 |
ret += QLatin1String("::"); |
|
994 |
ret += namespaces.at(i).value(); |
|
995 |
} |
|
996 |
return ret; |
|
997 |
} |
|
998 |
||
999 |
QStringList CppParser::stringListifyNamespace(const NamespaceList &namespaces) |
|
1000 |
{ |
|
1001 |
QStringList ret; |
|
1002 |
for (int i = 1; i < namespaces.count(); ++i) |
|
1003 |
ret << namespaces.at(i).value(); |
|
1004 |
return ret; |
|
1005 |
} |
|
1006 |
||
1007 |
bool CppParser::visitNamespace(const NamespaceList &namespaces, int nsCount, |
|
1008 |
VisitNamespaceCallback callback, void *context, |
|
1009 |
VisitRecorder &vr, const ParseResults *rslt) const |
|
1010 |
{ |
|
1011 |
const Namespace *ns = &rslt->rootNamespace; |
|
1012 |
for (int i = 1; i < nsCount; ++i) |
|
1013 |
if (!(ns = ns->children.value(namespaces.at(i)))) |
|
1014 |
goto supers; |
|
1015 |
if ((this->*callback)(ns, context)) |
|
1016 |
return true; |
|
1017 |
supers: |
|
1018 |
foreach (const ParseResults *sup, rslt->includes) |
|
1019 |
if (vr.tryVisit(sup->fileId) |
|
1020 |
&& visitNamespace(namespaces, nsCount, callback, context, vr, sup)) |
|
1021 |
return true; |
|
1022 |
return false; |
|
1023 |
} |
|
1024 |
||
1025 |
bool CppParser::visitNamespace(const NamespaceList &namespaces, int nsCount, |
|
1026 |
VisitNamespaceCallback callback, void *context) const |
|
1027 |
{ |
|
1028 |
VisitRecorder vr; |
|
1029 |
return visitNamespace(namespaces, nsCount, callback, context, vr, results); |
|
1030 |
} |
|
1031 |
||
1032 |
QStringList CppParser::stringListifySegments(const QList<HashString> &segments) |
|
1033 |
{ |
|
1034 |
QStringList ret; |
|
1035 |
for (int i = 0; i < segments.count(); ++i) |
|
1036 |
ret << segments.at(i).value(); |
|
1037 |
return ret; |
|
1038 |
} |
|
1039 |
||
1040 |
struct QualifyOneData { |
|
7
3f74d0d4af4c
qt:70947f0f93d948bc89b3b43d00da758a51f1ef84
Eckhart Koeppen <eckhart.koppen@nokia.com>
parents:
4
diff
changeset
|
1041 |
QualifyOneData(const NamespaceList &ns, int nsc, const HashString &seg, NamespaceList *rslvd, |
3f74d0d4af4c
qt:70947f0f93d948bc89b3b43d00da758a51f1ef84
Eckhart Koeppen <eckhart.koppen@nokia.com>
parents:
4
diff
changeset
|
1042 |
QSet<HashStringList> *visited) |
3f74d0d4af4c
qt:70947f0f93d948bc89b3b43d00da758a51f1ef84
Eckhart Koeppen <eckhart.koppen@nokia.com>
parents:
4
diff
changeset
|
1043 |
: namespaces(ns), nsCount(nsc), segment(seg), resolved(rslvd), visitedUsings(visited) |
0 | 1044 |
{} |
1045 |
||
1046 |
const NamespaceList &namespaces; |
|
1047 |
int nsCount; |
|
1048 |
const HashString &segment; |
|
1049 |
NamespaceList *resolved; |
|
7
3f74d0d4af4c
qt:70947f0f93d948bc89b3b43d00da758a51f1ef84
Eckhart Koeppen <eckhart.koppen@nokia.com>
parents:
4
diff
changeset
|
1050 |
QSet<HashStringList> *visitedUsings; |
0 | 1051 |
}; |
1052 |
||
1053 |
bool CppParser::qualifyOneCallbackOwn(const Namespace *ns, void *context) const |
|
1054 |
{ |
|
1055 |
QualifyOneData *data = (QualifyOneData *)context; |
|
1056 |
if (ns->children.contains(data->segment)) { |
|
1057 |
*data->resolved = data->namespaces.mid(0, data->nsCount); |
|
1058 |
*data->resolved << data->segment; |
|
1059 |
return true; |
|
1060 |
} |
|
1061 |
QHash<HashString, NamespaceList>::ConstIterator nsai = ns->aliases.constFind(data->segment); |
|
1062 |
if (nsai != ns->aliases.constEnd()) { |
|
3
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1063 |
const NamespaceList &nsl = *nsai; |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1064 |
if (nsl.last().value().isEmpty()) { // Delayed alias resolution |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1065 |
NamespaceList &nslIn = *const_cast<NamespaceList *>(&nsl); |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1066 |
nslIn.removeLast(); |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1067 |
NamespaceList nslOut; |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1068 |
if (!fullyQualify(data->namespaces, data->nsCount, nslIn, false, &nslOut, 0)) { |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1069 |
const_cast<Namespace *>(ns)->aliases.remove(data->segment); |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1070 |
return false; |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1071 |
} |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1072 |
nslIn = nslOut; |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1073 |
} |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1074 |
*data->resolved = nsl; |
0 | 1075 |
return true; |
1076 |
} |
|
1077 |
return false; |
|
1078 |
} |
|
1079 |
||
1080 |
bool CppParser::qualifyOneCallbackUsing(const Namespace *ns, void *context) const |
|
1081 |
{ |
|
1082 |
QualifyOneData *data = (QualifyOneData *)context; |
|
1083 |
foreach (const HashStringList &use, ns->usings) |
|
7
3f74d0d4af4c
qt:70947f0f93d948bc89b3b43d00da758a51f1ef84
Eckhart Koeppen <eckhart.koppen@nokia.com>
parents:
4
diff
changeset
|
1084 |
if (!data->visitedUsings->contains(use)) { |
3f74d0d4af4c
qt:70947f0f93d948bc89b3b43d00da758a51f1ef84
Eckhart Koeppen <eckhart.koppen@nokia.com>
parents:
4
diff
changeset
|
1085 |
data->visitedUsings->insert(use); |
3f74d0d4af4c
qt:70947f0f93d948bc89b3b43d00da758a51f1ef84
Eckhart Koeppen <eckhart.koppen@nokia.com>
parents:
4
diff
changeset
|
1086 |
if (qualifyOne(use.value(), use.value().count(), data->segment, data->resolved, |
3f74d0d4af4c
qt:70947f0f93d948bc89b3b43d00da758a51f1ef84
Eckhart Koeppen <eckhart.koppen@nokia.com>
parents:
4
diff
changeset
|
1087 |
data->visitedUsings)) |
0 | 1088 |
return true; |
1089 |
} |
|
1090 |
return false; |
|
1091 |
} |
|
1092 |
||
1093 |
bool CppParser::qualifyOne(const NamespaceList &namespaces, int nsCnt, const HashString &segment, |
|
7
3f74d0d4af4c
qt:70947f0f93d948bc89b3b43d00da758a51f1ef84
Eckhart Koeppen <eckhart.koppen@nokia.com>
parents:
4
diff
changeset
|
1094 |
NamespaceList *resolved, QSet<HashStringList> *visitedUsings) const |
0 | 1095 |
{ |
7
3f74d0d4af4c
qt:70947f0f93d948bc89b3b43d00da758a51f1ef84
Eckhart Koeppen <eckhart.koppen@nokia.com>
parents:
4
diff
changeset
|
1096 |
QualifyOneData data(namespaces, nsCnt, segment, resolved, visitedUsings); |
0 | 1097 |
|
1098 |
if (visitNamespace(namespaces, nsCnt, &CppParser::qualifyOneCallbackOwn, &data)) |
|
1099 |
return true; |
|
1100 |
||
1101 |
return visitNamespace(namespaces, nsCnt, &CppParser::qualifyOneCallbackUsing, &data); |
|
1102 |
} |
|
1103 |
||
7
3f74d0d4af4c
qt:70947f0f93d948bc89b3b43d00da758a51f1ef84
Eckhart Koeppen <eckhart.koppen@nokia.com>
parents:
4
diff
changeset
|
1104 |
bool CppParser::qualifyOne(const NamespaceList &namespaces, int nsCnt, const HashString &segment, |
3f74d0d4af4c
qt:70947f0f93d948bc89b3b43d00da758a51f1ef84
Eckhart Koeppen <eckhart.koppen@nokia.com>
parents:
4
diff
changeset
|
1105 |
NamespaceList *resolved) const |
3f74d0d4af4c
qt:70947f0f93d948bc89b3b43d00da758a51f1ef84
Eckhart Koeppen <eckhart.koppen@nokia.com>
parents:
4
diff
changeset
|
1106 |
{ |
3f74d0d4af4c
qt:70947f0f93d948bc89b3b43d00da758a51f1ef84
Eckhart Koeppen <eckhart.koppen@nokia.com>
parents:
4
diff
changeset
|
1107 |
QSet<HashStringList> visitedUsings; |
3f74d0d4af4c
qt:70947f0f93d948bc89b3b43d00da758a51f1ef84
Eckhart Koeppen <eckhart.koppen@nokia.com>
parents:
4
diff
changeset
|
1108 |
|
3f74d0d4af4c
qt:70947f0f93d948bc89b3b43d00da758a51f1ef84
Eckhart Koeppen <eckhart.koppen@nokia.com>
parents:
4
diff
changeset
|
1109 |
return qualifyOne(namespaces, nsCnt, segment, resolved, &visitedUsings); |
3f74d0d4af4c
qt:70947f0f93d948bc89b3b43d00da758a51f1ef84
Eckhart Koeppen <eckhart.koppen@nokia.com>
parents:
4
diff
changeset
|
1110 |
} |
3f74d0d4af4c
qt:70947f0f93d948bc89b3b43d00da758a51f1ef84
Eckhart Koeppen <eckhart.koppen@nokia.com>
parents:
4
diff
changeset
|
1111 |
|
3
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1112 |
bool CppParser::fullyQualify(const NamespaceList &namespaces, int nsCnt, |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1113 |
const QList<HashString> &segments, bool isDeclaration, |
0 | 1114 |
NamespaceList *resolved, QStringList *unresolved) const |
1115 |
{ |
|
1116 |
int nsIdx; |
|
1117 |
int initSegIdx; |
|
1118 |
||
1119 |
if (segments.first().value().isEmpty()) { |
|
1120 |
// fully qualified |
|
1121 |
if (segments.count() == 1) { |
|
1122 |
resolved->clear(); |
|
1123 |
*resolved << HashString(QString()); |
|
1124 |
return true; |
|
1125 |
} |
|
1126 |
initSegIdx = 1; |
|
1127 |
nsIdx = 0; |
|
1128 |
} else { |
|
1129 |
initSegIdx = 0; |
|
3
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1130 |
nsIdx = nsCnt - 1; |
0 | 1131 |
} |
1132 |
||
1133 |
do { |
|
1134 |
if (qualifyOne(namespaces, nsIdx + 1, segments[initSegIdx], resolved)) { |
|
1135 |
int segIdx = initSegIdx; |
|
1136 |
while (++segIdx < segments.count()) { |
|
1137 |
if (!qualifyOne(*resolved, resolved->count(), segments[segIdx], resolved)) { |
|
1138 |
if (unresolved) |
|
1139 |
*unresolved = stringListifySegments(segments.mid(segIdx)); |
|
1140 |
return false; |
|
1141 |
} |
|
1142 |
} |
|
1143 |
return true; |
|
1144 |
} |
|
1145 |
} while (!isDeclaration && --nsIdx >= 0); |
|
1146 |
resolved->clear(); |
|
1147 |
*resolved << HashString(QString()); |
|
1148 |
if (unresolved) |
|
1149 |
*unresolved = stringListifySegments(segments.mid(initSegIdx)); |
|
1150 |
return false; |
|
1151 |
} |
|
1152 |
||
3
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1153 |
bool CppParser::fullyQualify(const NamespaceList &namespaces, |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1154 |
const QList<HashString> &segments, bool isDeclaration, |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1155 |
NamespaceList *resolved, QStringList *unresolved) const |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1156 |
{ |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1157 |
return fullyQualify(namespaces, namespaces.count(), |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1158 |
segments, isDeclaration, resolved, unresolved); |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1159 |
} |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1160 |
|
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1161 |
bool CppParser::fullyQualify(const NamespaceList &namespaces, |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1162 |
const QString &quali, bool isDeclaration, |
0 | 1163 |
NamespaceList *resolved, QStringList *unresolved) const |
1164 |
{ |
|
1165 |
static QString strColons(QLatin1String("::")); |
|
1166 |
||
1167 |
QList<HashString> segments; |
|
1168 |
foreach (const QString &str, quali.split(strColons)) // XXX slow, but needs to be fast(?) |
|
1169 |
segments << HashString(str); |
|
1170 |
return fullyQualify(namespaces, segments, isDeclaration, resolved, unresolved); |
|
1171 |
} |
|
1172 |
||
1173 |
bool CppParser::findNamespaceCallback(const Namespace *ns, void *context) const |
|
1174 |
{ |
|
1175 |
*((const Namespace **)context) = ns; |
|
1176 |
return true; |
|
1177 |
} |
|
1178 |
||
1179 |
const Namespace *CppParser::findNamespace(const NamespaceList &namespaces, int nsCount) const |
|
1180 |
{ |
|
1181 |
const Namespace *ns = 0; |
|
1182 |
if (nsCount == -1) |
|
1183 |
nsCount = namespaces.count(); |
|
1184 |
visitNamespace(namespaces, nsCount, &CppParser::findNamespaceCallback, &ns); |
|
1185 |
return ns; |
|
1186 |
} |
|
1187 |
||
1188 |
void CppParser::enterNamespace(NamespaceList *namespaces, const HashString &name) |
|
1189 |
{ |
|
1190 |
*namespaces << name; |
|
1191 |
if (!findNamespace(*namespaces)) |
|
1192 |
modifyNamespace(namespaces, false); |
|
1193 |
} |
|
1194 |
||
1195 |
void CppParser::truncateNamespaces(NamespaceList *namespaces, int length) |
|
1196 |
{ |
|
1197 |
if (namespaces->count() > length) |
|
1198 |
namespaces->erase(namespaces->begin() + length, namespaces->end()); |
|
1199 |
} |
|
1200 |
||
1201 |
/* |
|
1202 |
Functions for processing include files. |
|
1203 |
*/ |
|
1204 |
||
1205 |
ParseResultHash &CppFiles::parsedFiles() |
|
1206 |
{ |
|
1207 |
static ParseResultHash parsed; |
|
1208 |
||
1209 |
return parsed; |
|
1210 |
} |
|
1211 |
||
1212 |
TranslatorHash &CppFiles::translatedFiles() |
|
1213 |
{ |
|
1214 |
static TranslatorHash tors; |
|
1215 |
||
1216 |
return tors; |
|
1217 |
} |
|
1218 |
||
1219 |
QSet<QString> &CppFiles::blacklistedFiles() |
|
1220 |
{ |
|
1221 |
static QSet<QString> blacklisted; |
|
1222 |
||
1223 |
return blacklisted; |
|
1224 |
} |
|
1225 |
||
1226 |
const ParseResults *CppFiles::getResults(const QString &cleanFile) |
|
1227 |
{ |
|
1228 |
return parsedFiles().value(cleanFile); |
|
1229 |
} |
|
1230 |
||
1231 |
void CppFiles::setResults(const QString &cleanFile, const ParseResults *results) |
|
1232 |
{ |
|
1233 |
parsedFiles().insert(cleanFile, results); |
|
1234 |
} |
|
1235 |
||
1236 |
const Translator *CppFiles::getTranslator(const QString &cleanFile) |
|
1237 |
{ |
|
1238 |
return translatedFiles().value(cleanFile); |
|
1239 |
} |
|
1240 |
||
1241 |
void CppFiles::setTranslator(const QString &cleanFile, const Translator *tor) |
|
1242 |
{ |
|
1243 |
translatedFiles().insert(cleanFile, tor); |
|
1244 |
} |
|
1245 |
||
1246 |
bool CppFiles::isBlacklisted(const QString &cleanFile) |
|
1247 |
{ |
|
1248 |
return blacklistedFiles().contains(cleanFile); |
|
1249 |
} |
|
1250 |
||
1251 |
void CppFiles::setBlacklisted(const QString &cleanFile) |
|
1252 |
{ |
|
1253 |
blacklistedFiles().insert(cleanFile); |
|
1254 |
} |
|
1255 |
||
1256 |
static bool isHeader(const QString &name) |
|
1257 |
{ |
|
1258 |
QString fileExt = QFileInfo(name).suffix(); |
|
1259 |
return fileExt.isEmpty() || fileExt.startsWith(QLatin1Char('h'), Qt::CaseInsensitive); |
|
1260 |
} |
|
1261 |
||
1262 |
void CppParser::processInclude(const QString &file, ConversionData &cd, |
|
1263 |
QSet<QString> &inclusions) |
|
1264 |
{ |
|
1265 |
QString cleanFile = QDir::cleanPath(file); |
|
1266 |
||
1267 |
if (inclusions.contains(cleanFile)) { |
|
1268 |
qWarning("%s:%d: circular inclusion of %s\n", |
|
1269 |
qPrintable(yyFileName), yyLineNo, qPrintable(cleanFile)); |
|
1270 |
return; |
|
1271 |
} |
|
1272 |
||
1273 |
// If the #include is in any kind of namespace, has been blacklisted previously, |
|
1274 |
// or is not a header file (stdc++ extensionless or *.h*), then really include |
|
1275 |
// it. Otherwise it is safe to process it stand-alone and re-use the parsed |
|
1276 |
// namespace data for inclusion into other files. |
|
1277 |
bool isIndirect = false; |
|
1278 |
if (namespaces.count() == 1 && functionContext.count() == 1 |
|
1279 |
&& functionContextUnresolved.isEmpty() && pendingContext.isEmpty() |
|
1280 |
&& !CppFiles::isBlacklisted(cleanFile) |
|
1281 |
&& isHeader(cleanFile)) { |
|
1282 |
||
1283 |
if (const ParseResults *res = CppFiles::getResults(cleanFile)) { |
|
1284 |
results->includes.insert(res); |
|
1285 |
return; |
|
1286 |
} |
|
1287 |
||
1288 |
isIndirect = true; |
|
1289 |
} |
|
1290 |
||
1291 |
QFile f(cleanFile); |
|
1292 |
if (!f.open(QIODevice::ReadOnly)) { |
|
1293 |
qWarning("%s:%d: Cannot open %s: %s\n", |
|
1294 |
qPrintable(yyFileName), yyLineNo, |
|
1295 |
qPrintable(cleanFile), qPrintable(f.errorString())); |
|
1296 |
return; |
|
1297 |
} |
|
1298 |
||
1299 |
QTextStream ts(&f); |
|
1300 |
ts.setCodec(yySourceCodec); |
|
1301 |
ts.setAutoDetectUnicode(true); |
|
1302 |
||
1303 |
inclusions.insert(cleanFile); |
|
1304 |
if (isIndirect) { |
|
1305 |
CppParser parser; |
|
1306 |
foreach (const QString &projectRoot, cd.m_projectRoots) |
|
1307 |
if (cleanFile.startsWith(projectRoot)) { |
|
1308 |
parser.setTranslator(new Translator); |
|
1309 |
break; |
|
1310 |
} |
|
1311 |
parser.setInput(ts, cleanFile); |
|
1312 |
parser.parse(cd.m_defaultContext, cd, inclusions); |
|
1313 |
results->includes.insert(parser.recordResults(true)); |
|
1314 |
} else { |
|
1315 |
CppParser parser(results); |
|
1316 |
parser.namespaces = namespaces; |
|
1317 |
parser.functionContext = functionContext; |
|
1318 |
parser.functionContextUnresolved = functionContextUnresolved; |
|
1319 |
parser.pendingContext = pendingContext; |
|
1320 |
parser.setInput(ts, cleanFile); |
|
1321 |
parser.parseInternal(cd, inclusions); |
|
1322 |
// Avoid that messages obtained by direct scanning are used |
|
1323 |
CppFiles::setBlacklisted(cleanFile); |
|
1324 |
} |
|
1325 |
inclusions.remove(cleanFile); |
|
1326 |
} |
|
1327 |
||
1328 |
/* |
|
1329 |
The third part of this source file is the parser. It accomplishes |
|
1330 |
a very easy task: It finds all strings inside a tr() or translate() |
|
1331 |
call, and possibly finds out the context of the call. It supports |
|
1332 |
three cases: (1) the context is specified, as in |
|
1333 |
FunnyDialog::tr("Hello") or translate("FunnyDialog", "Hello"); |
|
1334 |
(2) the call appears within an inlined function; (3) the call |
|
1335 |
appears within a function defined outside the class definition. |
|
1336 |
*/ |
|
1337 |
||
1338 |
bool CppParser::match(uint t) |
|
1339 |
{ |
|
1340 |
bool matches = (yyTok == t); |
|
1341 |
if (matches) |
|
1342 |
yyTok = getToken(); |
|
1343 |
return matches; |
|
1344 |
} |
|
1345 |
||
1346 |
bool CppParser::matchString(QString *s) |
|
1347 |
{ |
|
1348 |
bool matches = false; |
|
1349 |
s->clear(); |
|
1350 |
forever { |
|
1351 |
while (yyTok == Tok_Comment) |
|
1352 |
yyTok = getToken(); |
|
1353 |
if (yyTok != Tok_String) |
|
1354 |
return matches; |
|
1355 |
matches = true; |
|
1356 |
*s += yyWord; |
|
1357 |
s->detach(); |
|
1358 |
yyTok = getToken(); |
|
1359 |
} |
|
1360 |
} |
|
1361 |
||
1362 |
STRING(QApplication); |
|
1363 |
STRING(QCoreApplication); |
|
1364 |
STRING(UnicodeUTF8); |
|
1365 |
STRING(DefaultCodec); |
|
1366 |
STRING(CodecForTr); |
|
1367 |
||
1368 |
bool CppParser::matchEncoding(bool *utf8) |
|
1369 |
{ |
|
1370 |
if (yyTok != Tok_Ident) |
|
1371 |
return false; |
|
1372 |
if (yyWord == strQApplication || yyWord == strQCoreApplication) { |
|
1373 |
yyTok = getToken(); |
|
1374 |
if (yyTok == Tok_ColonColon) |
|
1375 |
yyTok = getToken(); |
|
1376 |
} |
|
1377 |
if (yyWord == strUnicodeUTF8) { |
|
1378 |
*utf8 = true; |
|
1379 |
yyTok = getToken(); |
|
1380 |
return true; |
|
1381 |
} |
|
1382 |
if (yyWord == strDefaultCodec || yyWord == strCodecForTr) { |
|
1383 |
*utf8 = false; |
|
1384 |
yyTok = getToken(); |
|
1385 |
return true; |
|
1386 |
} |
|
1387 |
return false; |
|
1388 |
} |
|
1389 |
||
1390 |
bool CppParser::matchStringOrNull(QString *s) |
|
1391 |
{ |
|
1392 |
return matchString(s) || match(Tok_Null); |
|
1393 |
} |
|
1394 |
||
1395 |
/* |
|
1396 |
* match any expression that can return a number, which can be |
|
1397 |
* 1. Literal number (e.g. '11') |
|
1398 |
* 2. simple identifier (e.g. 'm_count') |
|
1399 |
* 3. simple function call (e.g. 'size()' ) |
|
1400 |
* 4. function call on an object (e.g. 'list.size()') |
|
1401 |
* 5. function call on an object (e.g. 'list->size()') |
|
1402 |
* |
|
1403 |
* Other cases: |
|
1404 |
* size(2,4) |
|
1405 |
* list().size() |
|
1406 |
* list(a,b).size(2,4) |
|
1407 |
* etc... |
|
1408 |
*/ |
|
1409 |
bool CppParser::matchExpression() |
|
1410 |
{ |
|
1411 |
if (match(Tok_Null) || match(Tok_Integer)) |
|
1412 |
return true; |
|
1413 |
||
1414 |
int parenlevel = 0; |
|
1415 |
while (match(Tok_Ident) || parenlevel > 0) { |
|
1416 |
if (yyTok == Tok_RightParen) { |
|
1417 |
if (parenlevel == 0) break; |
|
1418 |
--parenlevel; |
|
1419 |
yyTok = getToken(); |
|
1420 |
} else if (yyTok == Tok_LeftParen) { |
|
1421 |
yyTok = getToken(); |
|
1422 |
if (yyTok == Tok_RightParen) { |
|
1423 |
yyTok = getToken(); |
|
1424 |
} else { |
|
1425 |
++parenlevel; |
|
1426 |
} |
|
1427 |
} else if (yyTok == Tok_Ident) { |
|
1428 |
continue; |
|
1429 |
} else if (yyTok == Tok_Arrow) { |
|
1430 |
yyTok = getToken(); |
|
1431 |
} else if (parenlevel == 0) { |
|
1432 |
return false; |
|
1433 |
} |
|
1434 |
} |
|
1435 |
return true; |
|
1436 |
} |
|
1437 |
||
1438 |
QString CppParser::transcode(const QString &str, bool utf8) |
|
1439 |
{ |
|
1440 |
static const char tab[] = "abfnrtv"; |
|
1441 |
static const char backTab[] = "\a\b\f\n\r\t\v"; |
|
3
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1442 |
// This function has to convert back to bytes, as C's \0* sequences work at that level. |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1443 |
const QByteArray in = yyForceUtf8 ? str.toUtf8() : tor->codec()->fromUnicode(str); |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1444 |
QByteArray out; |
0 | 1445 |
|
1446 |
out.reserve(in.length()); |
|
1447 |
for (int i = 0; i < in.length();) { |
|
3
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1448 |
uchar c = in[i++]; |
0 | 1449 |
if (c == '\\') { |
1450 |
if (i >= in.length()) |
|
1451 |
break; |
|
3
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1452 |
c = in[i++]; |
0 | 1453 |
|
1454 |
if (c == '\n') |
|
1455 |
continue; |
|
1456 |
||
1457 |
if (c == 'x') { |
|
1458 |
QByteArray hex; |
|
3
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1459 |
while (i < in.length() && isxdigit((c = in[i]))) { |
0 | 1460 |
hex += c; |
1461 |
i++; |
|
1462 |
} |
|
1463 |
out += hex.toUInt(0, 16); |
|
1464 |
} else if (c >= '0' && c < '8') { |
|
1465 |
QByteArray oct; |
|
1466 |
int n = 0; |
|
1467 |
oct += c; |
|
3
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1468 |
while (n < 2 && i < in.length() && (c = in[i]) >= '0' && c < '8') { |
0 | 1469 |
i++; |
1470 |
n++; |
|
1471 |
oct += c; |
|
1472 |
} |
|
1473 |
out += oct.toUInt(0, 8); |
|
1474 |
} else { |
|
1475 |
const char *p = strchr(tab, c); |
|
3
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1476 |
out += !p ? c : backTab[p - tab]; |
0 | 1477 |
} |
1478 |
} else { |
|
1479 |
out += c; |
|
1480 |
} |
|
1481 |
} |
|
3
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1482 |
return (utf8 || yyForceUtf8) ? QString::fromUtf8(out.constData(), out.length()) |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1483 |
: tor->codec()->toUnicode(out); |
0 | 1484 |
} |
1485 |
||
1486 |
void CppParser::recordMessage( |
|
1487 |
int line, const QString &context, const QString &text, const QString &comment, |
|
1488 |
const QString &extracomment, const QString &msgid, const TranslatorMessage::ExtraData &extra, |
|
1489 |
bool utf8, bool plural) |
|
1490 |
{ |
|
1491 |
TranslatorMessage msg( |
|
1492 |
transcode(context, utf8), transcode(text, utf8), transcode(comment, utf8), QString(), |
|
1493 |
yyFileName, line, QStringList(), |
|
1494 |
TranslatorMessage::Unfinished, plural); |
|
1495 |
msg.setExtraComment(transcode(extracomment.simplified(), utf8)); |
|
1496 |
msg.setId(msgid); |
|
1497 |
msg.setExtras(extra); |
|
1498 |
if ((utf8 || yyForceUtf8) && !yyCodecIsUtf8 && msg.needs8Bit()) |
|
1499 |
msg.setUtf8(true); |
|
1500 |
tor->append(msg); |
|
1501 |
} |
|
1502 |
||
1503 |
void CppParser::parse(const QString &initialContext, ConversionData &cd, |
|
1504 |
QSet<QString> &inclusions) |
|
1505 |
{ |
|
1506 |
if (tor) |
|
1507 |
yyCodecIsUtf8 = (tor->codecName() == "UTF-8"); |
|
1508 |
||
1509 |
namespaces << HashString(); |
|
1510 |
functionContext = namespaces; |
|
1511 |
functionContextUnresolved = initialContext; |
|
1512 |
||
1513 |
parseInternal(cd, inclusions); |
|
1514 |
} |
|
1515 |
||
1516 |
void CppParser::parseInternal(ConversionData &cd, QSet<QString> &inclusions) |
|
1517 |
{ |
|
1518 |
static QString strColons(QLatin1String("::")); |
|
1519 |
||
1520 |
QString context; |
|
1521 |
QString text; |
|
1522 |
QString comment; |
|
1523 |
QString extracomment; |
|
1524 |
QString msgid; |
|
1525 |
QString sourcetext; |
|
1526 |
TranslatorMessage::ExtraData extra; |
|
1527 |
QString prefix; |
|
1528 |
#ifdef DIAGNOSE_RETRANSLATABILITY |
|
1529 |
QString functionName; |
|
1530 |
#endif |
|
1531 |
int line; |
|
1532 |
bool utf8; |
|
1533 |
bool yyTokColonSeen = false; // Start of c'tor's initializer list |
|
1534 |
||
1535 |
yyWord.reserve(yyInStr.size()); // Rather insane. That's because we do no length checking. |
|
1536 |
yyInPtr = (const ushort *)yyInStr.unicode(); |
|
1537 |
yyCh = getChar(); |
|
1538 |
yyTok = getToken(); |
|
1539 |
while (yyTok != Tok_Eof) { |
|
1540 |
//qDebug() << "TOKEN: " << yyTok; |
|
1541 |
switch (yyTok) { |
|
1542 |
case Tok_QuotedInclude: { |
|
1543 |
text = QDir(QFileInfo(yyFileName).absolutePath()).absoluteFilePath(yyWord); |
|
1544 |
text.detach(); |
|
1545 |
if (QFileInfo(text).isFile()) { |
|
1546 |
processInclude(text, cd, inclusions); |
|
1547 |
yyTok = getToken(); |
|
1548 |
break; |
|
1549 |
} |
|
1550 |
} |
|
1551 |
/* fall through */ |
|
1552 |
case Tok_AngledInclude: { |
|
1553 |
QStringList cSources = cd.m_allCSources.values(yyWord); |
|
1554 |
if (!cSources.isEmpty()) { |
|
1555 |
foreach (const QString &cSource, cSources) |
|
1556 |
processInclude(cSource, cd, inclusions); |
|
1557 |
goto incOk; |
|
1558 |
} |
|
1559 |
foreach (const QString &incPath, cd.m_includePath) { |
|
1560 |
text = QDir(incPath).absoluteFilePath(yyWord); |
|
1561 |
text.detach(); |
|
1562 |
if (QFileInfo(text).isFile()) { |
|
1563 |
processInclude(text, cd, inclusions); |
|
1564 |
goto incOk; |
|
1565 |
} |
|
1566 |
} |
|
1567 |
incOk: |
|
1568 |
yyTok = getToken(); |
|
1569 |
break; |
|
1570 |
} |
|
1571 |
case Tok_friend: |
|
1572 |
yyTok = getToken(); |
|
1573 |
// These are forward declarations, so ignore them. |
|
1574 |
if (yyTok == Tok_class) |
|
1575 |
yyTok = getToken(); |
|
1576 |
break; |
|
1577 |
case Tok_class: |
|
1578 |
yyTokColonSeen = false; |
|
1579 |
/* |
|
1580 |
Partial support for inlined functions. |
|
1581 |
*/ |
|
1582 |
yyTok = getToken(); |
|
1583 |
if (yyBraceDepth == namespaceDepths.count() && yyParenDepth == 0) { |
|
1584 |
QList<HashString> quali; |
|
1585 |
HashString fct; |
|
1586 |
do { |
|
1587 |
/* |
|
1588 |
This code should execute only once, but we play |
|
1589 |
safe with impure definitions such as |
|
1590 |
'class Q_EXPORT QMessageBox', in which case |
|
1591 |
'QMessageBox' is the class name, not 'Q_EXPORT'. |
|
1592 |
*/ |
|
1593 |
text = yyWord; |
|
1594 |
text.detach(); |
|
1595 |
fct.setValue(text); |
|
1596 |
yyTok = getToken(); |
|
1597 |
} while (yyTok == Tok_Ident); |
|
1598 |
while (yyTok == Tok_ColonColon) { |
|
1599 |
yyTok = getToken(); |
|
1600 |
if (yyTok != Tok_Ident) |
|
1601 |
break; // Oops ... |
|
1602 |
quali << fct; |
|
1603 |
text = yyWord; |
|
1604 |
text.detach(); |
|
1605 |
fct.setValue(text); |
|
1606 |
yyTok = getToken(); |
|
1607 |
} |
|
1608 |
while (yyTok == Tok_Comment) |
|
1609 |
yyTok = getToken(); |
|
1610 |
if (yyTok == Tok_Colon) { |
|
1611 |
// Skip any token until '{' since we might do things wrong if we find |
|
1612 |
// a '::' token here. |
|
1613 |
do { |
|
1614 |
yyTok = getToken(); |
|
1615 |
} while (yyTok != Tok_LeftBrace && yyTok != Tok_Eof); |
|
1616 |
} else { |
|
1617 |
if (yyTok != Tok_LeftBrace) { |
|
1618 |
// Obviously a forward declaration. We skip those, as they |
|
1619 |
// don't create actually usable namespaces. |
|
1620 |
break; |
|
1621 |
} |
|
1622 |
} |
|
1623 |
||
1624 |
if (!quali.isEmpty()) { |
|
1625 |
// Forward-declared class definitions can be namespaced. |
|
1626 |
NamespaceList nsl; |
|
1627 |
if (!fullyQualify(namespaces, quali, true, &nsl, 0)) { |
|
1628 |
qWarning("%s:%d: Ignoring definition of undeclared qualified class\n", |
|
1629 |
qPrintable(yyFileName), yyLineNo); |
|
1630 |
break; |
|
1631 |
} |
|
1632 |
namespaceDepths.push(namespaces.count()); |
|
1633 |
namespaces = nsl; |
|
1634 |
} else { |
|
1635 |
namespaceDepths.push(namespaces.count()); |
|
1636 |
} |
|
1637 |
enterNamespace(&namespaces, fct); |
|
1638 |
||
1639 |
functionContext = namespaces; |
|
1640 |
functionContextUnresolved.clear(); // Pointless |
|
1641 |
prospectiveContext.clear(); |
|
1642 |
pendingContext.clear(); |
|
1643 |
} |
|
1644 |
break; |
|
1645 |
case Tok_namespace: |
|
1646 |
yyTokColonSeen = false; |
|
1647 |
yyTok = getToken(); |
|
1648 |
if (yyTok == Tok_Ident) { |
|
1649 |
text = yyWord; |
|
1650 |
text.detach(); |
|
1651 |
HashString ns = HashString(text); |
|
1652 |
yyTok = getToken(); |
|
1653 |
if (yyTok == Tok_LeftBrace) { |
|
1654 |
namespaceDepths.push(namespaces.count()); |
|
1655 |
enterNamespace(&namespaces, ns); |
|
1656 |
yyTok = getToken(); |
|
1657 |
} else if (yyTok == Tok_Equals) { |
|
1658 |
// e.g. namespace Is = OuterSpace::InnerSpace; |
|
1659 |
QList<HashString> fullName; |
|
1660 |
yyTok = getToken(); |
|
1661 |
if (yyTok == Tok_ColonColon) |
|
1662 |
fullName.append(HashString(QString())); |
|
1663 |
while (yyTok == Tok_ColonColon || yyTok == Tok_Ident) { |
|
1664 |
if (yyTok == Tok_Ident) { |
|
1665 |
text = yyWord; |
|
1666 |
text.detach(); |
|
1667 |
fullName.append(HashString(text)); |
|
1668 |
} |
|
1669 |
yyTok = getToken(); |
|
1670 |
} |
|
1671 |
if (fullName.isEmpty()) |
|
1672 |
break; |
|
3
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1673 |
fullName.append(HashString(QString())); // Mark as unresolved |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1674 |
modifyNamespace(&namespaces)->aliases[ns] = fullName; |
0 | 1675 |
} |
1676 |
} else if (yyTok == Tok_LeftBrace) { |
|
1677 |
// Anonymous namespace |
|
1678 |
namespaceDepths.push(namespaces.count()); |
|
1679 |
yyTok = getToken(); |
|
1680 |
} |
|
1681 |
break; |
|
1682 |
case Tok_using: |
|
1683 |
yyTok = getToken(); |
|
1684 |
// XXX this should affect only the current scope, not the entire current namespace |
|
1685 |
if (yyTok == Tok_namespace) { |
|
1686 |
QList<HashString> fullName; |
|
1687 |
yyTok = getToken(); |
|
1688 |
if (yyTok == Tok_ColonColon) |
|
1689 |
fullName.append(HashString(QString())); |
|
1690 |
while (yyTok == Tok_ColonColon || yyTok == Tok_Ident) { |
|
1691 |
if (yyTok == Tok_Ident) { |
|
1692 |
text = yyWord; |
|
1693 |
text.detach(); |
|
1694 |
fullName.append(HashString(text)); |
|
1695 |
} |
|
1696 |
yyTok = getToken(); |
|
1697 |
} |
|
1698 |
NamespaceList nsl; |
|
1699 |
if (fullyQualify(namespaces, fullName, false, &nsl, 0)) |
|
3
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1700 |
modifyNamespace(&namespaces)->usings << HashStringList(nsl); |
0 | 1701 |
} else { |
1702 |
QList<HashString> fullName; |
|
1703 |
if (yyTok == Tok_ColonColon) |
|
1704 |
fullName.append(HashString(QString())); |
|
1705 |
while (yyTok == Tok_ColonColon || yyTok == Tok_Ident) { |
|
1706 |
if (yyTok == Tok_Ident) { |
|
1707 |
text = yyWord; |
|
1708 |
text.detach(); |
|
1709 |
fullName.append(HashString(text)); |
|
1710 |
} |
|
1711 |
yyTok = getToken(); |
|
1712 |
} |
|
1713 |
if (fullName.isEmpty()) |
|
1714 |
break; |
|
3
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1715 |
// using-declarations cannot rename classes, so the last element of |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1716 |
// fullName is already the resolved name we actually want. |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1717 |
// As we do no resolution here, we'll collect useless usings of data |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1718 |
// members and methods as well. This is no big deal. |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1719 |
HashString &ns = fullName.last(); |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1720 |
fullName.append(HashString(QString())); // Mark as unresolved |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1721 |
modifyNamespace(&namespaces)->aliases[ns] = fullName; |
0 | 1722 |
} |
1723 |
break; |
|
1724 |
case Tok_tr: |
|
1725 |
case Tok_trUtf8: |
|
1726 |
if (!tor) |
|
1727 |
goto case_default; |
|
1728 |
if (!sourcetext.isEmpty()) |
|
1729 |
qWarning("%s:%d: //%% cannot be used with tr() / QT_TR_NOOP(). Ignoring\n", |
|
1730 |
qPrintable(yyFileName), yyLineNo); |
|
1731 |
utf8 = (yyTok == Tok_trUtf8); |
|
1732 |
line = yyLineNo; |
|
1733 |
yyTok = getToken(); |
|
1734 |
if (match(Tok_LeftParen) && matchString(&text) && !text.isEmpty()) { |
|
1735 |
comment.clear(); |
|
1736 |
bool plural = false; |
|
1737 |
||
1738 |
if (match(Tok_RightParen)) { |
|
1739 |
// no comment |
|
1740 |
} else if (match(Tok_Comma) && matchStringOrNull(&comment)) { //comment |
|
1741 |
if (match(Tok_RightParen)) { |
|
1742 |
// ok, |
|
1743 |
} else if (match(Tok_Comma)) { |
|
1744 |
plural = true; |
|
1745 |
} |
|
1746 |
} |
|
4
3b1da2848fc7
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
3
diff
changeset
|
1747 |
if (!pendingContext.isEmpty() && !prefix.startsWith(strColons)) { |
0 | 1748 |
QStringList unresolved; |
1749 |
if (!fullyQualify(namespaces, pendingContext, true, &functionContext, &unresolved)) { |
|
1750 |
functionContextUnresolved = unresolved.join(strColons); |
|
1751 |
qWarning("%s:%d: Qualifying with unknown namespace/class %s::%s\n", |
|
1752 |
qPrintable(yyFileName), yyLineNo, |
|
1753 |
qPrintable(stringifyNamespace(functionContext)), |
|
1754 |
qPrintable(unresolved.first())); |
|
1755 |
} |
|
1756 |
pendingContext.clear(); |
|
1757 |
} |
|
1758 |
if (prefix.isEmpty()) { |
|
1759 |
if (functionContextUnresolved.isEmpty()) { |
|
1760 |
int idx = functionContext.length(); |
|
1761 |
if (idx < 2) { |
|
1762 |
qWarning("%s:%d: tr() cannot be called without context\n", |
|
1763 |
qPrintable(yyFileName), yyLineNo); |
|
1764 |
break; |
|
1765 |
} |
|
1766 |
Namespace *fctx; |
|
1767 |
while (!(fctx = findNamespace(functionContext, idx)->classDef)->hasTrFunctions) { |
|
1768 |
if (idx == 1) { |
|
1769 |
context = stringifyNamespace(functionContext); |
|
1770 |
fctx = findNamespace(functionContext)->classDef; |
|
1771 |
if (!fctx->complained) { |
|
1772 |
qWarning("%s:%d: Class '%s' lacks Q_OBJECT macro\n", |
|
1773 |
qPrintable(yyFileName), yyLineNo, |
|
1774 |
qPrintable(context)); |
|
1775 |
fctx->complained = true; |
|
1776 |
} |
|
1777 |
goto gotctx; |
|
1778 |
} |
|
1779 |
--idx; |
|
1780 |
} |
|
1781 |
if (fctx->trQualification.isEmpty()) { |
|
1782 |
context.clear(); |
|
1783 |
for (int i = 1;;) { |
|
1784 |
context += functionContext.at(i).value(); |
|
1785 |
if (++i == idx) |
|
1786 |
break; |
|
1787 |
context += strColons; |
|
1788 |
} |
|
1789 |
fctx->trQualification = context; |
|
1790 |
} else { |
|
1791 |
context = fctx->trQualification; |
|
1792 |
} |
|
1793 |
} else { |
|
1794 |
context = (stringListifyNamespace(functionContext) |
|
1795 |
<< functionContextUnresolved).join(strColons); |
|
1796 |
} |
|
1797 |
} else { |
|
1798 |
#ifdef DIAGNOSE_RETRANSLATABILITY |
|
1799 |
int last = prefix.lastIndexOf(strColons); |
|
1800 |
QString className = prefix.mid(last == -1 ? 0 : last + 2); |
|
1801 |
if (!className.isEmpty() && className == functionName) { |
|
1802 |
qWarning("%s::%d: It is not recommended to call tr() from within a constructor '%s::%s' ", |
|
1803 |
qPrintable(yyFileName), yyLineNo, |
|
1804 |
className.constData(), functionName.constData()); |
|
1805 |
} |
|
1806 |
#endif |
|
1807 |
prefix.chop(2); |
|
1808 |
NamespaceList nsl; |
|
1809 |
QStringList unresolved; |
|
1810 |
if (fullyQualify(functionContext, prefix, false, &nsl, &unresolved)) { |
|
1811 |
Namespace *fctx = findNamespace(nsl)->classDef; |
|
1812 |
if (fctx->trQualification.isEmpty()) { |
|
1813 |
context = stringifyNamespace(nsl); |
|
1814 |
fctx->trQualification = context; |
|
1815 |
} else { |
|
1816 |
context = fctx->trQualification; |
|
1817 |
} |
|
1818 |
if (!fctx->hasTrFunctions && !fctx->complained) { |
|
1819 |
qWarning("%s:%d: Class '%s' lacks Q_OBJECT macro\n", |
|
1820 |
qPrintable(yyFileName), yyLineNo, |
|
1821 |
qPrintable(context)); |
|
1822 |
fctx->complained = true; |
|
1823 |
} |
|
1824 |
} else { |
|
1825 |
context = (stringListifyNamespace(nsl) + unresolved).join(strColons); |
|
1826 |
} |
|
1827 |
prefix.clear(); |
|
1828 |
} |
|
1829 |
||
1830 |
gotctx: |
|
1831 |
recordMessage(line, context, text, comment, extracomment, msgid, extra, utf8, plural); |
|
1832 |
} |
|
1833 |
extracomment.clear(); |
|
1834 |
msgid.clear(); |
|
1835 |
extra.clear(); |
|
1836 |
break; |
|
1837 |
case Tok_translateUtf8: |
|
1838 |
case Tok_translate: |
|
1839 |
if (!tor) |
|
1840 |
goto case_default; |
|
1841 |
if (!sourcetext.isEmpty()) |
|
1842 |
qWarning("%s:%d: //%% cannot be used with translate() / QT_TRANSLATE_NOOP(). Ignoring\n", |
|
1843 |
qPrintable(yyFileName), yyLineNo); |
|
1844 |
utf8 = (yyTok == Tok_translateUtf8); |
|
1845 |
line = yyLineNo; |
|
1846 |
yyTok = getToken(); |
|
1847 |
if (match(Tok_LeftParen) |
|
1848 |
&& matchString(&context) |
|
1849 |
&& match(Tok_Comma) |
|
1850 |
&& matchString(&text) && !text.isEmpty()) |
|
1851 |
{ |
|
1852 |
comment.clear(); |
|
1853 |
bool plural = false; |
|
1854 |
if (!match(Tok_RightParen)) { |
|
1855 |
// look for comment |
|
1856 |
if (match(Tok_Comma) && matchStringOrNull(&comment)) { |
|
1857 |
if (!match(Tok_RightParen)) { |
|
1858 |
// look for encoding |
|
1859 |
if (match(Tok_Comma)) { |
|
1860 |
if (matchEncoding(&utf8)) { |
|
1861 |
if (!match(Tok_RightParen)) { |
|
1862 |
// look for the plural quantifier, |
|
1863 |
// this can be a number, an identifier or |
|
1864 |
// a function call, |
|
1865 |
// so for simplicity we mark it as plural if |
|
1866 |
// we know we have a comma instead of an |
|
1867 |
// right parentheses. |
|
1868 |
plural = match(Tok_Comma); |
|
1869 |
} |
|
1870 |
} else { |
|
1871 |
// This can be a QTranslator::translate("context", |
|
1872 |
// "source", "comment", n) plural translation |
|
1873 |
if (matchExpression() && match(Tok_RightParen)) { |
|
1874 |
plural = true; |
|
1875 |
} else { |
|
1876 |
break; |
|
1877 |
} |
|
1878 |
} |
|
1879 |
} else { |
|
1880 |
break; |
|
1881 |
} |
|
1882 |
} |
|
1883 |
} else { |
|
1884 |
break; |
|
1885 |
} |
|
1886 |
} |
|
1887 |
recordMessage(line, context, text, comment, extracomment, msgid, extra, utf8, plural); |
|
1888 |
} |
|
1889 |
extracomment.clear(); |
|
1890 |
msgid.clear(); |
|
1891 |
extra.clear(); |
|
1892 |
break; |
|
1893 |
case Tok_trid: |
|
1894 |
if (!tor) |
|
1895 |
goto case_default; |
|
3
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1896 |
if (!msgid.isEmpty()) |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1897 |
qWarning("%s:%d: //= cannot be used with qtTrId() / QT_TRID_NOOP(). Ignoring\n", |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1898 |
qPrintable(yyFileName), yyLineNo); |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1899 |
//utf8 = false; // Maybe use //%% or something like that |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1900 |
line = yyLineNo; |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1901 |
yyTok = getToken(); |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1902 |
if (match(Tok_LeftParen) && matchString(&msgid) && !msgid.isEmpty()) { |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1903 |
bool plural = match(Tok_Comma); |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1904 |
recordMessage(line, QString(), sourcetext, QString(), extracomment, |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1905 |
msgid, extra, false, plural); |
0 | 1906 |
} |
3
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1907 |
sourcetext.clear(); |
0 | 1908 |
extracomment.clear(); |
1909 |
msgid.clear(); |
|
1910 |
extra.clear(); |
|
1911 |
break; |
|
1912 |
case Tok_Q_DECLARE_TR_FUNCTIONS: |
|
1913 |
if (getMacroArgs()) { |
|
3
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1914 |
Namespace *ns = modifyNamespace(&namespaces); |
0 | 1915 |
ns->hasTrFunctions = true; |
1916 |
ns->trQualification = yyWord; |
|
1917 |
ns->trQualification.detach(); |
|
1918 |
} |
|
1919 |
yyTok = getToken(); |
|
1920 |
break; |
|
1921 |
case Tok_Q_OBJECT: |
|
3
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1922 |
modifyNamespace(&namespaces)->hasTrFunctions = true; |
0 | 1923 |
yyTok = getToken(); |
1924 |
break; |
|
1925 |
case Tok_Ident: |
|
1926 |
prefix += yyWord; |
|
1927 |
prefix.detach(); |
|
1928 |
yyTok = getToken(); |
|
1929 |
if (yyTok != Tok_ColonColon) { |
|
1930 |
prefix.clear(); |
|
1931 |
if (yyTok == Tok_Ident && !yyParenDepth) |
|
1932 |
prospectiveContext.clear(); |
|
1933 |
} |
|
1934 |
break; |
|
3
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1935 |
case Tok_Comment: { |
0 | 1936 |
if (!tor) |
1937 |
goto case_default; |
|
3
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1938 |
const QChar *ptr = yyWord.unicode(); |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1939 |
if (*ptr == QLatin1Char(':') && ptr[1].isSpace()) { |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1940 |
yyWord.remove(0, 2); |
0 | 1941 |
extracomment += yyWord; |
1942 |
extracomment.detach(); |
|
3
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1943 |
} else if (*ptr == QLatin1Char('=') && ptr[1].isSpace()) { |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1944 |
yyWord.remove(0, 2); |
0 | 1945 |
msgid = yyWord.simplified(); |
1946 |
msgid.detach(); |
|
3
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1947 |
} else if (*ptr == QLatin1Char('~') && ptr[1].isSpace()) { |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1948 |
yyWord.remove(0, 2); |
0 | 1949 |
text = yyWord.trimmed(); |
1950 |
int k = text.indexOf(QLatin1Char(' ')); |
|
1951 |
if (k > -1) |
|
1952 |
extra.insert(text.left(k), text.mid(k + 1).trimmed()); |
|
1953 |
text.clear(); |
|
3
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1954 |
} else if (*ptr == QLatin1Char('%') && ptr[1].isSpace()) { |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1955 |
sourcetext.reserve(sourcetext.length() + yyWord.length() - 2); |
0 | 1956 |
ushort *ptr = (ushort *)sourcetext.data() + sourcetext.length(); |
3
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
1957 |
int p = 2, c; |
0 | 1958 |
forever { |
1959 |
if (p >= yyWord.length()) |
|
1960 |
break; |
|
1961 |
c = yyWord.unicode()[p++].unicode(); |
|
1962 |
if (isspace(c)) |
|
1963 |
continue; |
|
1964 |
if (c != '"') { |
|
1965 |
qWarning("%s:%d: Unexpected character in meta string\n", |
|
1966 |
qPrintable(yyFileName), yyLineNo); |
|
1967 |
break; |
|
1968 |
} |
|
1969 |
forever { |
|
1970 |
if (p >= yyWord.length()) { |
|
1971 |
whoops: |
|
1972 |
qWarning("%s:%d: Unterminated meta string\n", |
|
1973 |
qPrintable(yyFileName), yyLineNo); |
|
1974 |
break; |
|
1975 |
} |
|
1976 |
c = yyWord.unicode()[p++].unicode(); |
|
1977 |
if (c == '"') |
|
1978 |
break; |
|
1979 |
if (c == '\\') { |
|
1980 |
if (p >= yyWord.length()) |
|
1981 |
goto whoops; |
|
1982 |
c = yyWord.unicode()[p++].unicode(); |
|
1983 |
if (c == '\n') |
|
1984 |
goto whoops; |
|
1985 |
*ptr++ = '\\'; |
|
1986 |
} |
|
1987 |
*ptr++ = c; |
|
1988 |
} |
|
1989 |
} |
|
1990 |
sourcetext.resize(ptr - (ushort *)sourcetext.data()); |
|
1991 |
} else { |
|
1992 |
const ushort *uc = (const ushort *)yyWord.unicode(); // Is zero-terminated |
|
1993 |
int idx = 0; |
|
1994 |
ushort c; |
|
1995 |
while ((c = uc[idx]) == ' ' || c == '\t' || c == '\n') |
|
1996 |
++idx; |
|
1997 |
if (!memcmp(uc + idx, MagicComment.unicode(), MagicComment.length() * 2)) { |
|
1998 |
idx += MagicComment.length(); |
|
1999 |
comment = QString::fromRawData(yyWord.unicode() + idx, |
|
2000 |
yyWord.length() - idx).simplified(); |
|
2001 |
int k = comment.indexOf(QLatin1Char(' ')); |
|
2002 |
if (k == -1) { |
|
2003 |
context = comment; |
|
2004 |
} else { |
|
2005 |
context = comment.left(k); |
|
2006 |
comment.remove(0, k + 1); |
|
2007 |
recordMessage(yyLineNo, context, QString(), comment, extracomment, |
|
2008 |
QString(), TranslatorMessage::ExtraData(), false, false); |
|
2009 |
extracomment.clear(); |
|
2010 |
tor->setExtras(extra); |
|
2011 |
extra.clear(); |
|
2012 |
} |
|
2013 |
} |
|
2014 |
} |
|
2015 |
yyTok = getToken(); |
|
2016 |
break; |
|
3
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
2017 |
} |
0 | 2018 |
case Tok_Arrow: |
2019 |
yyTok = getToken(); |
|
2020 |
if (yyTok == Tok_tr || yyTok == Tok_trUtf8) |
|
2021 |
qWarning("%s:%d: Cannot invoke tr() like this\n", |
|
2022 |
qPrintable(yyFileName), yyLineNo); |
|
2023 |
break; |
|
2024 |
case Tok_ColonColon: |
|
2025 |
if (yyBraceDepth == namespaceDepths.count() && yyParenDepth == 0 && !yyTokColonSeen) |
|
2026 |
prospectiveContext = prefix; |
|
2027 |
prefix += strColons; |
|
2028 |
yyTok = getToken(); |
|
2029 |
#ifdef DIAGNOSE_RETRANSLATABILITY |
|
2030 |
if (yyTok == Tok_Ident && yyBraceDepth == namespaceDepths.count() && yyParenDepth == 0) { |
|
2031 |
functionName = yyWord; |
|
2032 |
functionName.detach(); |
|
2033 |
} |
|
2034 |
#endif |
|
2035 |
break; |
|
2036 |
case Tok_RightBrace: |
|
2037 |
if (yyBraceDepth + 1 == namespaceDepths.count()) // class or namespace |
|
2038 |
truncateNamespaces(&namespaces, namespaceDepths.pop()); |
|
2039 |
if (yyBraceDepth == namespaceDepths.count()) { |
|
2040 |
// function, class or namespace |
|
2041 |
if (!yyBraceDepth && !directInclude) { |
|
2042 |
truncateNamespaces(&functionContext, 1); |
|
2043 |
functionContextUnresolved = cd.m_defaultContext; |
|
2044 |
} else { |
|
2045 |
functionContext = namespaces; |
|
2046 |
functionContextUnresolved.clear(); |
|
2047 |
} |
|
2048 |
pendingContext.clear(); |
|
2049 |
} |
|
2050 |
// fallthrough |
|
2051 |
case Tok_Semicolon: |
|
2052 |
prospectiveContext.clear(); |
|
2053 |
prefix.clear(); |
|
2054 |
extracomment.clear(); |
|
2055 |
msgid.clear(); |
|
2056 |
extra.clear(); |
|
2057 |
yyTokColonSeen = false; |
|
2058 |
yyTok = getToken(); |
|
2059 |
break; |
|
2060 |
case Tok_Colon: |
|
2061 |
if (!prospectiveContext.isEmpty() |
|
2062 |
&& yyBraceDepth == namespaceDepths.count() && yyParenDepth == 0) |
|
2063 |
pendingContext = prospectiveContext; |
|
2064 |
yyTokColonSeen = true; |
|
2065 |
yyTok = getToken(); |
|
2066 |
break; |
|
2067 |
case Tok_LeftBrace: |
|
2068 |
if (!prospectiveContext.isEmpty() |
|
2069 |
&& yyBraceDepth == namespaceDepths.count() + 1 && yyParenDepth == 0) |
|
2070 |
pendingContext = prospectiveContext; |
|
2071 |
// fallthrough |
|
2072 |
case Tok_LeftParen: |
|
2073 |
case Tok_RightParen: |
|
2074 |
yyTokColonSeen = false; |
|
2075 |
yyTok = getToken(); |
|
2076 |
break; |
|
2077 |
default: |
|
2078 |
if (!yyParenDepth) |
|
2079 |
prospectiveContext.clear(); |
|
2080 |
case_default: |
|
2081 |
yyTok = getToken(); |
|
2082 |
break; |
|
2083 |
} |
|
2084 |
} |
|
2085 |
||
2086 |
if (yyBraceDepth != 0) |
|
2087 |
qWarning("%s:%d: Unbalanced opening brace in C++ code" |
|
2088 |
" (or abuse of the C++ preprocessor)\n", |
|
2089 |
qPrintable(yyFileName), yyBraceLineNo); |
|
2090 |
else if (yyParenDepth != 0) |
|
2091 |
qWarning("%s:%d: Unbalanced opening parenthesis in C++ code" |
|
2092 |
" (or abuse of the C++ preprocessor)\n", |
|
2093 |
qPrintable(yyFileName), yyParenLineNo); |
|
2094 |
} |
|
2095 |
||
2096 |
const ParseResults *CppParser::recordResults(bool isHeader) |
|
2097 |
{ |
|
2098 |
if (tor) { |
|
2099 |
if (tor->messageCount()) { |
|
2100 |
CppFiles::setTranslator(yyFileName, tor); |
|
2101 |
} else { |
|
2102 |
delete tor; |
|
2103 |
tor = 0; |
|
2104 |
} |
|
2105 |
} |
|
2106 |
if (isHeader) { |
|
2107 |
const ParseResults *pr; |
|
2108 |
if (!tor && results->includes.count() == 1 |
|
2109 |
&& results->rootNamespace.children.isEmpty() |
|
2110 |
&& results->rootNamespace.aliases.isEmpty() |
|
2111 |
&& results->rootNamespace.usings.isEmpty()) { |
|
2112 |
// This is a forwarding header. Slash it. |
|
2113 |
pr = *results->includes.begin(); |
|
2114 |
delete results; |
|
2115 |
} else { |
|
2116 |
results->fileId = nextFileId++; |
|
2117 |
pr = results; |
|
2118 |
} |
|
2119 |
CppFiles::setResults(yyFileName, pr); |
|
2120 |
return pr; |
|
2121 |
} else { |
|
2122 |
delete results; |
|
2123 |
return 0; |
|
2124 |
} |
|
2125 |
} |
|
2126 |
||
2127 |
/* |
|
2128 |
Fetches tr() calls in C++ code in UI files (inside "<function>" |
|
2129 |
tag). This mechanism is obsolete. |
|
2130 |
*/ |
|
2131 |
void fetchtrInlinedCpp(const QString &in, Translator &translator, const QString &context) |
|
2132 |
{ |
|
2133 |
CppParser parser; |
|
2134 |
parser.setInput(in); |
|
2135 |
ConversionData cd; |
|
2136 |
QSet<QString> inclusions; |
|
2137 |
parser.setTranslator(&translator); |
|
2138 |
parser.parse(context, cd, inclusions); |
|
2139 |
parser.deleteResults(); |
|
2140 |
} |
|
2141 |
||
2142 |
void loadCPP(Translator &translator, const QStringList &filenames, ConversionData &cd) |
|
2143 |
{ |
|
2144 |
QByteArray codecName = cd.m_codecForSource.isEmpty() |
|
2145 |
? translator.codecName() : cd.m_codecForSource; |
|
2146 |
QTextCodec *codec = QTextCodec::codecForName(codecName); |
|
2147 |
||
2148 |
foreach (const QString &filename, filenames) { |
|
2149 |
if (CppFiles::getResults(filename) || CppFiles::isBlacklisted(filename)) |
|
2150 |
continue; |
|
2151 |
||
2152 |
QFile file(filename); |
|
2153 |
if (!file.open(QIODevice::ReadOnly)) { |
|
2154 |
cd.appendError(QString::fromLatin1("Cannot open %1: %2") |
|
2155 |
.arg(filename, file.errorString())); |
|
2156 |
continue; |
|
2157 |
} |
|
2158 |
||
2159 |
CppParser parser; |
|
2160 |
QTextStream ts(&file); |
|
2161 |
ts.setCodec(codec); |
|
2162 |
ts.setAutoDetectUnicode(true); |
|
3
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
2163 |
parser.setInput(ts, filename); |
41300fa6a67c
Revision: 201003
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
2164 |
if (cd.m_outputCodec.isEmpty() && ts.codec()->name() == "UTF-16") |
0 | 2165 |
translator.setCodecName("System"); |
2166 |
Translator *tor = new Translator; |
|
2167 |
tor->setCodecName(translator.codecName()); |
|
2168 |
parser.setTranslator(tor); |
|
2169 |
QSet<QString> inclusions; |
|
2170 |
parser.parse(cd.m_defaultContext, cd, inclusions); |
|
2171 |
parser.recordResults(isHeader(filename)); |
|
2172 |
} |
|
2173 |
||
2174 |
foreach (const QString &filename, filenames) |
|
2175 |
if (!CppFiles::isBlacklisted(filename)) |
|
2176 |
if (const Translator *tor = CppFiles::getTranslator(filename)) |
|
2177 |
foreach (const TranslatorMessage &msg, tor->messages()) |
|
2178 |
translator.extend(msg); |
|
2179 |
} |
|
2180 |
||
2181 |
QT_END_NAMESPACE |