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