2 * C pre-processor functions.
3 * Portions of this code are copyright (C) 1989 John R. Dunning.
4 * See copyleft.jrd for license information.
36 /*****************************************************************************/
38 /*****************************************************************************/
42 static int Pass1 (const char* From, char* To);
43 /* Preprocessor pass 1. Remove whitespace and comments. */
47 /*****************************************************************************/
49 /*****************************************************************************/
53 /* Set when the preprocessor calls expr() recursively */
54 unsigned char Preprocessing = 0;
56 /* Management data for #if */
58 #define IFCOND_NONE 0x00U
59 #define IFCOND_SKIP 0x01U
60 #define IFCOND_ELSE 0x02U
61 #define IFCOND_NEEDTERM 0x04U
62 static unsigned char IfStack[MAX_IFS];
63 static int IfIndex = -1;
65 /* Buffer for macro expansion */
66 static char mlinebuf [LINESIZE];
67 static char* mline = mlinebuf;
72 /*****************************************************************************/
73 /* Low level preprocessor token handling */
74 /*****************************************************************************/
78 /* Types of preprocessor tokens */
97 /* Preprocessor keyword to token mapping table */
98 static const struct PPToken {
99 const char* Key; /* Keyword */
100 pptoken_t Tok; /* Token */
102 { "define", PP_DEFINE },
105 { "endif", PP_ENDIF },
106 { "error", PP_ERROR },
108 { "ifdef", PP_IFDEF },
109 { "ifndef", PP_IFNDEF },
110 { "include", PP_INCLUDE },
112 { "pragma", PP_PRAGMA },
113 { "undef", PP_UNDEF },
116 /* Number of preprocessor tokens */
117 #define PPTOKEN_COUNT (sizeof(PPTokens) / sizeof(PPTokens[0]))
121 static int CmpToken (const void* Key, const void* Elem)
122 /* Compare function for bsearch */
124 return strcmp ((const char*) Key, ((const struct PPToken*) Elem)->Key);
129 static pptoken_t FindPPToken (const char* Ident)
130 /* Find a preprocessor token and return ut. Return PP_ILLEGAL if the identifier
131 * is not a valid preprocessor token.
135 P = bsearch (Ident, PPTokens, PPTOKEN_COUNT, sizeof (PPTokens[0]), CmpToken);
136 return P? P->Tok : PP_ILLEGAL;
141 /*****************************************************************************/
143 /*****************************************************************************/
148 INLINE void KeepChar (char c)
149 /* Put character c into translation buffer. */
154 #define KeepChar(c) *mptr++ = (c)
159 static void KeepStr (const char* S)
160 /* Put string str into translation buffer. */
162 unsigned Len = strlen (S);
163 memcpy (mptr, S, Len);
169 static void Stringize (const char* S)
170 /* Stringize the given string: Add double quotes at start and end and preceed
171 * each occurance of " and \ by a backslash.
175 /* Replace any characters inside the string may not be part of a string
195 static void SwapLineBuffers (void)
196 /* Swap both line buffers */
198 /* Swap mline and line */
206 static void OldStyleComment (void)
207 /* Remove an old style C comment from line. */
209 /* Remember the current line number, so we can output better error
210 * messages if the comment is not terminated in the current file.
212 unsigned StartingLine = GetCurrentLine();
214 /* Skip the start of comment chars */
218 /* Skip the comment */
219 while (CurC != '*' || NextC != '/') {
221 if (NextLine () == 0) {
222 PPError ("End-of-file reached in comment starting at line %u",
227 if (CurC == '/' && NextC == '*') {
228 PPWarning ("`/*' found inside a comment");
234 /* Skip the end of comment chars */
241 static void NewStyleComment (void)
242 /* Remove a new style C comment from line. */
244 /* Beware: Because line continuation chars are handled when reading
245 * lines, we may only skip til the end of the source line, which
246 * may not be the same as the end of the input line. The end of the
247 * source line is denoted by a lf (\n) character.
251 } while (CurC != '\n' && CurC != '\0');
259 static void SkipBlank (void)
260 /* Skip blanks and tabs in the input stream. */
262 while (IsBlank (CurC)) {
269 static char* CopyQuotedString (char* Target)
270 /* Copy a single or double quoted string from the input to Target. Return the
271 * new target pointer. Target will not be terminated after the copy.
274 /* Remember the quote character, copy it to the target buffer and skip it */
279 /* Copy the characters inside the string */
280 while (CurC != '\0' && CurC != Quote) {
281 /* Keep an escaped char */
286 /* Copy the character */
291 /* If we had a terminating quote, copy it */
297 /* Return the new target pointer */
303 /*****************************************************************************/
305 /*****************************************************************************/
309 static int MacName (char* Ident)
310 /* Get a macro symbol name into Ident. If we have an error, print a
311 * diagnostic message and clear the line.
314 if (IsSym (Ident) == 0) {
315 PPError ("Identifier expected");
325 static void ExpandMacroArgs (Macro* M)
326 /* Expand the arguments of a macro */
329 const char* Replacement;
332 /* Save the current line pointer and setup the new ones */
334 InitLine (M->Replacement);
336 /* Copy the macro replacement checking for parameters to replace */
337 while (CurC != '\0') {
338 /* If the next token is an identifier, check for a macro arg */
339 if (IsIdent (CurC)) {
341 Replacement = FindMacroArg (M, Ident);
343 /* Macro arg, keep the replacement */
344 KeepStr (Replacement);
346 /* No macro argument, keep the original identifier */
349 } else if (CurC == '#' && IsIdent (NextC)) {
352 Replacement = FindMacroArg (M, Ident);
354 /* Make a valid string from Replacement */
355 Stringize (Replacement);
357 /* No replacement - keep the input */
361 } else if (IsQuote (CurC)) {
362 mptr = CopyQuotedString (mptr);
369 /* Reset the line pointer */
375 static int MacroCall (Macro* M)
376 /* Process a function like macro */
378 int ArgCount; /* Macro argument count */
379 unsigned ParCount; /* Number of open parenthesis */
380 char Buf[LINESIZE]; /* Argument buffer */
381 const char* ArgStart;
384 /* Check for an argument list. If we don't have a list, we won't expand
394 /* Eat the left paren */
397 /* Read the actual macro arguments and store pointers to these arguments
398 * into the array of actual arguments in the macro definition.
406 /* Nested parenthesis */
410 } else if (IsQuote (CurC)) {
411 B = CopyQuotedString (B);
412 } else if (CurC == ',' || CurC == ')') {
414 /* End of actual argument */
416 while (IsBlank(*ArgStart)) {
419 if (ArgCount < M->ArgCount) {
420 M->ActualArgs[ArgCount++] = ArgStart;
421 } else if (CurC != ')' || *ArgStart != '\0' || M->ArgCount > 0) {
422 /* Be sure not to count the single empty argument for a
423 * macro that does not have arguments.
428 /* Check for end of macro param list */
434 /* Start the next param */
438 /* Comma or right paren inside nested parenthesis */
445 } else if (IsBlank (CurC)) {
446 /* Squeeze runs of blanks */
449 } else if (CurC == '/' && NextC == '*') {
452 } else if (IS_Get (&Standard) >= STD_C99 && CurC == '/' && NextC == '/') {
455 } else if (CurC == '\0') {
456 /* End of line inside macro argument list - read next line */
457 if (NextLine () == 0) {
461 /* Just copy the character */
467 /* Compare formal argument count with actual */
468 if (M->ArgCount != ArgCount) {
469 PPError ("Macro argument count mismatch");
470 /* Be sure to make enough empty arguments available */
471 while (ArgCount < M->ArgCount) {
472 M->ActualArgs [ArgCount++] = "";
476 /* Preprocess the line, replacing macro parameters */
485 static void ExpandMacro (Macro* M)
488 /* Check if this is a function like macro */
489 if (M->ArgCount >= 0) {
490 /* Function like macro */
491 if (MacroCall (M) == 0) {
495 /* Just copy the replacement text */
496 KeepStr (M->Replacement);
502 static void DefineMacro (void)
503 /* Handle a macro definition. */
511 /* Read the macro name */
513 if (!MacName (Ident)) {
517 /* Get an existing macro definition with this name */
518 Existing = FindMacro (Ident);
520 /* Create a new macro definition */
521 M = NewMacro (Ident);
523 /* Check if this is a function like macro */
526 /* Skip the left paren */
529 /* Set the marker that this is a function like macro */
532 /* Read the formal parameter list */
537 if (MacName (Ident) == 0) {
540 AddMacroArg (M, Ident);
547 /* Check for a right paren and eat it if we find one */
549 PPError ("`)' expected");
556 /* Insert the macro into the macro table and allocate the ActualArgs array */
559 /* Remove whitespace and comments from the line, store the preprocessed
567 /* Create a copy of the replacement */
568 M->Replacement = xstrdup (Buf);
570 /* If we have an existing macro, check if the redefinition is identical.
571 * Print a diagnostic if not.
574 if (MacroCmp (M, Existing) != 0) {
575 PPError ("Macro redefinition is not identical");
582 /*****************************************************************************/
584 /*****************************************************************************/
588 static int Pass1 (const char* From, char* To)
589 /* Preprocessor pass 1. Remove whitespace and comments. */
595 /* Initialize reading from "From" */
601 /* Loop removing ws and comments */
603 while (CurC != '\0') {
604 if (IsBlank (CurC)) {
607 } else if (IsIdent (CurC)) {
609 if (Preprocessing && strcmp(Ident, "defined") == 0) {
610 /* Handle the "defined" operator */
618 if (!IsIdent (CurC)) {
619 PPError ("Identifier expected");
623 KeepChar (IsMacro (Ident)? '1' : '0');
627 PPError ("`)' expected");
634 if (MaybeMacro (Ident[0])) {
639 } else if (IsQuote (CurC)) {
640 mptr = CopyQuotedString (mptr);
641 } else if (CurC == '/' && NextC == '*') {
644 } else if (IS_Get (&Standard) >= STD_C99 && CurC == '/' && NextC == '/') {
658 static int Pass2 (const char* From, char* To)
659 /* Preprocessor pass 2. Perform macro substitution. */
665 /* Initialize reading from "From" */
671 /* Loop substituting macros */
673 while (CurC != '\0') {
674 /* If we have an identifier, check if it's a macro */
675 if (IsIdent (CurC)) {
677 M = FindMacro (Ident);
684 } else if (IsQuote (CurC)) {
685 mptr = CopyQuotedString (mptr);
696 static void PreprocessLine (void)
697 /* Translate one line. */
701 /* Trim whitespace and remove comments. The function returns false if no
702 * identifiers were found that may be macros. If this is the case, no
703 * macro substitution is performed.
705 int Done = Pass1 (line, mline);
707 /* Repeatedly expand macros in the line */
708 for (I = 0; I < 256; ++I) {
709 /* Swap mline and line */
714 /* Perform macro expansion */
715 Done = Pass2 (line, mline);
719 /* Reinitialize line parsing */
725 static void DoUndef (void)
726 /* Process the #undef directive */
731 if (MacName (Ident)) {
732 UndefineMacro (Ident);
738 static int PushIf (int Skip, int Invert, int Cond)
739 /* Push a new if level onto the if stack */
741 /* Check for an overflow of the if stack */
742 if (IfIndex >= MAX_IFS-1) {
743 PPError ("Too many nested #if clauses");
747 /* Push the #if condition */
750 IfStack[IfIndex] = IFCOND_SKIP | IFCOND_NEEDTERM;
753 IfStack[IfIndex] = IFCOND_NONE | IFCOND_NEEDTERM;
754 return (Invert ^ Cond);
760 static int DoIf (int Skip)
761 /* Process #if directive */
766 /* We're about to abuse the compiler expression parser to evaluate the
767 * #if expression. Save the current tokens to come back here later.
768 * NOTE: Yes, this is a hack, but it saves a complete separate expression
769 * evaluation for the preprocessor.
774 /* Make sure the line infos for the tokens won't get removed */
776 UseLineInfo (sv1.LI);
779 UseLineInfo (sv2.LI);
782 /* Remove the #if from the line */
785 while (CurC != '\0') {
791 /* Start over parsing from line */
794 /* Switch into special preprocessing mode */
797 /* Expand macros in this line */
800 /* Add two semicolons as sentinels to the line, so the following
801 * expression evaluation will eat these two tokens but nothing from
802 * the following line.
806 /* Prime the token pump (remove old tokens from the stream) */
810 /* Call the expression parser */
811 ConstExpr (hie1, &Expr);
813 /* End preprocessing mode */
816 /* Reset the old tokens */
820 /* Set the #if condition according to the expression result */
821 return PushIf (Skip, 1, Expr.IVal != 0);
826 static int DoIfDef (int skip, int flag)
827 /* Process #ifdef if flag == 1, or #ifndef if flag == 0. */
832 if (MacName (Ident) == 0) {
835 return PushIf (skip, flag, IsMacro(Ident));
841 static void DoInclude (void)
842 /* Open an include file. */
851 /* Get the next char and check for a valid file name terminator. Setup
852 * the include directory spec (SYS/USR) by looking at the terminator.
867 PPError ("`\"' or `<' expected");
872 /* Copy the filename into mline. Since mline has the same size as the
873 * input line, we don't need to check for an overflow here.
876 while (CurC != '\0' && CurC != RTerm) {
882 /* Check if we got a terminator */
884 /* No terminator found */
885 PPError ("Missing terminator or file name too long");
889 /* Open the include file */
890 OpenIncludeFile (mline, DirSpec);
893 /* Clear the remaining line so the next input will come from the new
901 static void DoError (void)
906 PPError ("Invalid #error directive");
908 PPError ("#error: %s", lptr);
911 /* Clear the rest of line */
917 static void DoPragma (void)
918 /* Handle a #pragma line by converting the #pragma preprocessor directive into
919 * the _Pragma() compiler operator.
922 /* Skip blanks following the #pragma directive */
925 /* Copy the remainder of the line into mline removing comments and ws */
928 /* Convert the directive into the operator */
930 KeepStr ("_Pragma (");
935 /* Initialize reading from line */
941 void Preprocess (void)
942 /* Preprocess a line */
947 /* Skip white space at the beginning of the line */
950 /* Check for stuff to skip */
952 while (CurC == '\0' || CurC == '#' || Skip) {
954 /* Check for preprocessor lines lines */
959 /* Ignore the empty preprocessor directive */
962 if (!IsSym (Directive)) {
963 PPError ("Preprocessor directive expected");
966 switch (FindPPToken (Directive)) {
976 if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) {
978 /* Handle as #else/#if combination */
979 if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) {
982 IfStack[IfIndex] |= IFCOND_ELSE;
985 /* #elif doesn't need a terminator */
986 IfStack[IfIndex] &= ~IFCOND_NEEDTERM;
988 PPError ("Duplicate #else/#elif");
991 PPError ("Unexpected #elif");
997 if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) {
998 if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) {
1001 IfStack[IfIndex] |= IFCOND_ELSE;
1003 PPError ("Duplicate #else");
1006 PPError ("Unexpected `#else'");
1012 /* Remove any clauses on top of stack that do not
1013 * need a terminating #endif.
1015 while (IfIndex >= 0 && (IfStack[IfIndex] & IFCOND_NEEDTERM) == 0) {
1019 /* Stack may not be empty here or something is wrong */
1020 CHECK (IfIndex >= 0);
1022 /* Remove the clause that needs a terminator */
1023 Skip = (IfStack[IfIndex--] & IFCOND_SKIP) != 0;
1025 PPError ("Unexpected `#endif'");
1040 Skip = DoIfDef (Skip, 1);
1044 Skip = DoIfDef (Skip, 0);
1054 /* Should do something in C99 at least, but we ignore it */
1074 PPError ("Preprocessor directive expected");
1080 if (NextLine () == 0) {
1082 PPError ("`#endif' expected");
1092 Print (stdout, 2, "line: %s\n", line);