2 /* C pre-processor functions */
32 /*****************************************************************************/
34 /*****************************************************************************/
38 static int Pass1 (const char* From, char* To);
39 /* Preprocessor pass 1. Remove whitespace and comments. */
43 /*****************************************************************************/
45 /*****************************************************************************/
49 /* Set when the preprocessor calls expr() recursively */
50 unsigned char Preprocessing = 0;
52 /* Management data for #if */
54 #define IFCOND_NONE 0x00U
55 #define IFCOND_SKIP 0x01U
56 #define IFCOND_ELSE 0x02U
57 #define IFCOND_NEEDTERM 0x04U
58 static unsigned char IfStack[MAX_IFS];
59 static int IfIndex = -1;
61 /* Buffer for macro expansion */
62 static char mlinebuf [LINESIZE];
63 static char* mline = mlinebuf;
68 /*****************************************************************************/
69 /* Low level preprocessor token handling */
70 /*****************************************************************************/
74 /* Types of preprocessor tokens */
93 /* Preprocessor keyword to token mapping table */
94 static const struct PPToken {
95 const char* Key; /* Keyword */
96 pptoken_t Tok; /* Token */
98 { "define", PP_DEFINE },
101 { "endif", PP_ENDIF },
102 { "error", PP_ERROR },
104 { "ifdef", PP_IFDEF },
105 { "ifndef", PP_IFNDEF },
106 { "include", PP_INCLUDE },
108 { "pragma", PP_PRAGMA },
109 { "undef", PP_UNDEF },
112 /* Number of preprocessor tokens */
113 #define PPTOKEN_COUNT (sizeof(PPTokens) / sizeof(PPTokens[0]))
117 static int CmpToken (const void* Key, const void* Elem)
118 /* Compare function for bsearch */
120 return strcmp ((const char*) Key, ((const struct PPToken*) Elem)->Key);
125 static pptoken_t FindPPToken (const char* Ident)
126 /* Find a preprocessor token and return ut. Return PP_ILLEGAL if the identifier
127 * is not a valid preprocessor token.
131 P = bsearch (Ident, PPTokens, PPTOKEN_COUNT, sizeof (PPTokens[0]), CmpToken);
132 return P? P->Tok : PP_ILLEGAL;
137 /*****************************************************************************/
139 /*****************************************************************************/
144 INLINE void KeepChar (char c)
145 /* Put character c into translation buffer. */
150 #define KeepChar(c) *mptr++ = (c)
155 static void KeepStr (const char* S)
156 /* Put string str into translation buffer. */
158 unsigned Len = strlen (S);
159 memcpy (mptr, S, Len);
165 static void Stringize (const char* S)
166 /* Stringize the given string: Add double quotes at start and end and preceed
167 * each occurance of " and \ by a backslash.
171 /* Replace any characters inside the string may not be part of a string
191 static void SwapLineBuffers (void)
192 /* Swap both line buffers */
194 /* Swap mline and line */
202 static void OldStyleComment (void)
203 /* Remove an old style C comment from line. */
205 /* Remember the current line number, so we can output better error
206 * messages if the comment is not terminated in the current file.
208 unsigned StartingLine = GetCurrentLine();
210 /* Skip the start of comment chars */
214 /* Skip the comment */
215 while (CurC != '*' || NextC != '/') {
217 if (NextLine () == 0) {
218 PPError ("End-of-file reached in comment starting at line %u",
223 if (CurC == '/' && NextC == '*') {
224 PPWarning ("`/*' found inside a comment");
230 /* Skip the end of comment chars */
237 static void NewStyleComment (void)
238 /* Remove a new style C comment from line. */
240 /* Beware: Because line continuation chars are handled when reading
241 * lines, we may only skip til the end of the source line, which
242 * may not be the same as the end of the input line. The end of the
243 * source line is denoted by a lf (\n) character.
247 } while (CurC != '\n' && CurC != '\0');
255 static void SkipBlank (void)
256 /* Skip blanks and tabs in the input stream. */
258 while (IsBlank (CurC)) {
265 static char* CopyQuotedString (char* Target)
266 /* Copy a single or double quoted string from the input to Target. Return the
267 * new target pointer. Target will not be terminated after the copy.
270 /* Remember the quote character, copy it to the target buffer and skip it */
275 /* Copy the characters inside the string */
276 while (CurC != '\0' && CurC != Quote) {
277 /* Keep an escaped char */
282 /* Copy the character */
287 /* If we had a terminating quote, copy it */
293 /* Return the new target pointer */
299 /*****************************************************************************/
301 /*****************************************************************************/
305 static int MacName (char* Ident)
306 /* Get a macro symbol name into Ident. If we have an error, print a
307 * diagnostic message and clear the line.
310 if (IsSym (Ident) == 0) {
311 PPError ("Identifier expected");
321 static void ExpandMacroArgs (Macro* M)
322 /* Expand the arguments of a macro */
325 const char* Replacement;
328 /* Save the current line pointer and setup the new ones */
330 InitLine (M->Replacement);
332 /* Copy the macro replacement checking for parameters to replace */
333 while (CurC != '\0') {
334 /* If the next token is an identifier, check for a macro arg */
335 if (IsIdent (CurC)) {
337 Replacement = FindMacroArg (M, Ident);
339 /* Macro arg, keep the replacement */
340 KeepStr (Replacement);
342 /* No macro argument, keep the original identifier */
345 } else if (CurC == '#' && IsIdent (NextC)) {
348 Replacement = FindMacroArg (M, Ident);
350 /* Make a valid string from Replacement */
351 Stringize (Replacement);
353 /* No replacement - keep the input */
357 } else if (IsQuote (CurC)) {
358 mptr = CopyQuotedString (mptr);
365 /* Reset the line pointer */
371 static int MacroCall (Macro* M)
372 /* Process a function like macro */
374 int ArgCount; /* Macro argument count */
375 unsigned ParCount; /* Number of open parenthesis */
376 char Buf[LINESIZE]; /* Argument buffer */
377 const char* ArgStart;
380 /* Check for an argument list. If we don't have a list, we won't expand
390 /* Eat the left paren */
393 /* Read the actual macro arguments and store pointers to these arguments
394 * into the array of actual arguments in the macro definition.
402 /* Nested parenthesis */
406 } else if (IsQuote (CurC)) {
407 B = CopyQuotedString (B);
408 } else if (CurC == ',' || CurC == ')') {
410 /* End of actual argument */
412 while (IsBlank(*ArgStart)) {
415 if (ArgCount < M->ArgCount) {
416 M->ActualArgs[ArgCount++] = ArgStart;
417 } else if (CurC != ')' || *ArgStart != '\0' || M->ArgCount > 0) {
418 /* Be sure not to count the single empty argument for a
419 * macro that does not have arguments.
424 /* Check for end of macro param list */
430 /* Start the next param */
434 /* Comma or right paren inside nested parenthesis */
441 } else if (IsBlank (CurC)) {
442 /* Squeeze runs of blanks */
445 } else if (CurC == '/' && NextC == '*') {
448 } else if (ANSI == 0 && CurC == '/' && NextC == '/') {
451 } else if (CurC == '\0') {
452 /* End of line inside macro argument list - read next line */
453 if (NextLine () == 0) {
457 /* Just copy the character */
463 /* Compare formal argument count with actual */
464 if (M->ArgCount != ArgCount) {
465 PPError ("Macro argument count mismatch");
466 /* Be sure to make enough empty arguments available */
467 while (ArgCount < M->ArgCount) {
468 M->ActualArgs [ArgCount++] = "";
472 /* Preprocess the line, replacing macro parameters */
481 static void ExpandMacro (Macro* M)
484 /* Check if this is a function like macro */
485 if (M->ArgCount >= 0) {
486 /* Function like macro */
487 if (MacroCall (M) == 0) {
491 /* Just copy the replacement text */
492 KeepStr (M->Replacement);
498 static void DefineMacro (void)
499 /* Handle a macro definition. */
507 /* Read the macro name */
509 if (!MacName (Ident)) {
513 /* Get an existing macro definition with this name */
514 Existing = FindMacro (Ident);
516 /* Create a new macro definition */
517 M = NewMacro (Ident);
519 /* Check if this is a function like macro */
522 /* Skip the left paren */
525 /* Set the marker that this is a function like macro */
528 /* Read the formal parameter list */
533 if (MacName (Ident) == 0) {
536 AddMacroArg (M, Ident);
543 /* Check for a right paren and eat it if we find one */
545 PPError ("`)' expected");
552 /* Insert the macro into the macro table and allocate the ActualArgs array */
555 /* Remove whitespace and comments from the line, store the preprocessed
563 /* Create a copy of the replacement */
564 M->Replacement = xstrdup (Buf);
566 /* If we have an existing macro, check if the redefinition is identical.
567 * Print a diagnostic if not.
570 if (MacroCmp (M, Existing) != 0) {
571 PPError ("Macro redefinition is not identical");
578 /*****************************************************************************/
580 /*****************************************************************************/
584 static int Pass1 (const char* From, char* To)
585 /* Preprocessor pass 1. Remove whitespace and comments. */
591 /* Initialize reading from "From" */
597 /* Loop removing ws and comments */
599 while (CurC != '\0') {
600 if (IsBlank (CurC)) {
603 } else if (IsIdent (CurC)) {
605 if (Preprocessing && strcmp(Ident, "defined") == 0) {
606 /* Handle the "defined" operator */
614 if (!IsIdent (CurC)) {
615 PPError ("Identifier expected");
619 KeepChar (IsMacro (Ident)? '1' : '0');
623 PPError ("`)' expected");
630 if (MaybeMacro (Ident[0])) {
635 } else if (IsQuote (CurC)) {
636 mptr = CopyQuotedString (mptr);
637 } else if (CurC == '/' && NextC == '*') {
640 } else if (ANSI == 0 && CurC == '/' && NextC == '/') {
654 static int Pass2 (const char* From, char* To)
655 /* Preprocessor pass 2. Perform macro substitution. */
661 /* Initialize reading from "From" */
667 /* Loop substituting macros */
669 while (CurC != '\0') {
670 /* If we have an identifier, check if it's a macro */
671 if (IsIdent (CurC)) {
673 M = FindMacro (Ident);
680 } else if (IsQuote (CurC)) {
681 mptr = CopyQuotedString (mptr);
692 static void PreprocessLine (void)
693 /* Translate one line. */
697 /* Trim whitespace and remove comments. The function returns false if no
698 * identifiers were found that may be macros. If this is the case, no
699 * macro substitution is performed.
701 int Done = Pass1 (line, mline);
703 /* Repeatedly expand macros in the line */
704 for (I = 0; I < 256; ++I) {
705 /* Swap mline and line */
710 /* Perform macro expansion */
711 Done = Pass2 (line, mline);
715 /* Reinitialize line parsing */
721 static void DoUndef (void)
722 /* Process the #undef directive */
727 if (MacName (Ident)) {
728 UndefineMacro (Ident);
734 static int PushIf (int Skip, int Invert, int Cond)
735 /* Push a new if level onto the if stack */
737 /* Check for an overflow of the if stack */
738 if (IfIndex >= MAX_IFS-1) {
739 PPError ("Too many nested #if clauses");
743 /* Push the #if condition */
746 IfStack[IfIndex] = IFCOND_SKIP | IFCOND_NEEDTERM;
749 IfStack[IfIndex] = IFCOND_NONE | IFCOND_NEEDTERM;
750 return (Invert ^ Cond);
756 static int DoIf (int Skip)
757 /* Process #if directive */
762 /* We're about to abuse the compiler expression parser to evaluate the
763 * #if expression. Save the current tokens to come back here later.
764 * NOTE: Yes, this is a hack, but it saves a complete separate expression
765 * evaluation for the preprocessor.
770 /* Make sure the line infos for the tokens won't get removed */
772 UseLineInfo (sv1.LI);
775 UseLineInfo (sv2.LI);
778 /* Remove the #if from the line */
781 while (CurC != '\0') {
787 /* Start over parsing from line */
790 /* Switch into special preprocessing mode */
793 /* Expand macros in this line */
796 /* Add two semicolons as sentinels to the line, so the following
797 * expression evaluation will eat these two tokens but nothing from
798 * the following line.
802 /* Prime the token pump (remove old tokens from the stream) */
806 /* Call the expression parser */
809 /* End preprocessing mode */
812 /* Reset the old tokens */
816 /* Set the #if condition according to the expression result */
817 return PushIf (Skip, 1, lval.ConstVal != 0);
822 static int DoIfDef (int skip, int flag)
823 /* Process #ifdef if flag == 1, or #ifndef if flag == 0. */
828 if (MacName (Ident) == 0) {
831 return PushIf (skip, flag, IsMacro(Ident));
837 static void DoInclude (void)
838 /* Open an include file. */
847 /* Get the next char and check for a valid file name terminator. Setup
848 * the include directory spec (SYS/USR) by looking at the terminator.
863 PPError ("`\"' or `<' expected");
868 /* Copy the filename into mline. Since mline has the same size as the
869 * input line, we don't need to check for an overflow here.
872 while (CurC != '\0' && CurC != RTerm) {
878 /* Check if we got a terminator */
880 /* No terminator found */
881 PPError ("Missing terminator or file name too long");
885 /* Open the include file */
886 OpenIncludeFile (mline, DirSpec);
889 /* Clear the remaining line so the next input will come from the new
897 static void DoError (void)
902 PPError ("Invalid #error directive");
904 PPError ("#error: %s", lptr);
907 /* Clear the rest of line */
913 static void DoPragma (void)
914 /* Handle a #pragma line by converting the #pragma preprocessor directive into
915 * the _Pragma() compiler operator.
918 /* Skip blanks following the #pragma directive */
921 /* Copy the remainder of the line into mline removing comments and ws */
924 /* Convert the directive into the operator */
926 KeepStr ("_Pragma (");
931 /* Initialize reading from line */
937 void Preprocess (void)
938 /* Preprocess a line */
943 /* Skip white space at the beginning of the line */
946 /* Check for stuff to skip */
948 while (CurC == '\0' || CurC == '#' || Skip) {
950 /* Check for preprocessor lines lines */
955 /* Ignore the empty preprocessor directive */
958 if (!IsSym (Directive)) {
959 PPError ("Preprocessor directive expected");
962 switch (FindPPToken (Directive)) {
972 if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) {
974 /* Handle as #else/#if combination */
975 if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) {
978 IfStack[IfIndex] |= IFCOND_ELSE;
981 /* #elif doesn't need a terminator */
982 IfStack[IfIndex] &= ~IFCOND_NEEDTERM;
984 PPError ("Duplicate #else/#elif");
987 PPError ("Unexpected #elif");
993 if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) {
994 if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) {
997 IfStack[IfIndex] |= IFCOND_ELSE;
999 PPError ("Duplicate #else");
1002 PPError ("Unexpected `#else'");
1008 /* Remove any clauses on top of stack that do not
1009 * need a terminating #endif.
1011 while (IfIndex >= 0 && (IfStack[IfIndex] & IFCOND_NEEDTERM) == 0) {
1015 /* Stack may not be empty here or something is wrong */
1016 CHECK (IfIndex >= 0);
1018 /* Remove the clause that needs a terminator */
1019 Skip = (IfStack[IfIndex--] & IFCOND_SKIP) != 0;
1021 PPError ("Unexpected `#endif'");
1036 Skip = DoIfDef (Skip, 1);
1040 Skip = DoIfDef (Skip, 0);
1050 /* Not allowed in strict ANSI mode */
1051 if (!Skip && ANSI) {
1052 PPError ("Preprocessor directive expected");
1071 PPError ("Preprocessor directive expected");
1077 if (NextLine () == 0) {
1079 PPError ("`#endif' expected");
1089 Print (stdout, 2, "line: %s\n", line);