2 * C pre-processor functions.
3 * Portions of this code are copyright (C) 1989 John R. Dunning.
4 * See copyleft.jrd for license information.
35 /*****************************************************************************/
37 /*****************************************************************************/
41 static int Pass1 (const char* From, char* To);
42 /* Preprocessor pass 1. Remove whitespace and comments. */
46 /*****************************************************************************/
48 /*****************************************************************************/
52 /* Set when the preprocessor calls expr() recursively */
53 unsigned char Preprocessing = 0;
55 /* Management data for #if */
57 #define IFCOND_NONE 0x00U
58 #define IFCOND_SKIP 0x01U
59 #define IFCOND_ELSE 0x02U
60 #define IFCOND_NEEDTERM 0x04U
61 static unsigned char IfStack[MAX_IFS];
62 static int IfIndex = -1;
64 /* Buffer for macro expansion */
65 static char mlinebuf [LINESIZE];
66 static char* mline = mlinebuf;
71 /*****************************************************************************/
72 /* Low level preprocessor token handling */
73 /*****************************************************************************/
77 /* Types of preprocessor tokens */
96 /* Preprocessor keyword to token mapping table */
97 static const struct PPToken {
98 const char* Key; /* Keyword */
99 pptoken_t Tok; /* Token */
101 { "define", PP_DEFINE },
104 { "endif", PP_ENDIF },
105 { "error", PP_ERROR },
107 { "ifdef", PP_IFDEF },
108 { "ifndef", PP_IFNDEF },
109 { "include", PP_INCLUDE },
111 { "pragma", PP_PRAGMA },
112 { "undef", PP_UNDEF },
115 /* Number of preprocessor tokens */
116 #define PPTOKEN_COUNT (sizeof(PPTokens) / sizeof(PPTokens[0]))
120 static int CmpToken (const void* Key, const void* Elem)
121 /* Compare function for bsearch */
123 return strcmp ((const char*) Key, ((const struct PPToken*) Elem)->Key);
128 static pptoken_t FindPPToken (const char* Ident)
129 /* Find a preprocessor token and return ut. Return PP_ILLEGAL if the identifier
130 * is not a valid preprocessor token.
134 P = bsearch (Ident, PPTokens, PPTOKEN_COUNT, sizeof (PPTokens[0]), CmpToken);
135 return P? P->Tok : PP_ILLEGAL;
140 /*****************************************************************************/
142 /*****************************************************************************/
147 INLINE void KeepChar (char c)
148 /* Put character c into translation buffer. */
153 #define KeepChar(c) *mptr++ = (c)
158 static void KeepStr (const char* S)
159 /* Put string str into translation buffer. */
161 unsigned Len = strlen (S);
162 memcpy (mptr, S, Len);
168 static void Stringize (const char* S)
169 /* Stringize the given string: Add double quotes at start and end and preceed
170 * each occurance of " and \ by a backslash.
174 /* Replace any characters inside the string may not be part of a string
194 static void SwapLineBuffers (void)
195 /* Swap both line buffers */
197 /* Swap mline and line */
205 static void OldStyleComment (void)
206 /* Remove an old style C comment from line. */
208 /* Remember the current line number, so we can output better error
209 * messages if the comment is not terminated in the current file.
211 unsigned StartingLine = GetCurrentLine();
213 /* Skip the start of comment chars */
217 /* Skip the comment */
218 while (CurC != '*' || NextC != '/') {
220 if (NextLine () == 0) {
221 PPError ("End-of-file reached in comment starting at line %u",
226 if (CurC == '/' && NextC == '*') {
227 PPWarning ("`/*' found inside a comment");
233 /* Skip the end of comment chars */
240 static void NewStyleComment (void)
241 /* Remove a new style C comment from line. */
243 /* Beware: Because line continuation chars are handled when reading
244 * lines, we may only skip til the end of the source line, which
245 * may not be the same as the end of the input line. The end of the
246 * source line is denoted by a lf (\n) character.
250 } while (CurC != '\n' && CurC != '\0');
258 static void SkipBlank (void)
259 /* Skip blanks and tabs in the input stream. */
261 while (IsBlank (CurC)) {
268 static char* CopyQuotedString (char* Target)
269 /* Copy a single or double quoted string from the input to Target. Return the
270 * new target pointer. Target will not be terminated after the copy.
273 /* Remember the quote character, copy it to the target buffer and skip it */
278 /* Copy the characters inside the string */
279 while (CurC != '\0' && CurC != Quote) {
280 /* Keep an escaped char */
285 /* Copy the character */
290 /* If we had a terminating quote, copy it */
296 /* Return the new target pointer */
302 /*****************************************************************************/
304 /*****************************************************************************/
308 static int MacName (char* Ident)
309 /* Get a macro symbol name into Ident. If we have an error, print a
310 * diagnostic message and clear the line.
313 if (IsSym (Ident) == 0) {
314 PPError ("Identifier expected");
324 static void ExpandMacroArgs (Macro* M)
325 /* Expand the arguments of a macro */
328 const char* Replacement;
331 /* Save the current line pointer and setup the new ones */
333 InitLine (M->Replacement);
335 /* Copy the macro replacement checking for parameters to replace */
336 while (CurC != '\0') {
337 /* If the next token is an identifier, check for a macro arg */
338 if (IsIdent (CurC)) {
340 Replacement = FindMacroArg (M, Ident);
342 /* Macro arg, keep the replacement */
343 KeepStr (Replacement);
345 /* No macro argument, keep the original identifier */
348 } else if (CurC == '#' && IsIdent (NextC)) {
351 Replacement = FindMacroArg (M, Ident);
353 /* Make a valid string from Replacement */
354 Stringize (Replacement);
356 /* No replacement - keep the input */
360 } else if (IsQuote (CurC)) {
361 mptr = CopyQuotedString (mptr);
368 /* Reset the line pointer */
374 static int MacroCall (Macro* M)
375 /* Process a function like macro */
377 int ArgCount; /* Macro argument count */
378 unsigned ParCount; /* Number of open parenthesis */
379 char Buf[LINESIZE]; /* Argument buffer */
380 const char* ArgStart;
383 /* Check for an argument list. If we don't have a list, we won't expand
393 /* Eat the left paren */
396 /* Read the actual macro arguments and store pointers to these arguments
397 * into the array of actual arguments in the macro definition.
405 /* Nested parenthesis */
409 } else if (IsQuote (CurC)) {
410 B = CopyQuotedString (B);
411 } else if (CurC == ',' || CurC == ')') {
413 /* End of actual argument */
415 while (IsBlank(*ArgStart)) {
418 if (ArgCount < M->ArgCount) {
419 M->ActualArgs[ArgCount++] = ArgStart;
420 } else if (CurC != ')' || *ArgStart != '\0' || M->ArgCount > 0) {
421 /* Be sure not to count the single empty argument for a
422 * macro that does not have arguments.
427 /* Check for end of macro param list */
433 /* Start the next param */
437 /* Comma or right paren inside nested parenthesis */
444 } else if (IsBlank (CurC)) {
445 /* Squeeze runs of blanks */
448 } else if (CurC == '/' && NextC == '*') {
451 } else if (IS_Get (&Standard) >= STD_C99 && CurC == '/' && NextC == '/') {
454 } else if (CurC == '\0') {
455 /* End of line inside macro argument list - read next line */
456 if (NextLine () == 0) {
460 /* Just copy the character */
466 /* Compare formal argument count with actual */
467 if (M->ArgCount != ArgCount) {
468 PPError ("Macro argument count mismatch");
469 /* Be sure to make enough empty arguments available */
470 while (ArgCount < M->ArgCount) {
471 M->ActualArgs [ArgCount++] = "";
475 /* Preprocess the line, replacing macro parameters */
484 static void ExpandMacro (Macro* M)
487 /* Check if this is a function like macro */
488 if (M->ArgCount >= 0) {
489 /* Function like macro */
490 if (MacroCall (M) == 0) {
494 /* Just copy the replacement text */
495 KeepStr (M->Replacement);
501 static void DefineMacro (void)
502 /* Handle a macro definition. */
510 /* Read the macro name */
512 if (!MacName (Ident)) {
516 /* Get an existing macro definition with this name */
517 Existing = FindMacro (Ident);
519 /* Create a new macro definition */
520 M = NewMacro (Ident);
522 /* Check if this is a function like macro */
525 /* Skip the left paren */
528 /* Set the marker that this is a function like macro */
531 /* Read the formal parameter list */
536 if (MacName (Ident) == 0) {
539 AddMacroArg (M, Ident);
546 /* Check for a right paren and eat it if we find one */
548 PPError ("`)' expected");
555 /* Insert the macro into the macro table and allocate the ActualArgs array */
558 /* Remove whitespace and comments from the line, store the preprocessed
566 /* Create a copy of the replacement */
567 M->Replacement = xstrdup (Buf);
569 /* If we have an existing macro, check if the redefinition is identical.
570 * Print a diagnostic if not.
573 if (MacroCmp (M, Existing) != 0) {
574 PPError ("Macro redefinition is not identical");
581 /*****************************************************************************/
583 /*****************************************************************************/
587 static int Pass1 (const char* From, char* To)
588 /* Preprocessor pass 1. Remove whitespace and comments. */
594 /* Initialize reading from "From" */
600 /* Loop removing ws and comments */
602 while (CurC != '\0') {
603 if (IsBlank (CurC)) {
606 } else if (IsIdent (CurC)) {
608 if (Preprocessing && strcmp(Ident, "defined") == 0) {
609 /* Handle the "defined" operator */
617 if (!IsIdent (CurC)) {
618 PPError ("Identifier expected");
622 KeepChar (IsMacro (Ident)? '1' : '0');
626 PPError ("`)' expected");
633 if (MaybeMacro (Ident[0])) {
638 } else if (IsQuote (CurC)) {
639 mptr = CopyQuotedString (mptr);
640 } else if (CurC == '/' && NextC == '*') {
643 } else if (IS_Get (&Standard) >= STD_C99 && CurC == '/' && NextC == '/') {
657 static int Pass2 (const char* From, char* To)
658 /* Preprocessor pass 2. Perform macro substitution. */
664 /* Initialize reading from "From" */
670 /* Loop substituting macros */
672 while (CurC != '\0') {
673 /* If we have an identifier, check if it's a macro */
674 if (IsIdent (CurC)) {
676 M = FindMacro (Ident);
683 } else if (IsQuote (CurC)) {
684 mptr = CopyQuotedString (mptr);
695 static void PreprocessLine (void)
696 /* Translate one line. */
700 /* Trim whitespace and remove comments. The function returns false if no
701 * identifiers were found that may be macros. If this is the case, no
702 * macro substitution is performed.
704 int Done = Pass1 (line, mline);
706 /* Repeatedly expand macros in the line */
707 for (I = 0; I < 256; ++I) {
708 /* Swap mline and line */
713 /* Perform macro expansion */
714 Done = Pass2 (line, mline);
718 /* Reinitialize line parsing */
724 static void DoUndef (void)
725 /* Process the #undef directive */
730 if (MacName (Ident)) {
731 UndefineMacro (Ident);
737 static int PushIf (int Skip, int Invert, int Cond)
738 /* Push a new if level onto the if stack */
740 /* Check for an overflow of the if stack */
741 if (IfIndex >= MAX_IFS-1) {
742 PPError ("Too many nested #if clauses");
746 /* Push the #if condition */
749 IfStack[IfIndex] = IFCOND_SKIP | IFCOND_NEEDTERM;
752 IfStack[IfIndex] = IFCOND_NONE | IFCOND_NEEDTERM;
753 return (Invert ^ Cond);
759 static int DoIf (int Skip)
760 /* Process #if directive */
765 /* We're about to abuse the compiler expression parser to evaluate the
766 * #if expression. Save the current tokens to come back here later.
767 * NOTE: Yes, this is a hack, but it saves a complete separate expression
768 * evaluation for the preprocessor.
773 /* Make sure the line infos for the tokens won't get removed */
775 UseLineInfo (sv1.LI);
778 UseLineInfo (sv2.LI);
781 /* Remove the #if from the line */
784 while (CurC != '\0') {
790 /* Start over parsing from line */
793 /* Switch into special preprocessing mode */
796 /* Expand macros in this line */
799 /* Add two semicolons as sentinels to the line, so the following
800 * expression evaluation will eat these two tokens but nothing from
801 * the following line.
805 /* Prime the token pump (remove old tokens from the stream) */
809 /* Call the expression parser */
810 ConstExpr (hie1, &Expr);
812 /* End preprocessing mode */
815 /* Reset the old tokens */
819 /* Set the #if condition according to the expression result */
820 return PushIf (Skip, 1, Expr.IVal != 0);
825 static int DoIfDef (int skip, int flag)
826 /* Process #ifdef if flag == 1, or #ifndef if flag == 0. */
831 if (MacName (Ident) == 0) {
834 return PushIf (skip, flag, IsMacro(Ident));
840 static void DoInclude (void)
841 /* Open an include file. */
850 /* Get the next char and check for a valid file name terminator. Setup
851 * the include directory spec (SYS/USR) by looking at the terminator.
866 PPError ("`\"' or `<' expected");
871 /* Copy the filename into mline. Since mline has the same size as the
872 * input line, we don't need to check for an overflow here.
875 while (CurC != '\0' && CurC != RTerm) {
881 /* Check if we got a terminator */
883 /* No terminator found */
884 PPError ("Missing terminator or file name too long");
888 /* Open the include file */
889 OpenIncludeFile (mline, DirSpec);
892 /* Clear the remaining line so the next input will come from the new
900 static void DoError (void)
905 PPError ("Invalid #error directive");
907 PPError ("#error: %s", lptr);
910 /* Clear the rest of line */
916 static void DoPragma (void)
917 /* Handle a #pragma line by converting the #pragma preprocessor directive into
918 * the _Pragma() compiler operator.
921 /* Skip blanks following the #pragma directive */
924 /* Copy the remainder of the line into mline removing comments and ws */
927 /* Convert the directive into the operator */
929 KeepStr ("_Pragma (");
934 /* Initialize reading from line */
940 void Preprocess (void)
941 /* Preprocess a line */
946 /* Skip white space at the beginning of the line */
949 /* Check for stuff to skip */
951 while (CurC == '\0' || CurC == '#' || Skip) {
953 /* Check for preprocessor lines lines */
958 /* Ignore the empty preprocessor directive */
961 if (!IsSym (Directive)) {
962 PPError ("Preprocessor directive expected");
965 switch (FindPPToken (Directive)) {
975 if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) {
977 /* Handle as #else/#if combination */
978 if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) {
981 IfStack[IfIndex] |= IFCOND_ELSE;
984 /* #elif doesn't need a terminator */
985 IfStack[IfIndex] &= ~IFCOND_NEEDTERM;
987 PPError ("Duplicate #else/#elif");
990 PPError ("Unexpected #elif");
996 if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) {
997 if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) {
1000 IfStack[IfIndex] |= IFCOND_ELSE;
1002 PPError ("Duplicate #else");
1005 PPError ("Unexpected `#else'");
1011 /* Remove any clauses on top of stack that do not
1012 * need a terminating #endif.
1014 while (IfIndex >= 0 && (IfStack[IfIndex] & IFCOND_NEEDTERM) == 0) {
1018 /* Stack may not be empty here or something is wrong */
1019 CHECK (IfIndex >= 0);
1021 /* Remove the clause that needs a terminator */
1022 Skip = (IfStack[IfIndex--] & IFCOND_SKIP) != 0;
1024 PPError ("Unexpected `#endif'");
1039 Skip = DoIfDef (Skip, 1);
1043 Skip = DoIfDef (Skip, 0);
1053 /* Should do something in C99 at least, but we ignore it */
1073 PPError ("Preprocessor directive expected");
1079 if (NextLine () == 0) {
1081 PPError ("`#endif' expected");
1091 Print (stdout, 2, "line: %s\n", line);