LCOV - code coverage report
Current view: top level - source/third_party/ogre3d_preprocessor - OgreGLSLPreprocessor.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 505 767 65.8 %
Date: 2023-01-19 00:18:29 Functions: 33 37 89.2 %

          Line data    Source code
       1             : /*
       2             :  * This source file originally came from OGRE v1.12.3 - http://www.ogre3d.org/
       3             :  * with some tweaks as part of 0 A.D.
       4             :  * All changes are released under the original license, as follows:
       5             :  */
       6             : 
       7             : /*
       8             : -----------------------------------------------------------------------------
       9             : This source file is part of OGRE
      10             : (Object-oriented Graphics Rendering Engine)
      11             : For the latest info, see http://www.ogre3d.org/
      12             : 
      13             : Copyright (c) 2000-2014 Torus Knot Software Ltd
      14             : 
      15             : Permission is hereby granted, free of charge, to any person obtaining a copy
      16             : of this software and associated documentation files (the "Software"), to deal
      17             : in the Software without restriction, including without limitation the rights
      18             : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      19             : copies of the Software, and to permit persons to whom the Software is
      20             : furnished to do so, subject to the following conditions:
      21             : 
      22             : The above copyright notice and this permission notice shall be included in
      23             : all copies or substantial portions of the Software.
      24             : 
      25             : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      26             : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      27             : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      28             : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      29             : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      30             : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      31             : THE SOFTWARE.
      32             : -----------------------------------------------------------------------------
      33             : */
      34             : 
      35             : #include "precompiled.h"
      36             : 
      37             : #include "lib/debug.h"
      38             : #include "OgreGLSLPreprocessor.h"
      39             : 
      40             : #include <ctype.h>
      41             : #include <stdio.h>
      42             : 
      43             : namespace Ogre
      44             : {
      45             : 
      46             :     // Limit max number of macro arguments to this
      47             :     #define MAX_MACRO_ARGS 16
      48             : 
      49             :     /// Return closest power of two not smaller than given number
      50         158 :     static size_t ClosestPow2 (size_t x)
      51             :     {
      52         158 :         if (!(x & (x - 1)))
      53          12 :             return x;
      54         772 :         while (x & (x + 1))
      55         313 :             x |= (x + 1);
      56         146 :         return x + 1;
      57             :     }
      58             : 
      59          12 :     void CPreprocessor::Token::Append (const char *iString, size_t iLength)
      60             :     {
      61          24 :         Token t (Token::TK_TEXT, iString, iLength);
      62          12 :         Append (t);
      63          12 :     }
      64             : 
      65         319 :     void CPreprocessor::Token::Append (const Token &iOther)
      66             :     {
      67         319 :         if (!iOther.String)
      68           0 :             return;
      69             : 
      70         319 :         if (!String)
      71             :         {
      72          27 :             String = iOther.String;
      73          27 :             Length = iOther.Length;
      74          27 :             Allocated = iOther.Allocated;
      75          27 :             iOther.Allocated = 0; // !!! not quite correct but effective
      76          27 :             return;
      77             :         }
      78             : 
      79         292 :         if (Allocated)
      80             :         {
      81         143 :             size_t new_alloc = ClosestPow2 (Length + iOther.Length);
      82         143 :             if (new_alloc < 64)
      83          74 :                 new_alloc = 64;
      84         143 :             if (new_alloc != Allocated)
      85             :             {
      86           2 :                 Allocated = new_alloc;
      87           2 :                 Buffer = (char *)realloc (Buffer, Allocated);
      88             :             }
      89             :         }
      90         149 :         else if (String + Length != iOther.String)
      91             :         {
      92          15 :             Allocated = ClosestPow2 (Length + iOther.Length);
      93          15 :             if (Allocated < 64)
      94          15 :                 Allocated = 64;
      95          15 :             char *newstr = (char *)malloc (Allocated);
      96          15 :             memcpy (newstr, String, Length);
      97          15 :             Buffer = newstr;
      98             :         }
      99             : 
     100         292 :         if (Allocated)
     101         158 :             memcpy (Buffer + Length, iOther.String, iOther.Length);
     102         292 :         Length += iOther.Length;
     103             :     }
     104             : 
     105           7 :     bool CPreprocessor::Token::GetValue (long &oValue) const
     106             :     {
     107           7 :         long val = 0;
     108           7 :         size_t i = 0;
     109             : 
     110           7 :         while (isspace (String [i]))
     111           0 :             i++;
     112             : 
     113           7 :         long base = 10;
     114           7 :         if (String [i] == '0')
     115             :         {
     116           4 :             if (Length > i + 1 && String [i + 1] == 'x')
     117           0 :                 base = 16, i += 2;
     118             :             else
     119           4 :                 base = 8;
     120             :         }
     121             : 
     122          21 :         for (; i < Length; i++)
     123             :         {
     124           7 :             int c = int (String [i]);
     125           7 :             if (isspace (c))
     126             :                 // Possible end of number
     127           0 :                 break;
     128             : 
     129           7 :             if (c >= 'a' && c <= 'z')
     130           0 :                 c -= ('a' - 'A');
     131             : 
     132           7 :             c -= '0';
     133           7 :             if (c < 0)
     134           0 :                 return false;
     135             : 
     136           7 :             if (c > 9)
     137           0 :                 c -= ('A' - '9' - 1);
     138             : 
     139           7 :             if (c >= base)
     140           0 :                 return false;
     141             : 
     142           7 :             val = (val * base) + c;
     143             :         }
     144             : 
     145             :         // Check that all other characters are just spaces
     146           7 :         for (; i < Length; i++)
     147           0 :             if (!isspace (String [i]))
     148           0 :                 return false;
     149             : 
     150           7 :         oValue = val;
     151           7 :         return true;
     152             :     }
     153             : 
     154             : 
     155           0 :     void CPreprocessor::Token::SetValue (long iValue)
     156             :     {
     157             :         char tmp [21];
     158           0 :         int len = snprintf (tmp, sizeof (tmp), "%ld", iValue);
     159           0 :         Length = 0;
     160           0 :         Append (tmp, len);
     161           0 :         Type = TK_NUMBER;
     162           0 :     }
     163             : 
     164             : 
     165          69 :     void CPreprocessor::Token::AppendNL (int iCount)
     166             :     {
     167             :         static const char newlines [8] =
     168             :             { '\n', '\n', '\n', '\n', '\n', '\n', '\n', '\n' };
     169             : 
     170          69 :         while (iCount > 8)
     171             :         {
     172           0 :             Append (newlines, 8);
     173           0 :             iCount -= 8;
     174             :         }
     175          69 :         if (iCount > 0)
     176           8 :             Append (newlines, iCount);
     177          69 :     }
     178             : 
     179             : 
     180          44 :     int CPreprocessor::Token::CountNL ()
     181             :     {
     182          44 :         if (Type == TK_EOS || Type == TK_ERROR)
     183           2 :             return 0;
     184             : 
     185          42 :         const char *s = String;
     186          42 :         size_t l = Length;
     187          42 :         int c = 0;
     188          94 :         while (l > 0)
     189             :         {
     190          42 :             const char *n = (const char *)memchr (s, '\n', l);
     191          42 :             if (!n)
     192          16 :                 return c;
     193          26 :             c++;
     194          26 :             l -= (n - s + 1);
     195          26 :             s = n + 1;
     196             :         }
     197          26 :         return c;
     198             :     }
     199             : 
     200          18 :     CPreprocessor::Token CPreprocessor::Macro::Expand(const std::vector<Token>& iArgs,
     201             :                                                       std::forward_list<Macro>& iMacros)
     202             :     {
     203          18 :         Expanding = true;
     204             : 
     205          36 :         CPreprocessor cpp;
     206          18 :         std::swap(cpp.MacroList, iMacros);
     207             : 
     208             :         // Define a new macro for every argument
     209             :         size_t i;
     210          22 :         for (i = 0; i < iArgs.size(); i++)
     211           4 :             cpp.Define (Args [i].String, Args [i].Length,
     212           4 :                         iArgs [i].String, iArgs [i].Length);
     213             :         // The rest arguments are empty
     214          18 :         for (; i < Args.size(); i++)
     215           0 :             cpp.Define (Args [i].String, Args [i].Length, "", 0);
     216             : 
     217             :         // Now run the macro expansion through the supplimentary preprocessor
     218          18 :         Token xt = cpp.Parse (Value);
     219             : 
     220          18 :         Expanding = false;
     221             : 
     222             :         // Remove the extra macros we have defined
     223          22 :         for (int j = Args.size() - 1; j >= 0; j--)
     224           4 :             cpp.Undef (Args [j].String, Args [j].Length);
     225             : 
     226          18 :         std::swap(cpp.MacroList, iMacros);
     227             : 
     228          36 :         return xt;
     229             :     }
     230             : 
     231           0 :     void CPreprocessor::Error(int iLine, const char *iError, const Token *iToken)
     232             :     {
     233             :         char line[1000];
     234           0 :         if (iToken)
     235           0 :             snprintf(line, sizeof(line), "line %d: %s: `%.*s'\n",
     236           0 :                 iLine, iError, int(iToken->Length), iToken->String);
     237             :         else
     238           0 :             snprintf(line, sizeof(line), "line %d: %s\n", iLine, iError);
     239           0 :     }
     240             : 
     241             :     CPreprocessor::ErrorHandlerFunc  CPreprocessor::ErrorHandler = CPreprocessor::Error;
     242             : 
     243          23 :     CPreprocessor::CPreprocessor (const Token &iToken, int iLine)
     244             :     {
     245          23 :         Source = iToken.String;
     246          23 :         SourceEnd = iToken.String + iToken.Length;
     247          23 :         EnableOutput = 1;
     248          23 :         EnableElif = 0;
     249          23 :         Line = iLine;
     250          23 :         BOL = true;
     251          23 :     }
     252             : 
     253          54 :     CPreprocessor::~CPreprocessor() {}
     254             : 
     255         607 :     CPreprocessor::Token CPreprocessor::GetToken (bool iExpand)
     256             :     {
     257         607 :         if (Source >= SourceEnd)
     258          14 :             return Token (Token::TK_EOS);
     259             : 
     260         593 :         const char *begin = Source;
     261         593 :         char c = *Source++;
     262             : 
     263             : 
     264         593 :         if (c == '\n' || (c == '\r' && *Source == '\n'))
     265             :         {
     266         101 :             Line++;
     267         101 :             BOL = true;
     268         101 :             if (c == '\r')
     269           0 :                 Source++;
     270         101 :             return Token (Token::TK_NEWLINE, begin, Source - begin);
     271             :         }
     272         492 :         else if (isspace (c))
     273             :         {
     274         715 :             while (Source < SourceEnd &&
     275         590 :                    *Source != '\r' &&
     276         890 :                    *Source != '\n' &&
     277         294 :                    isspace (*Source))
     278         119 :                 Source++;
     279             : 
     280         182 :             return Token (Token::TK_WHITESPACE, begin, Source - begin);
     281             :         }
     282         310 :         else if (isdigit (c))
     283             :         {
     284          85 :             BOL = false;
     285          85 :             if (c == '0' && Source < SourceEnd && Source [0] == 'x') // hex numbers
     286             :             {
     287           0 :                 Source++;
     288           0 :                 while (Source < SourceEnd && isxdigit (*Source))
     289           0 :                     Source++;
     290             :             }
     291             :             else
     292         147 :                 while (Source < SourceEnd && isdigit (*Source))
     293          31 :                     Source++;
     294          85 :             return Token (Token::TK_NUMBER, begin, Source - begin);
     295             :         }
     296         225 :         else if (c == '_' || isalnum (c))
     297             :         {
     298         106 :             BOL = false;
     299         702 :             while (Source < SourceEnd && (*Source == '_' || isalnum (*Source)))
     300         298 :                 Source++;
     301         212 :             Token t (Token::TK_KEYWORD, begin, Source - begin);
     302         106 :             if (iExpand)
     303          42 :                 t = ExpandMacro (t);
     304         106 :             return t;
     305             :         }
     306         119 :         else if (c == '"' || c == '\'')
     307             :         {
     308           0 :             BOL = false;
     309           0 :             while (Source < SourceEnd && *Source != c)
     310             :             {
     311           0 :                 if (*Source == '\\')
     312             :                 {
     313           0 :                     Source++;
     314           0 :                     if (Source >= SourceEnd)
     315           0 :                         break;
     316             :                 }
     317           0 :                 if (*Source == '\n')
     318           0 :                     Line++;
     319           0 :                 Source++;
     320             :             }
     321           0 :             if (Source < SourceEnd)
     322           0 :                 Source++;
     323           0 :             return Token (Token::TK_STRING, begin, Source - begin);
     324             :         }
     325         119 :         else if (c == '/' && *Source == '/')
     326             :         {
     327           0 :             BOL = false;
     328           0 :             Source++;
     329           0 :             while (Source < SourceEnd && *Source != '\r' && *Source != '\n')
     330           0 :                 Source++;
     331           0 :             return Token (Token::TK_LINECOMMENT, begin, Source - begin);
     332             :         }
     333         119 :         else if (c == '/' && *Source == '*')
     334             :         {
     335           0 :             BOL = false;
     336           0 :             Source++;
     337           0 :             while (Source < SourceEnd && (Source [0] != '*' || Source [1] != '/'))
     338             :             {
     339           0 :                 if (*Source == '\n')
     340           0 :                     Line++;
     341           0 :                 Source++;
     342             :             }
     343           0 :             if (Source < SourceEnd && *Source == '*')
     344           0 :                 Source++;
     345           0 :             if (Source < SourceEnd && *Source == '/')
     346           0 :                 Source++;
     347           0 :             return Token (Token::TK_COMMENT, begin, Source - begin);
     348             :         }
     349         119 :         else if (c == '#' && BOL)
     350             :         {
     351             :             // Skip all whitespaces after '#'
     352          55 :             while (Source < SourceEnd && isspace (*Source))
     353           0 :                 Source++;
     354         577 :             while (Source < SourceEnd && !isspace (*Source))
     355         261 :                 Source++;
     356          55 :             return Token (Token::TK_DIRECTIVE, begin, Source - begin);
     357             :         }
     358          64 :         else if (c == '\\' && Source < SourceEnd && (*Source == '\r' || *Source == '\n'))
     359             :         {
     360             :             // Treat backslash-newline as a whole token
     361           0 :             if (*Source == '\r')
     362           0 :                 Source++;
     363           0 :             if (*Source == '\n')
     364           0 :                 Source++;
     365           0 :             Line++;
     366           0 :             BOL = true;
     367           0 :             return Token (Token::TK_LINECONT, begin, Source - begin);
     368             :         }
     369             :         else
     370             :         {
     371          64 :             BOL = false;
     372             :             // Handle double-char operators here
     373          64 :             if (c == '>' && (*Source == '>' || *Source == '='))
     374           0 :                 Source++;
     375          64 :             else if (c == '<' && (*Source == '<' || *Source == '='))
     376           0 :                 Source++;
     377          64 :             else if (c == '!' && *Source == '=')
     378           0 :                 Source++;
     379          64 :             else if (c == '=' && *Source == '=')
     380           0 :                 Source++;
     381          64 :             else if ((c == '|' || c == '&' || c == '^') && *Source == c)
     382           0 :                 Source++;
     383          64 :             return Token (Token::TK_PUNCTUATION, begin, Source - begin);
     384             :         }
     385             :     }
     386             : 
     387             : 
     388          52 :     CPreprocessor::Macro *CPreprocessor::IsDefined (const Token &iToken)
     389             :     {
     390         160 :         for (Macro& cur : MacroList)
     391         134 :             if (cur.Name == iToken)
     392          26 :                 return &cur;
     393             : 
     394          26 :         return NULL;
     395             :     }
     396             : 
     397             : 
     398          44 :     CPreprocessor::Token CPreprocessor::ExpandMacro (const Token &iToken)
     399             :     {
     400          44 :         Macro *cur = IsDefined (iToken);
     401          44 :         if (cur && !cur->Expanding)
     402             :         {
     403          40 :             std::vector<Token> args;
     404          20 :             int old_line = Line;
     405             : 
     406          20 :             if (!cur->Args.empty())
     407             :             {
     408           6 :                 Token t = GetArguments (args, cur->ExpandFunc ? false : true, false);
     409           3 :                 if (t.Type == Token::TK_ERROR)
     410             :                 {
     411           0 :                     return t;
     412             :                 }
     413             : 
     414             :                 // Put the token back into the source pool; we'll handle it later
     415           3 :                 if (t.String)
     416             :                 {
     417             :                     // Returned token should never be allocated on heap
     418           1 :                     ENSURE(t.Allocated == 0);
     419           1 :                     Source = t.String;
     420           1 :                     Line -= t.CountNL ();
     421             :                 }
     422             :             }
     423             : 
     424          20 :             if (args.size() > cur->Args.size())
     425             :             {
     426             :                 char tmp [60];
     427           0 :                 snprintf (tmp, sizeof (tmp), "Macro `%.*s' passed %zu arguments, but takes just %zu",
     428           0 :                           int (cur->Name.Length), cur->Name.String,
     429             :                           args.size(), cur->Args.size());
     430           0 :                 ErrorHandler (old_line, tmp, nullptr);
     431           0 :                 return Token (Token::TK_ERROR);
     432             :             }
     433             : 
     434          20 :             Token t = cur->ExpandFunc ?
     435           2 :                 cur->ExpandFunc (this, args) :
     436          42 :                 cur->Expand (args, MacroList);
     437          20 :             t.AppendNL (Line - old_line);
     438             : 
     439          20 :             return t;
     440             :         }
     441             : 
     442          24 :         return iToken;
     443             :     }
     444             : 
     445             : 
     446             :     /**
     447             :      * Operator priority:
     448             :      *  0: Whole expression
     449             :      *  1: '(' ')'
     450             :      *  2: ||
     451             :      *  3: &&
     452             :      *  4: |
     453             :      *  5: ^
     454             :      *  6: &
     455             :      *  7: '==' '!='
     456             :      *  8: '<' '<=' '>' '>='
     457             :      *  9: '<<' '>>'
     458             :      * 10: '+' '-'
     459             :      * 11: '*' '/' '%'
     460             :      * 12: unary '+' '-' '!' '~'
     461             :      */
     462           9 :     CPreprocessor::Token CPreprocessor::GetExpression (
     463             :         Token &oResult, int iLine, int iOpPriority)
     464             :     {
     465             :         char tmp [40];
     466             : 
     467           1 :         do
     468             :         {
     469           9 :             oResult = GetToken (true);
     470          17 :         } while (oResult.Type == Token::TK_WHITESPACE ||
     471          16 :                  oResult.Type == Token::TK_NEWLINE ||
     472          16 :                  oResult.Type == Token::TK_COMMENT ||
     473          25 :                  oResult.Type == Token::TK_LINECOMMENT ||
     474           8 :                  oResult.Type == Token::TK_LINECONT);
     475             : 
     476          16 :         Token op (Token::TK_WHITESPACE, "", 0);
     477             : 
     478             :         // Handle unary operators here
     479           8 :         if (oResult.Type == Token::TK_PUNCTUATION && oResult.Length == 1)
     480             :         {
     481           2 :             if (strchr ("+-!~", oResult.String [0]))
     482             :             {
     483           0 :                 char uop = oResult.String [0];
     484           0 :                 op = GetExpression (oResult, iLine, 12);
     485             :                 long val;
     486           0 :                 if (!GetValue (oResult, val, iLine))
     487             :                 {
     488           0 :                     snprintf (tmp, sizeof (tmp), "Unary '%c' not applicable", uop);
     489           0 :                     ErrorHandler(iLine, tmp, &oResult);
     490           0 :                     return Token (Token::TK_ERROR);
     491             :                 }
     492             : 
     493           0 :                 if (uop == '-')
     494           0 :                     oResult.SetValue (-val);
     495           0 :                 else if (uop == '!')
     496           0 :                     oResult.SetValue (!val);
     497           0 :                 else if (uop == '~')
     498           0 :                     oResult.SetValue (~val);
     499             :             }
     500           2 :             else if (oResult.String [0] == '(')
     501             :             {
     502           1 :                 op = GetExpression (oResult, iLine, 1);
     503           1 :                 if (op.Type == Token::TK_ERROR)
     504           0 :                     return op;
     505           1 :                 if (op.Type == Token::TK_EOS)
     506             :                 {
     507           1 :                     ErrorHandler(iLine, "Unclosed parenthesis in #if expression", nullptr);
     508           1 :                     return Token (Token::TK_ERROR);
     509             :                 }
     510             : 
     511           0 :                 ENSURE(op.Type == Token::TK_PUNCTUATION &&
     512             :                         op.Length == 1 &&
     513             :                         op.String [0] == ')');
     514           0 :                 op = GetToken (true);
     515             :             }
     516             :         }
     517             : 
     518          30 :         while (op.Type == Token::TK_WHITESPACE ||
     519          14 :                op.Type == Token::TK_NEWLINE ||
     520          14 :                op.Type == Token::TK_COMMENT ||
     521          29 :                op.Type == Token::TK_LINECOMMENT ||
     522           7 :                op.Type == Token::TK_LINECONT)
     523           8 :             op = GetToken (true);
     524             : 
     525             :         while (true)
     526             :         {
     527           7 :             if (op.Type != Token::TK_PUNCTUATION)
     528          13 :                 return op;
     529             : 
     530           1 :             int prio = 0;
     531           1 :             if (op.Length == 1)
     532           1 :                 switch (op.String [0])
     533             :                 {
     534           0 :                 case ')': return op;
     535           0 :                 case '|': prio = 4; break;
     536           0 :                 case '^': prio = 5; break;
     537           0 :                 case '&': prio = 6; break;
     538           0 :                 case '<':
     539           0 :                 case '>': prio = 8; break;
     540           0 :                 case '+':
     541           0 :                 case '-': prio = 10; break;
     542           1 :                 case '*':
     543             :                 case '/':
     544           1 :                 case '%': prio = 11; break;
     545             :                 }
     546           0 :             else if (op.Length == 2)
     547           0 :                 switch (op.String [0])
     548             :                 {
     549           0 :                 case '|': if (op.String [1] == '|') prio = 2; break;
     550           0 :                 case '&': if (op.String [1] == '&') prio = 3; break;
     551           0 :                 case '=': if (op.String [1] == '=') prio = 7; break;
     552           0 :                 case '!': if (op.String [1] == '=') prio = 7; break;
     553           0 :                 case '<':
     554           0 :                     if (op.String [1] == '=')
     555           0 :                         prio = 8;
     556           0 :                     else if (op.String [1] == '<')
     557           0 :                         prio = 9;
     558           0 :                     break;
     559           0 :                 case '>':
     560           0 :                     if (op.String [1] == '=')
     561           0 :                         prio = 8;
     562           0 :                     else if (op.String [1] == '>')
     563           0 :                         prio = 9;
     564           0 :                     break;
     565             :                 }
     566             : 
     567           1 :             if (!prio)
     568             :             {
     569           0 :                 ErrorHandler(iLine, "Expecting operator, got", &op);
     570           0 :                 return Token (Token::TK_ERROR);
     571             :             }
     572             : 
     573           1 :             if (iOpPriority >= prio)
     574           0 :                 return op;
     575             : 
     576           1 :             Token rop;
     577           1 :             Token nextop = GetExpression (rop, iLine, prio);
     578             :             long vlop, vrop;
     579           1 :             if (!GetValue (oResult, vlop, iLine))
     580             :             {
     581           0 :                 snprintf (tmp, sizeof (tmp), "Left operand of '%.*s' is not a number",
     582           0 :                           int (op.Length), op.String);
     583           0 :                 ErrorHandler(iLine, tmp, &oResult);
     584           0 :                 return Token (Token::TK_ERROR);
     585             :             }
     586           1 :             if (!GetValue (rop, vrop, iLine))
     587             :             {
     588           0 :                 snprintf (tmp, sizeof (tmp), "Right operand of '%.*s' is not a number",
     589           0 :                           int (op.Length), op.String);
     590           0 :                 ErrorHandler(iLine, tmp, &rop);
     591           0 :                 return Token (Token::TK_ERROR);
     592             :             }
     593             : 
     594           1 :             switch (op.String [0])
     595             :             {
     596           0 :             case '|':
     597           0 :                 if (prio == 2)
     598           0 :                     oResult.SetValue (vlop || vrop);
     599             :                 else
     600           0 :                     oResult.SetValue (vlop | vrop);
     601           0 :                 break;
     602           0 :             case '&':
     603           0 :                 if (prio == 3)
     604           0 :                     oResult.SetValue (vlop && vrop);
     605             :                 else
     606           0 :                     oResult.SetValue (vlop & vrop);
     607           0 :                 break;
     608           0 :             case '<':
     609           0 :                 if (op.Length == 1)
     610           0 :                     oResult.SetValue (vlop < vrop);
     611           0 :                 else if (prio == 8)
     612           0 :                     oResult.SetValue (vlop <= vrop);
     613           0 :                 else if (prio == 9)
     614           0 :                     oResult.SetValue (vlop << vrop);
     615           0 :                 break;
     616           0 :             case '>':
     617           0 :                 if (op.Length == 1)
     618           0 :                     oResult.SetValue (vlop > vrop);
     619           0 :                 else if (prio == 8)
     620           0 :                     oResult.SetValue (vlop >= vrop);
     621           0 :                 else if (prio == 9)
     622           0 :                     oResult.SetValue (vlop >> vrop);
     623           0 :                 break;
     624           0 :             case '^': oResult.SetValue (vlop ^ vrop); break;
     625           0 :             case '!': oResult.SetValue (vlop != vrop); break;
     626           0 :             case '=': oResult.SetValue (vlop == vrop); break;
     627           0 :             case '+': oResult.SetValue (vlop + vrop); break;
     628           0 :             case '-': oResult.SetValue (vlop - vrop); break;
     629           0 :             case '*': oResult.SetValue (vlop * vrop); break;
     630           1 :             case '/':
     631             :             case '%':
     632           1 :                 if (vrop == 0)
     633             :                 {
     634           1 :                     ErrorHandler(iLine, "Division by zero", nullptr);
     635           1 :                     return Token (Token::TK_ERROR);
     636             :                 }
     637           0 :                 if (op.String [0] == '/')
     638           0 :                     oResult.SetValue (vlop / vrop);
     639             :                 else
     640           0 :                     oResult.SetValue (vlop % vrop);
     641           0 :                 break;
     642             :             }
     643             : 
     644           0 :             op = nextop;
     645           0 :         }
     646             :     }
     647             : 
     648             : 
     649          11 :     bool CPreprocessor::GetValue (const Token &iToken, long &oValue, int iLine)
     650             :     {
     651          22 :         Token r;
     652          11 :         const Token *vt = &iToken;
     653             : 
     654          20 :         if ((vt->Type == Token::TK_KEYWORD ||
     655          12 :              vt->Type == Token::TK_TEXT ||
     656          14 :              vt->Type == Token::TK_NUMBER) &&
     657          11 :             !vt->String)
     658             :         {
     659           0 :             ErrorHandler (iLine, "Trying to evaluate an empty expression", nullptr);
     660           0 :             return false;
     661             :         }
     662             : 
     663          11 :         if (vt->Type == Token::TK_TEXT)
     664             :         {
     665          10 :             CPreprocessor cpp (iToken, iLine);
     666           6 :             std::swap(cpp.MacroList, MacroList);
     667             : 
     668          10 :             Token t;
     669           6 :             t = cpp.GetExpression (r, iLine);
     670             : 
     671           6 :             std::swap(cpp.MacroList, MacroList);
     672             : 
     673           6 :             if (t.Type == Token::TK_ERROR)
     674           2 :                 return false;
     675             : 
     676           4 :             if (t.Type != Token::TK_EOS)
     677             :             {
     678           0 :                 ErrorHandler(iLine, "Garbage after expression", &t);
     679           0 :                 return false;
     680             :             }
     681             : 
     682           4 :             vt = &r;
     683             :         }
     684             : 
     685             :         Macro *m;
     686           9 :         switch (vt->Type)
     687             :         {
     688           0 :         case Token::TK_EOS:
     689             :         case Token::TK_ERROR:
     690           0 :             return false;
     691             : 
     692           2 :         case Token::TK_KEYWORD:
     693             :             // Try to expand the macro
     694           2 :             m = IsDefined(*vt);
     695           2 :             if (m != nullptr && !m->Expanding)
     696             :             {
     697           4 :                 Token x = ExpandMacro (*vt);
     698           2 :                 m->Expanding = true;
     699           2 :                 bool rc = GetValue (x, oValue, iLine);
     700           2 :                 m->Expanding = false;
     701           2 :                 return rc;
     702             :             }
     703             : 
     704             :             // Undefined macro, "expand" to 0 (mimic cpp behaviour)
     705           0 :             oValue = 0;
     706           0 :             break;
     707             : 
     708           7 :         case Token::TK_TEXT:
     709             :         case Token::TK_NUMBER:
     710           7 :             if (!vt->GetValue (oValue))
     711             :             {
     712           0 :                 ErrorHandler(iLine, "Not a numeric expression", vt);
     713           0 :                 return false;
     714             :             }
     715           7 :             break;
     716             : 
     717           0 :         default:
     718           0 :             ErrorHandler(iLine, "Unexpected token", vt);
     719           0 :             return false;
     720             :         }
     721             : 
     722           7 :         return true;
     723             :     }
     724             : 
     725             : 
     726          16 :     CPreprocessor::Token CPreprocessor::GetArgument (Token &oArg, bool iExpand,
     727             :                                                      bool shouldAppendArg)
     728             :     {
     729           6 :         do
     730             :         {
     731          16 :             oArg = GetToken (iExpand);
     732          26 :         } while (oArg.Type == Token::TK_WHITESPACE ||
     733          20 :                  oArg.Type == Token::TK_NEWLINE ||
     734          20 :                  oArg.Type == Token::TK_COMMENT ||
     735          36 :                  oArg.Type == Token::TK_LINECOMMENT ||
     736          10 :                  oArg.Type == Token::TK_LINECONT);
     737             : 
     738          10 :         if (!iExpand)
     739             :         {
     740           6 :             if (oArg.Type == Token::TK_EOS)
     741           0 :                 return oArg;
     742           6 :             else if (oArg.Type == Token::TK_PUNCTUATION &&
     743           0 :                      (oArg.String [0] == ',' ||
     744           0 :                       oArg.String [0] == ')'))
     745             :             {
     746           0 :                 Token t = oArg;
     747           0 :                 oArg = Token (Token::TK_TEXT, "", 0);
     748           0 :                 return t;
     749             :             }
     750           6 :             else if (oArg.Type != Token::TK_KEYWORD)
     751             :             {
     752           0 :                 ErrorHandler(Line, "Unexpected token", &oArg);
     753           0 :                 return Token (Token::TK_ERROR);
     754             :             }
     755             :         }
     756             : 
     757          10 :         size_t braceCount = 0;
     758             : 
     759          10 :         if( oArg.Type == Token::TK_PUNCTUATION && oArg.String[0] == '(' )
     760           0 :             ++braceCount;
     761             : 
     762          10 :         size_t len = oArg.Length;
     763             :         while (true)
     764             :         {
     765          22 :             Token t = GetToken (iExpand);
     766          16 :             switch (t.Type)
     767             :             {
     768           0 :             case Token::TK_EOS:
     769           0 :                 ErrorHandler(Line, "Unfinished list of arguments", nullptr);
     770             :                 FALLTHROUGH;
     771           0 :             case Token::TK_ERROR:
     772           0 :                 return Token (Token::TK_ERROR);
     773          13 :             case Token::TK_PUNCTUATION:
     774          13 :                 if( t.String [0] == '(' )
     775             :                 {
     776           1 :                     ++braceCount;
     777             :                 }
     778          12 :                 else if( !braceCount )
     779             :                 {
     780          14 :                     if (t.String [0] == ',' ||
     781           4 :                         t.String [0] == ')')
     782             :                     {
     783             :                         // Trim whitespaces at the end
     784          10 :                         oArg.Length = len;
     785             : 
     786             :                         //Append "__arg_" to all macro arguments, otherwise if user does:
     787             :                         //  #define mad( a, b, c ) fma( a, b, c )
     788             :                         //  mad( x.s, y, a );
     789             :                         //It will be translated to:
     790             :                         //  fma( x.s, y, x.s );
     791             :                         //instead of:
     792             :                         //  fma( x.s, y, a );
     793             :                         //This does not fix the problem by the root, but
     794             :                         //typing "__arg_" by the user is extremely rare.
     795          10 :                         if( shouldAppendArg )
     796           4 :                             oArg.Append( "__arg_", 6 );
     797          10 :                         return t;
     798             :                     }
     799             :                 }
     800             :                 else
     801             :                 {
     802           2 :                     if( t.String [0] == ')' )
     803           1 :                         --braceCount;
     804             :                 }
     805           3 :                 break;
     806           0 :             case Token::TK_LINECONT:
     807             :             case Token::TK_COMMENT:
     808             :             case Token::TK_LINECOMMENT:
     809             :             case Token::TK_NEWLINE:
     810             :                 // ignore these tokens
     811           0 :                 continue;
     812           3 :             default:
     813           3 :                 break;
     814             :             }
     815             : 
     816           6 :             if (!iExpand && t.Type != Token::TK_WHITESPACE)
     817             :             {
     818           0 :                 ErrorHandler(Line, "Unexpected token", &oArg);
     819           0 :                 return Token (Token::TK_ERROR);
     820             :             }
     821             : 
     822           6 :             oArg.Append (t);
     823             : 
     824           6 :             if (t.Type != Token::TK_WHITESPACE)
     825           5 :                 len = oArg.Length;
     826           6 :         }
     827             :     }
     828             : 
     829             : 
     830          15 :     CPreprocessor::Token CPreprocessor::GetArguments (std::vector<Token>& oArgs,
     831             :                                                       bool iExpand, bool shouldAppendArg)
     832             :     {
     833          30 :         Token args [MAX_MACRO_ARGS];
     834          15 :         int nargs = 0;
     835             : 
     836             :         // Suppose we'll leave by the wrong path
     837          15 :         oArgs.clear();
     838             : 
     839          15 :         bool isFirstTokenParsed = false;
     840          15 :         bool isFirstTokenNotAnOpenBrace = false;
     841             : 
     842          30 :         Token t;
     843          10 :         do
     844             :         {
     845          25 :             t = GetToken (iExpand);
     846             : 
     847          40 :             if( !isFirstTokenParsed &&
     848          19 :                 (t.Type != Token::TK_PUNCTUATION || t.String [0] != '(') )
     849             :             {
     850          11 :                 isFirstTokenNotAnOpenBrace = true;
     851             :             }
     852          25 :             isFirstTokenParsed = true;
     853          40 :         } while (t.Type == Token::TK_WHITESPACE ||
     854          40 :                  t.Type == Token::TK_COMMENT ||
     855          15 :                  t.Type == Token::TK_LINECOMMENT);
     856             : 
     857          15 :         if( isFirstTokenNotAnOpenBrace )
     858             :         {
     859          11 :             oArgs.clear();
     860          11 :             return t;
     861             :         }
     862             : 
     863             :         while (true)
     864             :         {
     865          16 :             if (nargs == MAX_MACRO_ARGS)
     866             :             {
     867           0 :                 ErrorHandler(Line, "Too many arguments to macro", nullptr);
     868           0 :                 return Token (Token::TK_ERROR);
     869             :             }
     870             : 
     871          10 :             t = GetArgument (args [nargs++], iExpand, shouldAppendArg);
     872             : 
     873          10 :             switch (t.Type)
     874             :             {
     875           0 :             case Token::TK_EOS:
     876           0 :                 ErrorHandler(Line, "Unfinished list of arguments", nullptr);
     877             :                 FALLTHROUGH;
     878           0 :             case Token::TK_ERROR:
     879           0 :                 return Token (Token::TK_ERROR);
     880             : 
     881          10 :             case Token::TK_PUNCTUATION:
     882          10 :                 if (t.String [0] == ')')
     883             :                 {
     884           4 :                     t = GetToken (iExpand);
     885           4 :                     goto Done;
     886             :                 } // otherwise we've got a ','
     887           6 :                 break;
     888             : 
     889           0 :             default:
     890           0 :                 ErrorHandler(Line, "Unexpected token", &t);
     891           0 :                 break;
     892             :             }
     893             :         }
     894             : 
     895           4 :     Done:
     896           4 :         oArgs.insert(oArgs.begin(), args, args + nargs);
     897           4 :         return t;
     898             :     }
     899             : 
     900             : 
     901          12 :     bool CPreprocessor::HandleDefine (Token &iBody, int iLine)
     902             :     {
     903             :         // Create an additional preprocessor to process macro body
     904          24 :         CPreprocessor cpp (iBody, iLine);
     905             : 
     906          24 :         Token t = cpp.GetToken (false);
     907          12 :         if (t.Type != Token::TK_KEYWORD)
     908             :         {
     909           0 :             ErrorHandler(iLine, "Macro name expected after #define", nullptr);
     910           0 :             return false;
     911             :         }
     912             : 
     913          24 :         Macro m(t);
     914          12 :         m.Body = iBody;
     915          12 :         t = cpp.GetArguments (m.Args, false, true);
     916          14 :         while (t.Type == Token::TK_WHITESPACE)
     917           1 :             t = cpp.GetToken (false);
     918             : 
     919          12 :         switch (t.Type)
     920             :         {
     921           1 :         case Token::TK_NEWLINE:
     922             :         case Token::TK_EOS:
     923             :             // Assign "" to token
     924           1 :             t = Token (Token::TK_TEXT, "", 0);
     925           1 :             break;
     926             : 
     927           0 :         case Token::TK_ERROR:
     928           0 :             return false;
     929             : 
     930          11 :         default:
     931          11 :             t.Type = Token::TK_TEXT;
     932          11 :             ENSURE(t.String + t.Length == cpp.Source);
     933          11 :             t.Length = cpp.SourceEnd - t.String;
     934          11 :             break;
     935             :         }
     936             : 
     937          12 :         if( !m.Args.empty() )
     938             :         {
     939           2 :             CPreprocessor cpp2;
     940             : 
     941             :             //We need to convert:
     942             :             //  #define mad( a__arg_, b__arg_, c__arg_ ) fma( a, b, c )
     943             :             //into:
     944             :             //  #define mad( a__arg_, b__arg_, c__arg_ ) fma( a__arg_, b__arg_, c__arg_ )
     945           5 :             for (const Token& arg : m.Args)
     946             :             {
     947           4 :                 cpp2.Define(arg.String, arg.Length - 6, arg.String, arg.Length);
     948             :             }
     949             : 
     950             :             // Now run the macro expansion through the supplimentary preprocessor
     951           2 :             Token xt = cpp2.Parse( t );
     952           1 :             t = xt;
     953             :         }
     954             : 
     955          12 :         m.Value = t;
     956          12 :         MacroList.push_front(std::move(m));
     957          12 :         return true;
     958             :     }
     959             : 
     960             : 
     961           1 :     bool CPreprocessor::HandleUnDef (Token &iBody, int iLine)
     962             :     {
     963           2 :         CPreprocessor cpp (iBody, iLine);
     964             : 
     965           2 :         Token t = cpp.GetToken (false);
     966             : 
     967           1 :         if (t.Type != Token::TK_KEYWORD)
     968             :         {
     969           0 :             ErrorHandler(iLine, "Expecting a macro name after #undef, got", &t);
     970           0 :             return false;
     971             :         }
     972             : 
     973             :         // Don't barf if macro does not exist - standard C behaviour
     974           1 :         Undef (t.String, t.Length);
     975             : 
     976           0 :         do
     977             :         {
     978           1 :             t = cpp.GetToken (false);
     979           2 :         } while (t.Type == Token::TK_WHITESPACE ||
     980           2 :                  t.Type == Token::TK_COMMENT ||
     981           1 :                  t.Type == Token::TK_LINECOMMENT);
     982             : 
     983           1 :         if (t.Type != Token::TK_EOS)
     984           0 :             ErrorHandler(iLine, "Warning: Ignoring garbage after directive", &t);
     985             : 
     986           1 :         return true;
     987             :     }
     988             : 
     989           7 :     bool CPreprocessor::HandleIf(bool val, int iLine)
     990             :     {
     991           7 :         if (EnableOutput & (1 << 31))
     992             :         {
     993           0 :             ErrorHandler(iLine, "Too many embedded #if directives", nullptr);
     994           0 :             return false;
     995             :         }
     996             : 
     997           7 :         EnableElif <<= 1;
     998           7 :         EnableOutput <<= 1;
     999           7 :         if (val)
    1000           3 :             EnableOutput |= 1;
    1001             :         else
    1002           4 :             EnableElif |= 1;
    1003             : 
    1004           7 :         return true;
    1005             :     }
    1006             : 
    1007           4 :     bool CPreprocessor::HandleIfDef (Token &iBody, int iLine)
    1008             :     {
    1009           8 :         CPreprocessor cpp (iBody, iLine);
    1010             : 
    1011           8 :         Token t = cpp.GetToken (false);
    1012             : 
    1013           4 :         if (t.Type != Token::TK_KEYWORD)
    1014             :         {
    1015           0 :             ErrorHandler(iLine, "Expecting a macro name after #ifdef, got", &t);
    1016           0 :             return false;
    1017             :         }
    1018             : 
    1019           4 :         if (!HandleIf(IsDefined(t) != nullptr, iLine))
    1020           0 :             return false;
    1021             : 
    1022           0 :         do
    1023             :         {
    1024           4 :             t = cpp.GetToken (false);
    1025           8 :         } while (t.Type == Token::TK_WHITESPACE ||
    1026           8 :                  t.Type == Token::TK_COMMENT ||
    1027           4 :                  t.Type == Token::TK_LINECOMMENT);
    1028             : 
    1029           4 :         if (t.Type != Token::TK_EOS)
    1030           0 :             ErrorHandler(iLine, "Warning: Ignoring garbage after directive", &t);
    1031             : 
    1032           4 :         return true;
    1033             :     }
    1034             : 
    1035             : 
    1036           2 :     CPreprocessor::Token CPreprocessor::ExpandDefined (CPreprocessor *iParent, const std::vector<Token>& iArgs)
    1037             :     {
    1038           2 :         if (iArgs.size() != 1)
    1039             :         {
    1040           0 :             iParent->ErrorHandler(iParent->Line, "The defined() function takes exactly one argument", nullptr);
    1041           0 :             return Token (Token::TK_ERROR);
    1042             :         }
    1043             : 
    1044           2 :         const char *v = iParent->IsDefined (iArgs [0]) ? "1" : "0";
    1045           2 :         return Token (Token::TK_NUMBER, v, 1);
    1046             :     }
    1047             : 
    1048           7 :     bool CPreprocessor::GetValueDef(const Token &iToken, long &oValue, int iLine)
    1049             :     {
    1050             :         // Temporary add the defined() function to the macro list
    1051           7 :         MacroList.emplace_front(Token(Token::TK_KEYWORD, "defined", 7));
    1052           7 :         MacroList.front().ExpandFunc = ExpandDefined;
    1053           7 :         MacroList.front().Args.resize(1);
    1054             : 
    1055           7 :         bool rc = GetValue (iToken, oValue, iLine);
    1056             : 
    1057             :         // Restore the macro list
    1058           7 :         MacroList.pop_front();
    1059             : 
    1060           7 :         return rc;
    1061             :     }
    1062             : 
    1063           5 :     bool CPreprocessor::HandleIf (Token &iBody, int iLine)
    1064             :     {
    1065             :         long val;
    1066           5 :         return GetValueDef(iBody, val, iLine) && HandleIf(val != 0, iLine);
    1067             :     }
    1068             : 
    1069           2 :     bool CPreprocessor::HandleElif (Token &iBody, int iLine)
    1070             :     {
    1071           2 :         if (EnableOutput == 1)
    1072             :         {
    1073           0 :             ErrorHandler(iLine, "#elif without #if", nullptr);
    1074           0 :             return false;
    1075             :         }
    1076             : 
    1077             :         long val;
    1078           2 :         if (!GetValueDef(iBody, val, iLine))
    1079           0 :             return false;
    1080             : 
    1081           2 :         if (val && (EnableElif & 1))
    1082             :         {
    1083           2 :             EnableOutput |= 1;
    1084           2 :             EnableElif &= ~1;
    1085             :         }
    1086             :         else
    1087           0 :             EnableOutput &= ~1;
    1088             : 
    1089           2 :         return true;
    1090             :     }
    1091             : 
    1092             : 
    1093           2 :     bool CPreprocessor::HandleElse (Token &iBody, int iLine)
    1094             :     {
    1095           2 :         if (EnableOutput == 1)
    1096             :         {
    1097           0 :             ErrorHandler(iLine, "#else without #if", nullptr);
    1098           0 :             return false;
    1099             :         }
    1100             : 
    1101             :         // Negate the result of last #if
    1102           2 :         if ((EnableElif & 1) || (EnableOutput & 1))
    1103           2 :             EnableOutput ^= 1;
    1104             : 
    1105           2 :         if (iBody.Length)
    1106           0 :             ErrorHandler(iLine, "Warning: Ignoring garbage after #else", &iBody);
    1107             : 
    1108           2 :         return true;
    1109             :     }
    1110             : 
    1111             : 
    1112           7 :     bool CPreprocessor::HandleEndIf (Token &iBody, int iLine)
    1113             :     {
    1114           7 :         EnableElif >>= 1;
    1115           7 :         EnableOutput >>= 1;
    1116           7 :         if (EnableOutput == 0)
    1117             :         {
    1118           0 :             ErrorHandler(iLine, "#endif without #if", nullptr);
    1119           0 :             return false;
    1120             :         }
    1121             : 
    1122           7 :         if (iBody.Length)
    1123           0 :             ErrorHandler(iLine, "Warning: Ignoring garbage after #endif", &iBody);
    1124             : 
    1125           7 :         return true;
    1126             :     }
    1127             : 
    1128             : 
    1129          55 :     CPreprocessor::Token CPreprocessor::HandleDirective (Token &iToken, int iLine)
    1130             :     {
    1131             :         // Analyze preprocessor directive
    1132          55 :         const char *directive = iToken.String + 1;
    1133          55 :         size_t dirlen = iToken.Length - 1;
    1134          55 :         while (dirlen && isspace (*directive))
    1135           0 :             dirlen--, directive++;
    1136             : 
    1137          55 :         int old_line = Line;
    1138             : 
    1139             :         // Collect the remaining part of the directive until EOL
    1140         110 :         Token t, last;
    1141          46 :         do
    1142             :         {
    1143         101 :             t = GetToken (false);
    1144         101 :             if (t.Type == Token::TK_NEWLINE)
    1145             :             {
    1146             :                 // No directive arguments
    1147           9 :                 last = t;
    1148           9 :                 t.Length = 0;
    1149           9 :                 goto Done;
    1150             :             }
    1151         138 :         } while (t.Type == Token::TK_WHITESPACE ||
    1152          92 :                  t.Type == Token::TK_LINECONT ||
    1153         184 :                  t.Type == Token::TK_COMMENT ||
    1154          46 :                  t.Type == Token::TK_LINECOMMENT);
    1155             : 
    1156             :         for (;;)
    1157             :         {
    1158         190 :             last = GetToken (false);
    1159         118 :             switch (last.Type)
    1160             :             {
    1161           0 :             case Token::TK_EOS:
    1162             :                 // Can happen and is not an error
    1163           0 :                 goto Done;
    1164             : 
    1165           0 :             case Token::TK_LINECOMMENT:
    1166             :             case Token::TK_COMMENT:
    1167             :                 // Skip comments in macros
    1168           0 :                 continue;
    1169             : 
    1170           0 :             case Token::TK_ERROR:
    1171           0 :                 return last;
    1172             : 
    1173           0 :             case Token::TK_LINECONT:
    1174           0 :                 continue;
    1175             : 
    1176          46 :             case Token::TK_NEWLINE:
    1177          46 :                 goto Done;
    1178             : 
    1179          72 :             default:
    1180          72 :                 break;
    1181             :             }
    1182             : 
    1183          72 :             t.Append (last);
    1184          72 :             t.Type = Token::TK_TEXT;
    1185             :         }
    1186          55 :     Done:
    1187             : 
    1188             : #define IS_DIRECTIVE(s)                                                 \
    1189             :         (dirlen == strlen(s) && (strncmp (directive, s, dirlen) == 0))
    1190             : 
    1191          55 :         bool outputEnabled = ((EnableOutput & (EnableOutput + 1)) == 0);
    1192             :         bool rc;
    1193             : 
    1194          55 :         if (IS_DIRECTIVE ("define") && outputEnabled)
    1195          12 :             rc = HandleDefine (t, iLine);
    1196          43 :         else if (IS_DIRECTIVE ("undef") && outputEnabled)
    1197           1 :             rc = HandleUnDef (t, iLine);
    1198          42 :         else if (IS_DIRECTIVE ("ifdef"))
    1199           1 :             rc = HandleIfDef (t, iLine);
    1200          41 :         else if (IS_DIRECTIVE ("ifndef"))
    1201             :         {
    1202           3 :             rc = HandleIfDef (t, iLine);
    1203           6 :             if (rc)
    1204             :             {
    1205           3 :                 EnableOutput ^= 1;
    1206           3 :                 EnableElif ^= 1;
    1207             :             }
    1208             :         }
    1209          38 :         else if (IS_DIRECTIVE ("if"))
    1210           5 :             rc = HandleIf (t, iLine);
    1211          33 :         else if (IS_DIRECTIVE ("elif"))
    1212           2 :             rc = HandleElif (t, iLine);
    1213             : 
    1214          31 :         else if (IS_DIRECTIVE ("else"))
    1215           2 :             rc = HandleElse (t, iLine);
    1216          29 :         else if (IS_DIRECTIVE ("endif"))
    1217           7 :             rc = HandleEndIf (t, iLine);
    1218             :         else
    1219             :         {
    1220             :             // Unknown preprocessor directive, roll back and pass through
    1221          22 :             Line = old_line;
    1222          22 :             Source = iToken.String + iToken.Length;
    1223          22 :             iToken.Type = Token::TK_TEXT;
    1224          22 :             return iToken;
    1225             :         }
    1226             : 
    1227             : #undef IS_DIRECTIVE
    1228             : 
    1229          33 :         if (!rc)
    1230           2 :             return Token (Token::TK_ERROR);
    1231          31 :         return last;
    1232             :     }
    1233             : 
    1234             : 
    1235           8 :     void CPreprocessor::Define (const char *iMacroName, size_t iMacroNameLen,
    1236             :                                 const char *iMacroValue, size_t iMacroValueLen)
    1237             :     {
    1238           8 :         MacroList.emplace_front(Token(Token::TK_KEYWORD, iMacroName, iMacroNameLen));
    1239           8 :         MacroList.front().Value = Token(Token::TK_TEXT, iMacroValue, iMacroValueLen);
    1240           8 :     }
    1241             : 
    1242             : 
    1243           0 :     void CPreprocessor::Define (const char *iMacroName, size_t iMacroNameLen,
    1244             :                                 long iMacroValue)
    1245             :     {
    1246           0 :         MacroList.emplace_front(Token(Token::TK_KEYWORD, iMacroName, iMacroNameLen));
    1247           0 :         MacroList.front().Value.SetValue(iMacroValue);
    1248           0 :     }
    1249             : 
    1250             : 
    1251           5 :     bool CPreprocessor::Undef (const char *iMacroName, size_t iMacroNameLen)
    1252             :     {
    1253          10 :         Token name (Token::TK_KEYWORD, iMacroName, iMacroNameLen);
    1254             : 
    1255           7 :         for (auto it = MacroList.before_begin();; ++it)
    1256             :         {
    1257           7 :             auto itpp = std::next(it);
    1258           7 :             if(itpp == MacroList.end()) break;
    1259             : 
    1260           7 :             if (itpp->Name == name)
    1261             :             {
    1262           5 :                 MacroList.erase_after(it);
    1263           5 :                 return true;
    1264             :             }
    1265           2 :         }
    1266             : 
    1267           0 :         return false;
    1268             :     }
    1269             : 
    1270             : 
    1271          31 :     CPreprocessor::Token CPreprocessor::Parse (const Token &iSource)
    1272             :     {
    1273          31 :         Source = iSource.String;
    1274          31 :         SourceEnd = Source + iSource.Length;
    1275          31 :         Line = 1;
    1276          31 :         BOL = true;
    1277          31 :         EnableOutput = 1;
    1278          31 :         EnableElif = 0;
    1279             : 
    1280             :         // Accumulate output into this token
    1281          62 :         Token output (Token::TK_TEXT);
    1282          31 :         int empty_lines = 0;
    1283             : 
    1284             :         // Enable output only if all embedded #if's were true
    1285          31 :         bool old_output_enabled = true;
    1286          31 :         bool output_enabled = true;
    1287          31 :         int output_disabled_line = 0;
    1288             : 
    1289         601 :         while (Source < SourceEnd)
    1290             :         {
    1291         287 :             int old_line = Line;
    1292         572 :             Token t = GetToken (true);
    1293             : 
    1294         342 :         NextToken:
    1295         342 :             switch (t.Type)
    1296             :             {
    1297           2 :             case Token::TK_ERROR:
    1298           2 :                 return t;
    1299             : 
    1300           0 :             case Token::TK_EOS:
    1301           0 :                 return output; // Force termination
    1302             : 
    1303           0 :             case Token::TK_COMMENT:
    1304             :                 // C comments are replaced with single spaces.
    1305           0 :                 if (output_enabled)
    1306             :                 {
    1307           0 :                     output.Append (" ", 1);
    1308           0 :                     output.AppendNL (Line - old_line);
    1309             :                 }
    1310           0 :                 break;
    1311             : 
    1312           0 :             case Token::TK_LINECOMMENT:
    1313             :                 // C++ comments are ignored
    1314           0 :                 continue;
    1315             : 
    1316          55 :             case Token::TK_DIRECTIVE:
    1317             :                 // Handle preprocessor directives
    1318          55 :                 t = HandleDirective (t, old_line);
    1319             : 
    1320          55 :                 output_enabled = ((EnableOutput & (EnableOutput + 1)) == 0);
    1321          55 :                 if (output_enabled != old_output_enabled)
    1322             :                 {
    1323          12 :                     if (output_enabled)
    1324           6 :                         output.AppendNL (old_line - output_disabled_line);
    1325             :                     else
    1326           6 :                         output_disabled_line = old_line;
    1327          12 :                     old_output_enabled = output_enabled;
    1328             :                 }
    1329             : 
    1330          55 :                 if (output_enabled)
    1331          43 :                     output.AppendNL (Line - old_line - t.CountNL ());
    1332          55 :                 goto NextToken;
    1333             : 
    1334           0 :             case Token::TK_LINECONT:
    1335             :                 // Backslash-Newline sequences are deleted, no matter where.
    1336           0 :                 empty_lines++;
    1337           0 :                 break;
    1338             : 
    1339          76 :             case Token::TK_NEWLINE:
    1340          76 :                 if (empty_lines)
    1341             :                 {
    1342             :                     // Compensate for the backslash-newline combinations
    1343             :                     // we have encountered, otherwise line numeration is broken
    1344           0 :                     if (output_enabled)
    1345           0 :                         output.AppendNL (empty_lines);
    1346           0 :                     empty_lines = 0;
    1347             :                 }
    1348             :                 FALLTHROUGH; // to default
    1349             :             case Token::TK_WHITESPACE:
    1350             :                 // Fallthrough to default
    1351             :             default:
    1352             :                 // Passthrough all other tokens
    1353         285 :                 if (output_enabled)
    1354         229 :                     output.Append (t);
    1355         285 :                 break;
    1356             :             }
    1357             :         }
    1358             : 
    1359          29 :         if (EnableOutput != 1)
    1360             :         {
    1361           0 :             ErrorHandler(Line, "Unclosed #if at end of source", nullptr);
    1362           0 :             return Token (Token::TK_ERROR);
    1363             :         }
    1364             : 
    1365          29 :         return output;
    1366             :     }
    1367             : 
    1368             : 
    1369          12 :     char *CPreprocessor::Parse (const char *iSource, size_t iLength, size_t &oLength)
    1370             :     {
    1371          24 :         Token retval = Parse (Token (Token::TK_TEXT, iSource, iLength));
    1372          12 :         if (retval.Type == Token::TK_ERROR)
    1373           2 :             return NULL;
    1374             : 
    1375          10 :         oLength = retval.Length;
    1376          10 :         retval.Allocated = 0;
    1377          10 :         return retval.Buffer;
    1378             :     }
    1379             : 
    1380           3 : } // namespace Ogre

Generated by: LCOV version 1.13