Line data Source code
1 : /*
2 : * This source file originally came from OGRE v1.12.3 - 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 : #include "precompiled.h"
36 :
37 : #include "lib/debug.h"
38 : #include "OgreGLSLPreprocessor.h"
39 :
40 : #include <ctype.h>
41 : #include <stdio.h>
42 :
43 : namespace Ogre
44 : {
45 :
46 : // Limit max number of macro arguments to this
47 : #define MAX_MACRO_ARGS 16
48 :
49 : /// Return closest power of two not smaller than given number
50 158 : static size_t ClosestPow2 (size_t x)
51 : {
52 158 : if (!(x & (x - 1)))
53 12 : return x;
54 772 : while (x & (x + 1))
55 313 : x |= (x + 1);
56 146 : return x + 1;
57 : }
58 :
59 12 : void CPreprocessor::Token::Append (const char *iString, size_t iLength)
60 : {
61 24 : Token t (Token::TK_TEXT, iString, iLength);
62 12 : Append (t);
63 12 : }
64 :
65 319 : void CPreprocessor::Token::Append (const Token &iOther)
66 : {
67 319 : if (!iOther.String)
68 0 : return;
69 :
70 319 : if (!String)
71 : {
72 27 : String = iOther.String;
73 27 : Length = iOther.Length;
74 27 : Allocated = iOther.Allocated;
75 27 : iOther.Allocated = 0; // !!! not quite correct but effective
76 27 : return;
77 : }
78 :
79 292 : if (Allocated)
80 : {
81 143 : size_t new_alloc = ClosestPow2 (Length + iOther.Length);
82 143 : if (new_alloc < 64)
83 74 : new_alloc = 64;
84 143 : if (new_alloc != Allocated)
85 : {
86 2 : Allocated = new_alloc;
87 2 : Buffer = (char *)realloc (Buffer, Allocated);
88 : }
89 : }
90 149 : else if (String + Length != iOther.String)
91 : {
92 15 : Allocated = ClosestPow2 (Length + iOther.Length);
93 15 : if (Allocated < 64)
94 15 : Allocated = 64;
95 15 : char *newstr = (char *)malloc (Allocated);
96 15 : memcpy (newstr, String, Length);
97 15 : Buffer = newstr;
98 : }
99 :
100 292 : if (Allocated)
101 158 : memcpy (Buffer + Length, iOther.String, iOther.Length);
102 292 : Length += iOther.Length;
103 : }
104 :
105 7 : bool CPreprocessor::Token::GetValue (long &oValue) const
106 : {
107 7 : long val = 0;
108 7 : size_t i = 0;
109 :
110 7 : while (isspace (String [i]))
111 0 : i++;
112 :
113 7 : long base = 10;
114 7 : if (String [i] == '0')
115 : {
116 4 : if (Length > i + 1 && String [i + 1] == 'x')
117 0 : base = 16, i += 2;
118 : else
119 4 : base = 8;
120 : }
121 :
122 21 : for (; i < Length; i++)
123 : {
124 7 : int c = int (String [i]);
125 7 : if (isspace (c))
126 : // Possible end of number
127 0 : break;
128 :
129 7 : if (c >= 'a' && c <= 'z')
130 0 : c -= ('a' - 'A');
131 :
132 7 : c -= '0';
133 7 : if (c < 0)
134 0 : return false;
135 :
136 7 : if (c > 9)
137 0 : c -= ('A' - '9' - 1);
138 :
139 7 : if (c >= base)
140 0 : return false;
141 :
142 7 : val = (val * base) + c;
143 : }
144 :
145 : // Check that all other characters are just spaces
146 7 : for (; i < Length; i++)
147 0 : if (!isspace (String [i]))
148 0 : return false;
149 :
150 7 : oValue = val;
151 7 : return true;
152 : }
153 :
154 :
155 0 : void CPreprocessor::Token::SetValue (long iValue)
156 : {
157 : char tmp [21];
158 0 : int len = snprintf (tmp, sizeof (tmp), "%ld", iValue);
159 0 : Length = 0;
160 0 : Append (tmp, len);
161 0 : Type = TK_NUMBER;
162 0 : }
163 :
164 :
165 69 : void CPreprocessor::Token::AppendNL (int iCount)
166 : {
167 : static const char newlines [8] =
168 : { '\n', '\n', '\n', '\n', '\n', '\n', '\n', '\n' };
169 :
170 69 : while (iCount > 8)
171 : {
172 0 : Append (newlines, 8);
173 0 : iCount -= 8;
174 : }
175 69 : if (iCount > 0)
176 8 : Append (newlines, iCount);
177 69 : }
178 :
179 :
180 44 : int CPreprocessor::Token::CountNL ()
181 : {
182 44 : if (Type == TK_EOS || Type == TK_ERROR)
183 2 : return 0;
184 :
185 42 : const char *s = String;
186 42 : size_t l = Length;
187 42 : int c = 0;
188 94 : while (l > 0)
189 : {
190 42 : const char *n = (const char *)memchr (s, '\n', l);
191 42 : if (!n)
192 16 : return c;
193 26 : c++;
194 26 : l -= (n - s + 1);
195 26 : s = n + 1;
196 : }
197 26 : return c;
198 : }
199 :
200 18 : CPreprocessor::Token CPreprocessor::Macro::Expand(const std::vector<Token>& iArgs,
201 : std::forward_list<Macro>& iMacros)
202 : {
203 18 : Expanding = true;
204 :
205 36 : CPreprocessor cpp;
206 18 : std::swap(cpp.MacroList, iMacros);
207 :
208 : // Define a new macro for every argument
209 : size_t i;
210 22 : for (i = 0; i < iArgs.size(); i++)
211 4 : cpp.Define (Args [i].String, Args [i].Length,
212 4 : iArgs [i].String, iArgs [i].Length);
213 : // The rest arguments are empty
214 18 : for (; i < Args.size(); i++)
215 0 : cpp.Define (Args [i].String, Args [i].Length, "", 0);
216 :
217 : // Now run the macro expansion through the supplimentary preprocessor
218 18 : Token xt = cpp.Parse (Value);
219 :
220 18 : Expanding = false;
221 :
222 : // Remove the extra macros we have defined
223 22 : for (int j = Args.size() - 1; j >= 0; j--)
224 4 : cpp.Undef (Args [j].String, Args [j].Length);
225 :
226 18 : std::swap(cpp.MacroList, iMacros);
227 :
228 36 : return xt;
229 : }
230 :
231 0 : void CPreprocessor::Error(int iLine, const char *iError, const Token *iToken)
232 : {
233 : char line[1000];
234 0 : if (iToken)
235 0 : snprintf(line, sizeof(line), "line %d: %s: `%.*s'\n",
236 0 : iLine, iError, int(iToken->Length), iToken->String);
237 : else
238 0 : snprintf(line, sizeof(line), "line %d: %s\n", iLine, iError);
239 0 : }
240 :
241 : CPreprocessor::ErrorHandlerFunc CPreprocessor::ErrorHandler = CPreprocessor::Error;
242 :
243 23 : CPreprocessor::CPreprocessor (const Token &iToken, int iLine)
244 : {
245 23 : Source = iToken.String;
246 23 : SourceEnd = iToken.String + iToken.Length;
247 23 : EnableOutput = 1;
248 23 : EnableElif = 0;
249 23 : Line = iLine;
250 23 : BOL = true;
251 23 : }
252 :
253 54 : CPreprocessor::~CPreprocessor() {}
254 :
255 607 : CPreprocessor::Token CPreprocessor::GetToken (bool iExpand)
256 : {
257 607 : if (Source >= SourceEnd)
258 14 : return Token (Token::TK_EOS);
259 :
260 593 : const char *begin = Source;
261 593 : char c = *Source++;
262 :
263 :
264 593 : if (c == '\n' || (c == '\r' && *Source == '\n'))
265 : {
266 101 : Line++;
267 101 : BOL = true;
268 101 : if (c == '\r')
269 0 : Source++;
270 101 : return Token (Token::TK_NEWLINE, begin, Source - begin);
271 : }
272 492 : else if (isspace (c))
273 : {
274 715 : while (Source < SourceEnd &&
275 590 : *Source != '\r' &&
276 890 : *Source != '\n' &&
277 294 : isspace (*Source))
278 119 : Source++;
279 :
280 182 : return Token (Token::TK_WHITESPACE, begin, Source - begin);
281 : }
282 310 : else if (isdigit (c))
283 : {
284 85 : BOL = false;
285 85 : if (c == '0' && Source < SourceEnd && Source [0] == 'x') // hex numbers
286 : {
287 0 : Source++;
288 0 : while (Source < SourceEnd && isxdigit (*Source))
289 0 : Source++;
290 : }
291 : else
292 147 : while (Source < SourceEnd && isdigit (*Source))
293 31 : Source++;
294 85 : return Token (Token::TK_NUMBER, begin, Source - begin);
295 : }
296 225 : else if (c == '_' || isalnum (c))
297 : {
298 106 : BOL = false;
299 702 : while (Source < SourceEnd && (*Source == '_' || isalnum (*Source)))
300 298 : Source++;
301 212 : Token t (Token::TK_KEYWORD, begin, Source - begin);
302 106 : if (iExpand)
303 42 : t = ExpandMacro (t);
304 106 : return t;
305 : }
306 119 : else if (c == '"' || c == '\'')
307 : {
308 0 : BOL = false;
309 0 : while (Source < SourceEnd && *Source != c)
310 : {
311 0 : if (*Source == '\\')
312 : {
313 0 : Source++;
314 0 : if (Source >= SourceEnd)
315 0 : break;
316 : }
317 0 : if (*Source == '\n')
318 0 : Line++;
319 0 : Source++;
320 : }
321 0 : if (Source < SourceEnd)
322 0 : Source++;
323 0 : return Token (Token::TK_STRING, begin, Source - begin);
324 : }
325 119 : else if (c == '/' && *Source == '/')
326 : {
327 0 : BOL = false;
328 0 : Source++;
329 0 : while (Source < SourceEnd && *Source != '\r' && *Source != '\n')
330 0 : Source++;
331 0 : return Token (Token::TK_LINECOMMENT, begin, Source - begin);
332 : }
333 119 : else if (c == '/' && *Source == '*')
334 : {
335 0 : BOL = false;
336 0 : Source++;
337 0 : while (Source < SourceEnd && (Source [0] != '*' || Source [1] != '/'))
338 : {
339 0 : if (*Source == '\n')
340 0 : Line++;
341 0 : Source++;
342 : }
343 0 : if (Source < SourceEnd && *Source == '*')
344 0 : Source++;
345 0 : if (Source < SourceEnd && *Source == '/')
346 0 : Source++;
347 0 : return Token (Token::TK_COMMENT, begin, Source - begin);
348 : }
349 119 : else if (c == '#' && BOL)
350 : {
351 : // Skip all whitespaces after '#'
352 55 : while (Source < SourceEnd && isspace (*Source))
353 0 : Source++;
354 577 : while (Source < SourceEnd && !isspace (*Source))
355 261 : Source++;
356 55 : return Token (Token::TK_DIRECTIVE, begin, Source - begin);
357 : }
358 64 : else if (c == '\\' && Source < SourceEnd && (*Source == '\r' || *Source == '\n'))
359 : {
360 : // Treat backslash-newline as a whole token
361 0 : if (*Source == '\r')
362 0 : Source++;
363 0 : if (*Source == '\n')
364 0 : Source++;
365 0 : Line++;
366 0 : BOL = true;
367 0 : return Token (Token::TK_LINECONT, begin, Source - begin);
368 : }
369 : else
370 : {
371 64 : BOL = false;
372 : // Handle double-char operators here
373 64 : if (c == '>' && (*Source == '>' || *Source == '='))
374 0 : Source++;
375 64 : else if (c == '<' && (*Source == '<' || *Source == '='))
376 0 : Source++;
377 64 : else if (c == '!' && *Source == '=')
378 0 : Source++;
379 64 : else if (c == '=' && *Source == '=')
380 0 : Source++;
381 64 : else if ((c == '|' || c == '&' || c == '^') && *Source == c)
382 0 : Source++;
383 64 : return Token (Token::TK_PUNCTUATION, begin, Source - begin);
384 : }
385 : }
386 :
387 :
388 52 : CPreprocessor::Macro *CPreprocessor::IsDefined (const Token &iToken)
389 : {
390 160 : for (Macro& cur : MacroList)
391 134 : if (cur.Name == iToken)
392 26 : return &cur;
393 :
394 26 : return NULL;
395 : }
396 :
397 :
398 44 : CPreprocessor::Token CPreprocessor::ExpandMacro (const Token &iToken)
399 : {
400 44 : Macro *cur = IsDefined (iToken);
401 44 : if (cur && !cur->Expanding)
402 : {
403 40 : std::vector<Token> args;
404 20 : int old_line = Line;
405 :
406 20 : if (!cur->Args.empty())
407 : {
408 6 : Token t = GetArguments (args, cur->ExpandFunc ? false : true, false);
409 3 : if (t.Type == Token::TK_ERROR)
410 : {
411 0 : return t;
412 : }
413 :
414 : // Put the token back into the source pool; we'll handle it later
415 3 : if (t.String)
416 : {
417 : // Returned token should never be allocated on heap
418 1 : ENSURE(t.Allocated == 0);
419 1 : Source = t.String;
420 1 : Line -= t.CountNL ();
421 : }
422 : }
423 :
424 20 : if (args.size() > cur->Args.size())
425 : {
426 : char tmp [60];
427 0 : snprintf (tmp, sizeof (tmp), "Macro `%.*s' passed %zu arguments, but takes just %zu",
428 0 : int (cur->Name.Length), cur->Name.String,
429 : args.size(), cur->Args.size());
430 0 : ErrorHandler (old_line, tmp, nullptr);
431 0 : return Token (Token::TK_ERROR);
432 : }
433 :
434 20 : Token t = cur->ExpandFunc ?
435 2 : cur->ExpandFunc (this, args) :
436 42 : cur->Expand (args, MacroList);
437 20 : t.AppendNL (Line - old_line);
438 :
439 20 : return t;
440 : }
441 :
442 24 : return iToken;
443 : }
444 :
445 :
446 : /**
447 : * Operator priority:
448 : * 0: Whole expression
449 : * 1: '(' ')'
450 : * 2: ||
451 : * 3: &&
452 : * 4: |
453 : * 5: ^
454 : * 6: &
455 : * 7: '==' '!='
456 : * 8: '<' '<=' '>' '>='
457 : * 9: '<<' '>>'
458 : * 10: '+' '-'
459 : * 11: '*' '/' '%'
460 : * 12: unary '+' '-' '!' '~'
461 : */
462 9 : CPreprocessor::Token CPreprocessor::GetExpression (
463 : Token &oResult, int iLine, int iOpPriority)
464 : {
465 : char tmp [40];
466 :
467 1 : do
468 : {
469 9 : oResult = GetToken (true);
470 17 : } while (oResult.Type == Token::TK_WHITESPACE ||
471 16 : oResult.Type == Token::TK_NEWLINE ||
472 16 : oResult.Type == Token::TK_COMMENT ||
473 25 : oResult.Type == Token::TK_LINECOMMENT ||
474 8 : oResult.Type == Token::TK_LINECONT);
475 :
476 16 : Token op (Token::TK_WHITESPACE, "", 0);
477 :
478 : // Handle unary operators here
479 8 : if (oResult.Type == Token::TK_PUNCTUATION && oResult.Length == 1)
480 : {
481 2 : if (strchr ("+-!~", oResult.String [0]))
482 : {
483 0 : char uop = oResult.String [0];
484 0 : op = GetExpression (oResult, iLine, 12);
485 : long val;
486 0 : if (!GetValue (oResult, val, iLine))
487 : {
488 0 : snprintf (tmp, sizeof (tmp), "Unary '%c' not applicable", uop);
489 0 : ErrorHandler(iLine, tmp, &oResult);
490 0 : return Token (Token::TK_ERROR);
491 : }
492 :
493 0 : if (uop == '-')
494 0 : oResult.SetValue (-val);
495 0 : else if (uop == '!')
496 0 : oResult.SetValue (!val);
497 0 : else if (uop == '~')
498 0 : oResult.SetValue (~val);
499 : }
500 2 : else if (oResult.String [0] == '(')
501 : {
502 1 : op = GetExpression (oResult, iLine, 1);
503 1 : if (op.Type == Token::TK_ERROR)
504 0 : return op;
505 1 : if (op.Type == Token::TK_EOS)
506 : {
507 1 : ErrorHandler(iLine, "Unclosed parenthesis in #if expression", nullptr);
508 1 : return Token (Token::TK_ERROR);
509 : }
510 :
511 0 : ENSURE(op.Type == Token::TK_PUNCTUATION &&
512 : op.Length == 1 &&
513 : op.String [0] == ')');
514 0 : op = GetToken (true);
515 : }
516 : }
517 :
518 30 : while (op.Type == Token::TK_WHITESPACE ||
519 14 : op.Type == Token::TK_NEWLINE ||
520 14 : op.Type == Token::TK_COMMENT ||
521 29 : op.Type == Token::TK_LINECOMMENT ||
522 7 : op.Type == Token::TK_LINECONT)
523 8 : op = GetToken (true);
524 :
525 : while (true)
526 : {
527 7 : if (op.Type != Token::TK_PUNCTUATION)
528 13 : return op;
529 :
530 1 : int prio = 0;
531 1 : if (op.Length == 1)
532 1 : switch (op.String [0])
533 : {
534 0 : case ')': return op;
535 0 : case '|': prio = 4; break;
536 0 : case '^': prio = 5; break;
537 0 : case '&': prio = 6; break;
538 0 : case '<':
539 0 : case '>': prio = 8; break;
540 0 : case '+':
541 0 : case '-': prio = 10; break;
542 1 : case '*':
543 : case '/':
544 1 : case '%': prio = 11; break;
545 : }
546 0 : else if (op.Length == 2)
547 0 : switch (op.String [0])
548 : {
549 0 : case '|': if (op.String [1] == '|') prio = 2; break;
550 0 : case '&': if (op.String [1] == '&') prio = 3; break;
551 0 : case '=': if (op.String [1] == '=') prio = 7; break;
552 0 : case '!': if (op.String [1] == '=') prio = 7; break;
553 0 : case '<':
554 0 : if (op.String [1] == '=')
555 0 : prio = 8;
556 0 : else if (op.String [1] == '<')
557 0 : prio = 9;
558 0 : break;
559 0 : case '>':
560 0 : if (op.String [1] == '=')
561 0 : prio = 8;
562 0 : else if (op.String [1] == '>')
563 0 : prio = 9;
564 0 : break;
565 : }
566 :
567 1 : if (!prio)
568 : {
569 0 : ErrorHandler(iLine, "Expecting operator, got", &op);
570 0 : return Token (Token::TK_ERROR);
571 : }
572 :
573 1 : if (iOpPriority >= prio)
574 0 : return op;
575 :
576 1 : Token rop;
577 1 : Token nextop = GetExpression (rop, iLine, prio);
578 : long vlop, vrop;
579 1 : if (!GetValue (oResult, vlop, iLine))
580 : {
581 0 : snprintf (tmp, sizeof (tmp), "Left operand of '%.*s' is not a number",
582 0 : int (op.Length), op.String);
583 0 : ErrorHandler(iLine, tmp, &oResult);
584 0 : return Token (Token::TK_ERROR);
585 : }
586 1 : if (!GetValue (rop, vrop, iLine))
587 : {
588 0 : snprintf (tmp, sizeof (tmp), "Right operand of '%.*s' is not a number",
589 0 : int (op.Length), op.String);
590 0 : ErrorHandler(iLine, tmp, &rop);
591 0 : return Token (Token::TK_ERROR);
592 : }
593 :
594 1 : switch (op.String [0])
595 : {
596 0 : case '|':
597 0 : if (prio == 2)
598 0 : oResult.SetValue (vlop || vrop);
599 : else
600 0 : oResult.SetValue (vlop | vrop);
601 0 : break;
602 0 : case '&':
603 0 : if (prio == 3)
604 0 : oResult.SetValue (vlop && vrop);
605 : else
606 0 : oResult.SetValue (vlop & vrop);
607 0 : break;
608 0 : case '<':
609 0 : if (op.Length == 1)
610 0 : oResult.SetValue (vlop < vrop);
611 0 : else if (prio == 8)
612 0 : oResult.SetValue (vlop <= vrop);
613 0 : else if (prio == 9)
614 0 : oResult.SetValue (vlop << vrop);
615 0 : break;
616 0 : case '>':
617 0 : if (op.Length == 1)
618 0 : oResult.SetValue (vlop > vrop);
619 0 : else if (prio == 8)
620 0 : oResult.SetValue (vlop >= vrop);
621 0 : else if (prio == 9)
622 0 : oResult.SetValue (vlop >> vrop);
623 0 : break;
624 0 : case '^': oResult.SetValue (vlop ^ vrop); break;
625 0 : case '!': oResult.SetValue (vlop != vrop); break;
626 0 : case '=': oResult.SetValue (vlop == vrop); break;
627 0 : case '+': oResult.SetValue (vlop + vrop); break;
628 0 : case '-': oResult.SetValue (vlop - vrop); break;
629 0 : case '*': oResult.SetValue (vlop * vrop); break;
630 1 : case '/':
631 : case '%':
632 1 : if (vrop == 0)
633 : {
634 1 : ErrorHandler(iLine, "Division by zero", nullptr);
635 1 : return Token (Token::TK_ERROR);
636 : }
637 0 : if (op.String [0] == '/')
638 0 : oResult.SetValue (vlop / vrop);
639 : else
640 0 : oResult.SetValue (vlop % vrop);
641 0 : break;
642 : }
643 :
644 0 : op = nextop;
645 0 : }
646 : }
647 :
648 :
649 11 : bool CPreprocessor::GetValue (const Token &iToken, long &oValue, int iLine)
650 : {
651 22 : Token r;
652 11 : const Token *vt = &iToken;
653 :
654 20 : if ((vt->Type == Token::TK_KEYWORD ||
655 12 : vt->Type == Token::TK_TEXT ||
656 14 : vt->Type == Token::TK_NUMBER) &&
657 11 : !vt->String)
658 : {
659 0 : ErrorHandler (iLine, "Trying to evaluate an empty expression", nullptr);
660 0 : return false;
661 : }
662 :
663 11 : if (vt->Type == Token::TK_TEXT)
664 : {
665 10 : CPreprocessor cpp (iToken, iLine);
666 6 : std::swap(cpp.MacroList, MacroList);
667 :
668 10 : Token t;
669 6 : t = cpp.GetExpression (r, iLine);
670 :
671 6 : std::swap(cpp.MacroList, MacroList);
672 :
673 6 : if (t.Type == Token::TK_ERROR)
674 2 : return false;
675 :
676 4 : if (t.Type != Token::TK_EOS)
677 : {
678 0 : ErrorHandler(iLine, "Garbage after expression", &t);
679 0 : return false;
680 : }
681 :
682 4 : vt = &r;
683 : }
684 :
685 : Macro *m;
686 9 : switch (vt->Type)
687 : {
688 0 : case Token::TK_EOS:
689 : case Token::TK_ERROR:
690 0 : return false;
691 :
692 2 : case Token::TK_KEYWORD:
693 : // Try to expand the macro
694 2 : m = IsDefined(*vt);
695 2 : if (m != nullptr && !m->Expanding)
696 : {
697 4 : Token x = ExpandMacro (*vt);
698 2 : m->Expanding = true;
699 2 : bool rc = GetValue (x, oValue, iLine);
700 2 : m->Expanding = false;
701 2 : return rc;
702 : }
703 :
704 : // Undefined macro, "expand" to 0 (mimic cpp behaviour)
705 0 : oValue = 0;
706 0 : break;
707 :
708 7 : case Token::TK_TEXT:
709 : case Token::TK_NUMBER:
710 7 : if (!vt->GetValue (oValue))
711 : {
712 0 : ErrorHandler(iLine, "Not a numeric expression", vt);
713 0 : return false;
714 : }
715 7 : break;
716 :
717 0 : default:
718 0 : ErrorHandler(iLine, "Unexpected token", vt);
719 0 : return false;
720 : }
721 :
722 7 : return true;
723 : }
724 :
725 :
726 16 : CPreprocessor::Token CPreprocessor::GetArgument (Token &oArg, bool iExpand,
727 : bool shouldAppendArg)
728 : {
729 6 : do
730 : {
731 16 : oArg = GetToken (iExpand);
732 26 : } while (oArg.Type == Token::TK_WHITESPACE ||
733 20 : oArg.Type == Token::TK_NEWLINE ||
734 20 : oArg.Type == Token::TK_COMMENT ||
735 36 : oArg.Type == Token::TK_LINECOMMENT ||
736 10 : oArg.Type == Token::TK_LINECONT);
737 :
738 10 : if (!iExpand)
739 : {
740 6 : if (oArg.Type == Token::TK_EOS)
741 0 : return oArg;
742 6 : else if (oArg.Type == Token::TK_PUNCTUATION &&
743 0 : (oArg.String [0] == ',' ||
744 0 : oArg.String [0] == ')'))
745 : {
746 0 : Token t = oArg;
747 0 : oArg = Token (Token::TK_TEXT, "", 0);
748 0 : return t;
749 : }
750 6 : else if (oArg.Type != Token::TK_KEYWORD)
751 : {
752 0 : ErrorHandler(Line, "Unexpected token", &oArg);
753 0 : return Token (Token::TK_ERROR);
754 : }
755 : }
756 :
757 10 : size_t braceCount = 0;
758 :
759 10 : if( oArg.Type == Token::TK_PUNCTUATION && oArg.String[0] == '(' )
760 0 : ++braceCount;
761 :
762 10 : size_t len = oArg.Length;
763 : while (true)
764 : {
765 22 : Token t = GetToken (iExpand);
766 16 : switch (t.Type)
767 : {
768 0 : case Token::TK_EOS:
769 0 : ErrorHandler(Line, "Unfinished list of arguments", nullptr);
770 : FALLTHROUGH;
771 0 : case Token::TK_ERROR:
772 0 : return Token (Token::TK_ERROR);
773 13 : case Token::TK_PUNCTUATION:
774 13 : if( t.String [0] == '(' )
775 : {
776 1 : ++braceCount;
777 : }
778 12 : else if( !braceCount )
779 : {
780 14 : if (t.String [0] == ',' ||
781 4 : t.String [0] == ')')
782 : {
783 : // Trim whitespaces at the end
784 10 : oArg.Length = len;
785 :
786 : //Append "__arg_" to all macro arguments, otherwise if user does:
787 : // #define mad( a, b, c ) fma( a, b, c )
788 : // mad( x.s, y, a );
789 : //It will be translated to:
790 : // fma( x.s, y, x.s );
791 : //instead of:
792 : // fma( x.s, y, a );
793 : //This does not fix the problem by the root, but
794 : //typing "__arg_" by the user is extremely rare.
795 10 : if( shouldAppendArg )
796 4 : oArg.Append( "__arg_", 6 );
797 10 : return t;
798 : }
799 : }
800 : else
801 : {
802 2 : if( t.String [0] == ')' )
803 1 : --braceCount;
804 : }
805 3 : break;
806 0 : case Token::TK_LINECONT:
807 : case Token::TK_COMMENT:
808 : case Token::TK_LINECOMMENT:
809 : case Token::TK_NEWLINE:
810 : // ignore these tokens
811 0 : continue;
812 3 : default:
813 3 : break;
814 : }
815 :
816 6 : if (!iExpand && t.Type != Token::TK_WHITESPACE)
817 : {
818 0 : ErrorHandler(Line, "Unexpected token", &oArg);
819 0 : return Token (Token::TK_ERROR);
820 : }
821 :
822 6 : oArg.Append (t);
823 :
824 6 : if (t.Type != Token::TK_WHITESPACE)
825 5 : len = oArg.Length;
826 6 : }
827 : }
828 :
829 :
830 15 : CPreprocessor::Token CPreprocessor::GetArguments (std::vector<Token>& oArgs,
831 : bool iExpand, bool shouldAppendArg)
832 : {
833 30 : Token args [MAX_MACRO_ARGS];
834 15 : int nargs = 0;
835 :
836 : // Suppose we'll leave by the wrong path
837 15 : oArgs.clear();
838 :
839 15 : bool isFirstTokenParsed = false;
840 15 : bool isFirstTokenNotAnOpenBrace = false;
841 :
842 30 : Token t;
843 10 : do
844 : {
845 25 : t = GetToken (iExpand);
846 :
847 40 : if( !isFirstTokenParsed &&
848 19 : (t.Type != Token::TK_PUNCTUATION || t.String [0] != '(') )
849 : {
850 11 : isFirstTokenNotAnOpenBrace = true;
851 : }
852 25 : isFirstTokenParsed = true;
853 40 : } while (t.Type == Token::TK_WHITESPACE ||
854 40 : t.Type == Token::TK_COMMENT ||
855 15 : t.Type == Token::TK_LINECOMMENT);
856 :
857 15 : if( isFirstTokenNotAnOpenBrace )
858 : {
859 11 : oArgs.clear();
860 11 : return t;
861 : }
862 :
863 : while (true)
864 : {
865 16 : if (nargs == MAX_MACRO_ARGS)
866 : {
867 0 : ErrorHandler(Line, "Too many arguments to macro", nullptr);
868 0 : return Token (Token::TK_ERROR);
869 : }
870 :
871 10 : t = GetArgument (args [nargs++], iExpand, shouldAppendArg);
872 :
873 10 : switch (t.Type)
874 : {
875 0 : case Token::TK_EOS:
876 0 : ErrorHandler(Line, "Unfinished list of arguments", nullptr);
877 : FALLTHROUGH;
878 0 : case Token::TK_ERROR:
879 0 : return Token (Token::TK_ERROR);
880 :
881 10 : case Token::TK_PUNCTUATION:
882 10 : if (t.String [0] == ')')
883 : {
884 4 : t = GetToken (iExpand);
885 4 : goto Done;
886 : } // otherwise we've got a ','
887 6 : break;
888 :
889 0 : default:
890 0 : ErrorHandler(Line, "Unexpected token", &t);
891 0 : break;
892 : }
893 : }
894 :
895 4 : Done:
896 4 : oArgs.insert(oArgs.begin(), args, args + nargs);
897 4 : return t;
898 : }
899 :
900 :
901 12 : bool CPreprocessor::HandleDefine (Token &iBody, int iLine)
902 : {
903 : // Create an additional preprocessor to process macro body
904 24 : CPreprocessor cpp (iBody, iLine);
905 :
906 24 : Token t = cpp.GetToken (false);
907 12 : if (t.Type != Token::TK_KEYWORD)
908 : {
909 0 : ErrorHandler(iLine, "Macro name expected after #define", nullptr);
910 0 : return false;
911 : }
912 :
913 24 : Macro m(t);
914 12 : m.Body = iBody;
915 12 : t = cpp.GetArguments (m.Args, false, true);
916 14 : while (t.Type == Token::TK_WHITESPACE)
917 1 : t = cpp.GetToken (false);
918 :
919 12 : switch (t.Type)
920 : {
921 1 : case Token::TK_NEWLINE:
922 : case Token::TK_EOS:
923 : // Assign "" to token
924 1 : t = Token (Token::TK_TEXT, "", 0);
925 1 : break;
926 :
927 0 : case Token::TK_ERROR:
928 0 : return false;
929 :
930 11 : default:
931 11 : t.Type = Token::TK_TEXT;
932 11 : ENSURE(t.String + t.Length == cpp.Source);
933 11 : t.Length = cpp.SourceEnd - t.String;
934 11 : break;
935 : }
936 :
937 12 : if( !m.Args.empty() )
938 : {
939 2 : CPreprocessor cpp2;
940 :
941 : //We need to convert:
942 : // #define mad( a__arg_, b__arg_, c__arg_ ) fma( a, b, c )
943 : //into:
944 : // #define mad( a__arg_, b__arg_, c__arg_ ) fma( a__arg_, b__arg_, c__arg_ )
945 5 : for (const Token& arg : m.Args)
946 : {
947 4 : cpp2.Define(arg.String, arg.Length - 6, arg.String, arg.Length);
948 : }
949 :
950 : // Now run the macro expansion through the supplimentary preprocessor
951 2 : Token xt = cpp2.Parse( t );
952 1 : t = xt;
953 : }
954 :
955 12 : m.Value = t;
956 12 : MacroList.push_front(std::move(m));
957 12 : return true;
958 : }
959 :
960 :
961 1 : bool CPreprocessor::HandleUnDef (Token &iBody, int iLine)
962 : {
963 2 : CPreprocessor cpp (iBody, iLine);
964 :
965 2 : Token t = cpp.GetToken (false);
966 :
967 1 : if (t.Type != Token::TK_KEYWORD)
968 : {
969 0 : ErrorHandler(iLine, "Expecting a macro name after #undef, got", &t);
970 0 : return false;
971 : }
972 :
973 : // Don't barf if macro does not exist - standard C behaviour
974 1 : Undef (t.String, t.Length);
975 :
976 0 : do
977 : {
978 1 : t = cpp.GetToken (false);
979 2 : } while (t.Type == Token::TK_WHITESPACE ||
980 2 : t.Type == Token::TK_COMMENT ||
981 1 : t.Type == Token::TK_LINECOMMENT);
982 :
983 1 : if (t.Type != Token::TK_EOS)
984 0 : ErrorHandler(iLine, "Warning: Ignoring garbage after directive", &t);
985 :
986 1 : return true;
987 : }
988 :
989 7 : bool CPreprocessor::HandleIf(bool val, int iLine)
990 : {
991 7 : if (EnableOutput & (1 << 31))
992 : {
993 0 : ErrorHandler(iLine, "Too many embedded #if directives", nullptr);
994 0 : return false;
995 : }
996 :
997 7 : EnableElif <<= 1;
998 7 : EnableOutput <<= 1;
999 7 : if (val)
1000 3 : EnableOutput |= 1;
1001 : else
1002 4 : EnableElif |= 1;
1003 :
1004 7 : return true;
1005 : }
1006 :
1007 4 : bool CPreprocessor::HandleIfDef (Token &iBody, int iLine)
1008 : {
1009 8 : CPreprocessor cpp (iBody, iLine);
1010 :
1011 8 : Token t = cpp.GetToken (false);
1012 :
1013 4 : if (t.Type != Token::TK_KEYWORD)
1014 : {
1015 0 : ErrorHandler(iLine, "Expecting a macro name after #ifdef, got", &t);
1016 0 : return false;
1017 : }
1018 :
1019 4 : if (!HandleIf(IsDefined(t) != nullptr, iLine))
1020 0 : return false;
1021 :
1022 0 : do
1023 : {
1024 4 : t = cpp.GetToken (false);
1025 8 : } while (t.Type == Token::TK_WHITESPACE ||
1026 8 : t.Type == Token::TK_COMMENT ||
1027 4 : t.Type == Token::TK_LINECOMMENT);
1028 :
1029 4 : if (t.Type != Token::TK_EOS)
1030 0 : ErrorHandler(iLine, "Warning: Ignoring garbage after directive", &t);
1031 :
1032 4 : return true;
1033 : }
1034 :
1035 :
1036 2 : CPreprocessor::Token CPreprocessor::ExpandDefined (CPreprocessor *iParent, const std::vector<Token>& iArgs)
1037 : {
1038 2 : if (iArgs.size() != 1)
1039 : {
1040 0 : iParent->ErrorHandler(iParent->Line, "The defined() function takes exactly one argument", nullptr);
1041 0 : return Token (Token::TK_ERROR);
1042 : }
1043 :
1044 2 : const char *v = iParent->IsDefined (iArgs [0]) ? "1" : "0";
1045 2 : return Token (Token::TK_NUMBER, v, 1);
1046 : }
1047 :
1048 7 : bool CPreprocessor::GetValueDef(const Token &iToken, long &oValue, int iLine)
1049 : {
1050 : // Temporary add the defined() function to the macro list
1051 7 : MacroList.emplace_front(Token(Token::TK_KEYWORD, "defined", 7));
1052 7 : MacroList.front().ExpandFunc = ExpandDefined;
1053 7 : MacroList.front().Args.resize(1);
1054 :
1055 7 : bool rc = GetValue (iToken, oValue, iLine);
1056 :
1057 : // Restore the macro list
1058 7 : MacroList.pop_front();
1059 :
1060 7 : return rc;
1061 : }
1062 :
1063 5 : bool CPreprocessor::HandleIf (Token &iBody, int iLine)
1064 : {
1065 : long val;
1066 5 : return GetValueDef(iBody, val, iLine) && HandleIf(val != 0, iLine);
1067 : }
1068 :
1069 2 : bool CPreprocessor::HandleElif (Token &iBody, int iLine)
1070 : {
1071 2 : if (EnableOutput == 1)
1072 : {
1073 0 : ErrorHandler(iLine, "#elif without #if", nullptr);
1074 0 : return false;
1075 : }
1076 :
1077 : long val;
1078 2 : if (!GetValueDef(iBody, val, iLine))
1079 0 : return false;
1080 :
1081 2 : if (val && (EnableElif & 1))
1082 : {
1083 2 : EnableOutput |= 1;
1084 2 : EnableElif &= ~1;
1085 : }
1086 : else
1087 0 : EnableOutput &= ~1;
1088 :
1089 2 : return true;
1090 : }
1091 :
1092 :
1093 2 : bool CPreprocessor::HandleElse (Token &iBody, int iLine)
1094 : {
1095 2 : if (EnableOutput == 1)
1096 : {
1097 0 : ErrorHandler(iLine, "#else without #if", nullptr);
1098 0 : return false;
1099 : }
1100 :
1101 : // Negate the result of last #if
1102 2 : if ((EnableElif & 1) || (EnableOutput & 1))
1103 2 : EnableOutput ^= 1;
1104 :
1105 2 : if (iBody.Length)
1106 0 : ErrorHandler(iLine, "Warning: Ignoring garbage after #else", &iBody);
1107 :
1108 2 : return true;
1109 : }
1110 :
1111 :
1112 7 : bool CPreprocessor::HandleEndIf (Token &iBody, int iLine)
1113 : {
1114 7 : EnableElif >>= 1;
1115 7 : EnableOutput >>= 1;
1116 7 : if (EnableOutput == 0)
1117 : {
1118 0 : ErrorHandler(iLine, "#endif without #if", nullptr);
1119 0 : return false;
1120 : }
1121 :
1122 7 : if (iBody.Length)
1123 0 : ErrorHandler(iLine, "Warning: Ignoring garbage after #endif", &iBody);
1124 :
1125 7 : return true;
1126 : }
1127 :
1128 :
1129 55 : CPreprocessor::Token CPreprocessor::HandleDirective (Token &iToken, int iLine)
1130 : {
1131 : // Analyze preprocessor directive
1132 55 : const char *directive = iToken.String + 1;
1133 55 : size_t dirlen = iToken.Length - 1;
1134 55 : while (dirlen && isspace (*directive))
1135 0 : dirlen--, directive++;
1136 :
1137 55 : int old_line = Line;
1138 :
1139 : // Collect the remaining part of the directive until EOL
1140 110 : Token t, last;
1141 46 : do
1142 : {
1143 101 : t = GetToken (false);
1144 101 : if (t.Type == Token::TK_NEWLINE)
1145 : {
1146 : // No directive arguments
1147 9 : last = t;
1148 9 : t.Length = 0;
1149 9 : goto Done;
1150 : }
1151 138 : } while (t.Type == Token::TK_WHITESPACE ||
1152 92 : t.Type == Token::TK_LINECONT ||
1153 184 : t.Type == Token::TK_COMMENT ||
1154 46 : t.Type == Token::TK_LINECOMMENT);
1155 :
1156 : for (;;)
1157 : {
1158 190 : last = GetToken (false);
1159 118 : switch (last.Type)
1160 : {
1161 0 : case Token::TK_EOS:
1162 : // Can happen and is not an error
1163 0 : goto Done;
1164 :
1165 0 : case Token::TK_LINECOMMENT:
1166 : case Token::TK_COMMENT:
1167 : // Skip comments in macros
1168 0 : continue;
1169 :
1170 0 : case Token::TK_ERROR:
1171 0 : return last;
1172 :
1173 0 : case Token::TK_LINECONT:
1174 0 : continue;
1175 :
1176 46 : case Token::TK_NEWLINE:
1177 46 : goto Done;
1178 :
1179 72 : default:
1180 72 : break;
1181 : }
1182 :
1183 72 : t.Append (last);
1184 72 : t.Type = Token::TK_TEXT;
1185 : }
1186 55 : Done:
1187 :
1188 : #define IS_DIRECTIVE(s) \
1189 : (dirlen == strlen(s) && (strncmp (directive, s, dirlen) == 0))
1190 :
1191 55 : bool outputEnabled = ((EnableOutput & (EnableOutput + 1)) == 0);
1192 : bool rc;
1193 :
1194 55 : if (IS_DIRECTIVE ("define") && outputEnabled)
1195 12 : rc = HandleDefine (t, iLine);
1196 43 : else if (IS_DIRECTIVE ("undef") && outputEnabled)
1197 1 : rc = HandleUnDef (t, iLine);
1198 42 : else if (IS_DIRECTIVE ("ifdef"))
1199 1 : rc = HandleIfDef (t, iLine);
1200 41 : else if (IS_DIRECTIVE ("ifndef"))
1201 : {
1202 3 : rc = HandleIfDef (t, iLine);
1203 6 : if (rc)
1204 : {
1205 3 : EnableOutput ^= 1;
1206 3 : EnableElif ^= 1;
1207 : }
1208 : }
1209 38 : else if (IS_DIRECTIVE ("if"))
1210 5 : rc = HandleIf (t, iLine);
1211 33 : else if (IS_DIRECTIVE ("elif"))
1212 2 : rc = HandleElif (t, iLine);
1213 :
1214 31 : else if (IS_DIRECTIVE ("else"))
1215 2 : rc = HandleElse (t, iLine);
1216 29 : else if (IS_DIRECTIVE ("endif"))
1217 7 : rc = HandleEndIf (t, iLine);
1218 : else
1219 : {
1220 : // Unknown preprocessor directive, roll back and pass through
1221 22 : Line = old_line;
1222 22 : Source = iToken.String + iToken.Length;
1223 22 : iToken.Type = Token::TK_TEXT;
1224 22 : return iToken;
1225 : }
1226 :
1227 : #undef IS_DIRECTIVE
1228 :
1229 33 : if (!rc)
1230 2 : return Token (Token::TK_ERROR);
1231 31 : return last;
1232 : }
1233 :
1234 :
1235 8 : void CPreprocessor::Define (const char *iMacroName, size_t iMacroNameLen,
1236 : const char *iMacroValue, size_t iMacroValueLen)
1237 : {
1238 8 : MacroList.emplace_front(Token(Token::TK_KEYWORD, iMacroName, iMacroNameLen));
1239 8 : MacroList.front().Value = Token(Token::TK_TEXT, iMacroValue, iMacroValueLen);
1240 8 : }
1241 :
1242 :
1243 0 : void CPreprocessor::Define (const char *iMacroName, size_t iMacroNameLen,
1244 : long iMacroValue)
1245 : {
1246 0 : MacroList.emplace_front(Token(Token::TK_KEYWORD, iMacroName, iMacroNameLen));
1247 0 : MacroList.front().Value.SetValue(iMacroValue);
1248 0 : }
1249 :
1250 :
1251 5 : bool CPreprocessor::Undef (const char *iMacroName, size_t iMacroNameLen)
1252 : {
1253 10 : Token name (Token::TK_KEYWORD, iMacroName, iMacroNameLen);
1254 :
1255 7 : for (auto it = MacroList.before_begin();; ++it)
1256 : {
1257 7 : auto itpp = std::next(it);
1258 7 : if(itpp == MacroList.end()) break;
1259 :
1260 7 : if (itpp->Name == name)
1261 : {
1262 5 : MacroList.erase_after(it);
1263 5 : return true;
1264 : }
1265 2 : }
1266 :
1267 0 : return false;
1268 : }
1269 :
1270 :
1271 31 : CPreprocessor::Token CPreprocessor::Parse (const Token &iSource)
1272 : {
1273 31 : Source = iSource.String;
1274 31 : SourceEnd = Source + iSource.Length;
1275 31 : Line = 1;
1276 31 : BOL = true;
1277 31 : EnableOutput = 1;
1278 31 : EnableElif = 0;
1279 :
1280 : // Accumulate output into this token
1281 62 : Token output (Token::TK_TEXT);
1282 31 : int empty_lines = 0;
1283 :
1284 : // Enable output only if all embedded #if's were true
1285 31 : bool old_output_enabled = true;
1286 31 : bool output_enabled = true;
1287 31 : int output_disabled_line = 0;
1288 :
1289 601 : while (Source < SourceEnd)
1290 : {
1291 287 : int old_line = Line;
1292 572 : Token t = GetToken (true);
1293 :
1294 342 : NextToken:
1295 342 : switch (t.Type)
1296 : {
1297 2 : case Token::TK_ERROR:
1298 2 : return t;
1299 :
1300 0 : case Token::TK_EOS:
1301 0 : return output; // Force termination
1302 :
1303 0 : case Token::TK_COMMENT:
1304 : // C comments are replaced with single spaces.
1305 0 : if (output_enabled)
1306 : {
1307 0 : output.Append (" ", 1);
1308 0 : output.AppendNL (Line - old_line);
1309 : }
1310 0 : break;
1311 :
1312 0 : case Token::TK_LINECOMMENT:
1313 : // C++ comments are ignored
1314 0 : continue;
1315 :
1316 55 : case Token::TK_DIRECTIVE:
1317 : // Handle preprocessor directives
1318 55 : t = HandleDirective (t, old_line);
1319 :
1320 55 : output_enabled = ((EnableOutput & (EnableOutput + 1)) == 0);
1321 55 : if (output_enabled != old_output_enabled)
1322 : {
1323 12 : if (output_enabled)
1324 6 : output.AppendNL (old_line - output_disabled_line);
1325 : else
1326 6 : output_disabled_line = old_line;
1327 12 : old_output_enabled = output_enabled;
1328 : }
1329 :
1330 55 : if (output_enabled)
1331 43 : output.AppendNL (Line - old_line - t.CountNL ());
1332 55 : goto NextToken;
1333 :
1334 0 : case Token::TK_LINECONT:
1335 : // Backslash-Newline sequences are deleted, no matter where.
1336 0 : empty_lines++;
1337 0 : break;
1338 :
1339 76 : case Token::TK_NEWLINE:
1340 76 : if (empty_lines)
1341 : {
1342 : // Compensate for the backslash-newline combinations
1343 : // we have encountered, otherwise line numeration is broken
1344 0 : if (output_enabled)
1345 0 : output.AppendNL (empty_lines);
1346 0 : empty_lines = 0;
1347 : }
1348 : FALLTHROUGH; // to default
1349 : case Token::TK_WHITESPACE:
1350 : // Fallthrough to default
1351 : default:
1352 : // Passthrough all other tokens
1353 285 : if (output_enabled)
1354 229 : output.Append (t);
1355 285 : break;
1356 : }
1357 : }
1358 :
1359 29 : if (EnableOutput != 1)
1360 : {
1361 0 : ErrorHandler(Line, "Unclosed #if at end of source", nullptr);
1362 0 : return Token (Token::TK_ERROR);
1363 : }
1364 :
1365 29 : return output;
1366 : }
1367 :
1368 :
1369 12 : char *CPreprocessor::Parse (const char *iSource, size_t iLength, size_t &oLength)
1370 : {
1371 24 : Token retval = Parse (Token (Token::TK_TEXT, iSource, iLength));
1372 12 : if (retval.Type == Token::TK_ERROR)
1373 2 : return NULL;
1374 :
1375 10 : oLength = retval.Length;
1376 10 : retval.Allocated = 0;
1377 10 : return retval.Buffer;
1378 : }
1379 :
1380 3 : } // namespace Ogre
|