Index: ps/trunk/source/graphics/PreprocessorWrapper.h =================================================================== --- ps/trunk/source/graphics/PreprocessorWrapper.h +++ ps/trunk/source/graphics/PreprocessorWrapper.h @@ -1,4 +1,4 @@ -/* 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 @@ -39,10 +39,10 @@ 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/graphics/PreprocessorWrapper.cpp =================================================================== --- ps/trunk/source/graphics/PreprocessorWrapper.cpp +++ ps/trunk/source/graphics/PreprocessorWrapper.cpp @@ -1,4 +1,4 @@ -/* 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 @@ -22,22 +22,22 @@ #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) Index: ps/trunk/source/third_party/ogre3d_preprocessor/OgreGLSLPreprocessor.h =================================================================== --- ps/trunk/source/third_party/ogre3d_preprocessor/OgreGLSLPreprocessor.h +++ ps/trunk/source/third_party/ogre3d_preprocessor/OgreGLSLPreprocessor.h @@ -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: - * - */ -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/OgreGLSLPreprocessor.cpp =================================================================== --- ps/trunk/source/third_party/ogre3d_preprocessor/OgreGLSLPreprocessor.cpp +++ ps/trunk/source/third_party/ogre3d_preprocessor/OgreGLSLPreprocessor.cpp @@ -1,5 +1,5 @@ /* - * 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: */ @@ -10,7 +10,7 @@ (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 @@ -34,514 +34,503 @@ #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; @@ -553,10 +542,10 @@ 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; @@ -573,37 +562,37 @@ 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); @@ -642,7 +631,7 @@ case '%': if (vrop == 0) { - Error (iLine, "Division by zero"); + ErrorHandler(iLine, "Division by zero", nullptr); return Token (Token::TK_ERROR); } if (op.String [0] == '/') @@ -650,59 +639,60 @@ 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; @@ -714,72 +704,103 @@ // 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: @@ -790,60 +811,69 @@ 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); @@ -857,43 +887,37 @@ 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 @@ -901,188 +925,239 @@ 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; @@ -1103,134 +1178,122 @@ 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; @@ -1282,8 +1345,7 @@ output.AppendNL (empty_lines); empty_lines = 0; } - // Fallthrough to default - FALLTHROUGH; + FALLTHROUGH; // to default case Token::TK_WHITESPACE: // Fallthrough to default default: @@ -1291,25 +1353,28 @@ 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/tests/test_Preprocessor.h =================================================================== --- ps/trunk/source/third_party/ogre3d_preprocessor/tests/test_Preprocessor.h +++ ps/trunk/source/third_party/ogre3d_preprocessor/tests/test_Preprocessor.h @@ -1,4 +1,4 @@ -/* 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 @@ -25,12 +25,12 @@ 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); @@ -45,7 +45,7 @@ { 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);