src/tools/moc/preprocessor.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the tools applications of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "preprocessor.h"
       
    43 #include "utils.h"
       
    44 #include <QStringList>
       
    45 #include <QFile>
       
    46 #include <QDir>
       
    47 #include <QFileInfo>
       
    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;
       
    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 };
       
   161 static Symbols tokenize(const QByteArray &input, int lineNum = 1, TokenizeMode mode = TokenizeCpp)
       
   162 {
       
   163     Symbols symbols;
       
   164     const char *begin = input;
       
   165     const char *data = begin;
       
   166     while (*data) {
       
   167         if (mode == TokenizeCpp) {
       
   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                     continue;
       
   282                 case BACKSLASH:
       
   283                 {
       
   284                     const char *rewind = data;
       
   285                     while (*data && (*data == ' ' || *data == '\t'))
       
   286                         ++data;
       
   287                     if (*data && *data == '\n') {
       
   288                         ++data;
       
   289                         continue;
       
   290                     }
       
   291                     data = rewind;
       
   292                 } break;
       
   293                 case CHARACTER:
       
   294                     while (is_ident_char(*data))
       
   295                         ++data;
       
   296                     token = IDENTIFIER;
       
   297                     break;
       
   298                 case C_COMMENT:
       
   299                     if (*data) {
       
   300                         if (*data == '\n')
       
   301                             ++lineNum;
       
   302                         ++data;
       
   303                         if (*data) {
       
   304                             if (*data == '\n')
       
   305                                 ++lineNum;
       
   306                             ++data;
       
   307                         }
       
   308                     }
       
   309                     while (*data && (*(data-1) != '/' || *(data-2) != '*')) {
       
   310                         if (*data == '\n')
       
   311                             ++lineNum;
       
   312                         ++data;
       
   313                     }
       
   314                     token = WHITESPACE; // one comment, one whitespace
       
   315                     // fall through;
       
   316                 case WHITESPACE:
       
   317                     if (column == 1)
       
   318                         column = 0;
       
   319                     while (*data && (*data == ' ' || *data == '\t'))
       
   320                         ++data;
       
   321                     if (Preprocessor::preprocessOnly) // tokenize whitespace
       
   322                         break;
       
   323                     continue;
       
   324                 case CPP_COMMENT:
       
   325                     while (*data && *data != '\n')
       
   326                         ++data;
       
   327                     continue; // ignore safely, the newline is a separator
       
   328                 default:
       
   329                     continue; //ignore
       
   330                 }
       
   331             }
       
   332 #ifdef USE_LEXEM_STORE
       
   333             if (!Preprocessor::preprocessOnly
       
   334                 && token != IDENTIFIER
       
   335                 && token != STRING_LITERAL
       
   336                 && token != FLOATING_LITERAL
       
   337                 && token != INTEGER_LITERAL)
       
   338                 symbols += Symbol(lineNum, token);
       
   339             else
       
   340 #endif
       
   341                 symbols += Symbol(lineNum, token, input, lexem-begin, data-lexem);
       
   342 
       
   343         } else { //   Preprocessor
       
   344 
       
   345             const char *lexem = data;
       
   346             int state = 0;
       
   347             Token token = NOTOKEN;
       
   348             if (mode == TokenizePreprocessorStatement) {
       
   349                 state = pp_keyword_trans[0][(int)'#'];
       
   350                 mode = TokenizePreprocessor;
       
   351             }
       
   352             for (;;) {
       
   353                 if (static_cast<signed char>(*data) < 0) {
       
   354                     ++data;
       
   355                     continue;
       
   356                 }
       
   357 
       
   358                 int nextindex = pp_keywords[state].next;
       
   359                 int next = 0;
       
   360                 if (*data == pp_keywords[state].defchar)
       
   361                     next = pp_keywords[state].defnext;
       
   362                 else if (!state || nextindex)
       
   363                     next = pp_keyword_trans[nextindex][(int)*data];
       
   364                 if (!next)
       
   365                     break;
       
   366                 state = next;
       
   367                 token = pp_keywords[state].token;
       
   368                 ++data;
       
   369             }
       
   370             // suboptimal, is_ident_char  should use a table
       
   371             if (pp_keywords[state].ident && is_ident_char(*data))
       
   372                 token = pp_keywords[state].ident;
       
   373 
       
   374             switch (token) {
       
   375             case NOTOKEN:
       
   376                 ++data;
       
   377                 break;
       
   378             case PP_IFDEF:
       
   379                 symbols += Symbol(lineNum, PP_IF);
       
   380                 symbols += Symbol(lineNum, PP_DEFINED);
       
   381                 continue;
       
   382             case PP_IFNDEF:
       
   383                 symbols += Symbol(lineNum, PP_IF);
       
   384                 symbols += Symbol(lineNum, PP_NOT);
       
   385                 symbols += Symbol(lineNum, PP_DEFINED);
       
   386                 continue;
       
   387             case PP_INCLUDE:
       
   388                 mode = TokenizeInclude;
       
   389                 break;
       
   390             case PP_QUOTE:
       
   391                 data = skipQuote(data);
       
   392                 token = PP_STRING_LITERAL;
       
   393                 break;
       
   394             case PP_SINGLEQUOTE:
       
   395                 while (*data && (*data != '\''
       
   396                                  || (*(data-1)=='\\'
       
   397                                      && *(data-2)!='\\')))
       
   398                     ++data;
       
   399                 if (*data)
       
   400                     ++data;
       
   401                 token = PP_CHARACTER_LITERAL;
       
   402                 break;
       
   403             case PP_DIGIT:
       
   404                 while (is_digit_char(*data))
       
   405                     ++data;
       
   406                 if (!*data || *data != '.') {
       
   407                     token = PP_INTEGER_LITERAL;
       
   408                     if (data - lexem == 1 &&
       
   409                         (*data == 'x' || *data == 'X')
       
   410                         && *lexem == '0') {
       
   411                         ++data;
       
   412                         while (is_hex_char(*data))
       
   413                             ++data;
       
   414                     }
       
   415                     break;
       
   416                 }
       
   417                 token = PP_FLOATING_LITERAL;
       
   418                 ++data;
       
   419                 // fall through
       
   420             case PP_FLOATING_LITERAL:
       
   421                 while (is_digit_char(*data))
       
   422                     ++data;
       
   423                 if (*data == '+' || *data == '-')
       
   424                     ++data;
       
   425                 if (*data == 'e' || *data == 'E') {
       
   426                     ++data;
       
   427                     while (is_digit_char(*data))
       
   428                         ++data;
       
   429                 }
       
   430                 if (*data == 'f' || *data == 'F'
       
   431                     || *data == 'l' || *data == 'L')
       
   432                     ++data;
       
   433                 break;
       
   434             case PP_CHARACTER:
       
   435                 if (mode == PreparePreprocessorStatement) {
       
   436                     // rewind entire token to begin
       
   437                     data = lexem;
       
   438                     mode = TokenizePreprocessorStatement;
       
   439                     continue;
       
   440                 }
       
   441                 while (is_ident_char(*data))
       
   442                     ++data;
       
   443                 token = PP_IDENTIFIER;
       
   444                 break;
       
   445             case PP_C_COMMENT:
       
   446                 if (*data) {
       
   447                     if (*data == '\n')
       
   448                         ++lineNum;
       
   449                     ++data;
       
   450                     if (*data) {
       
   451                         if (*data == '\n')
       
   452                             ++lineNum;
       
   453                         ++data;
       
   454                     }
       
   455                 }
       
   456                 while (*data && (*(data-1) != '/' || *(data-2) != '*')) {
       
   457                     if (*data == '\n')
       
   458                         ++lineNum;
       
   459                     ++data;
       
   460                 }
       
   461                 token = PP_WHITESPACE; // one comment, one whitespace
       
   462                 // fall through;
       
   463             case PP_WHITESPACE:
       
   464                 while (*data && (*data == ' ' || *data == '\t'))
       
   465                     ++data;
       
   466                 continue; // the preprocessor needs no whitespace
       
   467             case PP_CPP_COMMENT:
       
   468                 while (*data && *data != '\n')
       
   469                     ++data;
       
   470                 continue; // ignore safely, the newline is a separator
       
   471             case PP_NEWLINE:
       
   472                 ++lineNum;
       
   473                 mode = TokenizeCpp;
       
   474                 break;
       
   475             case PP_BACKSLASH:
       
   476             {
       
   477                 const char *rewind = data;
       
   478                 while (*data && (*data == ' ' || *data == '\t'))
       
   479                     ++data;
       
   480                 if (*data && *data == '\n') {
       
   481                     ++data;
       
   482                     continue;
       
   483                 }
       
   484                 data = rewind;
       
   485             } break;
       
   486             case PP_LANGLE:
       
   487                 if (mode != TokenizeInclude)
       
   488                     break;
       
   489                 token = PP_STRING_LITERAL;
       
   490                 while (*data && *data != '\n' && *(data-1) != '>')
       
   491                     ++data;
       
   492                 break;
       
   493             default:
       
   494                 break;
       
   495             }
       
   496             if (mode == PreparePreprocessorStatement)
       
   497                 continue;
       
   498 #ifdef USE_LEXEM_STORE
       
   499             if (token != PP_IDENTIFIER
       
   500                 && token != PP_STRING_LITERAL
       
   501                 && token != PP_FLOATING_LITERAL
       
   502                 && token != PP_INTEGER_LITERAL)
       
   503                 symbols += Symbol(lineNum, token);
       
   504             else
       
   505 #endif
       
   506                 symbols += Symbol(lineNum, token, input, lexem-begin, data-lexem);
       
   507         }
       
   508     }
       
   509     symbols += Symbol(); // eof symbol
       
   510     return symbols;
       
   511 }
       
   512 
       
   513 void Preprocessor::substituteMacro(const MacroName &macro, Symbols &substituted, MacroSafeSet safeset)
       
   514 {
       
   515     Symbols saveSymbols = symbols;
       
   516     int saveIndex = index;
       
   517 
       
   518     symbols = macros.value(macro).symbols;
       
   519     index = 0;
       
   520 
       
   521     safeset += macro;
       
   522     substituteUntilNewline(substituted, safeset);
       
   523 
       
   524     symbols = saveSymbols;
       
   525     index = saveIndex;
       
   526 }
       
   527 
       
   528 
       
   529 
       
   530 void Preprocessor::substituteUntilNewline(Symbols &substituted, MacroSafeSet safeset)
       
   531 {
       
   532     while (hasNext()) {
       
   533         Token token = next();
       
   534         if (token == PP_IDENTIFIER) {
       
   535             MacroName macro = symbol();
       
   536             if (macros.contains(macro) && !safeset.contains(macro)) {
       
   537                 substituteMacro(macro, substituted, safeset);
       
   538                 continue;
       
   539             }
       
   540         } else if (token == PP_DEFINED) {
       
   541             test(PP_LPAREN);
       
   542             next(PP_IDENTIFIER);
       
   543             Symbol definedOrNotDefined = symbol();
       
   544             definedOrNotDefined.token = macros.contains(definedOrNotDefined)? PP_MOC_TRUE : PP_MOC_FALSE;
       
   545             substituted += definedOrNotDefined;
       
   546             test(PP_RPAREN);
       
   547             continue;
       
   548         } else if (token == PP_NEWLINE) {
       
   549             substituted += symbol();
       
   550             break;
       
   551         }
       
   552         substituted += symbol();
       
   553     }
       
   554 }
       
   555 
       
   556 
       
   557 class PP_Expression : public Parser
       
   558 {
       
   559 public:
       
   560     int value() { index = 0; return unary_expression_lookup() ?  conditional_expression() : 0; }
       
   561 
       
   562     int conditional_expression();
       
   563     int logical_OR_expression();
       
   564     int logical_AND_expression();
       
   565     int inclusive_OR_expression();
       
   566     int exclusive_OR_expression();
       
   567     int AND_expression();
       
   568     int equality_expression();
       
   569     int relational_expression();
       
   570     int shift_expression();
       
   571     int additive_expression();
       
   572     int multiplicative_expression();
       
   573     int unary_expression();
       
   574     bool unary_expression_lookup();
       
   575     int primary_expression();
       
   576     bool primary_expression_lookup();
       
   577 };
       
   578 
       
   579 int PP_Expression::conditional_expression()
       
   580 {
       
   581     int value = logical_OR_expression();
       
   582     if (test(PP_QUESTION)) {
       
   583         int alt1 = conditional_expression();
       
   584         int alt2 = test(PP_COLON) ? conditional_expression() : 0;
       
   585         return value ? alt1 : alt2;
       
   586     }
       
   587     return value;
       
   588 }
       
   589 
       
   590 int PP_Expression::logical_OR_expression()
       
   591 {
       
   592     int value = logical_AND_expression();
       
   593     if (test(PP_OROR))
       
   594         return logical_OR_expression() || value;
       
   595     return value;
       
   596 }
       
   597 
       
   598 int PP_Expression::logical_AND_expression()
       
   599 {
       
   600     int value = inclusive_OR_expression();
       
   601     if (test(PP_ANDAND))
       
   602         return logical_AND_expression() && value;
       
   603     return value;
       
   604 }
       
   605 
       
   606 int PP_Expression::inclusive_OR_expression()
       
   607 {
       
   608     int value = exclusive_OR_expression();
       
   609     if (test(PP_OR))
       
   610         return value | inclusive_OR_expression();
       
   611     return value;
       
   612 }
       
   613 
       
   614 int PP_Expression::exclusive_OR_expression()
       
   615 {
       
   616     int value = AND_expression();
       
   617     if (test(PP_HAT))
       
   618         return value ^ exclusive_OR_expression();
       
   619     return value;
       
   620 }
       
   621 
       
   622 int PP_Expression::AND_expression()
       
   623 {
       
   624     int value = equality_expression();
       
   625     if (test(PP_AND))
       
   626         return value & AND_expression();
       
   627     return value;
       
   628 }
       
   629 
       
   630 int PP_Expression::equality_expression()
       
   631 {
       
   632     int value = relational_expression();
       
   633     switch (next()) {
       
   634     case PP_EQEQ:
       
   635         return value == equality_expression();
       
   636     case PP_NE:
       
   637         return value != equality_expression();
       
   638     default:
       
   639         prev();
       
   640         return value;
       
   641     }
       
   642 }
       
   643 
       
   644 int PP_Expression::relational_expression()
       
   645 {
       
   646     int value = shift_expression();
       
   647     switch (next()) {
       
   648     case PP_LANGLE:
       
   649         return value < relational_expression();
       
   650     case PP_RANGLE:
       
   651         return value > relational_expression();
       
   652     case PP_LE:
       
   653         return value <= relational_expression();
       
   654     case PP_GE:
       
   655         return value >= relational_expression();
       
   656     default:
       
   657         prev();
       
   658         return value;
       
   659     }
       
   660 }
       
   661 
       
   662 int PP_Expression::shift_expression()
       
   663 {
       
   664     int value = additive_expression();
       
   665     switch (next()) {
       
   666     case PP_LTLT:
       
   667         return value << shift_expression();
       
   668     case PP_GTGT:
       
   669         return value >> shift_expression();
       
   670     default:
       
   671         prev();
       
   672         return value;
       
   673     }
       
   674 }
       
   675 
       
   676 int PP_Expression::additive_expression()
       
   677 {
       
   678     int value = multiplicative_expression();
       
   679     switch (next()) {
       
   680     case PP_PLUS:
       
   681         return value + additive_expression();
       
   682     case PP_MINUS:
       
   683         return value - additive_expression();
       
   684     default:
       
   685         prev();
       
   686         return value;
       
   687     }
       
   688 }
       
   689 
       
   690 int PP_Expression::multiplicative_expression()
       
   691 {
       
   692     int value = unary_expression();
       
   693     switch (next()) {
       
   694     case PP_STAR:
       
   695         return value * multiplicative_expression();
       
   696     case PP_PERCENT:
       
   697     {
       
   698         int remainder = multiplicative_expression();
       
   699         return remainder ? value % remainder : 0;
       
   700     }
       
   701     case PP_SLASH:
       
   702     {
       
   703         int div = multiplicative_expression();
       
   704         return div ? value / div : 0;
       
   705     }
       
   706     default:
       
   707         prev();
       
   708         return value;
       
   709     };
       
   710 }
       
   711 
       
   712 int PP_Expression::unary_expression()
       
   713 {
       
   714     switch (next()) {
       
   715     case PP_PLUS:
       
   716         return unary_expression();
       
   717     case PP_MINUS:
       
   718         return -unary_expression();
       
   719     case PP_NOT:
       
   720         return !unary_expression();
       
   721     case PP_TILDE:
       
   722         return ~unary_expression();
       
   723     case PP_MOC_TRUE:
       
   724         return 1;
       
   725     case PP_MOC_FALSE:
       
   726         return 0;
       
   727     default:
       
   728         prev();
       
   729         return primary_expression();
       
   730     }
       
   731 }
       
   732 
       
   733 bool PP_Expression::unary_expression_lookup()
       
   734 {
       
   735     Token t = lookup();
       
   736     return (primary_expression_lookup()
       
   737             || t == PP_PLUS
       
   738             || t == PP_MINUS
       
   739             || t == PP_NOT
       
   740             || t == PP_TILDE
       
   741             || t == PP_DEFINED);
       
   742 }
       
   743 
       
   744 int PP_Expression::primary_expression()
       
   745 {
       
   746     int value;
       
   747     if (test(PP_LPAREN)) {
       
   748         value = conditional_expression();
       
   749         test(PP_RPAREN);
       
   750     } else {
       
   751         next();
       
   752         value = lexem().toInt(0, 0);
       
   753     }
       
   754     return value;
       
   755 }
       
   756 
       
   757 bool PP_Expression::primary_expression_lookup()
       
   758 {
       
   759     Token t = lookup();
       
   760     return (t == PP_IDENTIFIER
       
   761             || t == PP_INTEGER_LITERAL
       
   762             || t == PP_FLOATING_LITERAL
       
   763             || t == PP_MOC_TRUE
       
   764             || t == PP_MOC_FALSE
       
   765             || t == PP_LPAREN);
       
   766 }
       
   767 
       
   768 int Preprocessor::evaluateCondition()
       
   769 {
       
   770     PP_Expression expression;
       
   771     expression.currentFilenames = currentFilenames;
       
   772 
       
   773     substituteUntilNewline(expression.symbols);
       
   774 
       
   775     return expression.value();
       
   776 }
       
   777 
       
   778 void Preprocessor::preprocess(const QByteArray &filename, Symbols &preprocessed)
       
   779 {
       
   780     currentFilenames.push(filename);
       
   781     preprocessed.reserve(preprocessed.size() + symbols.size());
       
   782     while (hasNext()) {
       
   783         Token token = next();
       
   784 
       
   785         switch (token) {
       
   786         case PP_INCLUDE:
       
   787         {
       
   788             int lineNum = symbol().lineNum;
       
   789             QByteArray include;
       
   790             bool local = false;
       
   791             if (test(PP_STRING_LITERAL)) {
       
   792                 local = lexem().startsWith('\"');
       
   793                 include = unquotedLexem();
       
   794             } else
       
   795                 continue;
       
   796             until(PP_NEWLINE);
       
   797 
       
   798             // #### stringery
       
   799             QFileInfo fi;
       
   800             if (local)
       
   801                 fi.setFile(QFileInfo(QString::fromLocal8Bit(filename)).dir(), QString::fromLocal8Bit(include));
       
   802             for (int j = 0; j < Preprocessor::includes.size() && !fi.exists(); ++j) {
       
   803                 const IncludePath &p = Preprocessor::includes.at(j);
       
   804                 if (p.isFrameworkPath) {
       
   805                     const int slashPos = include.indexOf('/');
       
   806                     if (slashPos == -1)
       
   807                         continue;
       
   808                     QByteArray frameworkCandidate = include.left(slashPos);
       
   809                     frameworkCandidate.append(".framework/Headers/");
       
   810                     fi.setFile(QString::fromLocal8Bit(p.path + '/' + frameworkCandidate), QString::fromLocal8Bit(include.mid(slashPos + 1)));
       
   811                 } else {
       
   812                     fi.setFile(QString::fromLocal8Bit(p.path), QString::fromLocal8Bit(include));
       
   813                 }
       
   814                 // try again, maybe there's a file later in the include paths with the same name
       
   815                 // (186067)
       
   816                 if (fi.isDir()) {
       
   817                     fi = QFileInfo();
       
   818                     continue;
       
   819                 }
       
   820             }
       
   821 
       
   822             if (!fi.exists() || fi.isDir())
       
   823                 continue;
       
   824             include = fi.canonicalFilePath().toLocal8Bit();
       
   825 
       
   826             if (Preprocessor::preprocessedIncludes.contains(include))
       
   827                 continue;
       
   828             Preprocessor::preprocessedIncludes.insert(include);
       
   829 
       
   830             QFile file(QString::fromLocal8Bit(include));
       
   831             if (!file.open(QFile::ReadOnly))
       
   832                 continue;
       
   833 
       
   834             QByteArray input = file.readAll();
       
   835             file.close();
       
   836             if (input.isEmpty())
       
   837                 continue;
       
   838 
       
   839             Symbols saveSymbols = symbols;
       
   840             int saveIndex = index;
       
   841 
       
   842             // phase 1: get rid of backslash-newlines
       
   843             input = cleaned(input);
       
   844 
       
   845             // phase 2: tokenize for the preprocessor
       
   846             symbols = tokenize(input);
       
   847             input.clear();
       
   848 
       
   849             index = 0;
       
   850 
       
   851             // phase 3: preprocess conditions and substitute macros
       
   852             preprocessed += Symbol(0, MOC_INCLUDE_BEGIN, include);
       
   853             preprocess(include, preprocessed);
       
   854             preprocessed += Symbol(lineNum, MOC_INCLUDE_END, include);
       
   855 
       
   856             symbols = saveSymbols;
       
   857             index = saveIndex;
       
   858             continue;
       
   859         }
       
   860         case PP_DEFINE:
       
   861         {
       
   862             next(IDENTIFIER);
       
   863             QByteArray name = lexem();
       
   864             int start = index;
       
   865             until(PP_NEWLINE);
       
   866             Macro macro;
       
   867             macro.symbols.reserve(index - start - 1);
       
   868             for (int i = start; i < index - 1; ++i)
       
   869                 macro.symbols += symbols.at(i);
       
   870             macros.insert(name, macro);
       
   871             continue;
       
   872         }
       
   873         case PP_UNDEF: {
       
   874             next(IDENTIFIER);
       
   875             QByteArray name = lexem();
       
   876             until(PP_NEWLINE);
       
   877             macros.remove(name);
       
   878             continue;
       
   879         }
       
   880         case PP_IDENTIFIER:
       
   881         {
       
   882 //             if (macros.contains(symbol()))
       
   883 //                 ;
       
   884         }
       
   885             // we _could_ easily substitute macros by the following
       
   886             // four lines, but we choose not to.
       
   887             /*
       
   888             if (macros.contains(sym.lexem())) {
       
   889                 preprocessed += substitute(macros, symbols, i);
       
   890                 continue;
       
   891             }
       
   892             */
       
   893             break;
       
   894         case PP_HASH:
       
   895             until(PP_NEWLINE);
       
   896             continue; // skip unknown preprocessor statement
       
   897         case PP_IFDEF:
       
   898         case PP_IFNDEF:
       
   899         case PP_IF:
       
   900             while (!evaluateCondition()) {
       
   901                 if (!skipBranch())
       
   902                     break;
       
   903                 if (test(PP_ELIF)) {
       
   904                 } else {
       
   905                     until(PP_NEWLINE);
       
   906                     break;
       
   907                 }
       
   908             }
       
   909             continue;
       
   910         case PP_ELIF:
       
   911         case PP_ELSE:
       
   912             skipUntilEndif();
       
   913             // fall through
       
   914         case PP_ENDIF:
       
   915             until(PP_NEWLINE);
       
   916             continue;
       
   917         case SIGNALS:
       
   918         case SLOTS: {
       
   919             Symbol sym = symbol();
       
   920             if (macros.contains("QT_NO_KEYWORDS"))
       
   921                 sym.token = IDENTIFIER;
       
   922             else
       
   923                 sym.token = (token == SIGNALS ? Q_SIGNALS_TOKEN : Q_SLOTS_TOKEN);
       
   924             preprocessed += sym;
       
   925         } continue;
       
   926         default:
       
   927             break;
       
   928         }
       
   929         preprocessed += symbol();
       
   930     }
       
   931 
       
   932     currentFilenames.pop();
       
   933 }
       
   934 
       
   935 Symbols Preprocessor::preprocessed(const QByteArray &filename, FILE *file)
       
   936 {
       
   937     QFile qfile;
       
   938     qfile.open(file, QFile::ReadOnly);
       
   939     QByteArray input = qfile.readAll();
       
   940     if (input.isEmpty())
       
   941         return symbols;
       
   942     
       
   943     // phase 1: get rid of backslash-newlines
       
   944     input = cleaned(input);
       
   945 
       
   946     // phase 2: tokenize for the preprocessor
       
   947     symbols = tokenize(input);
       
   948 
       
   949 #if 0
       
   950     for (int j = 0; j < symbols.size(); ++j)
       
   951         fprintf(stderr, "line %d: %s(%s)\n",
       
   952                symbols[j].lineNum,
       
   953                symbols[j].lexem().constData(),
       
   954                tokenTypeName(symbols[j].token));
       
   955 #endif
       
   956 
       
   957     // phase 3: preprocess conditions and substitute macros
       
   958     Symbols result;
       
   959     preprocess(filename, result);
       
   960 
       
   961 #if 0
       
   962     for (int j = 0; j < result.size(); ++j)
       
   963         fprintf(stderr, "line %d: %s(%s)\n",
       
   964                result[j].lineNum,
       
   965                result[j].lexem().constData(),
       
   966                tokenTypeName(result[j].token));
       
   967 #endif
       
   968 
       
   969     return result;
       
   970 }
       
   971 
       
   972 void Preprocessor::until(Token t)
       
   973 {
       
   974     while(hasNext() && next() != t)
       
   975         ;
       
   976 }
       
   977 
       
   978 QT_END_NAMESPACE