2 /* C pre-processor functions */
33 /*****************************************************************************/
35 /*****************************************************************************/
39 static int Pass1 (const char* From, char* To);
40 /* Preprocessor pass 1. Remove whitespace and comments. */
44 /*****************************************************************************/
46 /*****************************************************************************/
50 /* Set when the preprocessor calls expr() recursively */
51 unsigned char Preprocessing = 0;
53 /* Management data for #if */
55 #define IFCOND_NONE 0x00U
56 #define IFCOND_SKIP 0x01U
57 #define IFCOND_ELSE 0x02U
58 #define IFCOND_NEEDTERM 0x04U
59 static unsigned char IfStack[MAX_IFS];
60 static int IfIndex = -1;
62 /* Buffer for macro expansion */
63 static char mlinebuf [LINESIZE];
64 static char* mline = mlinebuf;
69 /*****************************************************************************/
70 /* Low level preprocessor token handling */
71 /*****************************************************************************/
75 /* Types of preprocessor tokens */
94 /* Preprocessor keyword to token mapping table */
95 static const struct PPToken {
96 const char* Key; /* Keyword */
97 pptoken_t Tok; /* Token */
99 { "define", PP_DEFINE },
102 { "endif", PP_ENDIF },
103 { "error", PP_ERROR },
105 { "ifdef", PP_IFDEF },
106 { "ifndef", PP_IFNDEF },
107 { "include", PP_INCLUDE },
109 { "pragma", PP_PRAGMA },
110 { "undef", PP_UNDEF },
113 /* Number of preprocessor tokens */
114 #define PPTOKEN_COUNT (sizeof(PPTokens) / sizeof(PPTokens[0]))
118 static int CmpToken (const void* Key, const void* Elem)
119 /* Compare function for bsearch */
121 return strcmp ((const char*) Key, ((const struct PPToken*) Elem)->Key);
126 static pptoken_t FindPPToken (const char* Ident)
127 /* Find a preprocessor token and return ut. Return PP_ILLEGAL if the identifier
128 * is not a valid preprocessor token.
132 P = bsearch (Ident, PPTokens, PPTOKEN_COUNT, sizeof (PPTokens[0]), CmpToken);
133 return P? P->Tok : PP_ILLEGAL;
138 /*****************************************************************************/
140 /*****************************************************************************/
145 INLINE void KeepChar (char c)
146 /* Put character c into translation buffer. */
151 #define KeepChar(c) *mptr++ = (c)
156 static void KeepStr (const char* S)
157 /* Put string str into translation buffer. */
159 unsigned Len = strlen (S);
160 memcpy (mptr, S, Len);
166 static void Stringize (const char* S)
167 /* Stringize the given string: Add double quotes at start and end and preceed
168 * each occurance of " and \ by a backslash.
172 /* Replace any characters inside the string may not be part of a string
192 static void SwapLineBuffers (void)
193 /* Swap both line buffers */
195 /* Swap mline and line */
203 static void OldStyleComment (void)
204 /* Remove an old style C comment from line. */
206 /* Remember the current line number, so we can output better error
207 * messages if the comment is not terminated in the current file.
209 unsigned StartingLine = GetCurrentLine();
211 /* Skip the start of comment chars */
215 /* Skip the comment */
216 while (CurC != '*' || NextC != '/') {
218 if (NextLine () == 0) {
219 PPError ("End-of-file reached in comment starting at line %u",
224 if (CurC == '/' && NextC == '*') {
225 PPWarning ("`/*' found inside a comment");
231 /* Skip the end of comment chars */
238 static void NewStyleComment (void)
239 /* Remove a new style C comment from line. */
241 /* Beware: Because line continuation chars are handled when reading
242 * lines, we may only skip til the end of the source line, which
243 * may not be the same as the end of the input line. The end of the
244 * source line is denoted by a lf (\n) character.
248 } while (CurC != '\n' && CurC != '\0');
256 static void SkipBlank (void)
257 /* Skip blanks and tabs in the input stream. */
259 while (IsBlank (CurC)) {
266 static char* CopyQuotedString (char* Target)
267 /* Copy a single or double quoted string from the input to Target. Return the
268 * new target pointer. Target will not be terminated after the copy.
271 /* Remember the quote character, copy it to the target buffer and skip it */
276 /* Copy the characters inside the string */
277 while (CurC != '\0' && CurC != Quote) {
278 /* Keep an escaped char */
283 /* Copy the character */
288 /* If we had a terminating quote, copy it */
294 /* Return the new target pointer */
300 /*****************************************************************************/
302 /*****************************************************************************/
306 static int MacName (char* Ident)
307 /* Get a macro symbol name into Ident. If we have an error, print a
308 * diagnostic message and clear the line.
311 if (IsSym (Ident) == 0) {
312 PPError ("Identifier expected");
322 static void ExpandMacroArgs (Macro* M)
323 /* Expand the arguments of a macro */
326 const char* Replacement;
329 /* Save the current line pointer and setup the new ones */
331 InitLine (M->Replacement);
333 /* Copy the macro replacement checking for parameters to replace */
334 while (CurC != '\0') {
335 /* If the next token is an identifier, check for a macro arg */
336 if (IsIdent (CurC)) {
338 Replacement = FindMacroArg (M, Ident);
340 /* Macro arg, keep the replacement */
341 KeepStr (Replacement);
343 /* No macro argument, keep the original identifier */
346 } else if (CurC == '#' && IsIdent (NextC)) {
349 Replacement = FindMacroArg (M, Ident);
351 /* Make a valid string from Replacement */
352 Stringize (Replacement);
354 /* No replacement - keep the input */
358 } else if (IsQuote (CurC)) {
359 mptr = CopyQuotedString (mptr);
366 /* Reset the line pointer */
372 static int MacroCall (Macro* M)
373 /* Process a function like macro */
375 int ArgCount; /* Macro argument count */
376 unsigned ParCount; /* Number of open parenthesis */
377 char Buf[LINESIZE]; /* Argument buffer */
378 const char* ArgStart;
381 /* Check for an argument list. If we don't have a list, we won't expand
391 /* Eat the left paren */
394 /* Read the actual macro arguments and store pointers to these arguments
395 * into the array of actual arguments in the macro definition.
403 /* Nested parenthesis */
407 } else if (IsQuote (CurC)) {
408 B = CopyQuotedString (B);
409 } else if (CurC == ',' || CurC == ')') {
411 /* End of actual argument */
413 while (IsBlank(*ArgStart)) {
416 if (ArgCount < M->ArgCount) {
417 M->ActualArgs[ArgCount++] = ArgStart;
418 } else if (CurC != ')' || *ArgStart != '\0' || M->ArgCount > 0) {
419 /* Be sure not to count the single empty argument for a
420 * macro that does not have arguments.
425 /* Check for end of macro param list */
431 /* Start the next param */
435 /* Comma or right paren inside nested parenthesis */
442 } else if (IsBlank (CurC)) {
443 /* Squeeze runs of blanks */
446 } else if (CurC == '/' && NextC == '*') {
449 } else if (IS_Get (&Standard) >= STD_C99 && CurC == '/' && NextC == '/') {
452 } else if (CurC == '\0') {
453 /* End of line inside macro argument list - read next line */
454 if (NextLine () == 0) {
458 /* Just copy the character */
464 /* Compare formal argument count with actual */
465 if (M->ArgCount != ArgCount) {
466 PPError ("Macro argument count mismatch");
467 /* Be sure to make enough empty arguments available */
468 while (ArgCount < M->ArgCount) {
469 M->ActualArgs [ArgCount++] = "";
473 /* Preprocess the line, replacing macro parameters */
482 static void ExpandMacro (Macro* M)
485 /* Check if this is a function like macro */
486 if (M->ArgCount >= 0) {
487 /* Function like macro */
488 if (MacroCall (M) == 0) {
492 /* Just copy the replacement text */
493 KeepStr (M->Replacement);
499 static void DefineMacro (void)
500 /* Handle a macro definition. */
508 /* Read the macro name */
510 if (!MacName (Ident)) {
514 /* Get an existing macro definition with this name */
515 Existing = FindMacro (Ident);
517 /* Create a new macro definition */
518 M = NewMacro (Ident);
520 /* Check if this is a function like macro */
523 /* Skip the left paren */
526 /* Set the marker that this is a function like macro */
529 /* Read the formal parameter list */
534 if (MacName (Ident) == 0) {
537 AddMacroArg (M, Ident);
544 /* Check for a right paren and eat it if we find one */
546 PPError ("`)' expected");
553 /* Insert the macro into the macro table and allocate the ActualArgs array */
556 /* Remove whitespace and comments from the line, store the preprocessed
564 /* Create a copy of the replacement */
565 M->Replacement = xstrdup (Buf);
567 /* If we have an existing macro, check if the redefinition is identical.
568 * Print a diagnostic if not.
571 if (MacroCmp (M, Existing) != 0) {
572 PPError ("Macro redefinition is not identical");
579 /*****************************************************************************/
581 /*****************************************************************************/
585 static int Pass1 (const char* From, char* To)
586 /* Preprocessor pass 1. Remove whitespace and comments. */
592 /* Initialize reading from "From" */
598 /* Loop removing ws and comments */
600 while (CurC != '\0') {
601 if (IsBlank (CurC)) {
604 } else if (IsIdent (CurC)) {
606 if (Preprocessing && strcmp(Ident, "defined") == 0) {
607 /* Handle the "defined" operator */
615 if (!IsIdent (CurC)) {
616 PPError ("Identifier expected");
620 KeepChar (IsMacro (Ident)? '1' : '0');
624 PPError ("`)' expected");
631 if (MaybeMacro (Ident[0])) {
636 } else if (IsQuote (CurC)) {
637 mptr = CopyQuotedString (mptr);
638 } else if (CurC == '/' && NextC == '*') {
641 } else if (IS_Get (&Standard) >= STD_C99 && CurC == '/' && NextC == '/') {
655 static int Pass2 (const char* From, char* To)
656 /* Preprocessor pass 2. Perform macro substitution. */
662 /* Initialize reading from "From" */
668 /* Loop substituting macros */
670 while (CurC != '\0') {
671 /* If we have an identifier, check if it's a macro */
672 if (IsIdent (CurC)) {
674 M = FindMacro (Ident);
681 } else if (IsQuote (CurC)) {
682 mptr = CopyQuotedString (mptr);
693 static void PreprocessLine (void)
694 /* Translate one line. */
698 /* Trim whitespace and remove comments. The function returns false if no
699 * identifiers were found that may be macros. If this is the case, no
700 * macro substitution is performed.
702 int Done = Pass1 (line, mline);
704 /* Repeatedly expand macros in the line */
705 for (I = 0; I < 256; ++I) {
706 /* Swap mline and line */
711 /* Perform macro expansion */
712 Done = Pass2 (line, mline);
716 /* Reinitialize line parsing */
722 static void DoUndef (void)
723 /* Process the #undef directive */
728 if (MacName (Ident)) {
729 UndefineMacro (Ident);
735 static int PushIf (int Skip, int Invert, int Cond)
736 /* Push a new if level onto the if stack */
738 /* Check for an overflow of the if stack */
739 if (IfIndex >= MAX_IFS-1) {
740 PPError ("Too many nested #if clauses");
744 /* Push the #if condition */
747 IfStack[IfIndex] = IFCOND_SKIP | IFCOND_NEEDTERM;
750 IfStack[IfIndex] = IFCOND_NONE | IFCOND_NEEDTERM;
751 return (Invert ^ Cond);
757 static int DoIf (int Skip)
758 /* Process #if directive */
763 /* We're about to abuse the compiler expression parser to evaluate the
764 * #if expression. Save the current tokens to come back here later.
765 * NOTE: Yes, this is a hack, but it saves a complete separate expression
766 * evaluation for the preprocessor.
771 /* Make sure the line infos for the tokens won't get removed */
773 UseLineInfo (sv1.LI);
776 UseLineInfo (sv2.LI);
779 /* Remove the #if from the line */
782 while (CurC != '\0') {
788 /* Start over parsing from line */
791 /* Switch into special preprocessing mode */
794 /* Expand macros in this line */
797 /* Add two semicolons as sentinels to the line, so the following
798 * expression evaluation will eat these two tokens but nothing from
799 * the following line.
803 /* Prime the token pump (remove old tokens from the stream) */
807 /* Call the expression parser */
808 ConstExpr (hie1, &Expr);
810 /* End preprocessing mode */
813 /* Reset the old tokens */
817 /* Set the #if condition according to the expression result */
818 return PushIf (Skip, 1, Expr.IVal != 0);
823 static int DoIfDef (int skip, int flag)
824 /* Process #ifdef if flag == 1, or #ifndef if flag == 0. */
829 if (MacName (Ident) == 0) {
832 return PushIf (skip, flag, IsMacro(Ident));
838 static void DoInclude (void)
839 /* Open an include file. */
848 /* Get the next char and check for a valid file name terminator. Setup
849 * the include directory spec (SYS/USR) by looking at the terminator.
864 PPError ("`\"' or `<' expected");
869 /* Copy the filename into mline. Since mline has the same size as the
870 * input line, we don't need to check for an overflow here.
873 while (CurC != '\0' && CurC != RTerm) {
879 /* Check if we got a terminator */
881 /* No terminator found */
882 PPError ("Missing terminator or file name too long");
886 /* Open the include file */
887 OpenIncludeFile (mline, DirSpec);
890 /* Clear the remaining line so the next input will come from the new
898 static void DoError (void)
903 PPError ("Invalid #error directive");
905 PPError ("#error: %s", lptr);
908 /* Clear the rest of line */
914 static void DoPragma (void)
915 /* Handle a #pragma line by converting the #pragma preprocessor directive into
916 * the _Pragma() compiler operator.
919 /* Skip blanks following the #pragma directive */
922 /* Copy the remainder of the line into mline removing comments and ws */
925 /* Convert the directive into the operator */
927 KeepStr ("_Pragma (");
932 /* Initialize reading from line */
938 void Preprocess (void)
939 /* Preprocess a line */
944 /* Skip white space at the beginning of the line */
947 /* Check for stuff to skip */
949 while (CurC == '\0' || CurC == '#' || Skip) {
951 /* Check for preprocessor lines lines */
956 /* Ignore the empty preprocessor directive */
959 if (!IsSym (Directive)) {
960 PPError ("Preprocessor directive expected");
963 switch (FindPPToken (Directive)) {
973 if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) {
975 /* Handle as #else/#if combination */
976 if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) {
979 IfStack[IfIndex] |= IFCOND_ELSE;
982 /* #elif doesn't need a terminator */
983 IfStack[IfIndex] &= ~IFCOND_NEEDTERM;
985 PPError ("Duplicate #else/#elif");
988 PPError ("Unexpected #elif");
994 if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) {
995 if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) {
998 IfStack[IfIndex] |= IFCOND_ELSE;
1000 PPError ("Duplicate #else");
1003 PPError ("Unexpected `#else'");
1009 /* Remove any clauses on top of stack that do not
1010 * need a terminating #endif.
1012 while (IfIndex >= 0 && (IfStack[IfIndex] & IFCOND_NEEDTERM) == 0) {
1016 /* Stack may not be empty here or something is wrong */
1017 CHECK (IfIndex >= 0);
1019 /* Remove the clause that needs a terminator */
1020 Skip = (IfStack[IfIndex--] & IFCOND_SKIP) != 0;
1022 PPError ("Unexpected `#endif'");
1037 Skip = DoIfDef (Skip, 1);
1041 Skip = DoIfDef (Skip, 0);
1051 /* Should do something in C99 at least, but we ignore it */
1071 PPError ("Preprocessor directive expected");
1077 if (NextLine () == 0) {
1079 PPError ("`#endif' expected");
1089 Print (stdout, 2, "line: %s\n", line);