Index: build/premake/premake5.lua
===================================================================
--- build/premake/premake5.lua
+++ build/premake/premake5.lua
@@ -730,7 +730,8 @@
"graphics/scripting",
"renderer",
"renderer/scripting",
- "third_party/mikktspace"
+ "third_party/mikktspace",
+ "third_party/ogre3d_preprocessor"
}
extern_libs = {
"opengl",
Index: source/graphics/MaterialManager.cpp
===================================================================
--- source/graphics/MaterialManager.cpp
+++ source/graphics/MaterialManager.cpp
@@ -19,13 +19,13 @@
#include "MaterialManager.h"
+#include "graphics/PreprocessorWrapper.h"
#include "lib/ogl.h"
#include "maths/MathUtil.h"
#include "maths/Vector4D.h"
#include "ps/CLogger.h"
#include "ps/ConfigDB.h"
#include "ps/Filesystem.h"
-#include "ps/PreprocessorWrapper.h"
#include "ps/XML/Xeromyces.h"
#include "renderer/RenderingOptions.h"
Index: source/graphics/PreprocessorWrapper.h
===================================================================
--- source/graphics/PreprocessorWrapper.h
+++ source/graphics/PreprocessorWrapper.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2012 Wildfire Games.
+/* Copyright (C) 2019 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -18,8 +18,8 @@
#ifndef INCLUDED_PREPROCESSORWRAPPER
#define INCLUDED_PREPROCESSORWRAPPER
-#include "ps/Preprocessor.h"
#include "ps/CStr.h"
+#include "third_party/ogre3d_preprocessor/OgreGLSLPreprocessor.h"
class CShaderDefines;
@@ -29,6 +29,8 @@
class CPreprocessorWrapper
{
public:
+ CPreprocessorWrapper();
+
void AddDefine(const char* name, const char* value);
void AddDefines(const CShaderDefines& defines);
@@ -37,6 +39,8 @@
CStr Preprocess(const CStr& input);
+ static void PyrogenesisShaderError(void* UNUSED(iData), int iLine, const char* iError, const char* iToken, size_t iTokenLen);
+
private:
CPreprocessor m_Preprocessor;
};
Index: source/graphics/PreprocessorWrapper.cpp
===================================================================
--- source/graphics/PreprocessorWrapper.cpp
+++ source/graphics/PreprocessorWrapper.cpp
@@ -1,4 +1,4 @@
-/* Copyright (C) 2012 Wildfire Games.
+/* Copyright (C) 2019 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -22,6 +22,19 @@
#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)
+{
+ if (iToken)
+ LOGERROR("Preprocessor error: line %d: %s: '%s'\n", iLine, iError, std::string(iToken, iTokenLen).c_str());
+ else
+ LOGERROR("Preprocessor error: line %d: %s\n", iLine, iError);
+}
+
+CPreprocessorWrapper::CPreprocessorWrapper()
+{
+ CPreprocessor::ErrorHandler = CPreprocessorWrapper::PyrogenesisShaderError;
+}
+
void CPreprocessorWrapper::AddDefine(const char* name, const char* value)
{
m_Preprocessor.Define(name, value);
Index: source/graphics/ShaderManager.cpp
===================================================================
--- source/graphics/ShaderManager.cpp
+++ source/graphics/ShaderManager.cpp
@@ -19,6 +19,7 @@
#include "ShaderManager.h"
+#include "graphics/PreprocessorWrapper.h"
#include "graphics/ShaderTechnique.h"
#include "lib/config2.h"
#include "lib/hash.h"
@@ -27,7 +28,6 @@
#include "ps/CLogger.h"
#include "ps/CStrIntern.h"
#include "ps/Filesystem.h"
-#include "ps/PreprocessorWrapper.h"
#include "ps/Profile.h"
#if USE_SHADER_XML_VALIDATION
# include "ps/XML/RelaxNG.h"
Index: source/graphics/ShaderProgram.cpp
===================================================================
--- source/graphics/ShaderProgram.cpp
+++ source/graphics/ShaderProgram.cpp
@@ -20,6 +20,7 @@
#include "ShaderProgram.h"
#include "graphics/Color.h"
+#include "graphics/PreprocessorWrapper.h"
#include "graphics/ShaderManager.h"
#include "graphics/TextureManager.h"
#include "lib/res/graphics/ogl_tex.h"
@@ -27,7 +28,6 @@
#include "maths/Vector3D.h"
#include "ps/CLogger.h"
#include "ps/Filesystem.h"
-#include "ps/PreprocessorWrapper.h"
#if !CONFIG2_GLES
Index: source/ps/Preprocessor.h
===================================================================
--- source/ps/Preprocessor.h
+++ source/ps/Preprocessor.h
@@ -1,541 +0,0 @@
-/*
- * This source file originally came from OGRE v1.7.2 - 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.
------------------------------------------------------------------------------
-*/
-
-#ifndef INCLUDED_CPREPROCESSOR
-#define INCLUDED_CPREPROCESSOR
-
-/**
- * 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
-{
- /**
- * 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
- {
- 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 (Kind iType) : Type (iType), Allocated (0), String (NULL), Length (0)
- { }
-
- 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);
-
- /// Test two tokens for equality
- bool operator == (const Token &iOther)
- {
- 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);
-
- /**
- * 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);
-
- /**
- * 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);
-
- /**
- * 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);
-
- /**
- * 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);
-
- /**
- * 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);
-
- /**
- * 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);
-
- /**
- * 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;
-};
-
-#endif // INCLUDED_CPREPROCESSOR
Index: source/ps/Preprocessor.cpp
===================================================================
--- source/ps/Preprocessor.cpp
+++ source/ps/Preprocessor.cpp
@@ -1,1316 +0,0 @@
-/*
- * This source file originally came from OGRE v1.7.2 - 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.
------------------------------------------------------------------------------
-*/
-
-#include "precompiled.h"
-
-#include "Preprocessor.h"
-
-#include "ps/CLogger.h"
-
-// Limit max number of macro arguments to this
-#define MAX_MACRO_ARGS 16
-
-//---------------------------------------------------------------------------//
-
-/// 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)
-{
- Token t (Token::TK_TEXT, iString, iLength);
- Append (t);
-}
-
-void CPreprocessor::Token::Append (const Token &iOther)
-{
- if (!iOther.String)
- return;
-
- if (!String)
- {
- String = iOther.String;
- Length = iOther.Length;
- Allocated = iOther.Allocated;
- iOther.Allocated = 0; // !!! not quite correct but effective
- return;
- }
-
- 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;
- }
-
- if (Allocated)
- memcpy (Buffer + Length, iOther.String, iOther.Length);
- Length += iOther.Length;
-}
-
-bool CPreprocessor::Token::GetValue (long &oValue) const
-{
- long val = 0;
- size_t i = 0;
-
- while (isspace (String [i]))
- i++;
-
- long base = 10;
- if (String [i] == '0')
- {
- if (Length > i + 1 && String [i + 1] == 'x')
- base = 16, i += 2;
- else
- base = 8;
- }
-
- for (; i < Length; i++)
- {
- long c = long (String [i]);
- if (isspace (c))
- // Possible end of number
- break;
-
- if (c >= 'a' && c <= 'z')
- c -= ('a' - 'A');
-
- c -= '0';
- if (c < 0)
- return false;
-
- if (c > 9)
- c -= ('A' - '9' - 1);
-
- if (c >= base)
- return false;
-
- val = (val * base) + c;
- }
-
- // Check that all other characters are just spaces
- for (; i < Length; i++)
- if (!isspace (String [i]))
- return false;
-
- oValue = val;
- return true;
-}
-
-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;
-}
-
-void CPreprocessor::Token::AppendNL (int iCount)
-{
- static const char newlines [8] =
- { '\n', '\n', '\n', '\n', '\n', '\n', '\n', '\n' };
-
- while (iCount > 8)
- {
- Append (newlines, 8);
- iCount -= 8;
- }
- 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;
- }
- 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);
-
- Expanding = false;
-
- // 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;
-
- return xt;
-}
-
-//---------------------------------------------------------------------------//
-
-static void DefaultError (void *iData, int iLine, const char *iError,
- const char *iToken, size_t iTokenLen)
-{
- (void)iData;
- if (iToken)
- LOGERROR("Preprocessor error: line %d: %s: '%s'\n",
- iLine, iError, std::string (iToken, iTokenLen));
- else
- LOGERROR("Preprocessor error: line %d: %s\n", iLine, iError);
-}
-
-//---------------------------------------------------------------------------//
-
-CPreprocessor::ErrorHandlerFunc CPreprocessor::ErrorHandler = DefaultError;
-
-CPreprocessor::CPreprocessor (const Token &iToken, int iLine) : MacroList (NULL)
-{
- Source = iToken.String;
- SourceEnd = iToken.String + iToken.Length;
- EnableOutput = 1;
- Line = iLine;
- BOL = true;
-}
-
-CPreprocessor::~CPreprocessor ()
-{
- delete MacroList;
-}
-
-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);
-}
-
-CPreprocessor::Token CPreprocessor::GetToken (bool iExpand)
-{
- if (Source >= SourceEnd)
- return Token (Token::TK_EOS);
-
- const char *begin = Source;
- char c = *Source++;
-
-
- if (c == '\n' || (c == '\r' && *Source == '\n'))
- {
- Line++;
- 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))
- {
- BOL = false;
- if (c == '0' && Source < SourceEnd && Source [0] == 'x') // hex numbers
- {
- Source++;
- while (Source < SourceEnd && isxdigit (*Source))
- 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);
- }
- else if (c == '/' && *Source == '/')
- {
- BOL = false;
- Source++;
- while (Source < SourceEnd && *Source != '\r' && *Source != '\n')
- Source++;
- return Token (Token::TK_LINECOMMENT, begin, Source - begin);
- }
- else if (c == '/' && *Source == '*')
- {
- BOL = false;
- Source++;
- 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')
- 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);
- }
-}
-
-CPreprocessor::Macro *CPreprocessor::IsDefined (const Token &iToken)
-{
- for (Macro *cur = MacroList; cur; cur = cur->Next)
- if (cur->Name == iToken)
- return cur;
-
- return NULL;
-}
-
-CPreprocessor::Token CPreprocessor::ExpandMacro (const Token &iToken)
-{
- Macro *cur = IsDefined (iToken);
- if (cur && !cur->Expanding)
- {
- Token *args = NULL;
- int nargs = 0;
- int old_line = Line;
-
- if (cur->NumArgs != 0)
- {
- Token t = GetArguments (nargs, args, cur->ExpandFunc ? false : true);
- if (t.Type == Token::TK_ERROR)
- {
- delete [] args;
- 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 ();
- }
- }
-
- 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);
-
- delete [] args;
-
- return t;
- }
-
- 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);
-
- Token op (Token::TK_WHITESPACE, "", 0);
-
- // Handle unary operators here
- if (oResult.Type == Token::TK_PUNCTUATION && oResult.Length == 1)
- {
- if (strchr ("+-!~", oResult.String [0]))
- {
- 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);
- Error (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);
- }
-
- 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 (true)
- {
- if (op.Type != Token::TK_PUNCTUATION)
- return op;
-
- 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])
- {
- 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 (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);
- }
-
- 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");
- return Token (Token::TK_ERROR);
- }
- if (op.String [0] == '/')
- oResult.SetValue (vlop / vrop);
- else
- oResult.SetValue (vlop % vrop);
- break;
- }
-
- 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)
- {
- Error (iLine, "Trying to evaluate an empty expression");
- return false;
- }
-
- if (vt->Type == Token::TK_TEXT)
- {
- CPreprocessor cpp (iToken, iLine);
- cpp.MacroList = MacroList;
-
- Token t;
- t = cpp.GetExpression (r, iLine);
-
- cpp.MacroList = NULL;
-
- if (t.Type == Token::TK_ERROR)
- return false;
-
- if (t.Type != Token::TK_EOS)
- {
- Error (iLine, "Garbage after expression", &t);
- return false;
- }
-
- vt = &r;
- }
-
- 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)
- {
- 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);
- return false;
- }
- break;
-
- default:
- Error (iLine, "Unexpected token", vt);
- return false;
- }
-
- return true;
-}
-
-CPreprocessor::Token CPreprocessor::GetArgument (Token &oArg, bool iExpand)
-{
- do
- {
- 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] == ')'))
- {
- Token t = oArg;
- oArg = Token (Token::TK_TEXT, "", 0);
- return t;
- }
- else if (oArg.Type != Token::TK_KEYWORD)
- {
- Error (Line, "Unexpected token", &oArg);
- return Token (Token::TK_ERROR);
- }
- }
-
- unsigned int len = oArg.Length;
- while (true)
- {
- Token t = GetToken (iExpand);
- switch (t.Type)
- {
- case Token::TK_EOS:
- Error (Line, "Unfinished list of arguments");
- FALLTHROUGH;
- case Token::TK_ERROR:
- return Token (Token::TK_ERROR);
- case Token::TK_PUNCTUATION:
- if (t.String [0] == ',' ||
- t.String [0] == ')')
- {
- // Trim whitespaces at the end
- oArg.Length = len;
- return t;
- }
- 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);
- }
-
- oArg.Append (t);
-
- 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
- {
- t = GetToken (iExpand);
- } while (t.Type == Token::TK_WHITESPACE ||
- t.Type == Token::TK_COMMENT ||
- t.Type == Token::TK_LINECOMMENT);
-
- if (t.Type != Token::TK_PUNCTUATION || t.String [0] != '(')
- {
- oNumArgs = 0;
- oArgs = NULL;
- return t;
- }
-
- while (true)
- {
- if (nargs == MAX_MACRO_ARGS)
- {
- Error (Line, "Too many arguments to macro");
- return Token (Token::TK_ERROR);
- }
-
- t = GetArgument (args [nargs++], iExpand);
-
- switch (t.Type)
- {
- case Token::TK_EOS:
- Error (Line, "Unfinished list of arguments");
- 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);
- break;
- }
- }
-
-Done:
- oNumArgs = nargs;
- oArgs = new Token [nargs];
- for (int i = 0; i < nargs; i++)
- oArgs [i] = args [i];
- 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)
- {
- Error (iLine, "Macro name expected after #define");
- return false;
- }
-
- 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);
-
- 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);
- t.Length = cpp.SourceEnd - t.String;
- break;
- }
-
- m->Value = t;
- m->Next = MacroList;
- MacroList = m;
- return true;
-}
-
-bool CPreprocessor::HandleUnDef (Token &iBody, int iLine)
-{
- CPreprocessor cpp (iBody, iLine);
-
- Token t = cpp.GetToken (false);
-
- if (t.Type != Token::TK_KEYWORD)
- {
- Error (iLine, "Expecting a macro name after #undef, got", &t);
- 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)
- Error (iLine, "Warning: Ignoring garbage after directive", &t);
-
- return true;
-}
-
-bool CPreprocessor::HandleIfDef (Token &iBody, int iLine)
-{
- if (EnableOutput & (1 << 31))
- {
- Error (iLine, "Too many embedded #if directives");
- return false;
- }
-
- CPreprocessor cpp (iBody, iLine);
-
- Token t = cpp.GetToken (false);
-
- if (t.Type != Token::TK_KEYWORD)
- {
- Error (iLine, "Expecting a macro name after #ifdef, got", &t);
- return false;
- }
-
- EnableOutput <<= 1;
- if (IsDefined (t))
- EnableOutput |= 1;
-
- 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)
- Error (iLine, "Warning: Ignoring garbage after directive", &t);
-
- return true;
-}
-
-CPreprocessor::Token CPreprocessor::ExpandDefined (CPreprocessor *iParent, int iNumArgs, Token *iArgs)
-{
- if (iNumArgs != 1)
- {
- iParent->Error (iParent->Line, "The defined() function takes exactly one argument");
- return Token (Token::TK_ERROR);
- }
-
- const char *v = iParent->IsDefined (iArgs [0]) ? "1" : "0";
- return Token (Token::TK_NUMBER, v, 1);
-}
-
-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;
-
- if (!rc)
- return false;
-
- EnableOutput <<= 1;
- if (val)
- EnableOutput |= 1;
-
- return true;
-}
-
-bool CPreprocessor::HandleElse (Token &iBody, int iLine)
-{
- if (EnableOutput == 1)
- {
- Error (iLine, "#else without #if");
- return false;
- }
-
- // Negate the result of last #if
- EnableOutput ^= 1;
-
- if (iBody.Length)
- Error (iLine, "Warning: Ignoring garbage after #else", &iBody);
-
- return true;
-}
-
-bool CPreprocessor::HandleEndIf (Token &iBody, int iLine)
-{
- EnableOutput >>= 1;
- if (EnableOutput == 0)
- {
- Error (iLine, "#endif without #if");
- return false;
- }
-
- if (iBody.Length)
- Error (iLine, "Warning: Ignoring garbage after #endif", &iBody);
-
- 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 (;;)
- {
- 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:
-
-#define IS_DIRECTIVE(s) \
- ((dirlen == sizeof (s) - 1) && (strncmp (directive, s, sizeof (s) - 1) == 0))
-
- 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;
- }
-
-#undef IS_DIRECTIVE
-
- 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, const char *iMacroValue)
-{
- Define (iMacroName, strlen(iMacroName), iMacroValue, strlen(iMacroValue));
-}
-
-void CPreprocessor::Define (const char *iMacroName, long iMacroValue)
-{
- Define (iMacroName, strlen(iMacroName), iMacroValue);
-}
-
-bool CPreprocessor::Undef (const char *iMacroName, size_t iMacroNameLen)
-{
- Macro **cur = &MacroList;
- Token name (Token::TK_KEYWORD, iMacroName, iMacroNameLen);
- while (*cur)
- {
- if ((*cur)->Name == name)
- {
- Macro *next = (*cur)->Next;
- (*cur)->Next = NULL;
- delete (*cur);
- *cur = next;
- return true;
- }
-
- cur = &(*cur)->Next;
- }
-
- 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)
- {
- 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;
- 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);
- }
-
- 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;
-
- oLength = retval.Length;
- retval.Allocated = 0;
- return retval.Buffer;
-}
Index: source/ps/PreprocessorWrapper.h
===================================================================
--- source/ps/PreprocessorWrapper.h
+++ source/ps/PreprocessorWrapper.h
@@ -1,44 +0,0 @@
-/* Copyright (C) 2012 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/Preprocessor.h"
-#include "ps/CStr.h"
-
-class CShaderDefines;
-
-/**
- * Convenience wrapper around CPreprocessor.
- */
-class CPreprocessorWrapper
-{
-public:
- void AddDefine(const char* name, const char* value);
-
- void AddDefines(const CShaderDefines& defines);
-
- bool TestConditional(const CStr& expr);
-
- CStr Preprocess(const CStr& input);
-
-private:
- CPreprocessor m_Preprocessor;
-};
-
-#endif // INCLUDED_PREPROCESSORWRAPPER
Index: source/ps/PreprocessorWrapper.cpp
===================================================================
--- source/ps/PreprocessorWrapper.cpp
+++ source/ps/PreprocessorWrapper.cpp
@@ -1,86 +0,0 @@
-/* Copyright (C) 2012 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::AddDefine(const char* name, const char* value)
-{
- m_Preprocessor.Define(name, 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: source/ps/tests/test_Preprocessor.h
===================================================================
--- source/ps/tests/test_Preprocessor.h
+++ source/ps/tests/test_Preprocessor.h
@@ -1,54 +0,0 @@
-/* Copyright (C) 2016 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 "ps/Preprocessor.h"
-
-class TestPreprocessor : public CxxTest::TestSuite
-{
-public:
- void test_basic()
- {
- 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;
- 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);
- }
-};
Index: source/third_party/ogre3d_preprocessor/OgreGLSLPreprocessor.cpp
===================================================================
--- source/third_party/ogre3d_preprocessor/OgreGLSLPreprocessor.cpp
+++ source/third_party/ogre3d_preprocessor/OgreGLSLPreprocessor.cpp
@@ -34,10 +34,8 @@
#include "precompiled.h"
-#include "Preprocessor.h"
+#include "OgreGLSLPreprocessor.h"
-#include "ps/CLogger.h"
-
// Limit max number of macro arguments to this
#define MAX_MACRO_ARGS 16
@@ -230,11 +228,12 @@
const char *iToken, size_t iTokenLen)
{
(void)iData;
+ char line [1000];
if (iToken)
- LOGERROR("Preprocessor error: line %d: %s: '%s'\n",
- iLine, iError, std::string (iToken, iTokenLen));
+ snprintf (line, sizeof (line), "line %d: %s: `%.*s'\n",
+ iLine, iError, int (iTokenLen), iToken);
else
- LOGERROR("Preprocessor error: line %d: %s\n", iLine, iError);
+ snprintf (line, sizeof (line), "line %d: %s\n", iLine, iError);
}
//---------------------------------------------------------------------------//
Index: source/third_party/ogre3d_preprocessor/tests/test_Preprocessor.h
===================================================================
--- source/third_party/ogre3d_preprocessor/tests/test_Preprocessor.h
+++ source/third_party/ogre3d_preprocessor/tests/test_Preprocessor.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2016 Wildfire Games.
+/* Copyright (C) 2019 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -17,11 +17,17 @@
#include "lib/self_test.h"
-#include "ps/Preprocessor.h"
+#include "graphics/PreprocessorWrapper.h"
+#include "third_party/ogre3d_preprocessor/OgreGLSLPreprocessor.h"
class TestPreprocessor : public CxxTest::TestSuite
{
public:
+ void setUp()
+ {
+ CPreprocessor::ErrorHandler = CPreprocessorWrapper::PyrogenesisShaderError;
+ }
+
void test_basic()
{
CPreprocessor preproc;