Index: ps/trunk/source/graphics/PreprocessorWrapper.cpp =================================================================== --- ps/trunk/source/graphics/PreprocessorWrapper.cpp (revision 23403) +++ ps/trunk/source/graphics/PreprocessorWrapper.cpp (revision 23404) @@ -1,99 +1,99 @@ -/* Copyright (C) 2019 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #include "precompiled.h" #include "PreprocessorWrapper.h" #include "graphics/ShaderDefines.h" #include "ps/CLogger.h" -void CPreprocessorWrapper::PyrogenesisShaderError(void* UNUSED(iData), int iLine, const char* iError, const char* iToken, size_t iTokenLen) +void CPreprocessorWrapper::PyrogenesisShaderError(int iLine, const char* iError, const Ogre::CPreprocessor::Token* iToken) { if (iToken) - LOGERROR("Preprocessor error: line %d: %s: '%s'\n", iLine, iError, std::string(iToken, iTokenLen).c_str()); + LOGERROR("Preprocessor error: line %d: %s: '%s'\n", iLine, iError, std::string(iToken->String, iToken->Length).c_str()); else LOGERROR("Preprocessor error: line %d: %s\n", iLine, iError); } CPreprocessorWrapper::CPreprocessorWrapper() { - CPreprocessor::ErrorHandler = CPreprocessorWrapper::PyrogenesisShaderError; + Ogre::CPreprocessor::ErrorHandler = CPreprocessorWrapper::PyrogenesisShaderError; } void CPreprocessorWrapper::AddDefine(const char* name, const char* value) { - m_Preprocessor.Define(name, value); + m_Preprocessor.Define(name, strlen(name), value, strlen(value)); } void CPreprocessorWrapper::AddDefines(const CShaderDefines& defines) { std::map map = defines.GetMap(); for (std::map::const_iterator it = map.begin(); it != map.end(); ++it) m_Preprocessor.Define(it->first.c_str(), it->first.length(), it->second.c_str(), it->second.length()); } bool CPreprocessorWrapper::TestConditional(const CStr& expr) { // Construct a dummy program so we can trigger the preprocessor's expression // code without modifying its public API. // Be careful that the API buggily returns a statically allocated pointer // (which we will try to free()) if the input just causes it to append a single // sequence of newlines to the output; the "\n" after the "#endif" is enough // to avoid this case. CStr input = "#if "; input += expr; input += "\n1\n#endif\n"; size_t len = 0; char* output = m_Preprocessor.Parse(input.c_str(), input.size(), len); if (!output) { LOGERROR("Failed to parse conditional expression '%s'", expr.c_str()); return false; } bool ret = (memchr(output, '1', len) != NULL); // Free output if it's not inside the source string if (!(output >= input.c_str() && output < input.c_str() + input.size())) free(output); return ret; } CStr CPreprocessorWrapper::Preprocess(const CStr& input) { size_t len = 0; char* output = m_Preprocessor.Parse(input.c_str(), input.size(), len); if (!output) { LOGERROR("Shader preprocessing failed"); return ""; } CStr ret(output, len); // Free output if it's not inside the source string if (!(output >= input.c_str() && output < input.c_str() + input.size())) free(output); return ret; } Index: ps/trunk/source/graphics/PreprocessorWrapper.h =================================================================== --- ps/trunk/source/graphics/PreprocessorWrapper.h (revision 23403) +++ ps/trunk/source/graphics/PreprocessorWrapper.h (revision 23404) @@ -1,48 +1,48 @@ -/* Copyright (C) 2019 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #ifndef INCLUDED_PREPROCESSORWRAPPER #define INCLUDED_PREPROCESSORWRAPPER #include "ps/CStr.h" #include "third_party/ogre3d_preprocessor/OgreGLSLPreprocessor.h" class CShaderDefines; /** * Convenience wrapper around CPreprocessor. */ class CPreprocessorWrapper { public: CPreprocessorWrapper(); void AddDefine(const char* name, const char* value); void AddDefines(const CShaderDefines& defines); bool TestConditional(const CStr& expr); CStr Preprocess(const CStr& input); - static void PyrogenesisShaderError(void* UNUSED(iData), int iLine, const char* iError, const char* iToken, size_t iTokenLen); + static void PyrogenesisShaderError(int iLine, const char* iError, const Ogre::CPreprocessor::Token* iToken); private: - CPreprocessor m_Preprocessor; + Ogre::CPreprocessor m_Preprocessor; }; #endif // INCLUDED_PREPROCESSORWRAPPER Index: ps/trunk/source/third_party/ogre3d_preprocessor/OgreGLSLPreprocessor.cpp =================================================================== --- ps/trunk/source/third_party/ogre3d_preprocessor/OgreGLSLPreprocessor.cpp (revision 23403) +++ ps/trunk/source/third_party/ogre3d_preprocessor/OgreGLSLPreprocessor.cpp (revision 23404) @@ -1,1315 +1,1380 @@ /* - * This source file originally came from OGRE v1.7.2 - http://www.ogre3d.org/ + * This source file originally came from OGRE v1.12.3 - http://www.ogre3d.org/ * with some tweaks as part of 0 A.D. * All changes are released under the original license, as follows: */ /* ----------------------------------------------------------------------------- This source file is part of OGRE (Object-oriented Graphics Rendering Engine) For the latest info, see http://www.ogre3d.org/ -Copyright (c) 2000-2009 Torus Knot Software Ltd +Copyright (c) 2000-2014 Torus Knot Software Ltd Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ----------------------------------------------------------------------------- */ #include "precompiled.h" +#include "lib/debug.h" #include "OgreGLSLPreprocessor.h" -// Limit max number of macro arguments to this -#define MAX_MACRO_ARGS 16 +#include +#include -//---------------------------------------------------------------------------// - -/// Return closest power of two not smaller than given number -static size_t ClosestPow2 (size_t x) -{ - if (!(x & (x - 1))) - return x; - while (x & (x + 1)) - x |= (x + 1); - return x + 1; -} - -void CPreprocessor::Token::Append (const char *iString, size_t iLength) +namespace Ogre { - Token t (Token::TK_TEXT, iString, iLength); - Append (t); -} -void CPreprocessor::Token::Append (const Token &iOther) -{ - if (!iOther.String) - return; + // Limit max number of macro arguments to this + #define MAX_MACRO_ARGS 16 - if (!String) + /// Return closest power of two not smaller than given number + static size_t ClosestPow2 (size_t x) { - String = iOther.String; - Length = iOther.Length; - Allocated = iOther.Allocated; - iOther.Allocated = 0; // !!! not quite correct but effective - return; + if (!(x & (x - 1))) + return x; + while (x & (x + 1)) + x |= (x + 1); + return x + 1; } - if (Allocated) - { - size_t new_alloc = ClosestPow2 (Length + iOther.Length); - if (new_alloc < 64) - new_alloc = 64; - if (new_alloc != Allocated) - { - Allocated = new_alloc; - Buffer = (char *)realloc (Buffer, Allocated); - } - } - else if (String + Length != iOther.String) + void CPreprocessor::Token::Append (const char *iString, size_t iLength) { - Allocated = ClosestPow2 (Length + iOther.Length); - if (Allocated < 64) - Allocated = 64; - char *newstr = (char *)malloc (Allocated); - memcpy (newstr, String, Length); - Buffer = newstr; + Token t (Token::TK_TEXT, iString, iLength); + Append (t); } - if (Allocated) - memcpy (Buffer + Length, iOther.String, iOther.Length); - Length += iOther.Length; -} + void CPreprocessor::Token::Append (const Token &iOther) + { + if (!iOther.String) + return; -bool CPreprocessor::Token::GetValue (long &oValue) const -{ - long val = 0; - size_t i = 0; + if (!String) + { + String = iOther.String; + Length = iOther.Length; + Allocated = iOther.Allocated; + iOther.Allocated = 0; // !!! not quite correct but effective + return; + } - while (isspace (String [i])) - i++; + if (Allocated) + { + size_t new_alloc = ClosestPow2 (Length + iOther.Length); + if (new_alloc < 64) + new_alloc = 64; + if (new_alloc != Allocated) + { + Allocated = new_alloc; + Buffer = (char *)realloc (Buffer, Allocated); + } + } + else if (String + Length != iOther.String) + { + Allocated = ClosestPow2 (Length + iOther.Length); + if (Allocated < 64) + Allocated = 64; + char *newstr = (char *)malloc (Allocated); + memcpy (newstr, String, Length); + Buffer = newstr; + } - long base = 10; - if (String [i] == '0') - { - if (Length > i + 1 && String [i + 1] == 'x') - base = 16, i += 2; - else - base = 8; + if (Allocated) + memcpy (Buffer + Length, iOther.String, iOther.Length); + Length += iOther.Length; } - for (; i < Length; i++) + bool CPreprocessor::Token::GetValue (long &oValue) const { - long c = long (String [i]); - if (isspace (c)) - // Possible end of number - break; + long val = 0; + size_t i = 0; - if (c >= 'a' && c <= 'z') - c -= ('a' - 'A'); + while (isspace (String [i])) + i++; - c -= '0'; - if (c < 0) - return false; + long base = 10; + if (String [i] == '0') + { + if (Length > i + 1 && String [i + 1] == 'x') + base = 16, i += 2; + else + base = 8; + } - if (c > 9) - c -= ('A' - '9' - 1); + for (; i < Length; i++) + { + int c = int (String [i]); + if (isspace (c)) + // Possible end of number + break; - if (c >= base) - return false; + if (c >= 'a' && c <= 'z') + c -= ('a' - 'A'); - val = (val * base) + c; - } + c -= '0'; + if (c < 0) + return false; - // Check that all other characters are just spaces - for (; i < Length; i++) - if (!isspace (String [i])) - return false; + if (c > 9) + c -= ('A' - '9' - 1); - oValue = val; - return true; -} + if (c >= base) + return false; -void CPreprocessor::Token::SetValue (long iValue) -{ - char tmp [21]; - int len = snprintf (tmp, sizeof (tmp), "%ld", iValue); - Length = 0; - Append (tmp, len); - Type = TK_NUMBER; -} + val = (val * base) + c; + } -void CPreprocessor::Token::AppendNL (int iCount) -{ - static const char newlines [8] = - { '\n', '\n', '\n', '\n', '\n', '\n', '\n', '\n' }; + // Check that all other characters are just spaces + for (; i < Length; i++) + if (!isspace (String [i])) + return false; - while (iCount > 8) - { - Append (newlines, 8); - iCount -= 8; + oValue = val; + return true; } - if (iCount > 0) - Append (newlines, iCount); -} -int CPreprocessor::Token::CountNL () -{ - if (Type == TK_EOS || Type == TK_ERROR) - return 0; - const char *s = String; - int l = Length; - int c = 0; - while (l > 0) - { - const char *n = (const char *)memchr (s, '\n', l); - if (!n) - return c; - c++; - l -= (n - s + 1); - s = n + 1; + void CPreprocessor::Token::SetValue (long iValue) + { + char tmp [21]; + int len = snprintf (tmp, sizeof (tmp), "%ld", iValue); + Length = 0; + Append (tmp, len); + Type = TK_NUMBER; } - return c; -} -//---------------------------------------------------------------------------// -CPreprocessor::Token CPreprocessor::Macro::Expand ( - int iNumArgs, CPreprocessor::Token *iArgs, Macro *iMacros) -{ - Expanding = true; - - CPreprocessor cpp; - cpp.MacroList = iMacros; - - // Define a new macro for every argument - int i; - for (i = 0; i < iNumArgs; i++) - cpp.Define (Args [i].String, Args [i].Length, - iArgs [i].String, iArgs [i].Length); - // The rest arguments are empty - for (; i < NumArgs; i++) - cpp.Define (Args [i].String, Args [i].Length, "", 0); - - // Now run the macro expansion through the supplimentary preprocessor - Token xt = cpp.Parse (Value); + void CPreprocessor::Token::AppendNL (int iCount) + { + static const char newlines [8] = + { '\n', '\n', '\n', '\n', '\n', '\n', '\n', '\n' }; - Expanding = false; + while (iCount > 8) + { + Append (newlines, 8); + iCount -= 8; + } + if (iCount > 0) + Append (newlines, iCount); + } - // Remove the extra macros we have defined - for (int j = NumArgs - 1; j >= 0; j--) - cpp.Undef (Args [j].String, Args [j].Length); - cpp.MacroList = NULL; + int CPreprocessor::Token::CountNL () + { + if (Type == TK_EOS || Type == TK_ERROR) + return 0; - return xt; -} + const char *s = String; + size_t l = Length; + int c = 0; + while (l > 0) + { + const char *n = (const char *)memchr (s, '\n', l); + if (!n) + return c; + c++; + l -= (n - s + 1); + s = n + 1; + } + return c; + } -//---------------------------------------------------------------------------// + CPreprocessor::Token CPreprocessor::Macro::Expand(const std::vector& iArgs, + std::forward_list& iMacros) + { + Expanding = true; -static void DefaultError (void *iData, int iLine, const char *iError, - const char *iToken, size_t iTokenLen) -{ - (void)iData; - char line [1000]; - if (iToken) - snprintf (line, sizeof (line), "line %d: %s: `%.*s'\n", - iLine, iError, int (iTokenLen), iToken); - else - snprintf (line, sizeof (line), "line %d: %s\n", iLine, iError); -} + CPreprocessor cpp; + std::swap(cpp.MacroList, iMacros); -//---------------------------------------------------------------------------// + // Define a new macro for every argument + size_t i; + for (i = 0; i < iArgs.size(); i++) + cpp.Define (Args [i].String, Args [i].Length, + iArgs [i].String, iArgs [i].Length); + // The rest arguments are empty + for (; i < Args.size(); i++) + cpp.Define (Args [i].String, Args [i].Length, "", 0); -CPreprocessor::ErrorHandlerFunc CPreprocessor::ErrorHandler = DefaultError; + // Now run the macro expansion through the supplimentary preprocessor + Token xt = cpp.Parse (Value); -CPreprocessor::CPreprocessor (const Token &iToken, int iLine) : MacroList (NULL) -{ - Source = iToken.String; - SourceEnd = iToken.String + iToken.Length; - EnableOutput = 1; - Line = iLine; - BOL = true; -} + Expanding = false; -CPreprocessor::~CPreprocessor () -{ - delete MacroList; -} + // Remove the extra macros we have defined + for (int j = Args.size() - 1; j >= 0; j--) + cpp.Undef (Args [j].String, Args [j].Length); -void CPreprocessor::Error (int iLine, const char *iError, const Token *iToken) -{ - if (iToken) - ErrorHandler (ErrorData, iLine, iError, iToken->String, iToken->Length); - else - ErrorHandler (ErrorData, iLine, iError, NULL, 0); -} + std::swap(cpp.MacroList, iMacros); -CPreprocessor::Token CPreprocessor::GetToken (bool iExpand) -{ - if (Source >= SourceEnd) - return Token (Token::TK_EOS); + return xt; + } - const char *begin = Source; - char c = *Source++; + void CPreprocessor::Error(int iLine, const char *iError, const Token *iToken) + { + char line[1000]; + if (iToken) + snprintf(line, sizeof(line), "line %d: %s: `%.*s'\n", + iLine, iError, int(iToken->Length), iToken->String); + else + snprintf(line, sizeof(line), "line %d: %s\n", iLine, iError); + } + CPreprocessor::ErrorHandlerFunc CPreprocessor::ErrorHandler = CPreprocessor::Error; - if (c == '\n' || (c == '\r' && *Source == '\n')) + CPreprocessor::CPreprocessor (const Token &iToken, int iLine) { - Line++; + Source = iToken.String; + SourceEnd = iToken.String + iToken.Length; + EnableOutput = 1; + EnableElif = 0; + Line = iLine; BOL = true; - if (c == '\r') - Source++; - return Token (Token::TK_NEWLINE, begin, Source - begin); } - else if (isspace (c)) - { - while (Source < SourceEnd && - *Source != '\r' && - *Source != '\n' && - isspace (*Source)) - Source++; - return Token (Token::TK_WHITESPACE, begin, Source - begin); - } - else if (isdigit (c)) + CPreprocessor::~CPreprocessor() {} + + CPreprocessor::Token CPreprocessor::GetToken (bool iExpand) { - BOL = false; - if (c == '0' && Source < SourceEnd && Source [0] == 'x') // hex numbers + if (Source >= SourceEnd) + return Token (Token::TK_EOS); + + const char *begin = Source; + char c = *Source++; + + + if (c == '\n' || (c == '\r' && *Source == '\n')) { - Source++; - while (Source < SourceEnd && isxdigit (*Source)) + Line++; + BOL = true; + if (c == '\r') Source++; + return Token (Token::TK_NEWLINE, begin, Source - begin); } - else - while (Source < SourceEnd && isdigit (*Source)) + else if (isspace (c)) + { + while (Source < SourceEnd && + *Source != '\r' && + *Source != '\n' && + isspace (*Source)) Source++; - return Token (Token::TK_NUMBER, begin, Source - begin); - } - else if (c == '_' || isalnum (c)) - { - BOL = false; - while (Source < SourceEnd && (*Source == '_' || isalnum (*Source))) - Source++; - Token t (Token::TK_KEYWORD, begin, Source - begin); - if (iExpand) - t = ExpandMacro (t); - return t; - } - else if (c == '"' || c == '\'') - { - BOL = false; - while (Source < SourceEnd && *Source != c) + + return Token (Token::TK_WHITESPACE, begin, Source - begin); + } + else if (isdigit (c)) { - if (*Source == '\\') + BOL = false; + if (c == '0' && Source < SourceEnd && Source [0] == 'x') // hex numbers { Source++; - if (Source >= SourceEnd) - break; + while (Source < SourceEnd && isxdigit (*Source)) + Source++; } - if (*Source == '\n') - Line++; - Source++; + else + while (Source < SourceEnd && isdigit (*Source)) + Source++; + return Token (Token::TK_NUMBER, begin, Source - begin); + } + else if (c == '_' || isalnum (c)) + { + BOL = false; + while (Source < SourceEnd && (*Source == '_' || isalnum (*Source))) + Source++; + Token t (Token::TK_KEYWORD, begin, Source - begin); + if (iExpand) + t = ExpandMacro (t); + return t; + } + else if (c == '"' || c == '\'') + { + BOL = false; + while (Source < SourceEnd && *Source != c) + { + if (*Source == '\\') + { + Source++; + if (Source >= SourceEnd) + break; + } + if (*Source == '\n') + Line++; + Source++; + } + if (Source < SourceEnd) + Source++; + return Token (Token::TK_STRING, begin, Source - begin); } - if (Source < SourceEnd) + else if (c == '/' && *Source == '/') + { + BOL = false; Source++; - return Token (Token::TK_STRING, begin, Source - begin); - } - else if (c == '/' && *Source == '/') - { - BOL = false; - Source++; - while (Source < SourceEnd && *Source != '\r' && *Source != '\n') + while (Source < SourceEnd && *Source != '\r' && *Source != '\n') + Source++; + return Token (Token::TK_LINECOMMENT, begin, Source - begin); + } + else if (c == '/' && *Source == '*') + { + BOL = false; Source++; - return Token (Token::TK_LINECOMMENT, begin, Source - begin); - } - else if (c == '/' && *Source == '*') - { - BOL = false; - Source++; - while (Source < SourceEnd && (Source [0] != '*' || Source [1] != '/')) + while (Source < SourceEnd && (Source [0] != '*' || Source [1] != '/')) + { + if (*Source == '\n') + Line++; + Source++; + } + if (Source < SourceEnd && *Source == '*') + Source++; + if (Source < SourceEnd && *Source == '/') + Source++; + return Token (Token::TK_COMMENT, begin, Source - begin); + } + else if (c == '#' && BOL) + { + // Skip all whitespaces after '#' + while (Source < SourceEnd && isspace (*Source)) + Source++; + while (Source < SourceEnd && !isspace (*Source)) + Source++; + return Token (Token::TK_DIRECTIVE, begin, Source - begin); + } + else if (c == '\\' && Source < SourceEnd && (*Source == '\r' || *Source == '\n')) { + // Treat backslash-newline as a whole token + if (*Source == '\r') + Source++; if (*Source == '\n') - Line++; - Source++; + Source++; + Line++; + BOL = true; + return Token (Token::TK_LINECONT, begin, Source - begin); + } + else + { + BOL = false; + // Handle double-char operators here + if (c == '>' && (*Source == '>' || *Source == '=')) + Source++; + else if (c == '<' && (*Source == '<' || *Source == '=')) + Source++; + else if (c == '!' && *Source == '=') + Source++; + else if (c == '=' && *Source == '=') + Source++; + else if ((c == '|' || c == '&' || c == '^') && *Source == c) + Source++; + return Token (Token::TK_PUNCTUATION, begin, Source - begin); } - if (Source < SourceEnd && *Source == '*') - Source++; - if (Source < SourceEnd && *Source == '/') - Source++; - return Token (Token::TK_COMMENT, begin, Source - begin); - } - else if (c == '#' && BOL) - { - // Skip all whitespaces after '#' - while (Source < SourceEnd && isspace (*Source)) - Source++; - while (Source < SourceEnd && !isspace (*Source)) - Source++; - return Token (Token::TK_DIRECTIVE, begin, Source - begin); - } - else if (c == '\\' && Source < SourceEnd && (*Source == '\r' || *Source == '\n')) - { - // Treat backslash-newline as a whole token - if (*Source == '\r') - Source++; - if (*Source == '\n') - Source++; - Line++; - BOL = true; - return Token (Token::TK_LINECONT, begin, Source - begin); } - else + + + CPreprocessor::Macro *CPreprocessor::IsDefined (const Token &iToken) { - BOL = false; - // Handle double-char operators here - if (c == '>' && (*Source == '>' || *Source == '=')) - Source++; - else if (c == '<' && (*Source == '<' || *Source == '=')) - Source++; - else if (c == '!' && *Source == '=') - Source++; - else if (c == '=' && *Source == '=') - Source++; - else if ((c == '|' || c == '&' || c == '^') && *Source == c) - Source++; - return Token (Token::TK_PUNCTUATION, begin, Source - begin); - } -} + for (Macro& cur : MacroList) + if (cur.Name == iToken) + return &cur; -CPreprocessor::Macro *CPreprocessor::IsDefined (const Token &iToken) -{ - for (Macro *cur = MacroList; cur; cur = cur->Next) - if (cur->Name == iToken) - return cur; + return NULL; + } - return NULL; -} -CPreprocessor::Token CPreprocessor::ExpandMacro (const Token &iToken) -{ - Macro *cur = IsDefined (iToken); - if (cur && !cur->Expanding) + CPreprocessor::Token CPreprocessor::ExpandMacro (const Token &iToken) { - Token *args = NULL; - int nargs = 0; - int old_line = Line; - - if (cur->NumArgs != 0) + Macro *cur = IsDefined (iToken); + if (cur && !cur->Expanding) { - Token t = GetArguments (nargs, args, cur->ExpandFunc ? false : true); - if (t.Type == Token::TK_ERROR) + std::vector args; + int old_line = Line; + + if (!cur->Args.empty()) { - delete [] args; - return t; + Token t = GetArguments (args, cur->ExpandFunc ? false : true, false); + if (t.Type == Token::TK_ERROR) + { + return t; + } + + // Put the token back into the source pool; we'll handle it later + if (t.String) + { + // Returned token should never be allocated on heap + ENSURE(t.Allocated == 0); + Source = t.String; + Line -= t.CountNL (); + } } - // Put the token back into the source pool; we'll handle it later - if (t.String) + if (args.size() > cur->Args.size()) { - // Returned token should never be allocated on heap - ENSURE (t.Allocated == 0); - Source = t.String; - Line -= t.CountNL (); + char tmp [60]; + snprintf (tmp, sizeof (tmp), "Macro `%.*s' passed %zu arguments, but takes just %zu", + int (cur->Name.Length), cur->Name.String, + args.size(), cur->Args.size()); + ErrorHandler (old_line, tmp, nullptr); + return Token (Token::TK_ERROR); } - } - if (nargs > cur->NumArgs) - { - char tmp [60]; - snprintf (tmp, sizeof (tmp), "Macro `%.*s' passed %d arguments, but takes just %d", - int (cur->Name.Length), cur->Name.String, - nargs, cur->NumArgs); - Error (old_line, tmp); - return Token (Token::TK_ERROR); - } - - Token t = cur->ExpandFunc ? - cur->ExpandFunc (this, nargs, args) : - cur->Expand (nargs, args, MacroList); - t.AppendNL (Line - old_line); + Token t = cur->ExpandFunc ? + cur->ExpandFunc (this, args) : + cur->Expand (args, MacroList); + t.AppendNL (Line - old_line); - delete [] args; + return t; + } - return t; + return iToken; } - return iToken; -} - -/** - * Operator priority: - * 0: Whole expression - * 1: '(' ')' - * 2: || - * 3: && - * 4: | - * 5: ^ - * 6: & - * 7: '==' '!=' - * 8: '<' '<=' '>' '>=' - * 9: '<<' '>>' - * 10: '+' '-' - * 11: '*' '/' '%' - * 12: unary '+' '-' '!' '~' - */ -CPreprocessor::Token CPreprocessor::GetExpression ( - Token &oResult, int iLine, int iOpPriority) -{ - char tmp [40]; - do - { - oResult = GetToken (true); - } while (oResult.Type == Token::TK_WHITESPACE || - oResult.Type == Token::TK_NEWLINE || - oResult.Type == Token::TK_COMMENT || - oResult.Type == Token::TK_LINECOMMENT || - oResult.Type == Token::TK_LINECONT); + /** + * Operator priority: + * 0: Whole expression + * 1: '(' ')' + * 2: || + * 3: && + * 4: | + * 5: ^ + * 6: & + * 7: '==' '!=' + * 8: '<' '<=' '>' '>=' + * 9: '<<' '>>' + * 10: '+' '-' + * 11: '*' '/' '%' + * 12: unary '+' '-' '!' '~' + */ + CPreprocessor::Token CPreprocessor::GetExpression ( + Token &oResult, int iLine, int iOpPriority) + { + char tmp [40]; + + do + { + oResult = GetToken (true); + } while (oResult.Type == Token::TK_WHITESPACE || + oResult.Type == Token::TK_NEWLINE || + oResult.Type == Token::TK_COMMENT || + oResult.Type == Token::TK_LINECOMMENT || + oResult.Type == Token::TK_LINECONT); - Token op (Token::TK_WHITESPACE, "", 0); + Token op (Token::TK_WHITESPACE, "", 0); - // Handle unary operators here - if (oResult.Type == Token::TK_PUNCTUATION && oResult.Length == 1) - { - if (strchr ("+-!~", oResult.String [0])) + // Handle unary operators here + if (oResult.Type == Token::TK_PUNCTUATION && oResult.Length == 1) { - char uop = oResult.String [0]; - op = GetExpression (oResult, iLine, 12); - long val; - if (!GetValue (oResult, val, iLine)) + if (strchr ("+-!~", oResult.String [0])) { - snprintf (tmp, sizeof (tmp), "Unary '%c' not applicable", uop); - Error (iLine, tmp, &oResult); - return Token (Token::TK_ERROR); - } + char uop = oResult.String [0]; + op = GetExpression (oResult, iLine, 12); + long val; + if (!GetValue (oResult, val, iLine)) + { + snprintf (tmp, sizeof (tmp), "Unary '%c' not applicable", uop); + ErrorHandler(iLine, tmp, &oResult); + return Token (Token::TK_ERROR); + } - if (uop == '-') - oResult.SetValue (-val); - else if (uop == '!') - oResult.SetValue (!val); - else if (uop == '~') - oResult.SetValue (~val); - } - else if (oResult.String [0] == '(') - { - op = GetExpression (oResult, iLine, 1); - if (op.Type == Token::TK_ERROR) - return op; - if (op.Type == Token::TK_EOS) - { - Error (iLine, "Unclosed parenthesis in #if expression"); - return Token (Token::TK_ERROR); + if (uop == '-') + oResult.SetValue (-val); + else if (uop == '!') + oResult.SetValue (!val); + else if (uop == '~') + oResult.SetValue (~val); } + else if (oResult.String [0] == '(') + { + op = GetExpression (oResult, iLine, 1); + if (op.Type == Token::TK_ERROR) + return op; + if (op.Type == Token::TK_EOS) + { + ErrorHandler(iLine, "Unclosed parenthesis in #if expression", nullptr); + return Token (Token::TK_ERROR); + } - ENSURE (op.Type == Token::TK_PUNCTUATION && - op.Length == 1 && - op.String [0] == ')'); - op = GetToken (true); + ENSURE(op.Type == Token::TK_PUNCTUATION && + op.Length == 1 && + op.String [0] == ')'); + op = GetToken (true); + } } - } - while (op.Type == Token::TK_WHITESPACE || - op.Type == Token::TK_NEWLINE || - op.Type == Token::TK_COMMENT || - op.Type == Token::TK_LINECOMMENT || - op.Type == Token::TK_LINECONT) - op = GetToken (true); + while (op.Type == Token::TK_WHITESPACE || + op.Type == Token::TK_NEWLINE || + op.Type == Token::TK_COMMENT || + op.Type == Token::TK_LINECOMMENT || + op.Type == Token::TK_LINECONT) + op = GetToken (true); - while (true) - { - if (op.Type != Token::TK_PUNCTUATION) - return op; + while (true) + { + if (op.Type != Token::TK_PUNCTUATION) + return op; - int prio = 0; - if (op.Length == 1) - switch (op.String [0]) - { + int prio = 0; + if (op.Length == 1) + switch (op.String [0]) + { case ')': return op; case '|': prio = 4; break; case '^': prio = 5; break; case '&': prio = 6; break; case '<': case '>': prio = 8; break; case '+': case '-': prio = 10; break; case '*': case '/': case '%': prio = 11; break; - } - else if (op.Length == 2) - switch (op.String [0]) - { + } + else if (op.Length == 2) + switch (op.String [0]) + { case '|': if (op.String [1] == '|') prio = 2; break; case '&': if (op.String [1] == '&') prio = 3; break; case '=': if (op.String [1] == '=') prio = 7; break; case '!': if (op.String [1] == '=') prio = 7; break; case '<': if (op.String [1] == '=') prio = 8; else if (op.String [1] == '<') prio = 9; break; case '>': if (op.String [1] == '=') prio = 8; else if (op.String [1] == '>') prio = 9; break; - } + } - if (!prio) - { - Error (iLine, "Expecting operator, got", &op); - return Token (Token::TK_ERROR); - } + if (!prio) + { + ErrorHandler(iLine, "Expecting operator, got", &op); + return Token (Token::TK_ERROR); + } - if (iOpPriority >= prio) - return op; + if (iOpPriority >= prio) + return op; - Token rop; - Token nextop = GetExpression (rop, iLine, prio); - long vlop, vrop; - if (!GetValue (oResult, vlop, iLine)) - { - snprintf (tmp, sizeof (tmp), "Left operand of '%.*s' is not a number", - int (op.Length), op.String); - Error (iLine, tmp, &oResult); - return Token (Token::TK_ERROR); - } - if (!GetValue (rop, vrop, iLine)) - { - snprintf (tmp, sizeof (tmp), "Right operand of '%.*s' is not a number", - int (op.Length), op.String); - Error (iLine, tmp, &rop); - return Token (Token::TK_ERROR); - } + Token rop; + Token nextop = GetExpression (rop, iLine, prio); + long vlop, vrop; + if (!GetValue (oResult, vlop, iLine)) + { + snprintf (tmp, sizeof (tmp), "Left operand of '%.*s' is not a number", + int (op.Length), op.String); + ErrorHandler(iLine, tmp, &oResult); + return Token (Token::TK_ERROR); + } + if (!GetValue (rop, vrop, iLine)) + { + snprintf (tmp, sizeof (tmp), "Right operand of '%.*s' is not a number", + int (op.Length), op.String); + ErrorHandler(iLine, tmp, &rop); + return Token (Token::TK_ERROR); + } - switch (op.String [0]) - { + switch (op.String [0]) + { case '|': if (prio == 2) oResult.SetValue (vlop || vrop); else oResult.SetValue (vlop | vrop); break; case '&': if (prio == 3) oResult.SetValue (vlop && vrop); else oResult.SetValue (vlop & vrop); break; case '<': if (op.Length == 1) oResult.SetValue (vlop < vrop); else if (prio == 8) oResult.SetValue (vlop <= vrop); else if (prio == 9) oResult.SetValue (vlop << vrop); break; case '>': if (op.Length == 1) oResult.SetValue (vlop > vrop); else if (prio == 8) oResult.SetValue (vlop >= vrop); else if (prio == 9) oResult.SetValue (vlop >> vrop); break; case '^': oResult.SetValue (vlop ^ vrop); break; case '!': oResult.SetValue (vlop != vrop); break; case '=': oResult.SetValue (vlop == vrop); break; case '+': oResult.SetValue (vlop + vrop); break; case '-': oResult.SetValue (vlop - vrop); break; case '*': oResult.SetValue (vlop * vrop); break; case '/': case '%': if (vrop == 0) { - Error (iLine, "Division by zero"); + ErrorHandler(iLine, "Division by zero", nullptr); return Token (Token::TK_ERROR); } if (op.String [0] == '/') oResult.SetValue (vlop / vrop); else oResult.SetValue (vlop % vrop); break; - } + } - op = nextop; + op = nextop; + } } -} -bool CPreprocessor::GetValue (const Token &iToken, long &oValue, int iLine) -{ - Token r; - const Token *vt = &iToken; - if ((vt->Type == Token::TK_KEYWORD || - vt->Type == Token::TK_TEXT || - vt->Type == Token::TK_NUMBER) && - !vt->String) + bool CPreprocessor::GetValue (const Token &iToken, long &oValue, int iLine) { - Error (iLine, "Trying to evaluate an empty expression"); - return false; - } + Token r; + const Token *vt = &iToken; - if (vt->Type == Token::TK_TEXT) - { - CPreprocessor cpp (iToken, iLine); - cpp.MacroList = MacroList; + if ((vt->Type == Token::TK_KEYWORD || + vt->Type == Token::TK_TEXT || + vt->Type == Token::TK_NUMBER) && + !vt->String) + { + ErrorHandler (iLine, "Trying to evaluate an empty expression", nullptr); + return false; + } - Token t; - t = cpp.GetExpression (r, iLine); + if (vt->Type == Token::TK_TEXT) + { + CPreprocessor cpp (iToken, iLine); + std::swap(cpp.MacroList, MacroList); - cpp.MacroList = NULL; + Token t; + t = cpp.GetExpression (r, iLine); - if (t.Type == Token::TK_ERROR) - return false; + std::swap(cpp.MacroList, MacroList); - if (t.Type != Token::TK_EOS) - { - Error (iLine, "Garbage after expression", &t); - return false; - } + if (t.Type == Token::TK_ERROR) + return false; - vt = &r; - } + if (t.Type != Token::TK_EOS) + { + ErrorHandler(iLine, "Garbage after expression", &t); + return false; + } - switch (vt->Type) - { + vt = &r; + } + + Macro *m; + switch (vt->Type) + { case Token::TK_EOS: case Token::TK_ERROR: return false; case Token::TK_KEYWORD: - { // Try to expand the macro - Macro *m = IsDefined (*vt); - if (m != NULL && !m->Expanding) + m = IsDefined(*vt); + if (m != nullptr && !m->Expanding) { Token x = ExpandMacro (*vt); m->Expanding = true; bool rc = GetValue (x, oValue, iLine); m->Expanding = false; return rc; } // Undefined macro, "expand" to 0 (mimic cpp behaviour) oValue = 0; break; - } + case Token::TK_TEXT: case Token::TK_NUMBER: if (!vt->GetValue (oValue)) { - Error (iLine, "Not a numeric expression", vt); + ErrorHandler(iLine, "Not a numeric expression", vt); return false; } break; default: - Error (iLine, "Unexpected token", vt); + ErrorHandler(iLine, "Unexpected token", vt); return false; + } + + return true; } - return true; -} -CPreprocessor::Token CPreprocessor::GetArgument (Token &oArg, bool iExpand) -{ - do + CPreprocessor::Token CPreprocessor::GetArgument (Token &oArg, bool iExpand, + bool shouldAppendArg) { - oArg = GetToken (iExpand); - } while (oArg.Type == Token::TK_WHITESPACE || - oArg.Type == Token::TK_NEWLINE || - oArg.Type == Token::TK_COMMENT || - oArg.Type == Token::TK_LINECOMMENT || - oArg.Type == Token::TK_LINECONT); - - if (!iExpand) - { - if (oArg.Type == Token::TK_EOS) - return oArg; - else if (oArg.Type == Token::TK_PUNCTUATION && - (oArg.String [0] == ',' || - oArg.String [0] == ')')) + do { - Token t = oArg; - oArg = Token (Token::TK_TEXT, "", 0); - return t; - } - else if (oArg.Type != Token::TK_KEYWORD) + oArg = GetToken (iExpand); + } while (oArg.Type == Token::TK_WHITESPACE || + oArg.Type == Token::TK_NEWLINE || + oArg.Type == Token::TK_COMMENT || + oArg.Type == Token::TK_LINECOMMENT || + oArg.Type == Token::TK_LINECONT); + + if (!iExpand) { - Error (Line, "Unexpected token", &oArg); - return Token (Token::TK_ERROR); + if (oArg.Type == Token::TK_EOS) + return oArg; + else if (oArg.Type == Token::TK_PUNCTUATION && + (oArg.String [0] == ',' || + oArg.String [0] == ')')) + { + Token t = oArg; + oArg = Token (Token::TK_TEXT, "", 0); + return t; + } + else if (oArg.Type != Token::TK_KEYWORD) + { + ErrorHandler(Line, "Unexpected token", &oArg); + return Token (Token::TK_ERROR); + } } - } - unsigned int len = oArg.Length; - while (true) - { - Token t = GetToken (iExpand); - switch (t.Type) + size_t braceCount = 0; + + if( oArg.Type == Token::TK_PUNCTUATION && oArg.String[0] == '(' ) + ++braceCount; + + size_t len = oArg.Length; + while (true) { + Token t = GetToken (iExpand); + switch (t.Type) + { case Token::TK_EOS: - Error (Line, "Unfinished list of arguments"); + ErrorHandler(Line, "Unfinished list of arguments", nullptr); FALLTHROUGH; case Token::TK_ERROR: return Token (Token::TK_ERROR); case Token::TK_PUNCTUATION: - if (t.String [0] == ',' || - t.String [0] == ')') + if( t.String [0] == '(' ) { - // Trim whitespaces at the end - oArg.Length = len; - return t; + ++braceCount; + } + else if( !braceCount ) + { + if (t.String [0] == ',' || + t.String [0] == ')') + { + // Trim whitespaces at the end + oArg.Length = len; + + //Append "__arg_" to all macro arguments, otherwise if user does: + // #define mad( a, b, c ) fma( a, b, c ) + // mad( x.s, y, a ); + //It will be translated to: + // fma( x.s, y, x.s ); + //instead of: + // fma( x.s, y, a ); + //This does not fix the problem by the root, but + //typing "__arg_" by the user is extremely rare. + if( shouldAppendArg ) + oArg.Append( "__arg_", 6 ); + return t; + } + } + else + { + if( t.String [0] == ')' ) + --braceCount; } break; case Token::TK_LINECONT: case Token::TK_COMMENT: case Token::TK_LINECOMMENT: case Token::TK_NEWLINE: // ignore these tokens continue; default: break; - } + } - if (!iExpand && t.Type != Token::TK_WHITESPACE) - { - Error (Line, "Unexpected token", &oArg); - return Token (Token::TK_ERROR); - } + if (!iExpand && t.Type != Token::TK_WHITESPACE) + { + ErrorHandler(Line, "Unexpected token", &oArg); + return Token (Token::TK_ERROR); + } - oArg.Append (t); + oArg.Append (t); - if (t.Type != Token::TK_WHITESPACE) - len = oArg.Length; + if (t.Type != Token::TK_WHITESPACE) + len = oArg.Length; + } } -} - -CPreprocessor::Token CPreprocessor::GetArguments (int &oNumArgs, Token *&oArgs, - bool iExpand) -{ - Token args [MAX_MACRO_ARGS]; - int nargs = 0; - // Suppose we'll leave by the wrong path - oNumArgs = 0; - oArgs = NULL; - Token t; - do + CPreprocessor::Token CPreprocessor::GetArguments (std::vector& oArgs, + bool iExpand, bool shouldAppendArg) { - t = GetToken (iExpand); - } while (t.Type == Token::TK_WHITESPACE || - t.Type == Token::TK_COMMENT || - t.Type == Token::TK_LINECOMMENT); + Token args [MAX_MACRO_ARGS]; + int nargs = 0; - if (t.Type != Token::TK_PUNCTUATION || t.String [0] != '(') - { - oNumArgs = 0; - oArgs = NULL; - return t; - } + // Suppose we'll leave by the wrong path + oArgs.clear(); - while (true) - { - if (nargs == MAX_MACRO_ARGS) + bool isFirstTokenParsed = false; + bool isFirstTokenNotAnOpenBrace = false; + + Token t; + do { - Error (Line, "Too many arguments to macro"); - return Token (Token::TK_ERROR); - } + t = GetToken (iExpand); - t = GetArgument (args [nargs++], iExpand); + if( !isFirstTokenParsed && + (t.Type != Token::TK_PUNCTUATION || t.String [0] != '(') ) + { + isFirstTokenNotAnOpenBrace = true; + } + isFirstTokenParsed = true; + } while (t.Type == Token::TK_WHITESPACE || + t.Type == Token::TK_COMMENT || + t.Type == Token::TK_LINECOMMENT); - switch (t.Type) + if( isFirstTokenNotAnOpenBrace ) { + oArgs.clear(); + return t; + } + + while (true) + { + if (nargs == MAX_MACRO_ARGS) + { + ErrorHandler(Line, "Too many arguments to macro", nullptr); + return Token (Token::TK_ERROR); + } + + t = GetArgument (args [nargs++], iExpand, shouldAppendArg); + + switch (t.Type) + { case Token::TK_EOS: - Error (Line, "Unfinished list of arguments"); + ErrorHandler(Line, "Unfinished list of arguments", nullptr); FALLTHROUGH; case Token::TK_ERROR: return Token (Token::TK_ERROR); case Token::TK_PUNCTUATION: if (t.String [0] == ')') { t = GetToken (iExpand); goto Done; } // otherwise we've got a ',' break; default: - Error (Line, "Unexpected token", &t); + ErrorHandler(Line, "Unexpected token", &t); break; + } } - } -Done: - oNumArgs = nargs; - oArgs = new Token [nargs]; - for (int i = 0; i < nargs; i++) - oArgs [i] = args [i]; - return t; -} + Done: + oArgs.insert(oArgs.begin(), args, args + nargs); + return t; + } -bool CPreprocessor::HandleDefine (Token &iBody, int iLine) -{ - // Create an additional preprocessor to process macro body - CPreprocessor cpp (iBody, iLine); - Token t = cpp.GetToken (false); - if (t.Type != Token::TK_KEYWORD) + bool CPreprocessor::HandleDefine (Token &iBody, int iLine) { - Error (iLine, "Macro name expected after #define"); - return false; - } + // Create an additional preprocessor to process macro body + CPreprocessor cpp (iBody, iLine); - bool output_enabled = ((EnableOutput & (EnableOutput + 1)) == 0); - if (!output_enabled) - return true; - - Macro *m = new Macro (t); - m->Body = iBody; - t = cpp.GetArguments (m->NumArgs, m->Args, false); - while (t.Type == Token::TK_WHITESPACE) - t = cpp.GetToken (false); + Token t = cpp.GetToken (false); + if (t.Type != Token::TK_KEYWORD) + { + ErrorHandler(iLine, "Macro name expected after #define", nullptr); + return false; + } - switch (t.Type) - { + Macro m(t); + m.Body = iBody; + t = cpp.GetArguments (m.Args, false, true); + while (t.Type == Token::TK_WHITESPACE) + t = cpp.GetToken (false); + + switch (t.Type) + { case Token::TK_NEWLINE: case Token::TK_EOS: // Assign "" to token t = Token (Token::TK_TEXT, "", 0); break; case Token::TK_ERROR: - delete m; return false; default: t.Type = Token::TK_TEXT; - ENSURE (t.String + t.Length == cpp.Source); + ENSURE(t.String + t.Length == cpp.Source); t.Length = cpp.SourceEnd - t.String; break; - } + } - m->Value = t; - m->Next = MacroList; - MacroList = m; - return true; -} + if( !m.Args.empty() ) + { + CPreprocessor cpp2; -bool CPreprocessor::HandleUnDef (Token &iBody, int iLine) -{ - CPreprocessor cpp (iBody, iLine); + //We need to convert: + // #define mad( a__arg_, b__arg_, c__arg_ ) fma( a, b, c ) + //into: + // #define mad( a__arg_, b__arg_, c__arg_ ) fma( a__arg_, b__arg_, c__arg_ ) + for (const Token& arg : m.Args) + { + cpp2.Define(arg.String, arg.Length - 6, arg.String, arg.Length); + } - Token t = cpp.GetToken (false); + // Now run the macro expansion through the supplimentary preprocessor + Token xt = cpp2.Parse( t ); + t = xt; + } - if (t.Type != Token::TK_KEYWORD) - { - Error (iLine, "Expecting a macro name after #undef, got", &t); - return false; + m.Value = t; + MacroList.push_front(std::move(m)); + return true; } - // Don't barf if macro does not exist - standard C behaviour - Undef (t.String, t.Length); - do + bool CPreprocessor::HandleUnDef (Token &iBody, int iLine) { - t = cpp.GetToken (false); - } while (t.Type == Token::TK_WHITESPACE || - t.Type == Token::TK_COMMENT || - t.Type == Token::TK_LINECOMMENT); + CPreprocessor cpp (iBody, iLine); - if (t.Type != Token::TK_EOS) - Error (iLine, "Warning: Ignoring garbage after directive", &t); + Token t = cpp.GetToken (false); - return true; -} + if (t.Type != Token::TK_KEYWORD) + { + ErrorHandler(iLine, "Expecting a macro name after #undef, got", &t); + return false; + } -bool CPreprocessor::HandleIfDef (Token &iBody, int iLine) -{ - if (EnableOutput & (1 << 31)) - { - Error (iLine, "Too many embedded #if directives"); - return false; + // Don't barf if macro does not exist - standard C behaviour + Undef (t.String, t.Length); + + do + { + t = cpp.GetToken (false); + } while (t.Type == Token::TK_WHITESPACE || + t.Type == Token::TK_COMMENT || + t.Type == Token::TK_LINECOMMENT); + + if (t.Type != Token::TK_EOS) + ErrorHandler(iLine, "Warning: Ignoring garbage after directive", &t); + + return true; } - CPreprocessor cpp (iBody, iLine); + bool CPreprocessor::HandleIf(bool val, int iLine) + { + if (EnableOutput & (1 << 31)) + { + ErrorHandler(iLine, "Too many embedded #if directives", nullptr); + return false; + } + + EnableElif <<= 1; + EnableOutput <<= 1; + if (val) + EnableOutput |= 1; + else + EnableElif |= 1; - Token t = cpp.GetToken (false); + return true; + } - if (t.Type != Token::TK_KEYWORD) + bool CPreprocessor::HandleIfDef (Token &iBody, int iLine) { - Error (iLine, "Expecting a macro name after #ifdef, got", &t); - return false; + CPreprocessor cpp (iBody, iLine); + + Token t = cpp.GetToken (false); + + if (t.Type != Token::TK_KEYWORD) + { + ErrorHandler(iLine, "Expecting a macro name after #ifdef, got", &t); + return false; + } + + if (!HandleIf(IsDefined(t) != nullptr, iLine)) + return false; + + do + { + t = cpp.GetToken (false); + } while (t.Type == Token::TK_WHITESPACE || + t.Type == Token::TK_COMMENT || + t.Type == Token::TK_LINECOMMENT); + + if (t.Type != Token::TK_EOS) + ErrorHandler(iLine, "Warning: Ignoring garbage after directive", &t); + + return true; } - EnableOutput <<= 1; - if (IsDefined (t)) - EnableOutput |= 1; - do + CPreprocessor::Token CPreprocessor::ExpandDefined (CPreprocessor *iParent, const std::vector& iArgs) + { + if (iArgs.size() != 1) + { + iParent->ErrorHandler(iParent->Line, "The defined() function takes exactly one argument", nullptr); + return Token (Token::TK_ERROR); + } + + const char *v = iParent->IsDefined (iArgs [0]) ? "1" : "0"; + return Token (Token::TK_NUMBER, v, 1); + } + + bool CPreprocessor::GetValueDef(const Token &iToken, long &oValue, int iLine) { - t = cpp.GetToken (false); - } while (t.Type == Token::TK_WHITESPACE || - t.Type == Token::TK_COMMENT || - t.Type == Token::TK_LINECOMMENT); + // Temporary add the defined() function to the macro list + MacroList.emplace_front(Token(Token::TK_KEYWORD, "defined", 7)); + MacroList.front().ExpandFunc = ExpandDefined; + MacroList.front().Args.resize(1); - if (t.Type != Token::TK_EOS) - Error (iLine, "Warning: Ignoring garbage after directive", &t); + bool rc = GetValue (iToken, oValue, iLine); - return true; -} + // Restore the macro list + MacroList.pop_front(); -CPreprocessor::Token CPreprocessor::ExpandDefined (CPreprocessor *iParent, int iNumArgs, Token *iArgs) -{ - if (iNumArgs != 1) + return rc; + } + + bool CPreprocessor::HandleIf (Token &iBody, int iLine) { - iParent->Error (iParent->Line, "The defined() function takes exactly one argument"); - return Token (Token::TK_ERROR); + long val; + return GetValueDef(iBody, val, iLine) && HandleIf(val != 0, iLine); } - const char *v = iParent->IsDefined (iArgs [0]) ? "1" : "0"; - return Token (Token::TK_NUMBER, v, 1); -} + bool CPreprocessor::HandleElif (Token &iBody, int iLine) + { + if (EnableOutput == 1) + { + ErrorHandler(iLine, "#elif without #if", nullptr); + return false; + } -bool CPreprocessor::HandleIf (Token &iBody, int iLine) -{ - Macro defined (Token (Token::TK_KEYWORD, "defined", 7)); - defined.Next = MacroList; - defined.ExpandFunc = ExpandDefined; - defined.NumArgs = 1; - - // Temporary add the defined() function to the macro list - MacroList = &defined; - - long val; - bool rc = GetValue (iBody, val, iLine); - - // Restore the macro list - MacroList = defined.Next; - defined.Next = NULL; + long val; + if (!GetValueDef(iBody, val, iLine)) + return false; - if (!rc) - return false; + if (val && (EnableElif & 1)) + { + EnableOutput |= 1; + EnableElif &= ~1; + } + else + EnableOutput &= ~1; - EnableOutput <<= 1; - if (val) - EnableOutput |= 1; + return true; + } - return true; -} -bool CPreprocessor::HandleElse (Token &iBody, int iLine) -{ - if (EnableOutput == 1) + bool CPreprocessor::HandleElse (Token &iBody, int iLine) { - Error (iLine, "#else without #if"); - return false; - } + if (EnableOutput == 1) + { + ErrorHandler(iLine, "#else without #if", nullptr); + return false; + } - // Negate the result of last #if - EnableOutput ^= 1; + // Negate the result of last #if + if ((EnableElif & 1) || (EnableOutput & 1)) + EnableOutput ^= 1; - if (iBody.Length) - Error (iLine, "Warning: Ignoring garbage after #else", &iBody); + if (iBody.Length) + ErrorHandler(iLine, "Warning: Ignoring garbage after #else", &iBody); - return true; -} + return true; + } -bool CPreprocessor::HandleEndIf (Token &iBody, int iLine) -{ - EnableOutput >>= 1; - if (EnableOutput == 0) + + bool CPreprocessor::HandleEndIf (Token &iBody, int iLine) { - Error (iLine, "#endif without #if"); - return false; - } + EnableElif >>= 1; + EnableOutput >>= 1; + if (EnableOutput == 0) + { + ErrorHandler(iLine, "#endif without #if", nullptr); + return false; + } - if (iBody.Length) - Error (iLine, "Warning: Ignoring garbage after #endif", &iBody); + if (iBody.Length) + ErrorHandler(iLine, "Warning: Ignoring garbage after #endif", &iBody); - return true; -} + return true; + } -CPreprocessor::Token CPreprocessor::HandleDirective (Token &iToken, int iLine) -{ - // Analyze preprocessor directive - const char *directive = iToken.String + 1; - int dirlen = iToken.Length - 1; - while (dirlen && isspace (*directive)) - dirlen--, directive++; - - int old_line = Line; - - // Collect the remaining part of the directive until EOL - Token t, last; - do - { - t = GetToken (false); - if (t.Type == Token::TK_NEWLINE) - { - // No directive arguments - last = t; - t.Length = 0; - goto Done; - } - } while (t.Type == Token::TK_WHITESPACE || - t.Type == Token::TK_LINECONT || - t.Type == Token::TK_COMMENT || - t.Type == Token::TK_LINECOMMENT); - for (;;) + CPreprocessor::Token CPreprocessor::HandleDirective (Token &iToken, int iLine) { - last = GetToken (false); - switch (last.Type) + // Analyze preprocessor directive + const char *directive = iToken.String + 1; + size_t dirlen = iToken.Length - 1; + while (dirlen && isspace (*directive)) + dirlen--, directive++; + + int old_line = Line; + + // Collect the remaining part of the directive until EOL + Token t, last; + do { + t = GetToken (false); + if (t.Type == Token::TK_NEWLINE) + { + // No directive arguments + last = t; + t.Length = 0; + goto Done; + } + } while (t.Type == Token::TK_WHITESPACE || + t.Type == Token::TK_LINECONT || + t.Type == Token::TK_COMMENT || + t.Type == Token::TK_LINECOMMENT); + + for (;;) + { + last = GetToken (false); + switch (last.Type) + { case Token::TK_EOS: // Can happen and is not an error goto Done; case Token::TK_LINECOMMENT: case Token::TK_COMMENT: // Skip comments in macros continue; case Token::TK_ERROR: return last; case Token::TK_LINECONT: continue; case Token::TK_NEWLINE: goto Done; default: break; + } + + t.Append (last); + t.Type = Token::TK_TEXT; } + Done: - t.Append (last); - t.Type = Token::TK_TEXT; - } -Done: +#define IS_DIRECTIVE(s) \ + (dirlen == strlen(s) && (strncmp (directive, s, dirlen) == 0)) -#define IS_DIRECTIVE(s) \ - ((dirlen == sizeof (s) - 1) && (strncmp (directive, s, sizeof (s) - 1) == 0)) + bool outputEnabled = ((EnableOutput & (EnableOutput + 1)) == 0); + bool rc; - bool rc; - if (IS_DIRECTIVE ("define")) - rc = HandleDefine (t, iLine); - else if (IS_DIRECTIVE ("undef")) - rc = HandleUnDef (t, iLine); - else if (IS_DIRECTIVE ("ifdef")) - rc = HandleIfDef (t, iLine); - else if (IS_DIRECTIVE ("ifndef")) - { - rc = HandleIfDef (t, iLine); - if (rc) - EnableOutput ^= 1; - } - else if (IS_DIRECTIVE ("if")) - rc = HandleIf (t, iLine); - else if (IS_DIRECTIVE ("else")) - rc = HandleElse (t, iLine); - else if (IS_DIRECTIVE ("endif")) - rc = HandleEndIf (t, iLine); - - else - { - // elif is tricky to support because the EnableOutput stack doesn't - // contain enough data to tell whether any previous branch matched - if (IS_DIRECTIVE ("elif")) - Error (iLine, "Unsupported preprocessor directive #elif"); - - //Error (iLine, "Unknown preprocessor directive", &iToken); - //return Token (Token::TK_ERROR); - - // Unknown preprocessor directive, roll back and pass through - Line = old_line; - Source = iToken.String + iToken.Length; - iToken.Type = Token::TK_TEXT; - return iToken; - } + if (IS_DIRECTIVE ("define") && outputEnabled) + rc = HandleDefine (t, iLine); + else if (IS_DIRECTIVE ("undef") && outputEnabled) + rc = HandleUnDef (t, iLine); + else if (IS_DIRECTIVE ("ifdef")) + rc = HandleIfDef (t, iLine); + else if (IS_DIRECTIVE ("ifndef")) + { + rc = HandleIfDef (t, iLine); + if (rc) + { + EnableOutput ^= 1; + EnableElif ^= 1; + } + } + else if (IS_DIRECTIVE ("if")) + rc = HandleIf (t, iLine); + else if (IS_DIRECTIVE ("elif")) + rc = HandleElif (t, iLine); + + else if (IS_DIRECTIVE ("else")) + rc = HandleElse (t, iLine); + else if (IS_DIRECTIVE ("endif")) + rc = HandleEndIf (t, iLine); + else + { + // Unknown preprocessor directive, roll back and pass through + Line = old_line; + Source = iToken.String + iToken.Length; + iToken.Type = Token::TK_TEXT; + return iToken; + } #undef IS_DIRECTIVE - if (!rc) - return Token (Token::TK_ERROR); - return last; -} + if (!rc) + return Token (Token::TK_ERROR); + return last; + } -void CPreprocessor::Define (const char *iMacroName, size_t iMacroNameLen, - const char *iMacroValue, size_t iMacroValueLen) -{ - Macro *m = new Macro (Token (Token::TK_KEYWORD, iMacroName, iMacroNameLen)); - m->Value = Token (Token::TK_TEXT, iMacroValue, iMacroValueLen); - m->Next = MacroList; - MacroList = m; -} -void CPreprocessor::Define (const char *iMacroName, size_t iMacroNameLen, - long iMacroValue) -{ - Macro *m = new Macro (Token (Token::TK_KEYWORD, iMacroName, iMacroNameLen)); - m->Value.SetValue (iMacroValue); - m->Next = MacroList; - MacroList = m; -} + void CPreprocessor::Define (const char *iMacroName, size_t iMacroNameLen, + const char *iMacroValue, size_t iMacroValueLen) + { + MacroList.emplace_front(Token(Token::TK_KEYWORD, iMacroName, iMacroNameLen)); + MacroList.front().Value = Token(Token::TK_TEXT, iMacroValue, iMacroValueLen); + } -void CPreprocessor::Define (const char *iMacroName, const char *iMacroValue) -{ - Define (iMacroName, strlen(iMacroName), iMacroValue, strlen(iMacroValue)); -} -void CPreprocessor::Define (const char *iMacroName, long iMacroValue) -{ - Define (iMacroName, strlen(iMacroName), iMacroValue); -} + void CPreprocessor::Define (const char *iMacroName, size_t iMacroNameLen, + long iMacroValue) + { + MacroList.emplace_front(Token(Token::TK_KEYWORD, iMacroName, iMacroNameLen)); + MacroList.front().Value.SetValue(iMacroValue); + } -bool CPreprocessor::Undef (const char *iMacroName, size_t iMacroNameLen) -{ - Macro **cur = &MacroList; - Token name (Token::TK_KEYWORD, iMacroName, iMacroNameLen); - while (*cur) + + bool CPreprocessor::Undef (const char *iMacroName, size_t iMacroNameLen) { - if ((*cur)->Name == name) + Token name (Token::TK_KEYWORD, iMacroName, iMacroNameLen); + + for (auto it = MacroList.before_begin();; ++it) { - Macro *next = (*cur)->Next; - (*cur)->Next = NULL; - delete (*cur); - *cur = next; - return true; + auto itpp = std::next(it); + if(itpp == MacroList.end()) break; + + if (itpp->Name == name) + { + MacroList.erase_after(it); + return true; + } } - cur = &(*cur)->Next; + return false; } - return false; -} - -CPreprocessor::Token CPreprocessor::Parse (const Token &iSource) -{ - Source = iSource.String; - SourceEnd = Source + iSource.Length; - Line = 1; - BOL = true; - EnableOutput = 1; - - // Accumulate output into this token - Token output (Token::TK_TEXT); - int empty_lines = 0; - - // Enable output only if all embedded #if's were true - bool old_output_enabled = true; - bool output_enabled = true; - int output_disabled_line = 0; - while (Source < SourceEnd) + CPreprocessor::Token CPreprocessor::Parse (const Token &iSource) { - int old_line = Line; - Token t = GetToken (true); + Source = iSource.String; + SourceEnd = Source + iSource.Length; + Line = 1; + BOL = true; + EnableOutput = 1; + EnableElif = 0; - NextToken: - switch (t.Type) + // Accumulate output into this token + Token output (Token::TK_TEXT); + int empty_lines = 0; + + // Enable output only if all embedded #if's were true + bool old_output_enabled = true; + bool output_enabled = true; + int output_disabled_line = 0; + + while (Source < SourceEnd) { + int old_line = Line; + Token t = GetToken (true); + + NextToken: + switch (t.Type) + { case Token::TK_ERROR: return t; case Token::TK_EOS: return output; // Force termination case Token::TK_COMMENT: // C comments are replaced with single spaces. if (output_enabled) { output.Append (" ", 1); output.AppendNL (Line - old_line); } break; case Token::TK_LINECOMMENT: // C++ comments are ignored continue; case Token::TK_DIRECTIVE: // Handle preprocessor directives t = HandleDirective (t, old_line); output_enabled = ((EnableOutput & (EnableOutput + 1)) == 0); if (output_enabled != old_output_enabled) { if (output_enabled) output.AppendNL (old_line - output_disabled_line); else output_disabled_line = old_line; old_output_enabled = output_enabled; } if (output_enabled) output.AppendNL (Line - old_line - t.CountNL ()); goto NextToken; case Token::TK_LINECONT: // Backslash-Newline sequences are deleted, no matter where. empty_lines++; break; case Token::TK_NEWLINE: if (empty_lines) { // Compensate for the backslash-newline combinations // we have encountered, otherwise line numeration is broken if (output_enabled) output.AppendNL (empty_lines); empty_lines = 0; } - // Fallthrough to default - FALLTHROUGH; + FALLTHROUGH; // to default case Token::TK_WHITESPACE: // Fallthrough to default default: // Passthrough all other tokens if (output_enabled) output.Append (t); break; + } } - } - if (EnableOutput != 1) - { - Error (Line, "Unclosed #if at end of source"); - return Token (Token::TK_ERROR); + if (EnableOutput != 1) + { + ErrorHandler(Line, "Unclosed #if at end of source", nullptr); + return Token (Token::TK_ERROR); + } + + return output; } - return output; -} -char *CPreprocessor::Parse (const char *iSource, size_t iLength, size_t &oLength) -{ - Token retval = Parse (Token (Token::TK_TEXT, iSource, iLength)); - if (retval.Type == Token::TK_ERROR) - return NULL; + char *CPreprocessor::Parse (const char *iSource, size_t iLength, size_t &oLength) + { + Token retval = Parse (Token (Token::TK_TEXT, iSource, iLength)); + if (retval.Type == Token::TK_ERROR) + return NULL; + + oLength = retval.Length; + retval.Allocated = 0; + return retval.Buffer; + } - oLength = retval.Length; - retval.Allocated = 0; - return retval.Buffer; -} +} // namespace Ogre Index: ps/trunk/source/third_party/ogre3d_preprocessor/OgreGLSLPreprocessor.h =================================================================== --- ps/trunk/source/third_party/ogre3d_preprocessor/OgreGLSLPreprocessor.h (revision 23403) +++ ps/trunk/source/third_party/ogre3d_preprocessor/OgreGLSLPreprocessor.h (revision 23404) @@ -1,541 +1,534 @@ /* - * This source file originally came from OGRE v1.7.2 - http://www.ogre3d.org/ + * This source file originally came from OGRE v1.12.4 - http://www.ogre3d.org/ * with some tweaks as part of 0 A.D. * All changes are released under the original license, as follows: */ /* ------------------------------------------------------------------------------ -This source file is part of OGRE - (Object-oriented Graphics Rendering Engine) -For the latest info, see http://www.ogre3d.org/ - -Copyright (c) 2000-2009 Torus Knot Software Ltd - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. ------------------------------------------------------------------------------ + ----------------------------------------------------------------------------- + This source file is part of OGRE + (Object-oriented Graphics Rendering Engine) + For the latest info, see http://www.ogre3d.org/ + + Copyright (c) 2000-2014 Torus Knot Software Ltd + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + ----------------------------------------------------------------------------- */ -#ifndef INCLUDED_CPREPROCESSOR -#define INCLUDED_CPREPROCESSOR +#ifndef __OGRE_CPREPROCESSOR_H__ +#define __OGRE_CPREPROCESSOR_H__ -/** - * This is a simplistic C/C++-like preprocessor. - * It takes an non-zero-terminated string on input and outputs a - * non-zero-terminated string buffer. - * - * This preprocessor was designed specifically for GLSL shaders, so - * if you want to use it for other purposes you might want to check - * if the feature set it provides is enough for you. - * - * Here's a list of supported features: - *
    - *
  • Fast memory allocation-less operation (mostly).
  • - *
  • Line continuation (backslash-newline) is swallowed.
  • - *
  • Line numeration is fully preserved by inserting empty lines where - * required. This is crucial if, say, GLSL compiler reports you an error - * with a line number.
  • - *
  • \#define: Parametrized and non-parametrized macros. Invoking a macro with - * less arguments than it takes assignes empty values to missing arguments.
  • - *
  • \#undef: Forget defined macros
  • - *
  • \#ifdef/\#ifndef/\#else/\#endif: Conditional suppression of parts of code.
  • - *
  • \#if: Supports numeric expression of any complexity, also supports the - * defined() pseudo-function.
  • - *
