OSDN Git Service

Update license.
[qt-creator-jp/qt-creator-jp.git] / src / shared / proparser / profileparser.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
8 **
9 **
10 ** GNU Lesser General Public License Usage
11 **
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 **
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 **
23 ** Other Usage
24 **
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
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 #include "profileparser.h"
34
35 #include "ioutils.h"
36 using namespace ProFileEvaluatorInternal;
37
38 #include <QtCore/QFile>
39 #ifdef PROPARSER_THREAD_SAFE
40 # include <QtCore/QThreadPool>
41 #endif
42
43 QT_BEGIN_NAMESPACE
44
45 ///////////////////////////////////////////////////////////////////////
46 //
47 // ProFileCache
48 //
49 ///////////////////////////////////////////////////////////////////////
50
51 ProFileCache::~ProFileCache()
52 {
53     foreach (const Entry &ent, parsed_files)
54         if (ent.pro)
55             ent.pro->deref();
56 }
57
58 void ProFileCache::discardFile(const QString &fileName)
59 {
60 #ifdef PROPARSER_THREAD_SAFE
61     QMutexLocker lck(&mutex);
62 #endif
63     QHash<QString, Entry>::Iterator it = parsed_files.find(fileName);
64     if (it != parsed_files.end()) {
65         if (it->pro)
66             it->pro->deref();
67         parsed_files.erase(it);
68     }
69 }
70
71 void ProFileCache::discardFiles(const QString &prefix)
72 {
73 #ifdef PROPARSER_THREAD_SAFE
74     QMutexLocker lck(&mutex);
75 #endif
76     QHash<QString, Entry>::Iterator
77             it = parsed_files.begin(),
78             end = parsed_files.end();
79     while (it != end)
80         if (it.key().startsWith(prefix)) {
81             if (it->pro)
82                 it->pro->deref();
83             it = parsed_files.erase(it);
84         } else {
85             ++it;
86         }
87 }
88
89
90 ////////// Parser ///////////
91
92 #define fL1S(s) QString::fromLatin1(s)
93
94 namespace { // MSVC2010 doesn't seem to know the semantics of "static" ...
95
96 static struct {
97     QString strelse;
98     QString strfor;
99     QString strdefineTest;
100     QString strdefineReplace;
101 } statics;
102
103 }
104
105 void ProFileParser::initialize()
106 {
107     if (!statics.strelse.isNull())
108         return;
109
110     statics.strelse = QLatin1String("else");
111     statics.strfor = QLatin1String("for");
112     statics.strdefineTest = QLatin1String("defineTest");
113     statics.strdefineReplace = QLatin1String("defineReplace");
114 }
115
116 ProFileParser::ProFileParser(ProFileCache *cache, ProFileParserHandler *handler)
117     : m_cache(cache)
118     , m_handler(handler)
119 {
120     // So that single-threaded apps don't have to call initialize() for now.
121     initialize();
122 }
123
124 ProFile *ProFileParser::parsedProFile(const QString &fileName, bool cache, const QString *contents)
125 {
126     ProFile *pro;
127     if (cache && m_cache) {
128         ProFileCache::Entry *ent;
129 #ifdef PROPARSER_THREAD_SAFE
130         QMutexLocker locker(&m_cache->mutex);
131 #endif
132         QHash<QString, ProFileCache::Entry>::Iterator it = m_cache->parsed_files.find(fileName);
133         if (it != m_cache->parsed_files.end()) {
134             ent = &*it;
135 #ifdef PROPARSER_THREAD_SAFE
136             if (ent->locker && !ent->locker->done) {
137                 ++ent->locker->waiters;
138                 QThreadPool::globalInstance()->releaseThread();
139                 ent->locker->cond.wait(locker.mutex());
140                 QThreadPool::globalInstance()->reserveThread();
141                 if (!--ent->locker->waiters) {
142                     delete ent->locker;
143                     ent->locker = 0;
144                 }
145             }
146 #endif
147             if ((pro = ent->pro))
148                 pro->ref();
149         } else {
150             ent = &m_cache->parsed_files[fileName];
151 #ifdef PROPARSER_THREAD_SAFE
152             ent->locker = new ProFileCache::Entry::Locker;
153             locker.unlock();
154 #endif
155             pro = new ProFile(fileName);
156             if (!(!contents ? read(pro) : read(pro, *contents))) {
157                 delete pro;
158                 pro = 0;
159             } else {
160                 pro->ref();
161             }
162             ent->pro = pro;
163 #ifdef PROPARSER_THREAD_SAFE
164             locker.relock();
165             if (ent->locker->waiters) {
166                 ent->locker->done = true;
167                 ent->locker->cond.wakeAll();
168             } else {
169                 delete ent->locker;
170                 ent->locker = 0;
171             }
172 #endif
173         }
174     } else {
175         pro = new ProFile(fileName);
176         if (!(!contents ? read(pro) : read(pro, *contents))) {
177             delete pro;
178             pro = 0;
179         }
180     }
181     return pro;
182 }
183
184 bool ProFileParser::read(ProFile *pro)
185 {
186     QFile file(pro->fileName());
187     if (!file.open(QIODevice::ReadOnly)) {
188         if (m_handler && IoUtils::exists(pro->fileName()))
189             m_handler->parseError(QString(), 0, fL1S("%1 not readable.").arg(pro->fileName()));
190         return false;
191     }
192
193     QString content(QString::fromLocal8Bit(file.readAll()));
194     file.close();
195     return read(pro, content);
196 }
197
198 void ProFileParser::putTok(ushort *&tokPtr, ushort tok)
199 {
200     *tokPtr++ = tok;
201 }
202
203 void ProFileParser::putBlockLen(ushort *&tokPtr, uint len)
204 {
205     *tokPtr++ = (ushort)len;
206     *tokPtr++ = (ushort)(len >> 16);
207 }
208
209 void ProFileParser::putBlock(ushort *&tokPtr, const ushort *buf, uint len)
210 {
211     memcpy(tokPtr, buf, len * 2);
212     tokPtr += len;
213 }
214
215 void ProFileParser::putHashStr(ushort *&pTokPtr, const ushort *buf, uint len)
216 {
217     uint hash = ProString::hash((const QChar *)buf, len);
218     ushort *tokPtr = pTokPtr;
219     *tokPtr++ = (ushort)hash;
220     *tokPtr++ = (ushort)(hash >> 16);
221     *tokPtr++ = (ushort)len;
222     memcpy(tokPtr, buf, len * 2);
223     pTokPtr = tokPtr + len;
224 }
225
226 void ProFileParser::finalizeHashStr(ushort *buf, uint len)
227 {
228     buf[-4] = TokHashLiteral;
229     buf[-1] = len;
230     uint hash = ProString::hash((const QChar *)buf, len);
231     buf[-3] = (ushort)hash;
232     buf[-2] = (ushort)(hash >> 16);
233 }
234
235 bool ProFileParser::read(ProFile *pro, const QString &in)
236 {
237     m_proFile = pro;
238     m_lineNo = 1;
239
240     // Final precompiled token stream buffer
241     QString tokBuff;
242     // Worst-case size calculations:
243     // - line marker adds 1 (2-nl) to 1st token of each line
244     // - empty assignment "A=":2 =>
245     //   TokHashLiteral(1) + hash(2) + len(1) + "A"(1) + TokAssign(1) +
246     //   TokValueTerminator(1) == 7 (8)
247     // - non-empty assignment "A=B C":5 =>
248     //   TokHashLiteral(1) + hash(2) + len(1) + "A"(1) + TokAssign(1) +
249     //   TokLiteral(1) + len(1) + "B"(1) +
250     //   TokLiteral(1) + len(1) + "C"(1) + TokValueTerminator(1) == 13 (14)
251     // - variable expansion: "$$f":3 =>
252     //   TokVariable(1) + hash(2) + len(1) + "f"(1) = 5
253     // - function expansion: "$$f()":5 =>
254     //   TokFuncName(1) + hash(2) + len(1) + "f"(1) + TokFuncTerminator(1) = 6
255     // - scope: "X:":2 =>
256     //   TokHashLiteral(1) + hash(2) + len(1) + "A"(1) + TokCondition(1) +
257     //   TokBranch(1) + len(2) + ... + len(2) + ... == 10
258     // - test: "X():":4 =>
259     //   TokHashLiteral(1) + hash(2) + len(1) + "A"(1) + TokTestCall(1) + TokFuncTerminator(1) +
260     //   TokBranch(1) + len(2) + ... + len(2) + ... == 11
261     // - "for(A,B):":9 =>
262     //   TokForLoop(1) + hash(2) + len(1) + "A"(1) +
263     //   len(2) + TokLiteral(1) + len(1) + "B"(1) + TokValueTerminator(1) +
264     //   len(2) + ... + TokTerminator(1) == 14 (15)
265     tokBuff.reserve((in.size() + 1) * 5);
266     ushort *tokPtr = (ushort *)tokBuff.constData(); // Current writing position
267
268     // Expression precompiler buffer.
269     QString xprBuff;
270     xprBuff.reserve(tokBuff.capacity()); // Excessive, but simple
271     ushort * const buf = (ushort *)xprBuff.constData();
272
273     // Parser state
274     m_blockstack.clear();
275     m_blockstack.resize(1);
276
277     QStack<ParseCtx> xprStack;
278     xprStack.reserve(10);
279
280     // We rely on QStrings being null-terminated, so don't maintain a global end pointer.
281     const ushort *cur = (const ushort *)in.unicode();
282     m_canElse = false;
283   freshLine:
284     m_state = StNew;
285     m_invert = false;
286     m_operator = NoOperator;
287     m_markLine = m_lineNo;
288     m_inError = false;
289     Context context = CtxTest;
290     int parens = 0; // Braces in value context
291     int argc = 0;
292     int wordCount = 0; // Number of words in currently accumulated expression
293     bool putSpace = false; // Only ever true inside quoted string
294     bool lineMarked = true; // For in-expression markers
295     ushort needSep = TokNewStr; // Complementary to putSpace: separator outside quotes
296     ushort quote = 0;
297     ushort term = 0;
298
299     ushort *ptr = buf;
300     ptr += 4;
301     ushort *xprPtr = ptr;
302
303 #define FLUSH_LHS_LITERAL() \
304     do { \
305         if ((tlen = ptr - xprPtr)) { \
306             finalizeHashStr(xprPtr, tlen); \
307             if (needSep) { \
308                 wordCount++; \
309                 needSep = 0; \
310             } \
311         } else { \
312             ptr -= 4; \
313         } \
314     } while (0)
315
316 #define FLUSH_RHS_LITERAL() \
317     do { \
318         if ((tlen = ptr - xprPtr)) { \
319             xprPtr[-2] = TokLiteral | needSep; \
320             xprPtr[-1] = tlen; \
321             if (needSep) { \
322                 wordCount++; \
323                 needSep = 0; \
324             } \
325         } else { \
326             ptr -= 2; \
327         } \
328     } while (0)
329
330 #define FLUSH_LITERAL() \
331     do { \
332         if (context == CtxTest) \
333             FLUSH_LHS_LITERAL(); \
334         else \
335             FLUSH_RHS_LITERAL(); \
336     } while (0)
337
338 #define FLUSH_VALUE_LIST() \
339     do { \
340         if (wordCount > 1) { \
341             xprPtr = tokPtr; \
342             if (*xprPtr == TokLine) \
343                 xprPtr += 2; \
344             tokPtr[-1] = ((*xprPtr & TokMask) == TokLiteral) ? wordCount : 0; \
345         } else { \
346             tokPtr[-1] = 0; \
347         } \
348         tokPtr = ptr; \
349         putTok(tokPtr, TokValueTerminator); \
350     } while (0)
351
352     forever {
353         ushort c;
354
355         // First, skip leading whitespace
356         for (;; ++cur) {
357             c = *cur;
358             if (c == '\n') {
359                 ++cur;
360                 goto flushLine;
361             } else if (!c) {
362                 goto flushLine;
363             } else if (c != ' ' && c != '\t' && c != '\r') {
364                 break;
365             }
366         }
367
368         // Then strip comments. Yep - no escaping is possible.
369         const ushort *end; // End of this line
370         const ushort *cptr; // Start of next line
371         for (cptr = cur;; ++cptr) {
372             c = *cptr;
373             if (c == '#') {
374                 for (end = cptr; (c = *++cptr);) {
375                     if (c == '\n') {
376                         ++cptr;
377                         break;
378                     }
379                 }
380                 if (end == cur) { // Line with only a comment (sans whitespace)
381                     if (m_markLine == m_lineNo)
382                         m_markLine++;
383                     // Qmake bizarreness: such lines do not affect line continuations
384                     goto ignore;
385                 }
386                 break;
387             }
388             if (!c) {
389                 end = cptr;
390                 break;
391             }
392             if (c == '\n') {
393                 end = cptr++;
394                 break;
395             }
396         }
397
398         // Then look for line continuations. Yep - no escaping here as well.
399         bool lineCont;
400         forever {
401             // We don't have to check for underrun here, as we already determined
402             // that the line is non-empty.
403             ushort ec = *(end - 1);
404             if (ec == '\\') {
405                 --end;
406                 lineCont = true;
407                 break;
408             }
409             if (ec != ' ' && ec != '\t' && ec != '\r') {
410                 lineCont = false;
411                 break;
412             }
413             --end;
414         }
415
416             // Finally, do the tokenization
417             ushort tok, rtok;
418             int tlen;
419           newWord:
420             do {
421                 if (cur == end)
422                     goto lineEnd;
423                 c = *cur++;
424             } while (c == ' ' || c == '\t');
425             forever {
426                 if (c == '$') {
427                     if (*cur == '$') { // may be EOF, EOL, WS, '#' or '\\' if past end
428                         cur++;
429                         if (putSpace) {
430                             putSpace = false;
431                             *ptr++ = ' ';
432                         }
433                         FLUSH_LITERAL();
434                         if (!lineMarked) {
435                             lineMarked = true;
436                             *ptr++ = TokLine;
437                             *ptr++ = (ushort)m_lineNo;
438                         }
439                         term = 0;
440                         tok = TokVariable;
441                         c = *cur;
442                         if (c == '[') {
443                             ptr += 2;
444                             tok = TokProperty;
445                             term = ']';
446                             c = *++cur;
447                         } else if (c == '{') {
448                             ptr += 4;
449                             term = '}';
450                             c = *++cur;
451                         } else if (c == '(') {
452                             // FIXME: could/should expand this immediately
453                             ptr += 2;
454                             tok = TokEnvVar;
455                             term = ')';
456                             c = *++cur;
457                         } else {
458                             ptr += 4;
459                         }
460                         xprPtr = ptr;
461                         rtok = tok;
462                         while ((c & 0xFF00) || c == '.' || c == '_' ||
463                                (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
464                                (c >= '0' && c <= '9')) {
465                             *ptr++ = c;
466                             if (++cur == end) {
467                                 c = 0;
468                                 goto notfunc;
469                             }
470                             c = *cur;
471                         }
472                         if (tok == TokVariable && c == '(')
473                             tok = TokFuncName;
474                       notfunc:
475                         if (quote)
476                             tok |= TokQuoted;
477                         if (needSep) {
478                             tok |= needSep;
479                             wordCount++;
480                         }
481                         tlen = ptr - xprPtr;
482                         if (rtok == TokVariable) {
483                             xprPtr[-4] = tok;
484                             uint hash = ProString::hash((const QChar *)xprPtr, tlen);
485                             xprPtr[-3] = (ushort)hash;
486                             xprPtr[-2] = (ushort)(hash >> 16);
487                         } else {
488                             xprPtr[-2] = tok;
489                         }
490                         xprPtr[-1] = tlen;
491                         if ((tok & TokMask) == TokFuncName) {
492                             cur++;
493                           funcCall:
494                             {
495                                 xprStack.resize(xprStack.size() + 1);
496                                 ParseCtx &top = xprStack.top();
497                                 top.parens = parens;
498                                 top.quote = quote;
499                                 top.terminator = term;
500                                 top.context = context;
501                                 top.argc = argc;
502                                 top.wordCount = wordCount;
503                             }
504                             parens = 0;
505                             quote = 0;
506                             term = 0;
507                             argc = 1;
508                             context = CtxArgs;
509                           nextToken:
510                             wordCount = 0;
511                           nextWord:
512                             ptr += (context == CtxTest) ? 4 : 2;
513                             xprPtr = ptr;
514                             needSep = TokNewStr;
515                             goto newWord;
516                         }
517                         if (term) {
518                             cur++;
519                           checkTerm:
520                             if (c != term) {
521                                 parseError(fL1S("Missing %1 terminator [found %2]")
522                                     .arg(QChar(term))
523                                     .arg(c ? QString(c) : QString::fromLatin1("end-of-line")));
524                                 pro->setOk(false);
525                                 m_inError = true;
526                                 if (c)
527                                     cur--;
528                                 // Just parse on, as if there was a terminator ...
529                             }
530                         }
531                       joinToken:
532                         ptr += (context == CtxTest) ? 4 : 2;
533                         xprPtr = ptr;
534                         needSep = 0;
535                         goto nextChr;
536                     }
537                 } else if (c == '\\' && cur != end) {
538                     static const char symbols[] = "[]{}()$\\'\"";
539                     ushort c2 = *cur;
540                     if (!(c2 & 0xff00) && strchr(symbols, c2)) {
541                         c = c2;
542                         cur++;
543                     }
544                 } else if (quote) {
545                     if (c == quote) {
546                         quote = 0;
547                         if (putSpace) {
548                             putSpace = false;
549                             *ptr++ = ' ';
550                         }
551                         goto nextChr;
552                     } else if (c == ' ' || c == '\t') {
553                         putSpace = true;
554                         goto nextChr;
555                     } else if (c == '!' && ptr == xprPtr && context == CtxTest) {
556                         m_invert ^= true;
557                         goto nextChr;
558                     }
559                 } else if (c == '\'' || c == '"') {
560                     quote = c;
561                     goto nextChr;
562                 } else if (c == ' ' || c == '\t') {
563                     FLUSH_LITERAL();
564                     goto nextWord;
565                 } else if (context == CtxArgs) {
566                     // Function arg context
567                     if (c == '(') {
568                         ++parens;
569                     } else if (c == ')') {
570                         if (--parens < 0) {
571                             FLUSH_RHS_LITERAL();
572                             *ptr++ = TokFuncTerminator;
573                             int theargc = argc;
574                             {
575                                 ParseCtx &top = xprStack.top();
576                                 parens = top.parens;
577                                 quote = top.quote;
578                                 term = top.terminator;
579                                 context = top.context;
580                                 argc = top.argc;
581                                 wordCount = top.wordCount;
582                                 xprStack.resize(xprStack.size() - 1);
583                             }
584                             if (term == ':') {
585                                 finalizeCall(tokPtr, buf, ptr, theargc);
586                                 goto nextItem;
587                             } else if (term == '}') {
588                                 c = (cur == end) ? 0 : *cur++;
589                                 goto checkTerm;
590                             } else {
591                                 Q_ASSERT(!term);
592                                 goto joinToken;
593                             }
594                         }
595                     } else if (!parens && c == ',') {
596                         FLUSH_RHS_LITERAL();
597                         *ptr++ = TokArgSeparator;
598                         argc++;
599                         goto nextToken;
600                     }
601                 } else if (context == CtxTest) {
602                     // Test or LHS context
603                     if (c == '(') {
604                         FLUSH_LHS_LITERAL();
605                         if (wordCount != 1) {
606                             if (wordCount)
607                                 parseError(fL1S("Extra characters after test expression."));
608                             else
609                                 parseError(fL1S("Opening parenthesis without prior test name."));
610                             pro->setOk(false);
611                             ptr = buf; // Put empty function name
612                         }
613                         *ptr++ = TokTestCall;
614                         term = ':';
615                         goto funcCall;
616                     } else if (c == '!' && ptr == xprPtr) {
617                         m_invert ^= true;
618                         goto nextChr;
619                     } else if (c == ':') {
620                         FLUSH_LHS_LITERAL();
621                         finalizeCond(tokPtr, buf, ptr, wordCount);
622                         if (m_state == StNew)
623                             parseError(fL1S("And operator without prior condition."));
624                         else
625                             m_operator = AndOperator;
626                       nextItem:
627                         ptr = buf;
628                         goto nextToken;
629                     } else if (c == '|') {
630                         FLUSH_LHS_LITERAL();
631                         finalizeCond(tokPtr, buf, ptr, wordCount);
632                         if (m_state != StCond)
633                             parseError(fL1S("Or operator without prior condition."));
634                         else
635                             m_operator = OrOperator;
636                         goto nextItem;
637                     } else if (c == '{') {
638                         FLUSH_LHS_LITERAL();
639                         finalizeCond(tokPtr, buf, ptr, wordCount);
640                         flushCond(tokPtr);
641                         ++m_blockstack.top().braceLevel;
642                         goto nextItem;
643                     } else if (c == '}') {
644                         FLUSH_LHS_LITERAL();
645                         finalizeCond(tokPtr, buf, ptr, wordCount);
646                         flushScopes(tokPtr);
647                       closeScope:
648                         if (!m_blockstack.top().braceLevel) {
649                             parseError(fL1S("Excess closing brace."));
650                         } else if (!--m_blockstack.top().braceLevel
651                                    && m_blockstack.count() != 1) {
652                             leaveScope(tokPtr);
653                             m_state = StNew;
654                             m_canElse = false;
655                             m_markLine = m_lineNo;
656                         }
657                         goto nextItem;
658                     } else if (c == '+') {
659                         tok = TokAppend;
660                         goto do2Op;
661                     } else if (c == '-') {
662                         tok = TokRemove;
663                         goto do2Op;
664                     } else if (c == '*') {
665                         tok = TokAppendUnique;
666                         goto do2Op;
667                     } else if (c == '~') {
668                         tok = TokReplace;
669                       do2Op:
670                         if (*cur == '=') {
671                             cur++;
672                             goto doOp;
673                         }
674                     } else if (c == '=') {
675                         tok = TokAssign;
676                       doOp:
677                         FLUSH_LHS_LITERAL();
678                         flushCond(tokPtr);
679                         putLineMarker(tokPtr);
680                         if (wordCount != 1) {
681                             parseError(fL1S("Assignment needs exactly one word on the left hand side."));
682                             pro->setOk(false);
683                             // Put empty variable name.
684                         } else {
685                             putBlock(tokPtr, buf, ptr - buf);
686                         }
687                         putTok(tokPtr, tok);
688                         context = CtxValue;
689                         ptr = ++tokPtr;
690                         goto nextToken;
691                     }
692                 } else { // context == CtxValue
693                     if (c == '{') {
694                         ++parens;
695                     } else if (c == '}') {
696                         if (!parens) {
697                             FLUSH_RHS_LITERAL();
698                             FLUSH_VALUE_LIST();
699                             context = CtxTest;
700                             goto closeScope;
701                         }
702                         --parens;
703                     }
704                 }
705                 if (putSpace) {
706                     putSpace = false;
707                     *ptr++ = ' ';
708                 }
709                 *ptr++ = c;
710               nextChr:
711                 if (cur == end)
712                     goto lineEnd;
713                 c = *cur++;
714             }
715
716           lineEnd:
717             if (lineCont) {
718                 if (quote) {
719                     putSpace = true;
720                 } else {
721                     FLUSH_LITERAL();
722                     needSep = TokNewStr;
723                     ptr += (context == CtxTest) ? 4 : 2;
724                     xprPtr = ptr;
725                 }
726             } else {
727                 c = '\n';
728                 cur = cptr;
729               flushLine:
730                 FLUSH_LITERAL();
731                 if (quote) {
732                     parseError(fL1S("Missing closing %1 quote").arg(QChar(quote)));
733                     if (!xprStack.isEmpty()) {
734                         context = xprStack.at(0).context;
735                         xprStack.clear();
736                     }
737                     goto flErr;
738                 } else if (!xprStack.isEmpty()) {
739                     parseError(fL1S("Missing closing parenthesis in function call"));
740                     context = xprStack.at(0).context;
741                     xprStack.clear();
742                   flErr:
743                     pro->setOk(false);
744                     if (context == CtxValue) {
745                         tokPtr[-1] = 0; // sizehint
746                         putTok(tokPtr, TokValueTerminator);
747                     } else {
748                         bogusTest(tokPtr);
749                     }
750                 } else if (context == CtxValue) {
751                     FLUSH_VALUE_LIST();
752                 } else {
753                     finalizeCond(tokPtr, buf, ptr, wordCount);
754                 }
755                 if (!c)
756                     break;
757                 ++m_lineNo;
758                 goto freshLine;
759             }
760
761         if (!lineCont) {
762             cur = cptr;
763             ++m_lineNo;
764             goto freshLine;
765         }
766         lineMarked = false;
767       ignore:
768         cur = cptr;
769         ++m_lineNo;
770     }
771
772     flushScopes(tokPtr);
773     if (m_blockstack.size() > 1) {
774         parseError(fL1S("Missing closing brace(s)."));
775         pro->setOk(false);
776     }
777     while (m_blockstack.size())
778         leaveScope(tokPtr);
779     xprBuff.clear();
780     *pro->itemsRef() = QString(tokBuff.constData(), tokPtr - (ushort *)tokBuff.constData());
781     return true;
782
783 #undef FLUSH_VALUE_LIST
784 #undef FLUSH_LITERAL
785 #undef FLUSH_LHS_LITERAL
786 #undef FLUSH_RHS_LITERAL
787 }
788
789 void ProFileParser::putLineMarker(ushort *&tokPtr)
790 {
791     if (m_markLine) {
792         *tokPtr++ = TokLine;
793         *tokPtr++ = (ushort)m_markLine;
794         m_markLine = 0;
795     }
796 }
797
798 void ProFileParser::enterScope(ushort *&tokPtr, bool special, ScopeState state)
799 {
800     m_blockstack.resize(m_blockstack.size() + 1);
801     m_blockstack.top().special = special;
802     m_blockstack.top().start = tokPtr;
803     tokPtr += 2;
804     m_state = state;
805     m_canElse = false;
806     if (special)
807         m_markLine = m_lineNo;
808 }
809
810 void ProFileParser::leaveScope(ushort *&tokPtr)
811 {
812     if (m_blockstack.top().inBranch) {
813         // Put empty else block
814         putBlockLen(tokPtr, 0);
815     }
816     if (ushort *start = m_blockstack.top().start) {
817         putTok(tokPtr, TokTerminator);
818         uint len = tokPtr - start - 2;
819         start[0] = (ushort)len;
820         start[1] = (ushort)(len >> 16);
821     }
822     m_blockstack.resize(m_blockstack.size() - 1);
823 }
824
825 // If we are on a fresh line, close all open one-line scopes.
826 void ProFileParser::flushScopes(ushort *&tokPtr)
827 {
828     if (m_state == StNew) {
829         while (!m_blockstack.top().braceLevel && m_blockstack.size() > 1)
830             leaveScope(tokPtr);
831         if (m_blockstack.top().inBranch) {
832             m_blockstack.top().inBranch = false;
833             // Put empty else block
834             putBlockLen(tokPtr, 0);
835         }
836         m_canElse = false;
837     }
838 }
839
840 // If there is a pending conditional, enter a new scope, otherwise flush scopes.
841 void ProFileParser::flushCond(ushort *&tokPtr)
842 {
843     if (m_state == StCond) {
844         putTok(tokPtr, TokBranch);
845         m_blockstack.top().inBranch = true;
846         enterScope(tokPtr, false, StNew);
847     } else {
848         flushScopes(tokPtr);
849     }
850 }
851
852 void ProFileParser::finalizeTest(ushort *&tokPtr)
853 {
854     flushScopes(tokPtr);
855     putLineMarker(tokPtr);
856     if (m_operator != NoOperator) {
857         putTok(tokPtr, (m_operator == AndOperator) ? TokAnd : TokOr);
858         m_operator = NoOperator;
859     }
860     if (m_invert) {
861         putTok(tokPtr, TokNot);
862         m_invert = false;
863     }
864     m_state = StCond;
865     m_canElse = true;
866 }
867
868 void ProFileParser::bogusTest(ushort *&tokPtr)
869 {
870     flushScopes(tokPtr);
871     m_operator = NoOperator;
872     m_invert = false;
873     m_state = StCond;
874     m_canElse = true;
875     m_proFile->setOk(false);
876 }
877
878 void ProFileParser::finalizeCond(ushort *&tokPtr, ushort *uc, ushort *ptr, int wordCount)
879 {
880     if (wordCount != 1) {
881         if (wordCount) {
882             parseError(fL1S("Extra characters after test expression."));
883             bogusTest(tokPtr);
884         }
885         return;
886     }
887
888     // Check for magic tokens
889     if (*uc == TokHashLiteral) {
890         uint nlen = uc[3];
891         ushort *uce = uc + 4 + nlen;
892         if (uce == ptr) {
893             m_tmp.setRawData((QChar *)uc + 4, nlen);
894             if (!m_tmp.compare(statics.strelse, Qt::CaseInsensitive)) {
895                 if (m_invert || m_operator != NoOperator) {
896                     parseError(fL1S("Unexpected operator in front of else."));
897                     return;
898                 }
899                 BlockScope &top = m_blockstack.top();
900                 if (m_canElse && (!top.special || top.braceLevel)) {
901                     // A list of tests (the last one likely with side effects),
902                     // but no assignment, scope, etc.
903                     putTok(tokPtr, TokBranch);
904                     // Put empty then block
905                     putBlockLen(tokPtr, 0);
906                     enterScope(tokPtr, false, StCtrl);
907                     return;
908                 }
909                 forever {
910                     BlockScope &top = m_blockstack.top();
911                     if (top.inBranch && (!top.special || top.braceLevel)) {
912                         top.inBranch = false;
913                         enterScope(tokPtr, false, StCtrl);
914                         return;
915                     }
916                     if (top.braceLevel || m_blockstack.size() == 1)
917                         break;
918                     leaveScope(tokPtr);
919                 }
920                 parseError(fL1S("Unexpected 'else'."));
921                 return;
922             }
923         }
924     }
925
926     finalizeTest(tokPtr);
927     putBlock(tokPtr, uc, ptr - uc);
928     putTok(tokPtr, TokCondition);
929 }
930
931 void ProFileParser::finalizeCall(ushort *&tokPtr, ushort *uc, ushort *ptr, int argc)
932 {
933     // Check for magic tokens
934     if (*uc == TokHashLiteral) {
935         uint nlen = uc[3];
936         ushort *uce = uc + 4 + nlen;
937         if (*uce == TokTestCall) {
938             uce++;
939             m_tmp.setRawData((QChar *)uc + 4, nlen);
940             const QString *defName;
941             ushort defType;
942             if (m_tmp == statics.strfor) {
943                 flushCond(tokPtr);
944                 putLineMarker(tokPtr);
945                 if (m_invert || m_operator == OrOperator) {
946                     // '|' could actually work reasonably, but qmake does nonsense here.
947                     parseError(fL1S("Unexpected operator in front of for()."));
948                     return;
949                 }
950                 if (*uce == (TokLiteral|TokNewStr)) {
951                     nlen = uce[1];
952                     uc = uce + 2 + nlen;
953                     if (*uc == TokFuncTerminator) {
954                         // for(literal) (only "ever" would be legal if qmake was sane)
955                         putTok(tokPtr, TokForLoop);
956                         putHashStr(tokPtr, (ushort *)0, (uint)0);
957                         putBlockLen(tokPtr, 1 + 3 + nlen + 1);
958                         putTok(tokPtr, TokHashLiteral);
959                         putHashStr(tokPtr, uce + 2, nlen);
960                       didFor:
961                         putTok(tokPtr, TokValueTerminator);
962                         enterScope(tokPtr, true, StCtrl);
963                         return;
964                     } else if (*uc == TokArgSeparator && argc == 2) {
965                         // for(var, something)
966                         uc++;
967                         putTok(tokPtr, TokForLoop);
968                         putHashStr(tokPtr, uce + 2, nlen);
969                       doFor:
970                         nlen = ptr - uc;
971                         putBlockLen(tokPtr, nlen + 1);
972                         putBlock(tokPtr, uc, nlen);
973                         goto didFor;
974                     }
975                 } else if (argc == 1) {
976                     // for(non-literal) (this wouldn't be here if qmake was sane)
977                     putTok(tokPtr, TokForLoop);
978                     putHashStr(tokPtr, (ushort *)0, (uint)0);
979                     uc = uce;
980                     goto doFor;
981                 }
982                 parseError(fL1S("Syntax is for(var, list), for(var, forever) or for(ever)."));
983                 return;
984             } else if (m_tmp == statics.strdefineReplace) {
985                 defName = &statics.strdefineReplace;
986                 defType = TokReplaceDef;
987                 goto deffunc;
988             } else if (m_tmp == statics.strdefineTest) {
989                 defName = &statics.strdefineTest;
990                 defType = TokTestDef;
991               deffunc:
992                 flushScopes(tokPtr);
993                 putLineMarker(tokPtr);
994                 if (m_invert) {
995                     parseError(fL1S("Unexpected operator in front of function definition."));
996                     return;
997                 }
998                 if (*uce == (TokLiteral|TokNewStr)) {
999                     uint nlen = uce[1];
1000                     if (uce[nlen + 2] == TokFuncTerminator) {
1001                         if (m_operator != NoOperator) {
1002                             putTok(tokPtr, (m_operator == AndOperator) ? TokAnd : TokOr);
1003                             m_operator = NoOperator;
1004                         }
1005                         putTok(tokPtr, defType);
1006                         putHashStr(tokPtr, uce + 2, nlen);
1007                         uc = uce + 2 + nlen + 1;
1008                         enterScope(tokPtr, true, StCtrl);
1009                         return;
1010                     }
1011                 }
1012                 parseError(fL1S("%1(function) requires one literal argument.").arg(*defName));
1013                 return;
1014             }
1015         }
1016     }
1017
1018     finalizeTest(tokPtr);
1019     putBlock(tokPtr, uc, ptr - uc);
1020 }
1021
1022 void ProFileParser::parseError(const QString &msg) const
1023 {
1024     if (!m_inError && m_handler)
1025         m_handler->parseError(m_proFile->fileName(), m_lineNo, msg);
1026 }
1027
1028 QT_END_NAMESPACE