Line data Source code
1 : /*
2 : * This source file originally came from OGRE v1.12.4 - http://www.ogre3d.org/
3 : * with some tweaks as part of 0 A.D.
4 : * All changes are released under the original license, as follows:
5 : */
6 :
7 : /*
8 : -----------------------------------------------------------------------------
9 : This source file is part of OGRE
10 : (Object-oriented Graphics Rendering Engine)
11 : For the latest info, see http://www.ogre3d.org/
12 :
13 : Copyright (c) 2000-2014 Torus Knot Software Ltd
14 :
15 : Permission is hereby granted, free of charge, to any person obtaining a copy
16 : of this software and associated documentation files (the "Software"), to deal
17 : in the Software without restriction, including without limitation the rights
18 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
19 : copies of the Software, and to permit persons to whom the Software is
20 : furnished to do so, subject to the following conditions:
21 :
22 : The above copyright notice and this permission notice shall be included in
23 : all copies or substantial portions of the Software.
24 :
25 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
31 : THE SOFTWARE.
32 : -----------------------------------------------------------------------------
33 : */
34 :
35 : #ifndef __OGRE_CPREPROCESSOR_H__
36 : #define __OGRE_CPREPROCESSOR_H__
37 :
38 : #include <forward_list>
39 : #include <stdlib.h>
40 : #include <string.h>
41 : #include <vector>
42 :
43 : namespace Ogre
44 : {
45 :
46 : /**
47 : * This is a simplistic C/C++-like preprocessor.
48 : * It takes an non-zero-terminated string on input and outputs a
49 : * non-zero-terminated string buffer.
50 : *
51 : * This preprocessor was designed specifically for GLSL shaders, so
52 : * if you want to use it for other purposes you might want to check
53 : * if the feature set it provides is enough for you.
54 : *
55 : * Here's a list of supported features:
56 : * - Fast memory allocation-less operation (mostly).
57 : * - Line continuation (backslash-newline) is swallowed.
58 : * - Line numeration is fully preserved by inserting empty lines where
59 : * required. This is crucial if, say, GLSL compiler reports you an error
60 : * with a line number.
61 : * - @c \#define: Parametrized and non-parametrized macros. Invoking a macro with
62 : * less arguments than it takes assignes empty values to missing arguments.
63 : * - @c \#undef: Forget defined macros
64 : * - @c \#ifdef / @c \#ifndef / @c \#else / @c \#endif: Conditional suppression of parts of code.
65 : * - @c \#if: Supports numeric expression of any complexity, also supports the
66 : * defined() pseudo-function.
67 : */
68 : class CPreprocessor
69 : {
70 : public:
71 : /**
72 : * A input token.
73 : *
74 : * For performance reasons most tokens will point to portions of the
75 : * input stream, so no unneeded memory allocation is done. However,
76 : * in some cases we must allocate different memory for token storage,
77 : * in this case this is signalled by setting the Allocated member
78 : * to non-zero in which case the destructor will know that it must
79 : * free memory on object destruction.
80 : *
81 : * Again for performance reasons we use malloc/realloc/free here because
82 : * C++-style new[] lacks the realloc() counterpart.
83 : */
84 : class Token
85 : {
86 : public:
87 : enum Kind
88 : {
89 : TK_EOS, // End of input stream
90 : TK_ERROR, // An error has been encountered
91 : TK_WHITESPACE, // A whitespace span (but not newline)
92 : TK_NEWLINE, // A single newline (CR & LF)
93 : TK_LINECONT, // Line continuation ('\' followed by LF)
94 : TK_NUMBER, // A number
95 : TK_KEYWORD, // A keyword
96 : TK_PUNCTUATION, // A punctuation character
97 : TK_DIRECTIVE, // A preprocessor directive
98 : TK_STRING, // A string
99 : TK_COMMENT, // A block comment
100 : TK_LINECOMMENT, // A line comment
101 : TK_TEXT // An unparsed text (cannot be returned from GetToken())
102 : };
103 :
104 : /// Token type
105 : Kind Type;
106 : /// True if string was allocated (and must be freed)
107 : mutable size_t Allocated;
108 : union
109 : {
110 : /// A pointer somewhere into the input buffer
111 : const char *String;
112 : /// A memory-allocated string
113 : char *Buffer;
114 : };
115 : /// Token length in bytes
116 : size_t Length;
117 :
118 444 : Token () : Allocated (0), String (NULL), Length(0)
119 444 : { }
120 :
121 49 : Token (Kind iType) : Type (iType), Allocated (0), String (NULL), Length(0)
122 49 : { }
123 :
124 656 : Token (Kind iType, const char *iString, size_t iLength) :
125 656 : Type (iType), Allocated (0), String (iString), Length (iLength)
126 656 : { }
127 :
128 338 : Token (const Token &iOther)
129 338 : {
130 338 : Type = iOther.Type;
131 338 : Allocated = iOther.Allocated;
132 338 : iOther.Allocated = 0; // !!! not quite correct but effective
133 338 : String = iOther.String;
134 338 : Length = iOther.Length;
135 338 : }
136 :
137 1487 : ~Token ()
138 1487 : { if (Allocated) free (Buffer); }
139 :
140 : /// Assignment operator
141 456 : Token &operator = (const Token &iOther)
142 : {
143 456 : if (Allocated) free (Buffer);
144 456 : Type = iOther.Type;
145 456 : Allocated = iOther.Allocated;
146 456 : iOther.Allocated = 0; // !!! not quite correct but effective
147 456 : String = iOther.String;
148 456 : Length = iOther.Length;
149 456 : return *this;
150 : }
151 :
152 : /// Append a string to this token
153 : void Append (const char *iString, size_t iLength);
154 :
155 : /// Append a token to this token
156 : void Append (const Token &iOther);
157 :
158 : /// Append given number of newlines to this token
159 : void AppendNL (int iCount);
160 :
161 : /// Count number of newlines in this token
162 : int CountNL ();
163 :
164 : /// Get the numeric value of the token
165 : bool GetValue (long &oValue) const;
166 :
167 : /// Set the numeric value of the token
168 : void SetValue (long iValue);
169 :
170 : /// Test two tokens for equality
171 141 : bool operator == (const Token &iOther)
172 : {
173 141 : if (iOther.Length != Length)
174 82 : return false;
175 59 : return (memcmp (String, iOther.String, Length) == 0);
176 : }
177 : };
178 :
179 : /// A macro definition
180 51 : class Macro
181 : {
182 : public:
183 : /// Macro name
184 : Token Name;
185 : /// The names of the arguments
186 : std::vector<Token> Args;
187 : /// The macro value
188 : Token Value;
189 : /// Unparsed macro body (keeps the whole raw unparsed macro body)
190 : Token Body;
191 : /// A pointer to function implementation (if macro is really a func)
192 : Token (*ExpandFunc) (CPreprocessor *iParent, const std::vector<Token>& iArgs);
193 : /// true if macro expansion is in progress
194 : bool Expanding;
195 :
196 27 : Macro(const Token& iName) : Name(iName), ExpandFunc(NULL), Expanding(false) {}
197 :
198 : /// Expand the macro value (will not work for functions)
199 : Token Expand (const std::vector<Token>& iArgs, std::forward_list<Macro>& iMacros);
200 : };
201 :
202 : friend class CPreprocessor::Macro;
203 :
204 : /// The current source text input
205 : const char *Source;
206 : /// The end of the source text
207 : const char *SourceEnd;
208 : /// Current line number
209 : int Line;
210 : /// True if we are at beginning of line
211 : bool BOL;
212 : /// A stack of 32 booleans packed into one value :)
213 : unsigned EnableOutput;
214 : unsigned EnableElif;
215 : /// The list of macros defined so far
216 : std::forward_list<Macro> MacroList;
217 :
218 : /**
219 : * Private constructor to re-parse a single token.
220 : */
221 : CPreprocessor (const Token &iToken, int iLine);
222 :
223 : /**
224 : * Stateless tokenizer: Parse the input text and return the next token.
225 : * @param iExpand
226 : * If true, macros will be expanded to their values
227 : * @return
228 : * The next token from the input stream
229 : */
230 : Token GetToken (bool iExpand);
231 :
232 : /**
233 : * Handle a preprocessor directive.
234 : * @param iToken
235 : * The whole preprocessor directive line (until EOL)
236 : * @param iLine
237 : * The line where the directive begins (for error reports)
238 : * @return
239 : * The last input token that was not proceeded.
240 : */
241 : Token HandleDirective (Token &iToken, int iLine);
242 :
243 : /**
244 : * Handle a #define directive.
245 : * @param iBody
246 : * The body of the directive (everything after the directive
247 : * until end of line).
248 : * @param iLine
249 : * The line where the directive begins (for error reports)
250 : * @return
251 : * true if everything went ok, false if not
252 : */
253 : bool HandleDefine (Token &iBody, int iLine);
254 :
255 : /**
256 : * Undefine a previously defined macro
257 : * @param iBody
258 : * The body of the directive (everything after the directive
259 : * until end of line).
260 : * @param iLine
261 : * The line where the directive begins (for error reports)
262 : * @return
263 : * true if everything went ok, false if not
264 : */
265 : bool HandleUnDef (Token &iBody, int iLine);
266 :
267 : /**
268 : * Handle an #ifdef directive.
269 : * @param iBody
270 : * The body of the directive (everything after the directive
271 : * until end of line).
272 : * @param iLine
273 : * The line where the directive begins (for error reports)
274 : * @return
275 : * true if everything went ok, false if not
276 : */
277 : bool HandleIfDef (Token &iBody, int iLine);
278 :
279 : /**
280 : * Handle an #if directive.
281 : * @param iBody
282 : * The body of the directive (everything after the directive
283 : * until end of line).
284 : * @param iLine
285 : * The line where the directive begins (for error reports)
286 : * @return
287 : * true if everything went ok, false if not
288 : */
289 : bool HandleIf (Token &iBody, int iLine);
290 :
291 : /// @overload
292 : bool HandleIf(bool val, int iLine);
293 :
294 : /**
295 : * Handle an #elif directive.
296 : * @param iBody
297 : * The body of the directive (everything after the directive
298 : * until end of line).
299 : * @param iLine
300 : * The line where the directive begins (for error reports)
301 : * @return
302 : * true if everything went ok, false if not
303 : */
304 : bool HandleElif (Token &iBody, int iLine);
305 :
306 : /**
307 : * Handle an #else directive.
308 : * @param iBody
309 : * The body of the directive (everything after the directive
310 : * until end of line).
311 : * @param iLine
312 : * The line where the directive begins (for error reports)
313 : * @return
314 : * true if everything went ok, false if not
315 : */
316 : bool HandleElse (Token &iBody, int iLine);
317 :
318 : /**
319 : * Handle an #endif directive.
320 : * @param iBody
321 : * The body of the directive (everything after the directive
322 : * until end of line).
323 : * @param iLine
324 : * The line where the directive begins (for error reports)
325 : * @return
326 : * true if everything went ok, false if not
327 : */
328 : bool HandleEndIf (Token &iBody, int iLine);
329 :
330 : /**
331 : * Get a single function argument until next ',' or ')'.
332 : * @param oArg
333 : * The argument is returned in this variable.
334 : * @param iExpand
335 : * If false, parameters are not expanded and no expressions are
336 : * allowed; only a single keyword is expected per argument.
337 : * @param shouldAppendArg
338 : * When true, the argument will be appended the word word __arg_
339 : * e.g. #define myMacro(x) --> #define myMacro(x__arg_)
340 : * This workaround a bug where calling myMacro( x ) would cause
341 : * issues.
342 : * @return
343 : * The first unhandled token after argument.
344 : */
345 : Token GetArgument (Token &oArg, bool iExpand, bool shouldAppendArg);
346 :
347 : /**
348 : * Get all the arguments of a macro: '(' arg1 { ',' arg2 { ',' ... }} ')'
349 : * @param oArgs
350 : * This is set to a pointer to an array of parsed arguments.
351 : * @param shouldAppendArg
352 : * See GetArgument.
353 : * @param iExpand
354 : * If false, parameters are not expanded and no expressions are
355 : * allowed; only a single keyword is expected per argument.
356 : */
357 : Token GetArguments (std::vector<Token>& oArgs, bool iExpand, bool shouldAppendArg);
358 :
359 : /**
360 : * Parse an expression, compute it and return the result.
361 : * @param oResult
362 : * A token containing the result of expression
363 : * @param iLine
364 : * The line at which the expression starts (for error reports)
365 : * @param iOpPriority
366 : * Operator priority (at which operator we will stop if
367 : * proceeding recursively -- used internally. Parser stops
368 : * when it encounters an operator with higher or equal priority).
369 : * @return
370 : * The last unhandled token after the expression
371 : */
372 : Token GetExpression (Token &oResult, int iLine, int iOpPriority = 0);
373 :
374 : /**
375 : * Get the numeric value of a token.
376 : * If the token was produced by expanding a macro, we will get
377 : * an TEXT token which can contain a whole expression; in this
378 : * case we will call GetExpression to parse it. Otherwise we
379 : * just call the token's GetValue() method.
380 : * @param iToken
381 : * The token to get the numeric value of
382 : * @param oValue
383 : * The variable to put the value into
384 : * @param iLine
385 : * The line where the directive begins (for error reports)
386 : * @return
387 : * true if ok, false if not
388 : */
389 : bool GetValue (const Token &iToken, long &oValue, int iLine);
390 :
391 : /// @overload
392 : /// same as above, but considers the defined() function
393 : bool GetValueDef(const Token &iToken, long &oValue, int iLine);
394 :
395 : /**
396 : * Expand the given macro, if it exists.
397 : * If macro has arguments, they are collected from source stream.
398 : * @param iToken
399 : * A KEYWORD token containing the (possible) macro name.
400 : * @return
401 : * The expanded token or iToken if it is not a macro
402 : */
403 : Token ExpandMacro (const Token &iToken);
404 :
405 : /**
406 : * Check if a macro is defined, and if so, return it
407 : * @param iToken
408 : * Macro name
409 : * @return
410 : * The macro object or NULL if a macro with this name does not exist
411 : */
412 : Macro *IsDefined (const Token &iToken);
413 :
414 : /**
415 : * The implementation of the defined() preprocessor function
416 : * @param iParent
417 : * The parent preprocessor object
418 : * @param iArgs
419 : * The arguments themselves
420 : * @return
421 : * The return value encapsulated in a token
422 : */
423 : static Token ExpandDefined (CPreprocessor *iParent, const std::vector<Token>& iArgs);
424 :
425 : /**
426 : * Parse the input string and return a token containing the whole output.
427 : * @param iSource
428 : * The source text enclosed in a token
429 : * @return
430 : * The output text enclosed in a token
431 : */
432 : Token Parse (const Token &iSource);
433 :
434 : /**
435 : * Call the error handler
436 : * @param iLine
437 : * The line at which the error happened.
438 : * @param iError
439 : * The error string.
440 : * @param iToken
441 : * If not NULL contains the erroneous token
442 : */
443 : static void Error (int iLine, const char *iError, const Token *iToken = NULL);
444 :
445 : public:
446 : /// Create an empty preprocessor object
447 31 : CPreprocessor() {}
448 :
449 : /// Destroy the preprocessor object
450 : virtual ~CPreprocessor ();
451 :
452 : /**
453 : * Define a macro without parameters.
454 : * @param iMacroName
455 : * The name of the defined macro
456 : * @param iMacroNameLen
457 : * The length of the name of the defined macro
458 : * @param iMacroValue
459 : * The value of the defined macro
460 : * @param iMacroValueLen
461 : * The length of the value of the defined macro
462 : */
463 : void Define (const char *iMacroName, size_t iMacroNameLen,
464 : const char *iMacroValue, size_t iMacroValueLen);
465 :
466 : /**
467 : * Define a numerical macro.
468 : * @param iMacroName
469 : * The name of the defined macro
470 : * @param iMacroNameLen
471 : * The length of the name of the defined macro
472 : * @param iMacroValue
473 : * The value of the defined macro
474 : */
475 : void Define (const char *iMacroName, size_t iMacroNameLen, long iMacroValue);
476 :
477 : /**
478 : * Undefine a macro.
479 : * @param iMacroName
480 : * The name of the macro to undefine
481 : * @param iMacroNameLen
482 : * The length of the name of the macro to undefine
483 : * @return
484 : * true if the macro has been undefined, false if macro doesn't exist
485 : */
486 : bool Undef (const char *iMacroName, size_t iMacroNameLen);
487 :
488 : /**
489 : * Parse the input string and return a newly-allocated output string.
490 : * @note
491 : * The returned preprocessed string is NOT zero-terminated
492 : * (just like the input string).
493 : * @param iSource
494 : * The source text
495 : * @param iLength
496 : * The length of the source text in characters
497 : * @param oLength
498 : * The length of the output string.
499 : * @return
500 : * The output from preprocessor, allocated with malloc().
501 : * The parser can actually allocate more than needed for performance
502 : * reasons, but this should not be a problem unless you will want
503 : * to store the returned pointer for long time in which case you
504 : * might want to realloc() it.
505 : * If an error has been encountered, the function returns NULL.
506 : * In some cases the function may return an unallocated address
507 : * that's *inside* the source buffer. You must free() the result
508 : * string only if the returned address is not inside the source text.
509 : */
510 : char *Parse (const char *iSource, size_t iLength, size_t &oLength);
511 :
512 : /**
513 : * Call the error handler
514 : * @param iLine
515 : * The line at which the error happened.
516 : * @param iError
517 : * The error string.
518 : * @param iToken
519 : * If not NULL contains the erroneous token
520 : */
521 : typedef void (*ErrorHandlerFunc) (int iLine, const char *iError, const Token *iToken);
522 :
523 : /**
524 : * A pointer to the preprocessor's error handler.
525 : * You can assign the address of your own function to this variable
526 : * and implement your own error handling (e.g. throwing an exception etc).
527 : */
528 : static ErrorHandlerFunc ErrorHandler;
529 :
530 : };
531 :
532 : } // namespace Ogre
533 :
534 : #endif // __OGRE_CPREPROCESSOR_H__
|