Correctly expand macros without arguments in moc
[profile/ivi/qtbase.git] / src / tools / moc / preprocessor.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the tools applications of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "preprocessor.h"
43 #include "utils.h"
44 #include <qstringlist.h>
45 #include <qfile.h>
46 #include <qdir.h>
47 #include <qfileinfo.h>
48
49 QT_BEGIN_NAMESPACE
50
51 #include "ppkeywords.cpp"
52 #include "keywords.cpp"
53
54 // transform \r\n into \n
55 // \r into \n (os9 style)
56 // backslash-newlines into newlines
57 static QByteArray cleaned(const QByteArray &input)
58 {
59     QByteArray result;
60     result.reserve(input.size());
61     const char *data = input.constData();
62     char *output = result.data();
63
64     int newlines = 0;
65     while (*data) {
66         while (*data && is_space(*data))
67             ++data;
68         bool takeLine = (*data == '#');
69         if (*data == '%' && *(data+1) == ':') {
70             takeLine = true;
71             ++data;
72         }
73         if (takeLine) {
74             *output = '#';
75             ++output;
76             do ++data; while (*data && is_space(*data));
77         }
78         while (*data) {
79             // handle \\\n, \\\r\n and \\\r
80             if (*data == '\\') {
81                 if (*(data + 1) == '\r') {
82                     ++data;
83                 }
84                 if (*data && (*(data + 1) == '\n' || (*data) == '\r')) {
85                     ++newlines;
86                     data += 1;
87                     if (*data != '\r')
88                         data += 1;
89                     continue;
90                 }
91             } else if (*data == '\r' && *(data + 1) == '\n') { // reduce \r\n to \n
92                 ++data;
93             }
94
95             char ch = *data;
96             if (ch == '\r') // os9: replace \r with \n
97                 ch = '\n';
98             *output = ch;
99             ++output;
100
101             if (*data == '\n') {
102                 // output additional newlines to keep the correct line-numbering
103                 // for the lines following the backslash-newline sequence(s)
104                 while (newlines) {
105                     *output = '\n';
106                     ++output;
107                     --newlines;
108                 }
109                 ++data;
110                 break;
111             }
112             ++data;
113         }
114     }
115     result.resize(output - result.constData());
116     return result;
117 }
118
119 bool Preprocessor::preprocessOnly = false;
120 void Preprocessor::skipUntilEndif()
121 {
122     while(index < symbols.size() - 1 && symbols.at(index).token != PP_ENDIF){
123         switch (symbols.at(index).token) {
124         case PP_IF:
125         case PP_IFDEF:
126         case PP_IFNDEF:
127             ++index;
128             skipUntilEndif();
129             break;
130         default:
131             ;
132         }
133         ++index;
134     }
135 }
136
137 bool Preprocessor::skipBranch()
138 {
139     while (index < symbols.size() - 1
140           && (symbols.at(index).token != PP_ENDIF
141                && symbols.at(index).token != PP_ELIF
142                && symbols.at(index).token != PP_ELSE)
143        ){
144         switch (symbols.at(index).token) {
145         case PP_IF:
146         case PP_IFDEF:
147         case PP_IFNDEF:
148             ++index;
149             skipUntilEndif();
150             break;
151         default:
152             ;
153         }
154         ++index;
155     }
156     return (index < symbols.size() - 1);
157 }
158
159
160 enum TokenizeMode { TokenizeCpp, TokenizePreprocessor, PreparePreprocessorStatement, TokenizePreprocessorStatement, TokenizeInclude, PrepareDefine, TokenizeDefine };
161 static Symbols tokenize(const QByteArray &input, int lineNum = 1, TokenizeMode mode = TokenizeCpp)
162 {
163     Symbols symbols;
164     const char *begin = input.constData();
165     const char *data = begin;
166     while (*data) {
167         if (mode == TokenizeCpp || mode == TokenizeDefine) {
168             int column = 0;
169
170             const char *lexem = data;
171             int state = 0;
172             Token token = NOTOKEN;
173             for (;;) {
174                 if (static_cast<signed char>(*data) < 0) {
175                     ++data;
176                     continue;
177                 }
178                 int nextindex = keywords[state].next;
179                 int next = 0;
180                 if (*data == keywords[state].defchar)
181                     next = keywords[state].defnext;
182                 else if (!state || nextindex)
183                     next = keyword_trans[nextindex][(int)*data];
184                 if (!next)
185                     break;
186                 state = next;
187                 token = keywords[state].token;
188                 ++data;
189             }
190
191             // suboptimal, is_ident_char  should use a table
192             if (keywords[state].ident && is_ident_char(*data))
193                 token = keywords[state].ident;
194
195             if (token == NOTOKEN) {
196                 // an error really
197                 ++data;
198                 continue;
199             }
200
201             ++column;
202
203             if (token > SPECIAL_TREATMENT_MARK) {
204                 switch (token) {
205                 case QUOTE:
206                     data = skipQuote(data);
207                     token = STRING_LITERAL;
208                     // concatenate multi-line strings for easier
209                     // STRING_LITERAAL handling in moc
210                     if (!Preprocessor::preprocessOnly
211                         && !symbols.isEmpty()
212                         && symbols.last().token == STRING_LITERAL) {
213
214                         QByteArray newString = symbols.last().unquotedLexem();
215                         newString += input.mid(lexem - begin + 1, data - lexem - 2);
216                         newString.prepend('\"');
217                         newString.append('\"');
218                         symbols.last() = Symbol(symbols.last().lineNum,
219                                                 STRING_LITERAL,
220                                                 newString);
221                         continue;
222                     }
223                     break;
224                 case SINGLEQUOTE:
225                     while (*data && (*data != '\''
226                                      || (*(data-1)=='\\'
227                                          && *(data-2)!='\\')))
228                         ++data;
229                     if (*data)
230                         ++data;
231                     token = CHARACTER_LITERAL;
232                     break;
233                 case LANGLE_SCOPE:
234                     // split <:: into two tokens, < and ::
235                     token = LANGLE;
236                     data -= 2;
237                     break;
238                 case DIGIT:
239                     while (is_digit_char(*data))
240                         ++data;
241                     if (!*data || *data != '.') {
242                         token = INTEGER_LITERAL;
243                         if (data - lexem == 1 &&
244                             (*data == 'x' || *data == 'X')
245                             && *lexem == '0') {
246                             ++data;
247                             while (is_hex_char(*data))
248                                 ++data;
249                         }
250                         break;
251                     }
252                     token = FLOATING_LITERAL;
253                     ++data;
254                     // fall through
255                 case FLOATING_LITERAL:
256                     while (is_digit_char(*data))
257                         ++data;
258                     if (*data == '+' || *data == '-')
259                         ++data;
260                     if (*data == 'e' || *data == 'E') {
261                         ++data;
262                         while (is_digit_char(*data))
263                             ++data;
264                     }
265                     if (*data == 'f' || *data == 'F'
266                         || *data == 'l' || *data == 'L')
267                         ++data;
268                     break;
269                 case HASH:
270                     if (column == 1) {
271                         mode = PreparePreprocessorStatement;
272                         while (*data && (*data == ' ' || *data == '\t'))
273                             ++data;
274                         if (is_ident_char(*data))
275                             mode = TokenizePreprocessorStatement;
276                         continue;
277                     }
278                     break;
279                 case NEWLINE:
280                     ++lineNum;
281                     if (mode == TokenizeDefine) {
282                         mode = TokenizeCpp;
283                         // emit the newline token
284                         break;
285                     }
286                     continue;
287                 case BACKSLASH:
288                 {
289                     const char *rewind = data;
290                     while (*data && (*data == ' ' || *data == '\t'))
291                         ++data;
292                     if (*data && *data == '\n') {
293                         ++data;
294                         continue;
295                     }
296                     data = rewind;
297                 } break;
298                 case CHARACTER:
299                     while (is_ident_char(*data))
300                         ++data;
301                     token = IDENTIFIER;
302                     break;
303                 case C_COMMENT:
304                     if (*data) {
305                         if (*data == '\n')
306                             ++lineNum;
307                         ++data;
308                         if (*data) {
309                             if (*data == '\n')
310                                 ++lineNum;
311                             ++data;
312                         }
313                     }
314                     while (*data && (*(data-1) != '/' || *(data-2) != '*')) {
315                         if (*data == '\n')
316                             ++lineNum;
317                         ++data;
318                     }
319                     token = WHITESPACE; // one comment, one whitespace
320                     // fall through;
321                 case WHITESPACE:
322                     if (column == 1)
323                         column = 0;
324                     while (*data && (*data == ' ' || *data == '\t'))
325                         ++data;
326                     if (Preprocessor::preprocessOnly) // tokenize whitespace
327                         break;
328                     continue;
329                 case CPP_COMMENT:
330                     while (*data && *data != '\n')
331                         ++data;
332                     continue; // ignore safely, the newline is a separator
333                 default:
334                     continue; //ignore
335                 }
336             }
337 #ifdef USE_LEXEM_STORE
338             if (!Preprocessor::preprocessOnly
339                 && token != IDENTIFIER
340                 && token != STRING_LITERAL
341                 && token != FLOATING_LITERAL
342                 && token != INTEGER_LITERAL)
343                 symbols += Symbol(lineNum, token);
344             else
345 #endif
346                 symbols += Symbol(lineNum, token, input, lexem-begin, data-lexem);
347
348         } else { //   Preprocessor
349
350             const char *lexem = data;
351             int state = 0;
352             Token token = NOTOKEN;
353             if (mode == TokenizePreprocessorStatement) {
354                 state = pp_keyword_trans[0][(int)'#'];
355                 mode = TokenizePreprocessor;
356             }
357             for (;;) {
358                 if (static_cast<signed char>(*data) < 0) {
359                     ++data;
360                     continue;
361                 }
362
363                 int nextindex = pp_keywords[state].next;
364                 int next = 0;
365                 if (*data == pp_keywords[state].defchar)
366                     next = pp_keywords[state].defnext;
367                 else if (!state || nextindex)
368                     next = pp_keyword_trans[nextindex][(int)*data];
369                 if (!next)
370                     break;
371                 state = next;
372                 token = pp_keywords[state].token;
373                 ++data;
374             }
375             // suboptimal, is_ident_char  should use a table
376             if (pp_keywords[state].ident && is_ident_char(*data))
377                 token = pp_keywords[state].ident;
378
379             switch (token) {
380             case NOTOKEN:
381                 ++data;
382                 break;
383             case PP_DEFINE:
384                 mode = PrepareDefine;
385                 break;
386             case PP_IFDEF:
387                 symbols += Symbol(lineNum, PP_IF);
388                 symbols += Symbol(lineNum, PP_DEFINED);
389                 continue;
390             case PP_IFNDEF:
391                 symbols += Symbol(lineNum, PP_IF);
392                 symbols += Symbol(lineNum, PP_NOT);
393                 symbols += Symbol(lineNum, PP_DEFINED);
394                 continue;
395             case PP_INCLUDE:
396                 mode = TokenizeInclude;
397                 break;
398             case PP_QUOTE:
399                 data = skipQuote(data);
400                 token = PP_STRING_LITERAL;
401                 break;
402             case PP_SINGLEQUOTE:
403                 while (*data && (*data != '\''
404                                  || (*(data-1)=='\\'
405                                      && *(data-2)!='\\')))
406                     ++data;
407                 if (*data)
408                     ++data;
409                 token = PP_CHARACTER_LITERAL;
410                 break;
411             case PP_DIGIT:
412                 while (is_digit_char(*data))
413                     ++data;
414                 if (!*data || *data != '.') {
415                     token = PP_INTEGER_LITERAL;
416                     if (data - lexem == 1 &&
417                         (*data == 'x' || *data == 'X')
418                         && *lexem == '0') {
419                         ++data;
420                         while (is_hex_char(*data))
421                             ++data;
422                     }
423                     break;
424                 }
425                 token = PP_FLOATING_LITERAL;
426                 ++data;
427                 // fall through
428             case PP_FLOATING_LITERAL:
429                 while (is_digit_char(*data))
430                     ++data;
431                 if (*data == '+' || *data == '-')
432                     ++data;
433                 if (*data == 'e' || *data == 'E') {
434                     ++data;
435                     while (is_digit_char(*data))
436                         ++data;
437                 }
438                 if (*data == 'f' || *data == 'F'
439                     || *data == 'l' || *data == 'L')
440                     ++data;
441                 break;
442             case PP_CHARACTER:
443                 if (mode == PreparePreprocessorStatement) {
444                     // rewind entire token to begin
445                     data = lexem;
446                     mode = TokenizePreprocessorStatement;
447                     continue;
448                 }
449                 while (is_ident_char(*data))
450                     ++data;
451                 token = PP_IDENTIFIER;
452
453                 if (mode == PrepareDefine) {
454                     symbols += Symbol(lineNum, token, input, lexem-begin, data-lexem);
455                     // make sure we explicitly add the whitespace here, so we can distinguish
456                     // correctly between regular and function macros
457                     if (is_space(*data))
458                         symbols += Symbol(lineNum, WHITESPACE);
459                     mode = TokenizeDefine;
460                     continue;
461                 }
462                 break;
463             case PP_C_COMMENT:
464                 if (*data) {
465                     if (*data == '\n')
466                         ++lineNum;
467                     ++data;
468                     if (*data) {
469                         if (*data == '\n')
470                             ++lineNum;
471                         ++data;
472                     }
473                 }
474                 while (*data && (*(data-1) != '/' || *(data-2) != '*')) {
475                     if (*data == '\n')
476                         ++lineNum;
477                     ++data;
478                 }
479                 token = PP_WHITESPACE; // one comment, one whitespace
480                 // fall through;
481             case PP_WHITESPACE:
482                 while (*data && (*data == ' ' || *data == '\t'))
483                     ++data;
484                 continue; // the preprocessor needs no whitespace
485             case PP_CPP_COMMENT:
486                 while (*data && *data != '\n')
487                     ++data;
488                 continue; // ignore safely, the newline is a separator
489             case PP_NEWLINE:
490                 ++lineNum;
491                 mode = TokenizeCpp;
492                 break;
493             case PP_BACKSLASH:
494             {
495                 const char *rewind = data;
496                 while (*data && (*data == ' ' || *data == '\t'))
497                     ++data;
498                 if (*data && *data == '\n') {
499                     ++data;
500                     continue;
501                 }
502                 data = rewind;
503             } break;
504             case PP_LANGLE:
505                 if (mode != TokenizeInclude)
506                     break;
507                 token = PP_STRING_LITERAL;
508                 while (*data && *data != '\n' && *(data-1) != '>')
509                     ++data;
510                 break;
511             default:
512                 break;
513             }
514             if (mode == PreparePreprocessorStatement)
515                 continue;
516 #ifdef USE_LEXEM_STORE
517             if (token != PP_IDENTIFIER
518                 && token != PP_STRING_LITERAL
519                 && token != PP_FLOATING_LITERAL
520                 && token != PP_INTEGER_LITERAL)
521                 symbols += Symbol(lineNum, token);
522             else
523 #endif
524                 symbols += Symbol(lineNum, token, input, lexem-begin, data-lexem);
525         }
526     }
527     symbols += Symbol(); // eof symbol
528     return symbols;
529 }
530
531 void Preprocessor::macroExpandIdentifier(const Symbol &s, Symbols &preprocessed, MacroSafeSet safeset)
532 {
533     // not a macro
534     if (!macros.contains(s)) {
535         preprocessed += s;
536         return;
537     }
538
539     Symbols expanded = macros.value(s).symbols;
540
541     // don't expand macros with arguments for now
542     if (expanded.size() && expanded.at(0).token == PP_LPAREN) {
543         preprocessed += s;
544         return;
545     }
546
547     for (int i = 0; i < expanded.size(); ++i) {
548         expanded[i].lineNum = s.lineNum;
549         if (expanded.at(i).token == PP_IDENTIFIER)
550             macroExpandIdentifier(expanded.at(i), preprocessed, safeset);
551         else
552             preprocessed += expanded.at(i);
553     }
554 }
555
556
557 void Preprocessor::substituteMacro(const MacroName &macro, Symbols &substituted, MacroSafeSet safeset)
558 {
559     Symbols saveSymbols = symbols;
560     int saveIndex = index;
561
562     symbols = macros.value(macro).symbols;
563     index = 0;
564
565     safeset += macro;
566     substituteUntilNewline(substituted, safeset);
567
568     symbols = saveSymbols;
569     index = saveIndex;
570 }
571
572
573
574 void Preprocessor::substituteUntilNewline(Symbols &substituted, MacroSafeSet safeset)
575 {
576     while (hasNext()) {
577         Token token = next();
578         if (token == PP_IDENTIFIER) {
579             MacroName macro = symbol();
580             if (macros.contains(macro) && !safeset.contains(macro)) {
581                 substituteMacro(macro, substituted, safeset);
582                 continue;
583             }
584         } else if (token == PP_DEFINED) {
585             bool braces = test(PP_LPAREN);
586             next(PP_IDENTIFIER);
587             Symbol definedOrNotDefined = symbol();
588             definedOrNotDefined.token = macros.contains(definedOrNotDefined)? PP_MOC_TRUE : PP_MOC_FALSE;
589             substituted += definedOrNotDefined;
590             if (braces)
591                 test(PP_RPAREN);
592             continue;
593         } else if (token == PP_NEWLINE) {
594             substituted += symbol();
595             break;
596         }
597         substituted += symbol();
598     }
599 }
600
601
602 class PP_Expression : public Parser
603 {
604 public:
605     int value() { index = 0; return unary_expression_lookup() ?  conditional_expression() : 0; }
606
607     int conditional_expression();
608     int logical_OR_expression();
609     int logical_AND_expression();
610     int inclusive_OR_expression();
611     int exclusive_OR_expression();
612     int AND_expression();
613     int equality_expression();
614     int relational_expression();
615     int shift_expression();
616     int additive_expression();
617     int multiplicative_expression();
618     int unary_expression();
619     bool unary_expression_lookup();
620     int primary_expression();
621     bool primary_expression_lookup();
622 };
623
624 int PP_Expression::conditional_expression()
625 {
626     int value = logical_OR_expression();
627     if (test(PP_QUESTION)) {
628         int alt1 = conditional_expression();
629         int alt2 = test(PP_COLON) ? conditional_expression() : 0;
630         return value ? alt1 : alt2;
631     }
632     return value;
633 }
634
635 int PP_Expression::logical_OR_expression()
636 {
637     int value = logical_AND_expression();
638     if (test(PP_OROR))
639         return logical_OR_expression() || value;
640     return value;
641 }
642
643 int PP_Expression::logical_AND_expression()
644 {
645     int value = inclusive_OR_expression();
646     if (test(PP_ANDAND))
647         return logical_AND_expression() && value;
648     return value;
649 }
650
651 int PP_Expression::inclusive_OR_expression()
652 {
653     int value = exclusive_OR_expression();
654     if (test(PP_OR))
655         return value | inclusive_OR_expression();
656     return value;
657 }
658
659 int PP_Expression::exclusive_OR_expression()
660 {
661     int value = AND_expression();
662     if (test(PP_HAT))
663         return value ^ exclusive_OR_expression();
664     return value;
665 }
666
667 int PP_Expression::AND_expression()
668 {
669     int value = equality_expression();
670     if (test(PP_AND))
671         return value & AND_expression();
672     return value;
673 }
674
675 int PP_Expression::equality_expression()
676 {
677     int value = relational_expression();
678     switch (next()) {
679     case PP_EQEQ:
680         return value == equality_expression();
681     case PP_NE:
682         return value != equality_expression();
683     default:
684         prev();
685         return value;
686     }
687 }
688
689 int PP_Expression::relational_expression()
690 {
691     int value = shift_expression();
692     switch (next()) {
693     case PP_LANGLE:
694         return value < relational_expression();
695     case PP_RANGLE:
696         return value > relational_expression();
697     case PP_LE:
698         return value <= relational_expression();
699     case PP_GE:
700         return value >= relational_expression();
701     default:
702         prev();
703         return value;
704     }
705 }
706
707 int PP_Expression::shift_expression()
708 {
709     int value = additive_expression();
710     switch (next()) {
711     case PP_LTLT:
712         return value << shift_expression();
713     case PP_GTGT:
714         return value >> shift_expression();
715     default:
716         prev();
717         return value;
718     }
719 }
720
721 int PP_Expression::additive_expression()
722 {
723     int value = multiplicative_expression();
724     switch (next()) {
725     case PP_PLUS:
726         return value + additive_expression();
727     case PP_MINUS:
728         return value - additive_expression();
729     default:
730         prev();
731         return value;
732     }
733 }
734
735 int PP_Expression::multiplicative_expression()
736 {
737     int value = unary_expression();
738     switch (next()) {
739     case PP_STAR:
740         return value * multiplicative_expression();
741     case PP_PERCENT:
742     {
743         int remainder = multiplicative_expression();
744         return remainder ? value % remainder : 0;
745     }
746     case PP_SLASH:
747     {
748         int div = multiplicative_expression();
749         return div ? value / div : 0;
750     }
751     default:
752         prev();
753         return value;
754     };
755 }
756
757 int PP_Expression::unary_expression()
758 {
759     switch (next()) {
760     case PP_PLUS:
761         return unary_expression();
762     case PP_MINUS:
763         return -unary_expression();
764     case PP_NOT:
765         return !unary_expression();
766     case PP_TILDE:
767         return ~unary_expression();
768     case PP_MOC_TRUE:
769         return 1;
770     case PP_MOC_FALSE:
771         return 0;
772     default:
773         prev();
774         return primary_expression();
775     }
776 }
777
778 bool PP_Expression::unary_expression_lookup()
779 {
780     Token t = lookup();
781     return (primary_expression_lookup()
782             || t == PP_PLUS
783             || t == PP_MINUS
784             || t == PP_NOT
785             || t == PP_TILDE
786             || t == PP_DEFINED);
787 }
788
789 int PP_Expression::primary_expression()
790 {
791     int value;
792     if (test(PP_LPAREN)) {
793         value = conditional_expression();
794         test(PP_RPAREN);
795     } else {
796         next();
797         value = lexem().toInt(0, 0);
798     }
799     return value;
800 }
801
802 bool PP_Expression::primary_expression_lookup()
803 {
804     Token t = lookup();
805     return (t == PP_IDENTIFIER
806             || t == PP_INTEGER_LITERAL
807             || t == PP_FLOATING_LITERAL
808             || t == PP_MOC_TRUE
809             || t == PP_MOC_FALSE
810             || t == PP_LPAREN);
811 }
812
813 int Preprocessor::evaluateCondition()
814 {
815     PP_Expression expression;
816     expression.currentFilenames = currentFilenames;
817
818     substituteUntilNewline(expression.symbols);
819
820     return expression.value();
821 }
822
823 void Preprocessor::preprocess(const QByteArray &filename, Symbols &preprocessed)
824 {
825     currentFilenames.push(filename);
826     preprocessed.reserve(preprocessed.size() + symbols.size());
827     while (hasNext()) {
828         Token token = next();
829
830         switch (token) {
831         case PP_INCLUDE:
832         {
833             int lineNum = symbol().lineNum;
834             QByteArray include;
835             bool local = false;
836             if (test(PP_STRING_LITERAL)) {
837                 local = lexem().startsWith('\"');
838                 include = unquotedLexem();
839             } else
840                 continue;
841             until(PP_NEWLINE);
842
843             // #### stringery
844             QFileInfo fi;
845             if (local)
846                 fi.setFile(QFileInfo(QString::fromLocal8Bit(filename.constData())).dir(), QString::fromLocal8Bit(include.constData()));
847             for (int j = 0; j < Preprocessor::includes.size() && !fi.exists(); ++j) {
848                 const IncludePath &p = Preprocessor::includes.at(j);
849                 if (p.isFrameworkPath) {
850                     const int slashPos = include.indexOf('/');
851                     if (slashPos == -1)
852                         continue;
853                     QByteArray frameworkCandidate = include.left(slashPos);
854                     frameworkCandidate.append(".framework/Headers/");
855                     fi.setFile(QString::fromLocal8Bit(QByteArray(p.path + '/' + frameworkCandidate).constData()), QString::fromLocal8Bit(include.mid(slashPos + 1).constData()));
856                 } else {
857                     fi.setFile(QString::fromLocal8Bit(p.path.constData()), QString::fromLocal8Bit(include.constData()));
858                 }
859                 // try again, maybe there's a file later in the include paths with the same name
860                 // (186067)
861                 if (fi.isDir()) {
862                     fi = QFileInfo();
863                     continue;
864                 }
865             }
866
867             if (!fi.exists() || fi.isDir())
868                 continue;
869             include = fi.canonicalFilePath().toLocal8Bit();
870
871             if (Preprocessor::preprocessedIncludes.contains(include))
872                 continue;
873             Preprocessor::preprocessedIncludes.insert(include);
874
875             QFile file(QString::fromLocal8Bit(include.constData()));
876             if (!file.open(QFile::ReadOnly))
877                 continue;
878
879             QByteArray input = file.readAll();
880             file.close();
881             if (input.isEmpty())
882                 continue;
883
884             Symbols saveSymbols = symbols;
885             int saveIndex = index;
886
887             // phase 1: get rid of backslash-newlines
888             input = cleaned(input);
889
890             // phase 2: tokenize for the preprocessor
891             symbols = tokenize(input);
892             input.clear();
893
894             index = 0;
895
896             // phase 3: preprocess conditions and substitute macros
897             preprocessed += Symbol(0, MOC_INCLUDE_BEGIN, include);
898             preprocess(include, preprocessed);
899             preprocessed += Symbol(lineNum, MOC_INCLUDE_END, include);
900
901             symbols = saveSymbols;
902             index = saveIndex;
903             continue;
904         }
905         case PP_DEFINE:
906         {
907             next(IDENTIFIER);
908             QByteArray name = lexem();
909             int start = index;
910             until(PP_NEWLINE);
911             Macro macro;
912             macro.symbols.reserve(index - start - 1);
913             for (int i = start; i < index - 1; ++i)
914                 macro.symbols += symbols.at(i);
915             macros.insert(name, macro);
916             continue;
917         }
918         case PP_UNDEF: {
919             next(IDENTIFIER);
920             QByteArray name = lexem();
921             until(PP_NEWLINE);
922             macros.remove(name);
923             continue;
924         }
925         case PP_IDENTIFIER: {
926             // substitute macros
927             macroExpandIdentifier(symbol(), preprocessed);
928             continue;
929         }
930         case PP_HASH:
931             until(PP_NEWLINE);
932             continue; // skip unknown preprocessor statement
933         case PP_IFDEF:
934         case PP_IFNDEF:
935         case PP_IF:
936             while (!evaluateCondition()) {
937                 if (!skipBranch())
938                     break;
939                 if (test(PP_ELIF)) {
940                 } else {
941                     until(PP_NEWLINE);
942                     break;
943                 }
944             }
945             continue;
946         case PP_ELIF:
947         case PP_ELSE:
948             skipUntilEndif();
949             // fall through
950         case PP_ENDIF:
951             until(PP_NEWLINE);
952             continue;
953         case PP_NEWLINE:
954             continue;
955         case SIGNALS:
956         case SLOTS: {
957             Symbol sym = symbol();
958             if (macros.contains("QT_NO_KEYWORDS"))
959                 sym.token = IDENTIFIER;
960             else
961                 sym.token = (token == SIGNALS ? Q_SIGNALS_TOKEN : Q_SLOTS_TOKEN);
962             preprocessed += sym;
963         } continue;
964         default:
965             break;
966         }
967         preprocessed += symbol();
968     }
969
970     currentFilenames.pop();
971 }
972
973 Symbols Preprocessor::preprocessed(const QByteArray &filename, FILE *file)
974 {
975     QFile qfile;
976     qfile.open(file, QFile::ReadOnly);
977     return preprocessed(filename, &qfile);
978 }
979
980 Symbols Preprocessor::preprocessed(const QByteArray &filename, QIODevice *file)
981 {
982     QByteArray input = file->readAll();
983     if (input.isEmpty())
984         return symbols;
985
986     // phase 1: get rid of backslash-newlines
987     input = cleaned(input);
988
989     // phase 2: tokenize for the preprocessor
990     symbols = tokenize(input);
991
992 #if 0
993     for (int j = 0; j < symbols.size(); ++j)
994         fprintf(stderr, "line %d: %s(%s)\n",
995                symbols[j].lineNum,
996                symbols[j].lexem().constData(),
997                tokenTypeName(symbols[j].token));
998 #endif
999
1000     // phase 3: preprocess conditions and substitute macros
1001     Symbols result;
1002     preprocess(filename, result);
1003
1004 #if 0
1005     for (int j = 0; j < result.size(); ++j)
1006         fprintf(stderr, "line %d: %s(%s)\n",
1007                result[j].lineNum,
1008                result[j].lexem().constData(),
1009                tokenTypeName(result[j].token));
1010 #endif
1011
1012     return result;
1013 }
1014
1015 void Preprocessor::until(Token t)
1016 {
1017     while(hasNext() && next() != t)
1018         ;
1019 }
1020
1021 QT_END_NAMESPACE