hbpreprocessor.h 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. /**
  2. * @file hbpreprocessor.h
  3. * @brief HBPreprocessor class definition
  4. *
  5. * HBPreprocessor is a simple sources pre-processor with support for #include,
  6. * #define (plain defines, no macros)/#undef/#if/#ifdef/#ifndef/#elif/#else/
  7. * #endif/#warning/#error directives (it also got special #pragma's).
  8. * It is of course not as complete as boost::wave, but it is about 20 times
  9. * smaller (when comparing stripped binaries).
  10. * It can be used to preprocess Lua or LSL source files, for example.
  11. *
  12. * $LicenseInfo:firstyear=2019&license=viewergpl$
  13. *
  14. * Copyright (c) 2019-2024, Henri Beauchamp.
  15. *
  16. * Second Life Viewer Source Code
  17. * The source code in this file ("Source Code") is provided by Linden Lab
  18. * to you under the terms of the GNU General Public License, version 2.0
  19. * ("GPL"), unless you have obtained a separate licensing agreement
  20. * ("Other License"), formally executed by you and Linden Lab. Terms of
  21. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  22. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  23. *
  24. * There are special exceptions to the terms and conditions of the GPL as
  25. * it is applied to this Source Code. View the full text of the exception
  26. * in the file doc/FLOSS-exception.txt in this software distribution, or
  27. * online at
  28. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  29. *
  30. * By copying, modifying or distributing this software, you acknowledge
  31. * that you have read and understood your obligations described above,
  32. * and agree to abide by those obligations.
  33. *
  34. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  35. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  36. * COMPLETENESS OR PERFORMANCE.
  37. * $/LicenseInfo$
  38. */
  39. #ifndef LL_HBPREPROCESSOR_H
  40. #define LL_HBPREPROCESSOR_H
  41. #include <map>
  42. #include <set>
  43. #include <stack>
  44. #include <string>
  45. #include <vector>
  46. #include "llerror.h"
  47. struct lua_State;
  48. class HBPreprocessor
  49. {
  50. protected:
  51. LOG_CLASS(HBPreprocessor);
  52. public:
  53. enum { PAUSED = -1, FAILURE = 0, SUCCESS = 1 };
  54. // The #include callback gets passed the include name, mDefaultIncludePath,
  55. // as well as the 'userdata' that was set on HBPreprocessor construction,
  56. // and must fill 'buffer' with the contents of the corresponding file or
  57. // asset. The callback should also modify include_name to prepend it with
  58. // the full path (so that it can properly be reported in __FILE__). The
  59. // callback shall return HBPreprocessor::FAILURE on failure to find or load
  60. // the include, HBPreprocessor::PAUSED if the include asset is not yet
  61. // available, or HBPreprocessor::SUCCESS on success.
  62. typedef S32 (*HBPPIncludeCB)(std::string& include_name,
  63. const std::string& default_path,
  64. std::string& buffer, void* userdata);
  65. HBPreprocessor(const std::string& file_name, HBPPIncludeCB callback,
  66. void* userdata = NULL);
  67. ~HBPreprocessor();
  68. // Resets the preprocessed data
  69. void clear();
  70. // Preprocesses 'sources'. Returns HBPreprocessor::FAILURE on failure,
  71. // HBPreprocessor::PAUSED when the preprocessing was paused due to a not
  72. // yet loaded include asset, or HBPreprocessor::SUCCESS on success.
  73. // The pre-processed sources (up to the last valid line when an error
  74. // occurred) can be retrieved with getResult() and any error message can be
  75. // retrieved with getError().
  76. S32 preprocess(const std::string& sources);
  77. // This method is to be called after a pause and the include asset that
  78. // caused it is available. It returns the same results as preprocess()
  79. // above.
  80. S32 resume();
  81. LL_INLINE const std::string& getResult() const { return mPreprocessed; }
  82. LL_INLINE const std::string& getError() const { return mErrorMessage; }
  83. // Returns the line number in the original, non-preprocessed sources, that
  84. // corresponds to the 'line' in the preprocessed sources, or 0 if there is
  85. // no match or no mapping.
  86. LL_INLINE S32 getOriginalLine(S32 line) const
  87. {
  88. if (line < 1 || line > (S32)mLineMapping.size())
  89. {
  90. return 0;
  91. }
  92. return mLineMapping[line - 1];
  93. }
  94. // Used to change the preprocessed file name when needed.
  95. LL_INLINE void setFilename(const std::string& file_name)
  96. {
  97. mFilename = file_name;
  98. }
  99. // This is an optional callback, for the caller to be informed of warning
  100. // and error messsages. The callback gets passed the 'userdata' that was
  101. // set on HBPreprocessor construction.
  102. typedef void (*HBPPMessageCB)(const std::string& message, bool is_warning,
  103. void* userdata);
  104. // This allows to set the optional error and warning message callback
  105. LL_INLINE void setMessageCallback(HBPPMessageCB callback)
  106. {
  107. mMessageCallback = callback;
  108. }
  109. // This is for language-specifc needs, when you want to prevent the use
  110. // of #define or #undef with special language tokens (e.g. "_G" for Lua).
  111. LL_INLINE void addForbiddenToken(const char* token)
  112. {
  113. mForbiddenTokens.emplace(token);
  114. }
  115. // This returns any <path> in the last '#pragma include-from: <path>'
  116. // directive encountered in the unprocessed sources, or an empty string
  117. // when no such directive was encountered.
  118. LL_INLINE const std::string& getDefaultIncludePath() const
  119. {
  120. return mDefaultIncludePath;
  121. }
  122. // Returns the value for 'name' if the latter is defined, else returns
  123. // 'name' itself.
  124. std::string getDefine(const std::string& name) const;
  125. // Returns true when 'sources' contains preprocessor directives
  126. static bool needsPreprocessing(const std::string& sources);
  127. // Tries and evaluates a mathematical 'expression' using any provided Lua
  128. // state, or creating a temporary one when none is provided; returns 'true'
  129. // and the result in 'expression' on success, or 'false' (and a tab-clean,
  130. // space-trimmed 'expression) on failure.
  131. // Valid operators for the math expression are: +, -, *, /, %, ^
  132. // Valid math functions are: acos(), cos(), asin(), sin(), atan(), tan(),
  133. // abs() (or fabs()), mod() (or fmod()), max(), min(), ceil(), floor(),
  134. // int(), deg(), rad(), sqrt(), exp(), log(), rand().
  135. // You may also use 'PI' as a constant in the expression.
  136. static bool evaluate(std::string& expression, lua_State* statep = NULL);
  137. private:
  138. void parsingError(const std::string& error_msg, bool is_warning = false);
  139. bool isValidToken(const std::string& token);
  140. // Replaces 'defined(TOKEN)' expressions in 'expr' with either 1 when TOKEN
  141. // is defined, or 0 when not, then returns the result.
  142. std::string replaceDefinedInExpr(std::string expr);
  143. // Replaces in 'line' all defined tokens with their value and returns the
  144. // result.
  145. std::string replaceDefinesInLine(const std::string& line);
  146. // Replaces 'eval(MATH_EPRESSION)' expressions in 'expr' with the result of
  147. // the evaluation of MATH_EPRESSION, then returns the result. When the
  148. // 'replace_tokens' boolean is true, each #defined tokens *inside* the each
  149. // eval() expression is also replaced with their value.
  150. std::string replaceEvalInExpr(std::string expr,
  151. bool replace_tokens = false);
  152. bool skipToElseOrEndif(const std::string& buffer, size_t& pos);
  153. // Method used to evaluate a logical expression using Lua. Note that the
  154. // "!=", "||", "&&", "!" and "^" operators are automatically translated
  155. // into their Lua equivalent. This method returns a bool, which is true if
  156. // the result is not zero or false otherwise (including in case of error).
  157. // In case of error, the mErrorMessage variables is set.
  158. bool isExpressionTrue(std::string expression);
  159. // Used to pre-populate the defines table with the default (and constant
  160. // during preprocess() execution) defines, namely __DATE__, __TIME__,
  161. // __AGENT_ID__ and __AGENT_NAME__.
  162. void setDefaultDefines();
  163. private:
  164. S32 (*mIncludeCallback)(std::string& include_name,
  165. const std::string& path,
  166. std::string& buffer,
  167. void* userdata);
  168. void (*mMessageCallback)(const std::string& message,
  169. bool is_warning,
  170. void* userdata);
  171. void* mCallbackUserData;
  172. lua_State* mLuaState;
  173. S32 mCurrentLine;
  174. S32 mSavedPos;
  175. // This is the line of the root #include directive in the unprocessed
  176. // sources. It is used to map #included sources preprocessed lines with
  177. // unprocessed sources lines.
  178. S32 mRootIncludeLine;
  179. std::string mFilename;
  180. std::string mErrorMessage;
  181. std::string mSourcesBuffer;
  182. std::string mIncludeBuffer;
  183. std::string mPreprocessed;
  184. std::string mDefaultIncludePath;
  185. typedef std::map<std::string, std::string> defines_map_t;
  186. defines_map_t mDefines;
  187. std::set<std::string> mIncludes;
  188. std::set<std::string> mForbiddenTokens;
  189. std::stack<std::string> mFilenames;
  190. std::stack<bool> mIfClauses;
  191. // This is the line mapping: each entry in this vector corresponds to a
  192. // line of the preprocessed sources (with line 1 at index 0 of the vector)
  193. // and contains the corresponding line number in the unprocessed sources
  194. // (for include files, it is the line number of the root #include directive
  195. // in the unprocessed sources).
  196. typedef std::vector<S32> line_map_vec_t;
  197. line_map_vec_t mLineMapping;
  198. };
  199. // Extracts one line of text from a buffer: the line shall always be terminated
  200. // with a line feed (true for Linux, macOS and even Windows that also got a
  201. // carriage return before the line feed). I.e. we will not be able to deal with
  202. // the deprecated MacOS (not X) text files format, which ended with a carriage
  203. // return, but this should not be an issue nowadays...
  204. // The line feed (and possible carriage return) is part of the returned 'line'.
  205. // On return, 'pos' is updated to point to the start of the next line in the
  206. // buffer.
  207. // Used also in llpreviewscript.cpp
  208. std::string get_one_line(const std::string& buffer, size_t& pos);
  209. #endif // LL_HBPREPROCESSOR_H