- */ -class CPreprocessor +#include +#include +#include +#include + +namespace Ogre { + /** - * A input token. + * This is a simplistic C/C++-like preprocessor. + * It takes an non-zero-terminated string on input and outputs a + * non-zero-terminated string buffer. * - * For performance reasons most tokens will point to portions of the - * input stream, so no unneeded memory allocation is done. However, - * in some cases we must allocate different memory for token storage, - * in this case this is signalled by setting the Allocated member - * to non-zero in which case the destructor will know that it must - * free memory on object destruction. + * This preprocessor was designed specifically for GLSL shaders, so + * if you want to use it for other purposes you might want to check + * if the feature set it provides is enough for you. * - * Again for performance reasons we use malloc/realloc/free here because - * C++-style new[] lacks the realloc() counterpart. + * Here's a list of supported features: + * - Fast memory allocation-less operation (mostly). + * - Line continuation (backslash-newline) is swallowed. + * - Line numeration is fully preserved by inserting empty lines where + * required. This is crucial if, say, GLSL compiler reports you an error + * with a line number. + * - @c \#define: Parametrized and non-parametrized macros. Invoking a macro with + * less arguments than it takes assignes empty values to missing arguments. + * - @c \#undef: Forget defined macros + * - @c \#ifdef / @c \#ifndef / @c \#else / @c \#endif: Conditional suppression of parts of code. + * - @c \#if: Supports numeric expression of any complexity, also supports the + * defined() pseudo-function. */ - class Token + class CPreprocessor { public: - enum Kind + /** + * A input token. + * + * For performance reasons most tokens will point to portions of the + * input stream, so no unneeded memory allocation is done. However, + * in some cases we must allocate different memory for token storage, + * in this case this is signalled by setting the Allocated member + * to non-zero in which case the destructor will know that it must + * free memory on object destruction. + * + * Again for performance reasons we use malloc/realloc/free here because + * C++-style new[] lacks the realloc() counterpart. + */ + class Token { - TK_EOS, // End of input stream - TK_ERROR, // An error has been encountered - TK_WHITESPACE, // A whitespace span (but not newline) - TK_NEWLINE, // A single newline (CR & LF) - TK_LINECONT, // Line continuation ('\' followed by LF) - TK_NUMBER, // A number - TK_KEYWORD, // A keyword - TK_PUNCTUATION, // A punctuation character - TK_DIRECTIVE, // A preprocessor directive - TK_STRING, // A string - TK_COMMENT, // A block comment - TK_LINECOMMENT, // A line comment - TK_TEXT, // An unparsed text (cannot be returned from GetToken()) - }; - - /// Token type - Kind Type; - /// True if string was allocated (and must be freed) - mutable size_t Allocated; - union - { - /// A pointer somewhere into the input buffer - const char *String; - /// A memory-allocated string - char *Buffer; - }; - /// Token length in bytes - size_t Length; + public: + enum Kind + { + TK_EOS, // End of input stream + TK_ERROR, // An error has been encountered + TK_WHITESPACE, // A whitespace span (but not newline) + TK_NEWLINE, // A single newline (CR & LF) + TK_LINECONT, // Line continuation ('\' followed by LF) + TK_NUMBER, // A number + TK_KEYWORD, // A keyword + TK_PUNCTUATION, // A punctuation character + TK_DIRECTIVE, // A preprocessor directive + TK_STRING, // A string + TK_COMMENT, // A block comment + TK_LINECOMMENT, // A line comment + TK_TEXT // An unparsed text (cannot be returned from GetToken()) + }; + + /// Token type + Kind Type; + /// True if string was allocated (and must be freed) + mutable size_t Allocated; + union + { + /// A pointer somewhere into the input buffer + const char *String; + /// A memory-allocated string + char *Buffer; + }; + /// Token length in bytes + size_t Length; - Token () : Type (TK_ERROR), Allocated (0), String (NULL), Length (0) - { } + Token () : Allocated (0), String (NULL), Length(0) + { } - Token (Kind iType) : Type (iType), Allocated (0), String (NULL), Length (0) - { } + Token (Kind iType) : Type (iType), Allocated (0), String (NULL), Length(0) + { } - Token (Kind iType, const char *iString, size_t iLength) : + Token (Kind iType, const char *iString, size_t iLength) : Type (iType), Allocated (0), String (iString), Length (iLength) - { } - - Token (const Token &iOther) - { - Type = iOther.Type; - Allocated = iOther.Allocated; - iOther.Allocated = 0; // !!! not quite correct but effective - String = iOther.String; - Length = iOther.Length; - } - - ~Token () - { if (Allocated) free (Buffer); } - - /// Assignment operator - Token &operator = (const Token &iOther) - { - if (Allocated) free (Buffer); - Type = iOther.Type; - Allocated = iOther.Allocated; - iOther.Allocated = 0; // !!! not quite correct but effective - String = iOther.String; - Length = iOther.Length; - return *this; - } - - /// Append a string to this token - void Append (const char *iString, size_t iLength); - - /// Append a token to this token - void Append (const Token &iOther); - - /// Append given number of newlines to this token - void AppendNL (int iCount); - - /// Count number of newlines in this token - int CountNL (); - - /// Get the numeric value of the token - bool GetValue (long &oValue) const; + { } - /// Set the numeric value of the token - void SetValue (long iValue); + Token (const Token &iOther) + { + Type = iOther.Type; + Allocated = iOther.Allocated; + iOther.Allocated = 0; // !!! not quite correct but effective + String = iOther.String; + Length = iOther.Length; + } + + ~Token () + { if (Allocated) free (Buffer); } + + /// Assignment operator + Token &operator = (const Token &iOther) + { + if (Allocated) free (Buffer); + Type = iOther.Type; + Allocated = iOther.Allocated; + iOther.Allocated = 0; // !!! not quite correct but effective + String = iOther.String; + Length = iOther.Length; + return *this; + } + + /// Append a string to this token + void Append (const char *iString, size_t iLength); + + /// Append a token to this token + void Append (const Token &iOther); + + /// Append given number of newlines to this token + void AppendNL (int iCount); + + /// Count number of newlines in this token + int CountNL (); + + /// Get the numeric value of the token + bool GetValue (long &oValue) const; + + /// Set the numeric value of the token + void SetValue (long iValue); + + /// Test two tokens for equality + bool operator == (const Token &iOther) + { + if (iOther.Length != Length) + return false; + return (memcmp (String, iOther.String, Length) == 0); + } + }; - /// Test two tokens for equality - bool operator == (const Token &iOther) + /// A macro definition + class Macro { - if (iOther.Length != Length) - return false; - return (memcmp (String, iOther.String, Length) == 0); - } - }; - - /// A macro definition - class Macro - { - public: - /// Macro name - Token Name; - /// Number of arguments - int NumArgs; - /// The names of the arguments - Token *Args; - /// The macro value - Token Value; - /// Unparsed macro body (keeps the whole raw unparsed macro body) - Token Body; - /// Next macro in chained list - Macro *Next; - /// A pointer to function implementation (if macro is really a func) - Token (*ExpandFunc) (CPreprocessor *iParent, int iNumArgs, Token *iArgs); - /// true if macro expansion is in progress - bool Expanding; - - Macro (const Token &iName) : - Name (iName), NumArgs (0), Args (NULL), Next (NULL), - ExpandFunc (NULL), Expanding (false) - { } - - ~Macro () - { delete [] Args; delete Next; } - - /// Expand the macro value (will not work for functions) - Token Expand (int iNumArgs, Token *iArgs, Macro *iMacros); - }; - - friend class CPreprocessor::Macro; - - /// The current source text input - const char *Source; - /// The end of the source text - const char *SourceEnd; - /// Current line number - int Line; - /// True if we are at beginning of line - bool BOL; - /// A stack of 32 booleans packed into one value :) - unsigned EnableOutput; - /// The list of macros defined so far - Macro *MacroList; - - /** - * Private constructor to re-parse a single token. - */ - CPreprocessor (const Token &iToken, int iLine); - - /** - * Stateless tokenizer: Parse the input text and return the next token. - * @param iExpand - * If true, macros will be expanded to their values - * @return - * The next token from the input stream - */ - Token GetToken (bool iExpand); - - /** - * Handle a preprocessor directive. - * @param iToken - * The whole preprocessor directive line (until EOL) - * @param iLine - * The line where the directive begins (for error reports) - * @return - * The last input token that was not proceeded. - */ - Token HandleDirective (Token &iToken, int iLine); - - /** - * Handle a \#define directive. - * @param iBody - * The body of the directive (everything after the directive - * until end of line). - * @param iLine - * The line where the directive begins (for error reports) - * @return - * true if everything went ok, false if not - */ - bool HandleDefine (Token &iBody, int iLine); - - /** - * Undefine a previously defined macro - * @param iBody - * The body of the directive (everything after the directive - * until end of line). - * @param iLine - * The line where the directive begins (for error reports) - * @return - * true if everything went ok, false if not - */ - bool HandleUnDef (Token &iBody, int iLine); - - /** - * Handle an \#ifdef directive. - * @param iBody - * The body of the directive (everything after the directive - * until end of line). - * @param iLine - * The line where the directive begins (for error reports) - * @return - * true if everything went ok, false if not - */ - bool HandleIfDef (Token &iBody, int iLine); - - /** - * Handle an \#if directive. - * @param iBody - * The body of the directive (everything after the directive - * until end of line). - * @param iLine - * The line where the directive begins (for error reports) - * @return - * true if everything went ok, false if not - */ - bool HandleIf (Token &iBody, int iLine); - - /** - * Handle an \#else directive. - * @param iBody - * The body of the directive (everything after the directive - * until end of line). - * @param iLine - * The line where the directive begins (for error reports) - * @return - * true if everything went ok, false if not - */ - bool HandleElse (Token &iBody, int iLine); - - /** - * Handle an \#endif directive. - * @param iBody - * The body of the directive (everything after the directive - * until end of line). - * @param iLine - * The line where the directive begins (for error reports) - * @return - * true if everything went ok, false if not - */ - bool HandleEndIf (Token &iBody, int iLine); + public: + /// Macro name + Token Name; + /// The names of the arguments + std::vector Args; + /// The macro value + Token Value; + /// Unparsed macro body (keeps the whole raw unparsed macro body) + Token Body; + /// A pointer to function implementation (if macro is really a func) + Token (*ExpandFunc) (CPreprocessor *iParent, const std::vector& iArgs); + /// true if macro expansion is in progress + bool Expanding; - /** - * Get a single function argument until next ',' or ')'. - * @param oArg - * The argument is returned in this variable. - * @param iExpand - * If false, parameters are not expanded and no expressions are - * allowed; only a single keyword is expected per argument. - * @return - * The first unhandled token after argument. - */ - Token GetArgument (Token &oArg, bool iExpand); + Macro(const Token& iName) : Name(iName), ExpandFunc(NULL), Expanding(false) {} - /** - * Get all the arguments of a macro: '(' arg1 { ',' arg2 { ',' ... }} ')' - * @param oNumArgs - * Number of parsed arguments is stored into this variable. - * @param oArgs - * This is set to a pointer to an array of parsed arguments. - * @param iExpand - * If false, parameters are not expanded and no expressions are - * allowed; only a single keyword is expected per argument. - */ - Token GetArguments (int &oNumArgs, Token *&oArgs, bool iExpand); - - /** - * Parse an expression, compute it and return the result. - * @param oResult - * A token containing the result of expression - * @param iLine - * The line at which the expression starts (for error reports) - * @param iOpPriority - * Operator priority (at which operator we will stop if - * proceeding recursively -- used internally. Parser stops - * when it encounters an operator with higher or equal priority). - * @return - * The last unhandled token after the expression - */ - Token GetExpression (Token &oResult, int iLine, int iOpPriority = 0); - - /** - * Get the numeric value of a token. - * If the token was produced by expanding a macro, we will get - * an TEXT token which can contain a whole expression; in this - * case we will call GetExpression to parse it. Otherwise we - * just call the token's GetValue() method. - * @param iToken - * The token to get the numeric value of - * @param oValue - * The variable to put the value into - * @param iLine - * The line where the directive begins (for error reports) - * @return - * true if ok, false if not - */ - bool GetValue (const Token &iToken, long &oValue, int iLine); - - /** - * Expand the given macro, if it exists. - * If macro has arguments, they are collected from source stream. - * @param iToken - * A KEYWORD token containing the (possible) macro name. - * @return - * The expanded token or iToken if it is not a macro - */ - Token ExpandMacro (const Token &iToken); - - /** - * Check if a macro is defined, and if so, return it - * @param iToken - * Macro name - * @return - * The macro object or NULL if a macro with this name does not exist - */ - Macro *IsDefined (const Token &iToken); - - /** - * The implementation of the defined() preprocessor function - * @param iParent - * The parent preprocessor object - * @param iNumArgs - * Number of arguments - * @param iArgs - * The arguments themselves - * @return - * The return value encapsulated in a token - */ - static Token ExpandDefined (CPreprocessor *iParent, int iNumArgs, Token *iArgs); - - /** - * Parse the input string and return a token containing the whole output. - * @param iSource - * The source text enclosed in a token - * @return - * The output text enclosed in a token - */ - Token Parse (const Token &iSource); - - /** - * Call the error handler - * @param iLine - * The line at which the error happened. - * @param iError - * The error string. - * @param iToken - * If not NULL contains the erroneous token - */ - void Error (int iLine, const char *iError, const Token *iToken = NULL); - -public: - /// Create an empty preprocessor object - CPreprocessor () : MacroList (NULL) - { } - - /// Destroy the preprocessor object - virtual ~CPreprocessor (); - - /** - * Define a macro without parameters. - * @param iMacroName - * The name of the defined macro - * @param iMacroNameLen - * The length of the name of the defined macro - * @param iMacroValue - * The value of the defined macro - * @param iMacroValueLen - * The length of the value of the defined macro - */ - void Define (const char *iMacroName, size_t iMacroNameLen, - const char *iMacroValue, size_t iMacroValueLen); - - /** - * Define a numerical macro. - * @param iMacroName - * The name of the defined macro - * @param iMacroNameLen - * The length of the name of the defined macro - * @param iMacroValue - * The value of the defined macro - */ - void Define (const char *iMacroName, size_t iMacroNameLen, long iMacroValue); + /// Expand the macro value (will not work for functions) + Token Expand (const std::vector& iArgs, std::forward_list& iMacros); + }; - /** - * Define a macro without parameters. - * @param iMacroName - * The name of the defined macro - * @param iMacroValue - * The value of the defined macro - */ - void Define (const char *iMacroName, const char *iMacroValue); + friend class CPreprocessor::Macro; - /** - * Define a numerical macro. - * @param iMacroName - * The name of the defined macro - * @param iMacroValue - * The value of the defined macro - */ - void Define (const char *iMacroName, long iMacroValue); + /// The current source text input + const char *Source; + /// The end of the source text + const char *SourceEnd; + /// Current line number + int Line; + /// True if we are at beginning of line + bool BOL; + /// A stack of 32 booleans packed into one value :) + unsigned EnableOutput; + unsigned EnableElif; + /// The list of macros defined so far + std::forward_list MacroList; + + /** + * Private constructor to re-parse a single token. + */ + CPreprocessor (const Token &iToken, int iLine); + + /** + * Stateless tokenizer: Parse the input text and return the next token. + * @param iExpand + * If true, macros will be expanded to their values + * @return + * The next token from the input stream + */ + Token GetToken (bool iExpand); + + /** + * Handle a preprocessor directive. + * @param iToken + * The whole preprocessor directive line (until EOL) + * @param iLine + * The line where the directive begins (for error reports) + * @return + * The last input token that was not proceeded. + */ + Token HandleDirective (Token &iToken, int iLine); + + /** + * Handle a #define directive. + * @param iBody + * The body of the directive (everything after the directive + * until end of line). + * @param iLine + * The line where the directive begins (for error reports) + * @return + * true if everything went ok, false if not + */ + bool HandleDefine (Token &iBody, int iLine); + + /** + * Undefine a previously defined macro + * @param iBody + * The body of the directive (everything after the directive + * until end of line). + * @param iLine + * The line where the directive begins (for error reports) + * @return + * true if everything went ok, false if not + */ + bool HandleUnDef (Token &iBody, int iLine); + + /** + * Handle an #ifdef directive. + * @param iBody + * The body of the directive (everything after the directive + * until end of line). + * @param iLine + * The line where the directive begins (for error reports) + * @return + * true if everything went ok, false if not + */ + bool HandleIfDef (Token &iBody, int iLine); + + /** + * Handle an #if directive. + * @param iBody + * The body of the directive (everything after the directive + * until end of line). + * @param iLine + * The line where the directive begins (for error reports) + * @return + * true if everything went ok, false if not + */ + bool HandleIf (Token &iBody, int iLine); + + /// @overload + bool HandleIf(bool val, int iLine); + + /** + * Handle an #elif directive. + * @param iBody + * The body of the directive (everything after the directive + * until end of line). + * @param iLine + * The line where the directive begins (for error reports) + * @return + * true if everything went ok, false if not + */ + bool HandleElif (Token &iBody, int iLine); + + /** + * Handle an #else directive. + * @param iBody + * The body of the directive (everything after the directive + * until end of line). + * @param iLine + * The line where the directive begins (for error reports) + * @return + * true if everything went ok, false if not + */ + bool HandleElse (Token &iBody, int iLine); + + /** + * Handle an #endif directive. + * @param iBody + * The body of the directive (everything after the directive + * until end of line). + * @param iLine + * The line where the directive begins (for error reports) + * @return + * true if everything went ok, false if not + */ + bool HandleEndIf (Token &iBody, int iLine); + + /** + * Get a single function argument until next ',' or ')'. + * @param oArg + * The argument is returned in this variable. + * @param iExpand + * If false, parameters are not expanded and no expressions are + * allowed; only a single keyword is expected per argument. + * @param shouldAppendArg + * When true, the argument will be appended the word word __arg_ + * e.g. #define myMacro(x) --> #define myMacro(x__arg_) + * This workaround a bug where calling myMacro( x ) would cause + * issues. + * @return + * The first unhandled token after argument. + */ + Token GetArgument (Token &oArg, bool iExpand, bool shouldAppendArg); + + /** + * Get all the arguments of a macro: '(' arg1 { ',' arg2 { ',' ... }} ')' + * @param oArgs + * This is set to a pointer to an array of parsed arguments. + * @param shouldAppendArg + * See GetArgument. + * @param iExpand + * If false, parameters are not expanded and no expressions are + * allowed; only a single keyword is expected per argument. + */ + Token GetArguments (std::vector& oArgs, bool iExpand, bool shouldAppendArg); + + /** + * Parse an expression, compute it and return the result. + * @param oResult + * A token containing the result of expression + * @param iLine + * The line at which the expression starts (for error reports) + * @param iOpPriority + * Operator priority (at which operator we will stop if + * proceeding recursively -- used internally. Parser stops + * when it encounters an operator with higher or equal priority). + * @return + * The last unhandled token after the expression + */ + Token GetExpression (Token &oResult, int iLine, int iOpPriority = 0); + + /** + * Get the numeric value of a token. + * If the token was produced by expanding a macro, we will get + * an TEXT token which can contain a whole expression; in this + * case we will call GetExpression to parse it. Otherwise we + * just call the token's GetValue() method. + * @param iToken + * The token to get the numeric value of + * @param oValue + * The variable to put the value into + * @param iLine + * The line where the directive begins (for error reports) + * @return + * true if ok, false if not + */ + bool GetValue (const Token &iToken, long &oValue, int iLine); + + /// @overload + /// same as above, but considers the defined() function + bool GetValueDef(const Token &iToken, long &oValue, int iLine); + + /** + * Expand the given macro, if it exists. + * If macro has arguments, they are collected from source stream. + * @param iToken + * A KEYWORD token containing the (possible) macro name. + * @return + * The expanded token or iToken if it is not a macro + */ + Token ExpandMacro (const Token &iToken); + + /** + * Check if a macro is defined, and if so, return it + * @param iToken + * Macro name + * @return + * The macro object or NULL if a macro with this name does not exist + */ + Macro *IsDefined (const Token &iToken); + + /** + * The implementation of the defined() preprocessor function + * @param iParent + * The parent preprocessor object + * @param iArgs + * The arguments themselves + * @return + * The return value encapsulated in a token + */ + static Token ExpandDefined (CPreprocessor *iParent, const std::vector& iArgs); + + /** + * Parse the input string and return a token containing the whole output. + * @param iSource + * The source text enclosed in a token + * @return + * The output text enclosed in a token + */ + Token Parse (const Token &iSource); + + /** + * Call the error handler + * @param iLine + * The line at which the error happened. + * @param iError + * The error string. + * @param iToken + * If not NULL contains the erroneous token + */ + static void Error (int iLine, const char *iError, const Token *iToken = NULL); - /** - * Undefine a macro. - * @param iMacroName - * The name of the macro to undefine - * @param iMacroNameLen - * The length of the name of the macro to undefine - * @return - * true if the macro has been undefined, false if macro doesn't exist - */ - bool Undef (const char *iMacroName, size_t iMacroNameLen); + public: + /// Create an empty preprocessor object + CPreprocessor() {} - /** - * Parse the input string and return a newly-allocated output string. - * @note - * The returned preprocessed string is NOT zero-terminated - * (just like the input string). - * @param iSource - * The source text - * @param iLength - * The length of the source text in characters - * @param oLength - * The length of the output string. - * @return - * The output from preprocessor, allocated with malloc(). - * The parser can actually allocate more than needed for performance - * reasons, but this should not be a problem unless you will want - * to store the returned pointer for long time in which case you - * might want to realloc() it. - * If an error has been encountered, the function returns NULL. - * In some cases the function may return an unallocated address - * that's *inside* the source buffer. You must free() the result - * string only if the returned address is not inside the source text. - */ - char *Parse (const char *iSource, size_t iLength, size_t &oLength); + /// Destroy the preprocessor object + virtual ~CPreprocessor (); - /** - * An error handler function type. - * The default implementation just drops a note to stderr and - * then the parser ends, returning NULL. - * @param iData - * User-specific pointer from the corresponding CPreprocessor object. - * @param iLine - * The line at which the error happened. - * @param iError - * The error string. - * @param iToken - * If not NULL contains the erroneous token - * @param iTokenLen - * The length of iToken. iToken is never zero-terminated! - */ - typedef void (*ErrorHandlerFunc) ( - void *iData, int iLine, const char *iError, - const char *iToken, size_t iTokenLen); + /** + * Define a macro without parameters. + * @param iMacroName + * The name of the defined macro + * @param iMacroNameLen + * The length of the name of the defined macro + * @param iMacroValue + * The value of the defined macro + * @param iMacroValueLen + * The length of the value of the defined macro + */ + void Define (const char *iMacroName, size_t iMacroNameLen, + const char *iMacroValue, size_t iMacroValueLen); + + /** + * Define a numerical macro. + * @param iMacroName + * The name of the defined macro + * @param iMacroNameLen + * The length of the name of the defined macro + * @param iMacroValue + * The value of the defined macro + */ + void Define (const char *iMacroName, size_t iMacroNameLen, long iMacroValue); + + /** + * Undefine a macro. + * @param iMacroName + * The name of the macro to undefine + * @param iMacroNameLen + * The length of the name of the macro to undefine + * @return + * true if the macro has been undefined, false if macro doesn't exist + */ + bool Undef (const char *iMacroName, size_t iMacroNameLen); + + /** + * Parse the input string and return a newly-allocated output string. + * @note + * The returned preprocessed string is NOT zero-terminated + * (just like the input string). + * @param iSource + * The source text + * @param iLength + * The length of the source text in characters + * @param oLength + * The length of the output string. + * @return + * The output from preprocessor, allocated with malloc(). + * The parser can actually allocate more than needed for performance + * reasons, but this should not be a problem unless you will want + * to store the returned pointer for long time in which case you + * might want to realloc() it. + * If an error has been encountered, the function returns NULL. + * In some cases the function may return an unallocated address + * that's *inside* the source buffer. You must free() the result + * string only if the returned address is not inside the source text. + */ + char *Parse (const char *iSource, size_t iLength, size_t &oLength); + + /** + * Call the error handler + * @param iLine + * The line at which the error happened. + * @param iError + * The error string. + * @param iToken + * If not NULL contains the erroneous token + */ + typedef void (*ErrorHandlerFunc) (int iLine, const char *iError, const Token *iToken); + + /** + * A pointer to the preprocessor's error handler. + * You can assign the address of your own function to this variable + * and implement your own error handling (e.g. throwing an exception etc). + */ + static ErrorHandlerFunc ErrorHandler; - /** - * A pointer to the preprocessor's error handler. - * You can assign the address of your own function to this variable - * and implement your own error handling (e.g. throwing an exception etc). - */ - static ErrorHandlerFunc ErrorHandler; + }; - /// User-specific storage, passed to Error() - void *ErrorData; -}; +} // namespace Ogre -#endif // INCLUDED_CPREPROCESSOR +#endif // __OGRE_CPREPROCESSOR_H__ Index: ps/trunk/source/third_party/ogre3d_preprocessor/tests/test_Preprocessor.h =================================================================== --- ps/trunk/source/third_party/ogre3d_preprocessor/tests/test_Preprocessor.h (revision 23403) +++ ps/trunk/source/third_party/ogre3d_preprocessor/tests/test_Preprocessor.h (revision 23404) @@ -1,60 +1,60 @@ -/* Copyright (C) 2019 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #include "lib/self_test.h" #include "graphics/PreprocessorWrapper.h" #include "third_party/ogre3d_preprocessor/OgreGLSLPreprocessor.h" class TestPreprocessor : public CxxTest::TestSuite { public: void setUp() { - CPreprocessor::ErrorHandler = CPreprocessorWrapper::PyrogenesisShaderError; + Ogre::CPreprocessor::ErrorHandler = CPreprocessorWrapper::PyrogenesisShaderError; } void test_basic() { - CPreprocessor preproc; + Ogre::CPreprocessor preproc; const char* in = "#define TEST 2\n1+1=TEST\n"; size_t len = 0; char* out = preproc.Parse(in, strlen(in), len); TS_ASSERT_EQUALS(std::string(out, len), "\n1+1=2\n"); // Free output if it's not inside the source string if (!(out >= in && out < in + strlen(in))) free(out); } void test_error() { TestLogger logger; - CPreprocessor preproc; + Ogre::CPreprocessor preproc; const char* in = "foo\n#if ()\nbar\n"; size_t len = 0; char* out = preproc.Parse(in, strlen(in), len); TS_ASSERT_EQUALS(std::string(out, len), ""); TS_ASSERT_STR_CONTAINS(logger.GetOutput(), "ERROR: Preprocessor error: line 2: Unclosed parenthesis in #if expression\n"); // Free output if it's not inside the source string if (!(out >= in && out < in + strlen(in))) free(out); } };