2 * C pre-processor functions.
3 * Small portions of this code are copyright (C) 1989 John R. Dunning.
4 * See copyleft.jrd for license information.
35 /*****************************************************************************/
37 /*****************************************************************************/
41 /* Set when the preprocessor calls expr() recursively */
42 unsigned char Preprocessing = 0;
44 /* Management data for #if */
46 #define IFCOND_NONE 0x00U
47 #define IFCOND_SKIP 0x01U
48 #define IFCOND_ELSE 0x02U
49 #define IFCOND_NEEDTERM 0x04U
50 static unsigned char IfStack[MAX_IFS];
51 static int IfIndex = -1;
53 /* Buffer for macro expansion */
56 /* Structure used when expanding macros */
57 typedef struct MacroExp MacroExp;
59 Collection ActualArgs; /* Actual arguments */
60 StrBuf Replacement; /* Replacement with arguments substituted */
61 Macro* M; /* The macro we're handling */
66 /*****************************************************************************/
68 /*****************************************************************************/
72 static unsigned Pass1 (StrBuf* Source, StrBuf* Target);
73 /* Preprocessor pass 1. Remove whitespace. Handle old and new style comments
74 * and the "defined" operator.
77 static void MacroReplacement (StrBuf* Source, StrBuf* Target);
78 /* Perform macro replacement. */
82 /*****************************************************************************/
83 /* Low level preprocessor token handling */
84 /*****************************************************************************/
88 /* Types of preprocessor tokens */
107 /* Preprocessor keyword to token mapping table */
108 static const struct PPToken {
109 const char* Key; /* Keyword */
110 pptoken_t Tok; /* Token */
112 { "define", PP_DEFINE },
115 { "endif", PP_ENDIF },
116 { "error", PP_ERROR },
118 { "ifdef", PP_IFDEF },
119 { "ifndef", PP_IFNDEF },
120 { "include", PP_INCLUDE },
122 { "pragma", PP_PRAGMA },
123 { "undef", PP_UNDEF },
126 /* Number of preprocessor tokens */
127 #define PPTOKEN_COUNT (sizeof(PPTokens) / sizeof(PPTokens[0]))
131 static int CmpToken (const void* Key, const void* Elem)
132 /* Compare function for bsearch */
134 return strcmp ((const char*) Key, ((const struct PPToken*) Elem)->Key);
139 static pptoken_t FindPPToken (const char* Ident)
140 /* Find a preprocessor token and return it. Return PP_ILLEGAL if the identifier
141 * is not a valid preprocessor token.
145 P = bsearch (Ident, PPTokens, PPTOKEN_COUNT, sizeof (PPTokens[0]), CmpToken);
146 return P? P->Tok : PP_ILLEGAL;
151 /*****************************************************************************/
152 /* struct MacroExp */
153 /*****************************************************************************/
157 static MacroExp* InitMacroExp (MacroExp* E, Macro* M)
158 /* Initialize a MacroExp structure */
160 InitCollection (&E->ActualArgs);
161 InitStrBuf (&E->Replacement);
168 static void DoneMacroExp (MacroExp* E)
169 /* Cleanup after use of a MacroExp structure */
173 /* Delete the list with actual arguments */
174 for (I = 0; I < CollCount (&E->ActualArgs); ++I) {
175 FreeStrBuf (CollAtUnchecked (&E->ActualArgs, I));
177 DoneCollection (&E->ActualArgs);
178 DoneStrBuf (&E->Replacement);
183 static void ME_AppendActual (MacroExp* E, StrBuf* Arg)
184 /* Add a copy of Arg to the list of actual macro arguments.
185 * NOTE: This function will clear Arg!
188 /* Create a new string buffer */
189 StrBuf* A = NewStrBuf ();
191 /* Move the contents of Arg to A */
194 /* Add A to the actual arguments */
195 CollAppend (&E->ActualArgs, A);
200 static StrBuf* ME_GetActual (MacroExp* E, unsigned Index)
201 /* Return an actual macro argument with the given index */
203 return CollAt (&E->ActualArgs, Index);
208 /*****************************************************************************/
210 /*****************************************************************************/
214 static void Stringize (StrBuf* Source, StrBuf* Target)
215 /* Stringize the given string: Add double quotes at start and end and preceed
216 * each occurance of " and \ by a backslash.
221 /* Add a starting quote */
222 SB_AppendChar (Target, '\"');
224 /* Replace any characters inside the string may not be part of a string
227 while ((C = SB_Get (Source)) != '\0') {
231 SB_AppendChar (Target, '\\');
234 SB_AppendChar (Target, C);
239 /* Add the closing quote */
240 SB_AppendChar (Target, '\"');
245 static void OldStyleComment (void)
246 /* Remove an old style C comment from line. */
248 /* Remember the current line number, so we can output better error
249 * messages if the comment is not terminated in the current file.
251 unsigned StartingLine = GetCurrentLine();
253 /* Skip the start of comment chars */
257 /* Skip the comment */
258 while (CurC != '*' || NextC != '/') {
260 if (NextLine () == 0) {
261 PPError ("End-of-file reached in comment starting at line %u",
266 if (CurC == '/' && NextC == '*') {
267 PPWarning ("`/*' found inside a comment");
273 /* Skip the end of comment chars */
280 static void NewStyleComment (void)
281 /* Remove a new style C comment from line. */
283 /* Beware: Because line continuation chars are handled when reading
284 * lines, we may only skip til the end of the source line, which
285 * may not be the same as the end of the input line. The end of the
286 * source line is denoted by a lf (\n) character.
290 } while (CurC != '\n' && CurC != '\0');
298 static void SkipWhitespace (void)
299 /* Skip white space in the input stream. */
301 while (IsSpace (CurC)) {
308 static void CopyQuotedString (StrBuf* Target)
309 /* Copy a single or double quoted string from the input to Target. */
311 /* Remember the quote character, copy it to the target buffer and skip it */
313 SB_AppendChar (Target, CurC);
316 /* Copy the characters inside the string */
317 while (CurC != '\0' && CurC != Quote) {
318 /* Keep an escaped char */
320 SB_AppendChar (Target, CurC);
323 /* Copy the character */
324 SB_AppendChar (Target, CurC);
328 /* If we had a terminating quote, copy it */
330 SB_AppendChar (Target, CurC);
337 /*****************************************************************************/
339 /*****************************************************************************/
343 static int MacName (char* Ident)
344 /* Get a macro symbol name into Ident. If we have an error, print a
345 * diagnostic message and clear the line.
348 if (IsSym (Ident) == 0) {
349 PPError ("Identifier expected");
359 static void ReadMacroArgs (MacroExp* E)
360 /* Identify the arguments to a macro call */
362 unsigned Parens; /* Number of open parenthesis */
363 StrBuf Arg = STATIC_STRBUF_INITIALIZER;
365 /* Read the actual macro arguments and store pointers to these arguments
366 * into the array of actual arguments in the macro definition.
371 /* Nested parenthesis */
372 SB_AppendChar (&Arg, CurC);
375 } else if (IsQuote (CurC)) {
376 CopyQuotedString (&Arg);
377 } else if (CurC == ',' || CurC == ')') {
380 /* End of actual argument. Remove whitespace from the end. */
381 while (IsSpace (SB_LookAtLast (&Arg))) {
385 /* If this is not the single empty argument for a macro with
386 * an empty argument list, remember it.
388 if (CurC != ')' || SB_NotEmpty (&Arg) || E->M->ArgCount > 0) {
389 ME_AppendActual (E, &Arg);
392 /* Check for end of macro param list */
398 /* Start the next param */
402 /* Comma or right paren inside nested parenthesis */
406 SB_AppendChar (&Arg, CurC);
409 } else if (IsSpace (CurC)) {
410 /* Squeeze runs of blanks within an arg */
411 if (SB_NotEmpty (&Arg)) {
412 SB_AppendChar (&Arg, ' ');
415 } else if (CurC == '/' && NextC == '*') {
416 if (SB_NotEmpty (&Arg)) {
417 SB_AppendChar (&Arg, ' ');
420 } else if (IS_Get (&Standard) >= STD_C99 && CurC == '/' && NextC == '/') {
421 if (SB_NotEmpty (&Arg)) {
422 SB_AppendChar (&Arg, ' ');
425 } else if (CurC == '\0') {
426 /* End of line inside macro argument list - read next line */
427 if (SB_NotEmpty (&Arg)) {
428 SB_AppendChar (&Arg, ' ');
430 if (NextLine () == 0) {
435 /* Just copy the character */
436 SB_AppendChar (&Arg, CurC);
441 /* Deallocate string buf resources */
447 static void MacroArgSubst (MacroExp* E)
448 /* Argument substitution according to ISO/IEC 9899:1999 (E), 6.10.3.1ff */
457 /* Remember the current input and switch to the macro replacement. */
458 SB_Reset (&E->M->Replacement);
459 OldSource = InitLine (&E->M->Replacement);
461 /* Argument handling loop */
462 while (CurC != '\0') {
464 /* If we have an identifier, check if it's a macro */
467 /* Check if it's a macro argument */
468 if ((ArgIdx = FindMacroArg (E->M, Ident)) >= 0) {
470 /* A macro argument. Get the corresponding actual argument. */
471 Arg = ME_GetActual (E, ArgIdx);
473 /* Copy any following whitespace */
474 HaveSpace = IsSpace (CurC);
479 /* If a ## operator follows, we have to insert the actual
480 * argument as is, otherwise it must be macro replaced.
482 if (CurC == '#' && NextC == '#') {
484 /* ### Add placemarker if necessary */
485 SB_Append (&E->Replacement, Arg);
489 /* Replace the formal argument by a macro replaced copy
493 MacroReplacement (Arg, &E->Replacement);
495 /* If we skipped whitespace before, re-add it now */
497 SB_AppendChar (&E->Replacement, ' ');
504 /* An identifier, keep it */
505 SB_AppendStr (&E->Replacement, Ident);
509 } else if (CurC == '#' && NextC == '#') {
516 /* Since we need to concatenate the token sequences, remove
517 * any whitespace that was added to target, since it must come
520 while (IsSpace (SB_LookAtLast (&E->Replacement))) {
521 SB_Drop (&E->Replacement, 1);
524 /* If the next token is an identifier which is a macro argument,
525 * replace it, otherwise do nothing.
529 /* Check if it's a macro argument */
530 if ((ArgIdx = FindMacroArg (E->M, Ident)) >= 0) {
532 /* Get the corresponding actual argument and add it. */
533 SB_Append (&E->Replacement, ME_GetActual (E, ArgIdx));
537 /* Just an ordinary identifier - add as is */
538 SB_AppendStr (&E->Replacement, Ident);
543 } else if (CurC == '#' && E->M->ArgCount >= 0) {
545 /* A # operator within a macro expansion of a function like
546 * macro. Read the following identifier and check if it's a
551 if (!IsSym (Ident) || (ArgIdx = FindMacroArg (E->M, Ident)) < 0) {
552 printf ("<%.*s>\n", SB_GetLen (Line), SB_GetConstBuf (Line));
553 PPError ("`#' is not followed by a macro parameter");
555 /* Make a valid string from Replacement */
556 Arg = ME_GetActual (E, ArgIdx);
558 Stringize (Arg, &E->Replacement);
561 } else if (IsQuote (CurC)) {
562 CopyQuotedString (&E->Replacement);
564 SB_AppendChar (&E->Replacement, CurC);
570 /* Remove whitespace from the end of the line */
571 while (IsSpace (SB_LookAtLast (&E->Replacement))) {
572 SB_Drop (&E->Replacement, 1);
576 /* Switch back the input */
577 InitLine (OldSource);
582 static void MacroCall (StrBuf* Target, Macro* M)
583 /* Process a function like macro */
587 /* Eat the left paren */
590 /* Initialize our MacroExp structure */
591 InitMacroExp (&E, M);
593 /* Read the actual macro arguments */
596 /* Compare formal and actual argument count */
597 if (CollCount (&E.ActualArgs) != (unsigned) M->ArgCount) {
598 PPError ("Macro argument count mismatch");
599 /* Be sure to make enough empty arguments available */
600 StrBuf Arg = STATIC_STRBUF_INITIALIZER;
601 while (CollCount (&E.ActualArgs) < (unsigned) M->ArgCount) {
602 ME_AppendActual (&E, &Arg);
606 /* Replace macro arguments handling the # and ## operators */
609 /* Do macro replacement on the macro that already has the parameters
613 MacroReplacement (&E.Replacement, Target);
616 /* Free memory allocated for the macro expansion structure */
622 static void ExpandMacro (StrBuf* Target, Macro* M)
623 /* Expand a macro into Target */
625 /* Check if this is a function like macro */
626 //printf ("Expanding %s(%u)\n", M->Name, ++V);
627 if (M->ArgCount >= 0) {
629 int Whitespace = IsSpace (CurC);
634 /* Function like macro but no parameter list */
635 SB_AppendStr (Target, M->Name);
637 SB_AppendChar (Target, ' ');
640 /* Function like macro */
641 MacroCall (Target, M);
647 InitMacroExp (&E, M);
649 /* Handle # and ## operators for object like macros */
652 /* Do macro replacement on the macro that already has the parameters
656 MacroReplacement (&E.Replacement, Target);
659 /* Free memory allocated for the macro expansion structure */
663 //printf ("Done with %s(%u)\n", M->Name, V--);
668 static void DefineMacro (void)
669 /* Handle a macro definition. */
675 /* Read the macro name */
677 if (!MacName (Ident)) {
681 /* Get an existing macro definition with this name */
682 Existing = FindMacro (Ident);
684 /* Create a new macro definition */
685 M = NewMacro (Ident);
687 /* Check if this is a function like macro */
690 /* Skip the left paren */
693 /* Set the marker that this is a function like macro */
696 /* Read the formal parameter list */
702 if (MacName (Ident) == 0) {
705 AddMacroArg (M, Ident);
713 /* Check for a right paren and eat it if we find one */
715 PPError ("`)' expected");
722 /* Skip whitespace before the macro replacement */
725 /* Insert the macro into the macro table and allocate the ActualArgs array */
728 /* Remove whitespace and comments from the line, store the preprocessed
729 * line into the macro replacement buffer.
731 Pass1 (Line, &M->Replacement);
733 /* Remove whitespace from the end of the line */
734 while (IsSpace (SB_LookAtLast (&M->Replacement))) {
735 SB_Drop (&M->Replacement, 1);
738 //printf ("%s: <%.*s>\n", M->Name, SB_GetLen (&M->Replacement), SB_GetConstBuf (&M->Replacement));
740 /* If we have an existing macro, check if the redefinition is identical.
741 * Print a diagnostic if not.
743 if (Existing && MacroCmp (M, Existing) != 0) {
744 PPError ("Macro redefinition is not identical");
750 /*****************************************************************************/
752 /*****************************************************************************/
756 static unsigned Pass1 (StrBuf* Source, StrBuf* Target)
757 /* Preprocessor pass 1. Remove whitespace. Handle old and new style comments
758 * and the "defined" operator.
765 /* Switch to the new input source */
766 StrBuf* OldSource = InitLine (Source);
768 /* Loop removing ws and comments */
770 while (CurC != '\0') {
771 if (IsSpace (CurC)) {
772 /* Squeeze runs of blanks */
773 if (!IsSpace (SB_LookAtLast (Target))) {
774 SB_AppendChar (Target, ' ');
777 } else if (IsSym (Ident)) {
778 if (Preprocessing && strcmp (Ident, "defined") == 0) {
779 /* Handle the "defined" operator */
788 SB_AppendChar (Target, IsMacro (Ident)? '1' : '0');
792 PPError ("`)' expected");
798 PPError ("Identifier expected");
799 SB_AppendChar (Target, '0');
803 SB_AppendStr (Target, Ident);
805 } else if (IsQuote (CurC)) {
806 CopyQuotedString (Target);
807 } else if (CurC == '/' && NextC == '*') {
808 if (!IsSpace (SB_LookAtLast (Target))) {
809 SB_AppendChar (Target, ' ');
812 } else if (IS_Get (&Standard) >= STD_C99 && CurC == '/' && NextC == '/') {
813 if (!IsSpace (SB_LookAtLast (Target))) {
814 SB_AppendChar (Target, ' ');
818 SB_AppendChar (Target, CurC);
823 /* Switch back to the old source */
824 InitLine (OldSource);
826 /* Return the number of identifiers found in the line */
832 static void MacroReplacement (StrBuf* Source, StrBuf* Target)
833 /* Perform macro replacement. */
838 /* Remember the current input and switch to Source */
839 StrBuf* OldSource = InitLine (Source);
841 /* Loop substituting macros */
842 while (CurC != '\0') {
843 /* If we have an identifier, check if it's a macro */
845 /* Check if it's a macro */
846 if ((M = FindMacro (Ident)) != 0 && !M->Expanding) {
847 /* It's a macro, expand it */
848 ExpandMacro (Target, M);
850 /* An identifier, keep it */
851 SB_AppendStr (Target, Ident);
853 } else if (IsQuote (CurC)) {
854 CopyQuotedString (Target);
855 } else if (IsSpace (CurC)) {
856 if (!IsSpace (SB_LookAtLast (Target))) {
857 SB_AppendChar (Target, CurC);
861 SB_AppendChar (Target, CurC);
866 /* Switch back the input */
867 InitLine (OldSource);
872 static void PreprocessLine (void)
873 /* Translate one line. */
875 /* Trim whitespace and remove comments. The function returns the number of
876 * identifiers found. If there were any, we will have to check for macros.
879 if (Pass1 (Line, MLine) > 0) {
880 MLine = InitLine (MLine);
883 MacroReplacement (Line, MLine);
886 /* Read from the new line */
888 MLine = InitLine (MLine);
893 static void DoUndef (void)
894 /* Process the #undef directive */
899 if (MacName (Ident)) {
900 UndefineMacro (Ident);
906 static int PushIf (int Skip, int Invert, int Cond)
907 /* Push a new if level onto the if stack */
909 /* Check for an overflow of the if stack */
910 if (IfIndex >= MAX_IFS-1) {
911 PPError ("Too many nested #if clauses");
915 /* Push the #if condition */
918 IfStack[IfIndex] = IFCOND_SKIP | IFCOND_NEEDTERM;
921 IfStack[IfIndex] = IFCOND_NONE | IFCOND_NEEDTERM;
922 return (Invert ^ Cond);
928 static int DoIf (int Skip)
929 /* Process #if directive */
933 /* We're about to abuse the compiler expression parser to evaluate the
934 * #if expression. Save the current tokens to come back here later.
935 * NOTE: Yes, this is a hack, but it saves a complete separate expression
936 * evaluation for the preprocessor.
938 Token SavedCurTok = CurTok;
939 Token SavedNextTok = NextTok;
941 /* Make sure the line infos for the tokens won't get removed */
942 if (SavedCurTok.LI) {
943 UseLineInfo (SavedCurTok.LI);
945 if (SavedNextTok.LI) {
946 UseLineInfo (SavedNextTok.LI);
950 /* Remove the #if from the line */
953 while (CurC != '\0') {
959 /* Start over parsing from line */
963 /* Switch into special preprocessing mode */
966 /* Expand macros in this line */
969 /* Add two semicolons as sentinels to the line, so the following
970 * expression evaluation will eat these two tokens but nothing from
971 * the following line.
973 SB_AppendStr (Line, ";;");
975 /* Load CurTok and NextTok with tokens from the new input */
979 /* Call the expression parser */
980 ConstExpr (hie1, &Expr);
982 /* End preprocessing mode */
985 /* Reset the old tokens */
986 CurTok = SavedCurTok;
987 NextTok = SavedNextTok;
989 /* Set the #if condition according to the expression result */
990 return PushIf (Skip, 1, Expr.IVal != 0);
995 static int DoIfDef (int skip, int flag)
996 /* Process #ifdef if flag == 1, or #ifndef if flag == 0. */
1001 if (MacName (Ident) == 0) {
1004 return PushIf (skip, flag, IsMacro(Ident));
1010 static void DoInclude (void)
1011 /* Open an include file. */
1015 StrBuf Filename = STATIC_STRBUF_INITIALIZER;
1021 /* Get the next char and check for a valid file name terminator. Setup
1022 * the include directory spec (SYS/USR) by looking at the terminator.
1037 PPError ("`\"' or `<' expected");
1042 /* Get a copy of the filename */
1043 while (CurC != '\0' && CurC != RTerm) {
1044 SB_AppendChar (&Filename, CurC);
1047 SB_Terminate (&Filename);
1049 /* Check if we got a terminator */
1050 if (CurC != RTerm) {
1051 /* No terminator found */
1052 PPError ("Missing terminator or file name too long");
1056 /* Open the include file */
1057 OpenIncludeFile (SB_GetConstBuf (&Filename), DirSpec);
1060 /* Free the allocated filename data */
1061 DoneStrBuf (&Filename);
1063 /* Clear the remaining line so the next input will come from the new
1071 static void DoError (void)
1072 /* Print an error */
1076 PPError ("Invalid #error directive");
1078 PPError ("#error: %s", SB_GetConstBuf (Line) + SB_GetIndex (Line));
1081 /* Clear the rest of line */
1087 static void DoPragma (void)
1088 /* Handle a #pragma line by converting the #pragma preprocessor directive into
1089 * the _Pragma() compiler operator.
1092 /* Skip blanks following the #pragma directive */
1095 /* Copy the remainder of the line into MLine removing comments and ws */
1097 Pass1 (Line, MLine);
1099 /* Convert the directive into the operator */
1100 SB_CopyStr (Line, "_Pragma (");
1102 Stringize (MLine, Line);
1103 SB_AppendChar (Line, ')');
1105 /* Initialize reading from line */
1112 void Preprocess (void)
1113 /* Preprocess a line */
1118 /* Create the output buffer if we don't already have one */
1120 MLine = NewStrBuf ();
1123 /* Skip white space at the beginning of the line */
1126 /* Check for stuff to skip */
1128 while (CurC == '\0' || CurC == '#' || Skip) {
1130 /* Check for preprocessor lines lines */
1135 /* Ignore the empty preprocessor directive */
1138 if (!IsSym (Directive)) {
1139 PPError ("Preprocessor directive expected");
1142 switch (FindPPToken (Directive)) {
1152 if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) {
1154 /* Handle as #else/#if combination */
1155 if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) {
1158 IfStack[IfIndex] |= IFCOND_ELSE;
1161 /* #elif doesn't need a terminator */
1162 IfStack[IfIndex] &= ~IFCOND_NEEDTERM;
1164 PPError ("Duplicate #else/#elif");
1167 PPError ("Unexpected #elif");
1173 if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) {
1174 if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) {
1177 IfStack[IfIndex] |= IFCOND_ELSE;
1179 PPError ("Duplicate #else");
1182 PPError ("Unexpected `#else'");
1188 /* Remove any clauses on top of stack that do not
1189 * need a terminating #endif.
1191 while (IfIndex >= 0 && (IfStack[IfIndex] & IFCOND_NEEDTERM) == 0) {
1195 /* Stack may not be empty here or something is wrong */
1196 CHECK (IfIndex >= 0);
1198 /* Remove the clause that needs a terminator */
1199 Skip = (IfStack[IfIndex--] & IFCOND_SKIP) != 0;
1201 PPError ("Unexpected `#endif'");
1216 Skip = DoIfDef (Skip, 1);
1220 Skip = DoIfDef (Skip, 0);
1230 /* Should do something in C99 at least, but we ignore it */
1250 PPError ("Preprocessor directive expected");
1256 if (NextLine () == 0) {
1258 PPError ("`#endif' expected");
1268 if (Verbosity > 1 && SB_NotEmpty (Line)) {
1269 printf ("%s(%u): %.*s\n", GetCurrentFile (), GetCurrentLine (),
1270 SB_GetLen (Line), SB_GetConstBuf (Line));