+/*****************************************************************************/
+/* */
+/* preproc.c */
+/* */
+/* cc65 preprocessor */
+/* */
+/* */
+/* */
+/* (C) 1998-2010, Ullrich von Bassewitz */
+/* Roemerstrasse 52 */
+/* D-70794 Filderstadt */
+/* EMail: uz@cc65.org */
+/* */
+/* */
+/* This software is provided 'as-is', without any expressed or implied */
+/* warranty. In no event will the authors be held liable for any damages */
+/* arising from the use of this software. */
+/* */
+/* Permission is granted to anyone to use this software for any purpose, */
+/* including commercial applications, and to alter it and redistribute it */
+/* freely, subject to the following restrictions: */
+/* */
+/* 1. The origin of this software must not be misrepresented; you must not */
+/* claim that you wrote the original software. If you use this software */
+/* in a product, an acknowledgment in the product documentation would be */
+/* appreciated but is not required. */
+/* 2. Altered source versions must be plainly marked as such, and must not */
+/* be misrepresented as being the original software. */
+/* 3. This notice may not be removed or altered from any source */
+/* distribution. */
+/* */
+/*****************************************************************************/
+
-/* C pre-processor functions */
#include <stdio.h>
#include <string.h>
/* common */
#include "chartype.h"
+#include "check.h"
+#include "inline.h"
+#include "print.h"
#include "xmalloc.h"
/* cc65 */
#include "ident.h"
#include "incpath.h"
#include "input.h"
+#include "lineinfo.h"
#include "macrotab.h"
-#include "scanner.h"
-#include "util.h"
#include "preproc.h"
+#include "scanner.h"
+#include "standard.h"
/*****************************************************************************/
-/* Forwards */
+/* Data */
/*****************************************************************************/
-static int Pass1 (const char* From, char* To);
-/* Preprocessor pass 1. Remove whitespace and comments. */
+/* Set when the preprocessor calls expr() recursively */
+unsigned char Preprocessing = 0;
+
+/* Management data for #if */
+#define MAX_IFS 64
+#define IFCOND_NONE 0x00U
+#define IFCOND_SKIP 0x01U
+#define IFCOND_ELSE 0x02U
+#define IFCOND_NEEDTERM 0x04U
+static unsigned char IfStack[MAX_IFS];
+static int IfIndex = -1;
+
+/* Buffer for macro expansion */
+static StrBuf* MLine;
+
+/* Structure used when expanding macros */
+typedef struct MacroExp MacroExp;
+struct MacroExp {
+ Collection ActualArgs; /* Actual arguments */
+ StrBuf Replacement; /* Replacement with arguments substituted */
+ Macro* M; /* The macro we're handling */
+};
/*****************************************************************************/
-/* data */
+/* Forwards */
/*****************************************************************************/
-/* Set when the pp calls expr() recursively */
-unsigned char Preprocessing = 0;
+static unsigned Pass1 (StrBuf* Source, StrBuf* Target);
+/* Preprocessor pass 1. Remove whitespace. Handle old and new style comments
+ * and the "defined" operator.
+ */
+
+static void MacroReplacement (StrBuf* Source, StrBuf* Target);
+/* Perform macro replacement. */
-/* Management data for #if */
-#define N_IFDEF 16
-static int i_ifdef = -1;
-static char s_ifdef[N_IFDEF];
-/* Buffer for macro expansion */
-static char mlinebuf [LINESIZE];
-static char* mline = mlinebuf;
-static char* mptr;
-/* Flag: Expand macros in this line */
-static int ExpandMacros = 1;
+/*****************************************************************************/
+/* Low level preprocessor token handling */
+/*****************************************************************************/
+
+
+
+/* Types of preprocessor tokens */
+typedef enum {
+ PP_ILLEGAL = -1,
+ PP_DEFINE,
+ PP_ELIF,
+ PP_ELSE,
+ PP_ENDIF,
+ PP_ERROR,
+ PP_IF,
+ PP_IFDEF,
+ PP_IFNDEF,
+ PP_INCLUDE,
+ PP_LINE,
+ PP_PRAGMA,
+ PP_UNDEF,
+ PP_WARNING,
+} pptoken_t;
+
+
+
+/* Preprocessor keyword to token mapping table */
+static const struct PPToken {
+ const char* Key; /* Keyword */
+ pptoken_t Tok; /* Token */
+} PPTokens[] = {
+ { "define", PP_DEFINE },
+ { "elif", PP_ELIF },
+ { "else", PP_ELSE },
+ { "endif", PP_ENDIF },
+ { "error", PP_ERROR },
+ { "if", PP_IF },
+ { "ifdef", PP_IFDEF },
+ { "ifndef", PP_IFNDEF },
+ { "include", PP_INCLUDE },
+ { "line", PP_LINE },
+ { "pragma", PP_PRAGMA },
+ { "undef", PP_UNDEF },
+ { "warning", PP_WARNING },
+};
+
+/* Number of preprocessor tokens */
+#define PPTOKEN_COUNT (sizeof(PPTokens) / sizeof(PPTokens[0]))
+
+
+
+static int CmpToken (const void* Key, const void* Elem)
+/* Compare function for bsearch */
+{
+ return strcmp ((const char*) Key, ((const struct PPToken*) Elem)->Key);
+}
+
+
+
+static pptoken_t FindPPToken (const char* Ident)
+/* Find a preprocessor token and return it. Return PP_ILLEGAL if the identifier
+ * is not a valid preprocessor token.
+ */
+{
+ struct PPToken* P;
+ P = bsearch (Ident, PPTokens, PPTOKEN_COUNT, sizeof (PPTokens[0]), CmpToken);
+ return P? P->Tok : PP_ILLEGAL;
+}
/*****************************************************************************/
-/* code */
+/* struct MacroExp */
/*****************************************************************************/
-static int keepch (char c)
-/* Put character c into translation buffer. */
+static MacroExp* InitMacroExp (MacroExp* E, Macro* M)
+/* Initialize a MacroExp structure */
{
- return (*mptr++ = c);
+ InitCollection (&E->ActualArgs);
+ SB_Init (&E->Replacement);
+ E->M = M;
+ return E;
}
-static void keepstr (const char* S)
-/* Put string str into translation buffer. */
+static void DoneMacroExp (MacroExp* E)
+/* Cleanup after use of a MacroExp structure */
{
- while (*S) {
- keepch (*S++);
+ unsigned I;
+
+ /* Delete the list with actual arguments */
+ for (I = 0; I < CollCount (&E->ActualArgs); ++I) {
+ FreeStrBuf (CollAtUnchecked (&E->ActualArgs, I));
}
+ DoneCollection (&E->ActualArgs);
+ SB_Done (&E->Replacement);
}
-static void Comment (void)
-/* Remove a C comment from line. */
+static void ME_AppendActual (MacroExp* E, StrBuf* Arg)
+/* Add a copy of Arg to the list of actual macro arguments.
+ * NOTE: This function will clear Arg!
+ */
+{
+ /* Create a new string buffer */
+ StrBuf* A = NewStrBuf ();
+
+ /* Move the contents of Arg to A */
+ SB_Move (A, Arg);
+
+ /* Add A to the actual arguments */
+ CollAppend (&E->ActualArgs, A);
+}
+
+
+
+static StrBuf* ME_GetActual (MacroExp* E, unsigned Index)
+/* Return an actual macro argument with the given index */
+{
+ return CollAt (&E->ActualArgs, Index);
+}
+
+
+
+static int ME_ArgIsVariadic (const MacroExp* E)
+/* Return true if the next actual argument we will add is a variadic one */
+{
+ return (E->M->Variadic &&
+ E->M->ArgCount == (int) CollCount (&E->ActualArgs) + 1);
+}
+
+
+
+/*****************************************************************************/
+/* Code */
+/*****************************************************************************/
+
+
+
+static void Stringize (StrBuf* Source, StrBuf* Target)
+/* Stringize the given string: Add double quotes at start and end and preceed
+ * each occurance of " and \ by a backslash.
+ */
+{
+ char C;
+
+ /* Add a starting quote */
+ SB_AppendChar (Target, '\"');
+
+ /* Replace any characters inside the string may not be part of a string
+ * unescaped.
+ */
+ while ((C = SB_Get (Source)) != '\0') {
+ switch (C) {
+ case '\"':
+ case '\\':
+ SB_AppendChar (Target, '\\');
+ /* FALLTHROUGH */
+ default:
+ SB_AppendChar (Target, C);
+ break;
+ }
+ }
+
+ /* Add the closing quote */
+ SB_AppendChar (Target, '\"');
+}
+
+
+
+static void OldStyleComment (void)
+/* Remove an old style C comment from line. */
{
/* Remember the current line number, so we can output better error
* messages if the comment is not terminated in the current file.
/* Skip the comment */
while (CurC != '*' || NextC != '/') {
- if (CurC == '\0') {
- if (NextLine () == 0) {
- PPError ("End-of-file reached in comment starting at line %u",
- StartingLine);
- return;
- }
- } else {
- if (CurC == '/' && NextC == '*') {
- PPWarning ("`/*' found inside a comment");
- }
- NextChar ();
- }
+ if (CurC == '\0') {
+ if (NextLine () == 0) {
+ PPError ("End-of-file reached in comment starting at line %u",
+ StartingLine);
+ return;
+ }
+ } else {
+ if (CurC == '/' && NextC == '*') {
+ PPWarning ("`/*' found inside a comment");
+ }
+ NextChar ();
+ }
}
/* Skip the end of comment chars */
-static void SkipBlank (void)
-/* Skip blanks and tabs in the input stream. */
+static void NewStyleComment (void)
+/* Remove a new style C comment from line. */
{
- while (IsBlank (CurC)) {
- NextChar ();
+ /* Beware: Because line continuation chars are handled when reading
+ * lines, we may only skip til the end of the source line, which
+ * may not be the same as the end of the input line. The end of the
+ * source line is denoted by a lf (\n) character.
+ */
+ do {
+ NextChar ();
+ } while (CurC != '\n' && CurC != '\0');
+ if (CurC == '\n') {
+ NextChar ();
}
}
-static char* CopyQuotedString (char* Target)
-/* Copy a single or double quoted string from the input to Target. Return the
- * new target pointer. Target will not be terminated after the copy.
+static int SkipWhitespace (int SkipLines)
+/* Skip white space in the input stream. Do also skip newlines if SkipLines
+ * is true. Return zero if nothing was skipped, otherwise return a
+ * value != zero.
*/
+{
+ int Skipped = 0;
+ while (1) {
+ if (IsSpace (CurC)) {
+ NextChar ();
+ Skipped = 1;
+ } else if (CurC == '\0' && SkipLines) {
+ /* End of line, read next */
+ if (NextLine () != 0) {
+ Skipped = 1;
+ } else {
+ /* End of input */
+ break;
+ }
+ } else {
+ /* No more white space */
+ break;
+ }
+ }
+ return Skipped;
+}
+
+
+
+static void CopyQuotedString (StrBuf* Target)
+/* Copy a single or double quoted string from the input to Target. */
{
/* Remember the quote character, copy it to the target buffer and skip it */
char Quote = CurC;
- *Target++ = CurC;
+ SB_AppendChar (Target, CurC);
NextChar ();
/* Copy the characters inside the string */
while (CurC != '\0' && CurC != Quote) {
- /* Keep an escaped char */
- if (CurC == '\\') {
- *Target++ = CurC;
- NextChar ();
- }
- /* Copy the character */
- *Target++ = CurC;
- NextChar ();
+ /* Keep an escaped char */
+ if (CurC == '\\') {
+ SB_AppendChar (Target, CurC);
+ NextChar ();
+ }
+ /* Copy the character */
+ SB_AppendChar (Target, CurC);
+ NextChar ();
}
/* If we had a terminating quote, copy it */
if (CurC != '\0') {
- *Target++ = CurC;
- NextChar ();
+ SB_AppendChar (Target, CurC);
+ NextChar ();
}
-
- /* Return the new target pointer */
- return Target;
}
/*****************************************************************************/
-/* Macro stuff */
+/* Macro stuff */
/*****************************************************************************/
static int MacName (char* Ident)
-/* Get macro symbol name. If error, print message and clear line. */
+/* Get a macro symbol name into Ident. If we have an error, print a
+ * diagnostic message and clear the line.
+ */
{
if (IsSym (Ident) == 0) {
- PPError ("Identifier expected");
- ClearLine ();
- return 0;
+ PPError ("Identifier expected");
+ ClearLine ();
+ return 0;
} else {
- return 1;
+ return 1;
}
}
-static void ExpandMacroArgs (Macro* M)
-/* Preprocessor pass 2. Perform macro substitution. */
+static void ReadMacroArgs (MacroExp* E)
+/* Identify the arguments to a macro call */
{
- ident Ident;
- const char* Replacement;
- const char* SavePtr;
+ unsigned Parens; /* Number of open parenthesis */
+ StrBuf Arg = STATIC_STRBUF_INITIALIZER;
- /* Save the current line pointer and setup the new ones */
- SavePtr = lptr;
- InitLine (M->Replacement);
+ /* Read the actual macro arguments */
+ Parens = 0;
+ while (1) {
+ if (CurC == '(') {
+
+ /* Nested parenthesis */
+ SB_AppendChar (&Arg, CurC);
+ NextChar ();
+ ++Parens;
+
+ } else if (IsQuote (CurC)) {
+
+ /* Quoted string - just copy */
+ CopyQuotedString (&Arg);
+
+ } else if (CurC == ',' || CurC == ')') {
+
+ if (Parens) {
+ /* Comma or right paren inside nested parenthesis */
+ if (CurC == ')') {
+ --Parens;
+ }
+ SB_AppendChar (&Arg, CurC);
+ NextChar ();
+ } else if (CurC == ',' && ME_ArgIsVariadic (E)) {
+ /* It's a comma, but we're inside a variadic macro argument, so
+ * just copy it and proceed.
+ */
+ SB_AppendChar (&Arg, CurC);
+ NextChar ();
+ } else {
+ /* End of actual argument. Remove whitespace from the end. */
+ while (IsSpace (SB_LookAtLast (&Arg))) {
+ SB_Drop (&Arg, 1);
+ }
+
+ /* If this is not the single empty argument for a macro with
+ * an empty argument list, remember it.
+ */
+ if (CurC != ')' || SB_NotEmpty (&Arg) || E->M->ArgCount > 0) {
+ ME_AppendActual (E, &Arg);
+ }
+
+ /* Check for end of macro param list */
+ if (CurC == ')') {
+ NextChar ();
+ break;
+ }
+
+ /* Start the next param */
+ NextChar ();
+ SB_Clear (&Arg);
+ }
+ } else if (SkipWhitespace (1)) {
+ /* Squeeze runs of blanks within an arg */
+ if (SB_NotEmpty (&Arg)) {
+ SB_AppendChar (&Arg, ' ');
+ }
+ } else if (CurC == '/' && NextC == '*') {
+ if (SB_NotEmpty (&Arg)) {
+ SB_AppendChar (&Arg, ' ');
+ }
+ OldStyleComment ();
+ } else if (IS_Get (&Standard) >= STD_C99 && CurC == '/' && NextC == '/') {
+ if (SB_NotEmpty (&Arg)) {
+ SB_AppendChar (&Arg, ' ');
+ }
+ NewStyleComment ();
+ } else if (CurC == '\0') {
+ /* End of input inside macro argument list */
+ PPError ("Unterminated argument list invoking macro `%s'", E->M->Name);
+
+ ClearLine ();
+ break;
+ } else {
+ /* Just copy the character */
+ SB_AppendChar (&Arg, CurC);
+ NextChar ();
+ }
+ }
- /* Copy the macro replacement checking for parameters to replace */
- while (CurC != '\0') {
- /* If the next token is an identifier, check for a macro arg */
- if (IsIdent (CurC)) {
- SymName (Ident);
- Replacement = FindMacroArg (M, Ident);
- if (Replacement) {
- /* Macro arg, keep the replacement */
- keepstr (Replacement);
- } else {
- /* No macro argument, keep the original identifier */
- keepstr (Ident);
- }
- } else if (CurC == '#' && IsIdent (NextC)) {
- NextChar ();
- SymName (Ident);
- Replacement = FindMacroArg (M, Ident);
- if (Replacement) {
- keepch ('\"');
- keepstr (Replacement);
- keepch ('\"');
- } else {
- keepch ('#');
- keepstr (Ident);
- }
- } else if (IsQuoteChar (CurC)) {
- mptr = CopyQuotedString (mptr);
- } else {
- *mptr++ = CurC;
- NextChar ();
- }
- }
-
- /* Reset the line pointer */
- InitLine (SavePtr);
+ /* Deallocate string buf resources */
+ SB_Done (&Arg);
}
-static int MacroCall (Macro* M)
-/* Process a function like macro */
+static void MacroArgSubst (MacroExp* E)
+/* Argument substitution according to ISO/IEC 9899:1999 (E), 6.10.3.1ff */
{
- unsigned ArgCount; /* Macro argument count */
- unsigned ParCount; /* Number of open parenthesis */
- char Buf[LINESIZE]; /* Argument buffer */
- const char* ArgStart;
- char* B;
+ ident Ident;
+ int ArgIdx;
+ StrBuf* OldSource;
+ StrBuf* Arg;
+ int HaveSpace;
+
+
+ /* Remember the current input and switch to the macro replacement. */
+ int OldIndex = SB_GetIndex (&E->M->Replacement);
+ SB_Reset (&E->M->Replacement);
+ OldSource = InitLine (&E->M->Replacement);
+
+ /* Argument handling loop */
+ while (CurC != '\0') {
+
+ /* If we have an identifier, check if it's a macro */
+ if (IsSym (Ident)) {
+
+ /* Check if it's a macro argument */
+ if ((ArgIdx = FindMacroArg (E->M, Ident)) >= 0) {
+
+ /* A macro argument. Get the corresponding actual argument. */
+ Arg = ME_GetActual (E, ArgIdx);
+
+ /* Copy any following whitespace */
+ HaveSpace = SkipWhitespace (0);
+
+ /* If a ## operator follows, we have to insert the actual
+ * argument as is, otherwise it must be macro replaced.
+ */
+ if (CurC == '#' && NextC == '#') {
+
+ /* ### Add placemarker if necessary */
+ SB_Append (&E->Replacement, Arg);
+
+ } else {
+
+ /* Replace the formal argument by a macro replaced copy
+ * of the actual.
+ */
+ SB_Reset (Arg);
+ MacroReplacement (Arg, &E->Replacement);
+
+ /* If we skipped whitespace before, re-add it now */
+ if (HaveSpace) {
+ SB_AppendChar (&E->Replacement, ' ');
+ }
+ }
+
- /* Expect an argument list */
- SkipBlank ();
- if (CurC != '(') {
- PPError ("Illegal macro call");
- return 0;
+ } else {
+
+ /* An identifier, keep it */
+ SB_AppendStr (&E->Replacement, Ident);
+
+ }
+
+ } else if (CurC == '#' && NextC == '#') {
+
+ /* ## operator. */
+ NextChar ();
+ NextChar ();
+ SkipWhitespace (0);
+
+ /* Since we need to concatenate the token sequences, remove
+ * any whitespace that was added to target, since it must come
+ * from the input.
+ */
+ while (IsSpace (SB_LookAtLast (&E->Replacement))) {
+ SB_Drop (&E->Replacement, 1);
+ }
+
+ /* If the next token is an identifier which is a macro argument,
+ * replace it, otherwise do nothing.
+ */
+ if (IsSym (Ident)) {
+
+ /* Check if it's a macro argument */
+ if ((ArgIdx = FindMacroArg (E->M, Ident)) >= 0) {
+
+ /* Get the corresponding actual argument and add it. */
+ SB_Append (&E->Replacement, ME_GetActual (E, ArgIdx));
+
+ } else {
+
+ /* Just an ordinary identifier - add as is */
+ SB_AppendStr (&E->Replacement, Ident);
+
+ }
+ }
+
+ } else if (CurC == '#' && E->M->ArgCount >= 0) {
+
+ /* A # operator within a macro expansion of a function like
+ * macro. Read the following identifier and check if it's a
+ * macro parameter.
+ */
+ NextChar ();
+ SkipWhitespace (0);
+ if (!IsSym (Ident) || (ArgIdx = FindMacroArg (E->M, Ident)) < 0) {
+ PPError ("`#' is not followed by a macro parameter");
+ } else {
+ /* Make a valid string from Replacement */
+ Arg = ME_GetActual (E, ArgIdx);
+ SB_Reset (Arg);
+ Stringize (Arg, &E->Replacement);
+ }
+
+ } else if (IsQuote (CurC)) {
+ CopyQuotedString (&E->Replacement);
+ } else {
+ SB_AppendChar (&E->Replacement, CurC);
+ NextChar ();
+ }
}
+#if 0
+ /* Remove whitespace from the end of the line */
+ while (IsSpace (SB_LookAtLast (&E->Replacement))) {
+ SB_Drop (&E->Replacement, 1);
+ }
+#endif
+
+ /* Switch back the input */
+ InitLine (OldSource);
+ SB_SetIndex (&E->M->Replacement, OldIndex);
+}
+
+
+
+static void MacroCall (StrBuf* Target, Macro* M)
+/* Process a function like macro */
+{
+ MacroExp E;
+
/* Eat the left paren */
NextChar ();
- /* Read the actual macro arguments and store pointers to these arguments
- * into the array of actual arguments in the macro definition.
+ /* Initialize our MacroExp structure */
+ InitMacroExp (&E, M);
+
+ /* Read the actual macro arguments */
+ ReadMacroArgs (&E);
+
+ /* Compare formal and actual argument count */
+ if (CollCount (&E.ActualArgs) != (unsigned) M->ArgCount) {
+
+ StrBuf Arg = STATIC_STRBUF_INITIALIZER;
+
+ /* Argument count mismatch */
+ PPError ("Macro argument count mismatch");
+
+ /* Be sure to make enough empty arguments available */
+ while (CollCount (&E.ActualArgs) < (unsigned) M->ArgCount) {
+ ME_AppendActual (&E, &Arg);
+ }
+ }
+
+ /* Replace macro arguments handling the # and ## operators */
+ MacroArgSubst (&E);
+
+ /* Do macro replacement on the macro that already has the parameters
+ * substituted.
*/
- ArgCount = 0;
- ParCount = 0;
- ArgStart = Buf;
- B = Buf;
- while (1) {
- if (CurC == '(') {
- /* Nested parenthesis */
- *B++ = CurC;
- NextChar ();
- ++ParCount;
- } else if (IsQuoteChar (CurC)) {
- B = CopyQuotedString (B);
- } else if (CurC == ',' || CurC == ')') {
- if (ParCount == 0) {
- /* End of actual argument */
- *B++ = '\0';
- while (IsBlank(*ArgStart)) {
- ++ArgStart;
- }
- if (ArgCount < M->ArgCount) {
- M->ActualArgs[ArgCount++] = ArgStart;
- } else if (CurC != ')' || *ArgStart != '\0' || M->ArgCount > 0) {
- /* Be sure not to count the single empty argument for a
- * macro that does not have arguments.
- */
- ++ArgCount;
- }
-
- /* Check for end of macro param list */
- if (CurC == ')') {
- NextChar ();
- break;
- }
-
- /* Start the next param */
- ArgStart = B;
- NextChar ();
- } else {
- /* Comma or right paren inside nested parenthesis */
- if (CurC == ')') {
- --ParCount;
- }
- *B++ = CurC;
- NextChar ();
- }
- } else if (IsBlank (CurC)) {
- /* Squeeze runs of blanks */
- *B++ = ' ';
- SkipBlank ();
- } else if (CurC == '\0') {
- /* End of line inside macro argument list - read next line */
- if (NextLine () == 0) {
- return 0;
- }
- } else {
- /* Just copy the character */
- *B++ = CurC;
- NextChar ();
- }
- }
-
- /* Compare formal argument count with actual */
- if (M->ArgCount != ArgCount) {
- PPError ("Macro argument count mismatch");
- /* Be sure to make enough empty arguments available */
- while (ArgCount < M->ArgCount) {
- M->ActualArgs [ArgCount++] = "";
- }
- }
-
- /* Preprocess the line, replacing macro parameters */
- ExpandMacroArgs (M);
-
- /* Done */
- return 1;
+ M->Expanding = 1;
+ MacroReplacement (&E.Replacement, Target);
+ M->Expanding = 0;
+
+ /* Free memory allocated for the macro expansion structure */
+ DoneMacroExp (&E);
}
-static void ExpandMacro (Macro* M)
-/* Expand a macro */
+static void ExpandMacro (StrBuf* Target, Macro* M)
+/* Expand a macro into Target */
{
+#if 0
+ static unsigned V = 0;
+ printf ("Expanding %s(%u)\n", M->Name, ++V);
+#endif
+
/* Check if this is a function like macro */
if (M->ArgCount >= 0) {
- /* Function like macro */
- if (MacroCall (M) == 0) {
- ClearLine ();
- }
+
+ int Whitespace = SkipWhitespace (1);
+ if (CurC != '(') {
+ /* Function like macro but no parameter list */
+ SB_AppendStr (Target, M->Name);
+ if (Whitespace) {
+ SB_AppendChar (Target, ' ');
+ }
+ } else {
+ /* Function like macro */
+ MacroCall (Target, M);
+ }
+
} else {
- /* Just copy the replacement text */
- keepstr (M->Replacement);
+
+ MacroExp E;
+ InitMacroExp (&E, M);
+
+ /* Handle # and ## operators for object like macros */
+ MacroArgSubst (&E);
+
+ /* Do macro replacement on the macro that already has the parameters
+ * substituted.
+ */
+ M->Expanding = 1;
+ MacroReplacement (&E.Replacement, Target);
+ M->Expanding = 0;
+
+ /* Free memory allocated for the macro expansion structure */
+ DoneMacroExp (&E);
+
}
+#if 0
+ printf ("Done with %s(%u)\n", M->Name, V--);
+#endif
}
-static void addmac (void)
-/* Add a macro to the macro table. */
+static void DefineMacro (void)
+/* Handle a macro definition. */
{
- char* saveptr;
- ident Ident;
- char Buf[LINESIZE];
- Macro* M;
- Macro* Existing;
+ ident Ident;
+ Macro* M;
+ Macro* Existing;
+ int C89;
/* Read the macro name */
- SkipBlank ();
+ SkipWhitespace (0);
if (!MacName (Ident)) {
- return;
+ return;
}
+ /* Remember if we're in C89 mode */
+ C89 = (IS_Get (&Standard) == STD_C89);
+
/* Get an existing macro definition with this name */
Existing = FindMacro (Ident);
/* Check if this is a function like macro */
if (CurC == '(') {
- /* Skip the left paren */
- NextChar ();
-
- /* Set the marker that this is a function like macro */
- M->ArgCount = 0;
-
- /* Read the formal parameter list */
- while (1) {
- SkipBlank ();
- if (CurC == ')')
- break;
- if (MacName (Ident) == 0) {
- return;
- }
- AddMacroArg (M, Ident);
- SkipBlank ();
- if (CurC != ',')
- break;
- NextChar ();
- }
-
- /* Check for a right paren and eat it if we find one */
- if (CurC != ')') {
- PPError ("`)' expected");
- ClearLine ();
- return;
- }
- NextChar ();
+ /* Skip the left paren */
+ NextChar ();
+
+ /* Set the marker that this is a function like macro */
+ M->ArgCount = 0;
+
+ /* Read the formal parameter list */
+ while (1) {
+
+ /* Skip white space and check for end of parameter list */
+ SkipWhitespace (0);
+ if (CurC == ')') {
+ break;
+ }
+
+ /* The next token must be either an identifier, or - if not in
+ * C89 mode - the ellipsis.
+ */
+ if (!C89 && CurC == '.') {
+ /* Ellipsis */
+ NextChar ();
+ if (CurC != '.' || NextC != '.') {
+ PPError ("`...' expected");
+ ClearLine ();
+ return;
+ }
+ NextChar ();
+ NextChar ();
+
+ /* Remember that the macro is variadic and use __VA_ARGS__ as
+ * the argument name.
+ */
+ AddMacroArg (M, "__VA_ARGS__");
+ M->Variadic = 1;
+
+ } else {
+ /* Must be macro argument name */
+ if (MacName (Ident) == 0) {
+ return;
+ }
+
+ /* __VA_ARGS__ is only allowed in C89 mode */
+ if (!C89 && strcmp (Ident, "__VA_ARGS__") == 0) {
+ PPWarning ("`__VA_ARGS__' can only appear in the expansion "
+ "of a C99 variadic macro");
+ }
+
+ /* Add the macro argument */
+ AddMacroArg (M, Ident);
+ }
+
+ /* If we had an ellipsis, or the next char is not a comma, we've
+ * reached the end of the macro argument list.
+ */
+ SkipWhitespace (0);
+ if (M->Variadic || CurC != ',') {
+ break;
+ }
+ NextChar ();
+ }
+
+ /* Check for a right paren and eat it if we find one */
+ if (CurC != ')') {
+ PPError ("`)' expected");
+ ClearLine ();
+ return;
+ }
+ NextChar ();
}
+ /* Skip whitespace before the macro replacement */
+ SkipWhitespace (0);
+
/* Insert the macro into the macro table and allocate the ActualArgs array */
InsertMacro (M);
/* Remove whitespace and comments from the line, store the preprocessed
- * line into Buf.
+ * line into the macro replacement buffer.
*/
- SkipBlank ();
- saveptr = mptr;
- Pass1 (lptr, Buf);
- mptr = saveptr;
+ Pass1 (Line, &M->Replacement);
- /* Create a copy of the replacement */
- M->Replacement = xstrdup (Buf);
+ /* Remove whitespace from the end of the line */
+ while (IsSpace (SB_LookAtLast (&M->Replacement))) {
+ SB_Drop (&M->Replacement, 1);
+ }
+#if 0
+ printf ("%s: <%.*s>\n", M->Name, SB_GetLen (&M->Replacement), SB_GetConstBuf (&M->Replacement));
+#endif
/* If we have an existing macro, check if the redefinition is identical.
* Print a diagnostic if not.
*/
- if (Existing) {
- if (MacroCmp (M, Existing) != 0) {
- PPError ("Macro redefinition is not identical");
- }
+ if (Existing && MacroCmp (M, Existing) != 0) {
+ PPError ("Macro redefinition is not identical");
}
}
/*****************************************************************************/
-
+/* Preprocessing */
/*****************************************************************************/
-static int Pass1 (const char* From, char* To)
-/* Preprocessor pass 1. Remove whitespace and comments. */
+static unsigned Pass1 (StrBuf* Source, StrBuf* Target)
+/* Preprocessor pass 1. Remove whitespace. Handle old and new style comments
+ * and the "defined" operator.
+ */
{
- int done;
- ident Ident;
- int HaveParen;
-
- /* Initialize reading from "From" */
- InitLine (From);
+ unsigned IdentCount;
+ ident Ident;
+ int HaveParen;
- /* Target is "To" */
- mptr = To;
+ /* Switch to the new input source */
+ StrBuf* OldSource = InitLine (Source);
/* Loop removing ws and comments */
- done = 1;
+ IdentCount = 0;
while (CurC != '\0') {
- if (IsBlank (CurC)) {
- keepch (' ');
- SkipBlank ();
- } else if (IsIdent (CurC)) {
- SymName (Ident);
- if (Preprocessing && strcmp(Ident, "defined") == 0) {
- /* Handle the "defined" operator */
- SkipBlank();
- HaveParen = 0;
- if (CurC == '(') {
- HaveParen = 1;
- NextChar ();
- SkipBlank();
- }
- if (!IsIdent (CurC)) {
- PPError ("Identifier expected");
- *mptr++ = '0';
- } else {
- SymName (Ident);
- *mptr++ = IsMacro (Ident)? '1' : '0';
- if (HaveParen) {
- SkipBlank();
- if (CurC != ')') {
- PPError ("`)' expected");
- } else {
- NextChar ();
- }
- }
- }
- } else {
- if (MaybeMacro (Ident[0])) {
- done = 0;
- }
- keepstr (Ident);
- }
- } else if (IsQuoteChar (CurC)) {
- mptr = CopyQuotedString (mptr);
- } else if (CurC == '/' && NextC == '*') {
- keepch (' ');
- Comment ();
- } else if (ANSI == 0 && CurC == '/' && NextC == '/') {
- keepch (' ');
- /* Beware: Because line continuation chars are handled when reading
- * lines, we may only skip til the end of the source line, which
- * may not be the same as the end of the input line. The end of the
- * source line is denoted by a lf (\n) character.
- */
- do {
- NextChar ();
- } while (CurC != '\n' && CurC != '\0');
- if (CurC == '\n') {
- NextChar ();
- }
- } else {
- *mptr++ = CurC;
- NextChar ();
- }
- }
- keepch ('\0');
- return done;
+ if (SkipWhitespace (0)) {
+ /* Squeeze runs of blanks */
+ if (!IsSpace (SB_LookAtLast (Target))) {
+ SB_AppendChar (Target, ' ');
+ }
+ } else if (IsSym (Ident)) {
+ if (Preprocessing && strcmp (Ident, "defined") == 0) {
+ /* Handle the "defined" operator */
+ SkipWhitespace (0);
+ HaveParen = 0;
+ if (CurC == '(') {
+ HaveParen = 1;
+ NextChar ();
+ SkipWhitespace (0);
+ }
+ if (IsSym (Ident)) {
+ SB_AppendChar (Target, IsMacro (Ident)? '1' : '0');
+ if (HaveParen) {
+ SkipWhitespace (0);
+ if (CurC != ')') {
+ PPError ("`)' expected");
+ } else {
+ NextChar ();
+ }
+ }
+ } else {
+ PPError ("Identifier expected");
+ SB_AppendChar (Target, '0');
+ }
+ } else {
+ ++IdentCount;
+ SB_AppendStr (Target, Ident);
+ }
+ } else if (IsQuote (CurC)) {
+ CopyQuotedString (Target);
+ } else if (CurC == '/' && NextC == '*') {
+ if (!IsSpace (SB_LookAtLast (Target))) {
+ SB_AppendChar (Target, ' ');
+ }
+ OldStyleComment ();
+ } else if (IS_Get (&Standard) >= STD_C99 && CurC == '/' && NextC == '/') {
+ if (!IsSpace (SB_LookAtLast (Target))) {
+ SB_AppendChar (Target, ' ');
+ }
+ NewStyleComment ();
+ } else {
+ SB_AppendChar (Target, CurC);
+ NextChar ();
+ }
+ }
+
+ /* Switch back to the old source */
+ InitLine (OldSource);
+
+ /* Return the number of identifiers found in the line */
+ return IdentCount;
}
-static int Pass2 (const char* From, char* To)
-/* Preprocessor pass 2. Perform macro substitution. */
+static void MacroReplacement (StrBuf* Source, StrBuf* Target)
+/* Perform macro replacement. */
{
- int no_chg;
- ident Ident;
- Macro* M;
-
- /* Initialize reading from "From" */
- InitLine (From);
+ ident Ident;
+ Macro* M;
- /* Target is "To" */
- mptr = To;
+ /* Remember the current input and switch to Source */
+ StrBuf* OldSource = InitLine (Source);
/* Loop substituting macros */
- no_chg = 1;
while (CurC != '\0') {
- /* If we have an identifier, check if it's a macro */
- if (IsIdent (CurC)) {
- SymName (Ident);
- M = FindMacro (Ident);
- if (M) {
- ExpandMacro (M);
- no_chg = 0;
- } else {
- keepstr (Ident);
- }
- } else if (IsQuoteChar(CurC)) {
- mptr = CopyQuotedString (mptr);
- } else {
- *mptr++ = CurC;
- NextChar ();
- }
- }
- return no_chg;
+ /* If we have an identifier, check if it's a macro */
+ if (IsSym (Ident)) {
+ /* Check if it's a macro */
+ if ((M = FindMacro (Ident)) != 0 && !M->Expanding) {
+ /* It's a macro, expand it */
+ ExpandMacro (Target, M);
+ } else {
+ /* An identifier, keep it */
+ SB_AppendStr (Target, Ident);
+ }
+ } else if (IsQuote (CurC)) {
+ CopyQuotedString (Target);
+ } else if (IsSpace (CurC)) {
+ if (!IsSpace (SB_LookAtLast (Target))) {
+ SB_AppendChar (Target, CurC);
+ }
+ NextChar ();
+ } else {
+ SB_AppendChar (Target, CurC);
+ NextChar ();
+ }
+ }
+
+ /* Switch back the input */
+ InitLine (OldSource);
}
-static void xlateline (void)
+static void PreprocessLine (void)
/* Translate one line. */
{
- int cnt;
- int Done;
-
- Done = Pass1 (line, mline);
- if (ExpandMacros == 0) {
- Done = 1;
- ExpandMacros = 1; /* Reset to default */
+ /* Trim whitespace and remove comments. The function returns the number of
+ * identifiers found. If there were any, we will have to check for macros.
+ */
+ SB_Clear (MLine);
+ if (Pass1 (Line, MLine) > 0) {
+ MLine = InitLine (MLine);
+ SB_Reset (Line);
+ SB_Clear (MLine);
+ MacroReplacement (Line, MLine);
}
- cnt = 5;
- do {
- /* Swap mline and line */
- char* p = line;
- line = mline;
- mline = p;
- if (Done)
- break;
- Done = Pass2 (line, mline);
- keepch ('\0');
- } while (--cnt);
-
- /* Reinitialize line parsing */
- InitLine (line);
+
+ /* Read from the new line */
+ SB_Reset (MLine);
+ MLine = InitLine (MLine);
}
-static void doundef (void)
-/* Process #undef directive */
+static int PushIf (int Skip, int Invert, int Cond)
+/* Push a new if level onto the if stack */
{
- ident Ident;
+ /* Check for an overflow of the if stack */
+ if (IfIndex >= MAX_IFS-1) {
+ PPError ("Too many nested #if clauses");
+ return 1;
+ }
- SkipBlank ();
- if (MacName (Ident)) {
- UndefineMacro (Ident);
+ /* Push the #if condition */
+ ++IfIndex;
+ if (Skip) {
+ IfStack[IfIndex] = IFCOND_SKIP | IFCOND_NEEDTERM;
+ return 1;
+ } else {
+ IfStack[IfIndex] = IFCOND_NONE | IFCOND_NEEDTERM;
+ return (Invert ^ Cond);
}
}
-static int setmflag (int skip, int flag, int cond)
-/* setmflag( skip, flag, cond ) */
+static void DoError (void)
+/* Print an error */
{
- if (skip) {
- s_ifdef[++i_ifdef] = 3;
- return (1);
+ SkipWhitespace (0);
+ if (CurC == '\0') {
+ PPError ("Invalid #error directive");
} else {
- s_ifdef[++i_ifdef] = 6;
- return (flag ^ cond);
+ PPError ("#error: %s", SB_GetConstBuf (Line) + SB_GetIndex (Line));
}
+
+ /* Clear the rest of line */
+ ClearLine ();
}
-static int doiff (int skip)
+static int DoIf (int Skip)
/* Process #if directive */
{
- struct expent lval;
- char* S;
+ ExprDesc Expr;
/* We're about to abuse the compiler expression parser to evaluate the
* #if expression. Save the current tokens to come back here later.
+ * NOTE: Yes, this is a hack, but it saves a complete separate expression
+ * evaluation for the preprocessor.
*/
- Token sv1 = CurTok;
- Token sv2 = NextTok;
+ Token SavedCurTok = CurTok;
+ Token SavedNextTok = NextTok;
- /* Remove the #if from the line and add two semicolons as sentinels */
- SkipBlank ();
- S = line;
- while (CurC != '\0') {
- *S++ = CurC;
- NextChar ();
+ /* Make sure the line infos for the tokens won't get removed */
+ if (SavedCurTok.LI) {
+ UseLineInfo (SavedCurTok.LI);
+ }
+ if (SavedNextTok.LI) {
+ UseLineInfo (SavedNextTok.LI);
}
- *S++ = ';';
- *S++ = ';';
- *S = '\0';
-
- /* Start over parsing from line */
- InitLine (line);
/* Switch into special preprocessing mode */
Preprocessing = 1;
/* Expand macros in this line */
- xlateline ();
+ PreprocessLine ();
- /* Prime the token pump (remove old tokens from the stream) */
+ /* Add two semicolons as sentinels to the line, so the following
+ * expression evaluation will eat these two tokens but nothing from
+ * the following line.
+ */
+ SB_AppendStr (Line, ";;");
+ SB_Terminate (Line);
+
+ /* Load CurTok and NextTok with tokens from the new input */
NextToken ();
NextToken ();
/* Call the expression parser */
- constexpr (&lval);
+ ConstExpr (hie1, &Expr);
/* End preprocessing mode */
Preprocessing = 0;
/* Reset the old tokens */
- CurTok = sv1;
- NextTok = sv2;
+ CurTok = SavedCurTok;
+ NextTok = SavedNextTok;
/* Set the #if condition according to the expression result */
- return (setmflag (skip, 1, lval.e_const != 0));
+ return PushIf (Skip, 1, Expr.IVal != 0);
}
-static int doifdef (int skip, int flag)
+static int DoIfDef (int skip, int flag)
/* Process #ifdef if flag == 1, or #ifndef if flag == 0. */
{
ident Ident;
- SkipBlank ();
+ SkipWhitespace (0);
if (MacName (Ident) == 0) {
- return 0;
+ return 0;
} else {
- return setmflag (skip, flag, IsMacro(Ident));
+ return PushIf (skip, flag, IsMacro(Ident));
}
}
-static void doinclude (void)
+static void DoInclude (void)
/* Open an include file. */
{
- char RTerm;
- unsigned DirSpec;
+ char RTerm;
+ InputType IT;
+ StrBuf Filename = STATIC_STRBUF_INITIALIZER;
+ /* Preprocess the remainder of the line */
+ PreprocessLine ();
+
/* Skip blanks */
- SkipBlank ();
+ SkipWhitespace (0);
/* Get the next char and check for a valid file name terminator. Setup
* the include directory spec (SYS/USR) by looking at the terminator.
*/
switch (CurC) {
- case '\"':
- RTerm = '\"';
- DirSpec = INC_USER;
- break;
+ case '\"':
+ RTerm = '\"';
+ IT = IT_USRINC;
+ break;
- case '<':
- RTerm = '>';
- DirSpec = INC_SYS;
- break;
+ case '<':
+ RTerm = '>';
+ IT = IT_SYSINC;
+ break;
- default:
- PPError ("`\"' or `<' expected");
- goto Done;
+ default:
+ PPError ("`\"' or `<' expected");
+ goto Done;
}
NextChar ();
- /* Copy the filename into mline. Since mline has the same size as the
- * input line, we don't need to check for an overflow here.
- */
- mptr = mline;
+ /* Get a copy of the filename */
while (CurC != '\0' && CurC != RTerm) {
- *mptr++ = CurC;
- NextChar ();
+ SB_AppendChar (&Filename, CurC);
+ NextChar ();
}
- *mptr = '\0';
+ SB_Terminate (&Filename);
/* Check if we got a terminator */
- if (CurC != RTerm) {
- /* No terminator found */
- PPError ("Missing terminator or file name too long");
- goto Done;
+ if (CurC == RTerm) {
+ /* Open the include file */
+ OpenIncludeFile (SB_GetConstBuf (&Filename), IT);
+ } else if (CurC == '\0') {
+ /* No terminator found */
+ PPError ("#include expects \"FILENAME\" or <FILENAME>");
}
- /* Open the include file */
- OpenIncludeFile (mline, DirSpec);
-
Done:
+ /* Free the allocated filename data */
+ SB_Done (&Filename);
+
/* Clear the remaining line so the next input will come from the new
* file (if open)
*/
-static void doerror (void)
-/* Print an error */
+static void DoPragma (void)
+/* Handle a #pragma line by converting the #pragma preprocessor directive into
+ * the _Pragma() compiler operator.
+ */
{
- SkipBlank ();
- if (CurC == '\0') {
- PPError ("Invalid #error directive");
- } else {
- PPError ("#error: %s", lptr);
- }
-
- /* clear rest of line */
- ClearLine ();
+ /* Skip blanks following the #pragma directive */
+ SkipWhitespace (0);
+
+ /* Copy the remainder of the line into MLine removing comments and ws */
+ SB_Clear (MLine);
+ Pass1 (Line, MLine);
+
+ /* Convert the directive into the operator */
+ SB_CopyStr (Line, "_Pragma (");
+ SB_Reset (MLine);
+ Stringize (MLine, Line);
+ SB_AppendChar (Line, ')');
+
+ /* Initialize reading from line */
+ SB_Reset (Line);
+ InitLine (Line);
}
-/* C preprocessor. */
-
-/* stuff used to bum the keyword dispatching stuff */
-enum {
- PP_DEFINE,
- PP_ELSE,
- PP_ENDIF,
- PP_ERROR,
- PP_IF,
- PP_IFDEF,
- PP_IFNDEF,
- PP_INCLUDE,
- PP_LINE,
- PP_PRAGMA,
- PP_UNDEF,
- PP_ILLEGAL
-};
+static void DoUndef (void)
+/* Process the #undef directive */
+{
+ ident Ident;
-static const struct tok_elt pre_toks[] = {
- { "define", PP_DEFINE },
- { "else", PP_ELSE },
- { "endif", PP_ENDIF },
- { "error", PP_ERROR },
- { "if", PP_IF },
- { "ifdef", PP_IFDEF },
- { "ifndef", PP_IFNDEF },
- { "include", PP_INCLUDE },
- { "line", PP_LINE },
- { "pragma", PP_PRAGMA },
- { "undef", PP_UNDEF },
- { 0, PP_ILLEGAL }
-};
+ SkipWhitespace (0);
+ if (MacName (Ident)) {
+ UndefineMacro (Ident);
+ }
+}
-static int searchtok (const char *sym, const struct tok_elt *toks)
-/* Search a token in a table */
+static void DoWarning (void)
+/* Print a warning */
{
- while (toks->toknam && strcmp (toks->toknam, sym))
- ++toks;
- return (toks->toknbr);
+ SkipWhitespace (0);
+ if (CurC == '\0') {
+ PPError ("Invalid #warning directive");
+ } else {
+ PPWarning ("#warning: %s", SB_GetConstBuf (Line) + SB_GetIndex (Line));
+ }
+
+ /* Clear the rest of line */
+ ClearLine ();
}
void Preprocess (void)
/* Preprocess a line */
{
- int Skip;
- ident Directive;
+ int Skip;
+ ident Directive;
+
+ /* Create the output buffer if we don't already have one */
+ if (MLine == 0) {
+ MLine = NewStrBuf ();
+ }
/* Skip white space at the beginning of the line */
- SkipBlank ();
+ SkipWhitespace (0);
/* Check for stuff to skip */
Skip = 0;
while (CurC == '\0' || CurC == '#' || Skip) {
- /* Check for preprocessor lines lines */
- if (CurC == '#') {
- NextChar ();
- SkipBlank ();
- if (CurC == '\0') {
- /* Ignore the empty preprocessor directive */
- continue;
- }
- if (!IsSym (Directive)) {
- PPError ("Preprocessor directive expected");
- ClearLine ();
- } else {
- switch (searchtok (Directive, pre_toks)) {
-
- case PP_DEFINE:
- if (!Skip) {
- addmac ();
- }
- break;
-
- case PP_ELSE:
- if (s_ifdef[i_ifdef] & 2) {
- if (s_ifdef[i_ifdef] & 4) {
- Skip = !Skip;
- }
- s_ifdef[i_ifdef] ^= 2;
- } else {
- PPError ("Unexpected `#else'");
- }
- break;
-
- case PP_ENDIF:
- if (i_ifdef >= 0) {
- Skip = s_ifdef[i_ifdef--] & 1;
- } else {
- PPError ("Unexpected `#endif'");
- }
- break;
-
- case PP_ERROR:
- if (!Skip) {
- doerror ();
- }
- break;
-
- case PP_IF:
- Skip = doiff (Skip);
- break;
-
- case PP_IFDEF:
- Skip = doifdef (Skip, 1);
- break;
-
- case PP_IFNDEF:
- Skip = doifdef (Skip, 0);
- break;
-
- case PP_INCLUDE:
- if (!Skip) {
- doinclude ();
- }
- break;
-
- case PP_LINE:
- /* Not allowed in strict ANSI mode */
- if (ANSI) {
- PPError ("Preprocessor directive expected");
- ClearLine ();
- }
- break;
-
- case PP_PRAGMA:
- if (!Skip) {
- /* Don't expand macros in this line */
- ExpandMacros = 0;
- /* #pragma is handled on the scanner level */
- goto Done;
- }
- break;
-
- case PP_UNDEF:
- if (!Skip) {
- doundef ();
- }
- break;
-
- default:
- PPError ("Preprocessor directive expected");
- ClearLine ();
- }
- }
-
- }
- if (NextLine () == 0) {
- if (i_ifdef >= 0) {
- PPError ("`#endif' expected");
- }
- return;
- }
- SkipBlank ();
+ /* Check for preprocessor lines lines */
+ if (CurC == '#') {
+ NextChar ();
+ SkipWhitespace (0);
+ if (CurC == '\0') {
+ /* Ignore the empty preprocessor directive */
+ continue;
+ }
+ if (!IsSym (Directive)) {
+ PPError ("Preprocessor directive expected");
+ ClearLine ();
+ } else {
+ switch (FindPPToken (Directive)) {
+
+ case PP_DEFINE:
+ if (!Skip) {
+ DefineMacro ();
+ }
+ break;
+
+ case PP_ELIF:
+ if (IfIndex >= 0) {
+ if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) {
+
+ /* Handle as #else/#if combination */
+ if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) {
+ Skip = !Skip;
+ }
+ IfStack[IfIndex] |= IFCOND_ELSE;
+ Skip = DoIf (Skip);
+
+ /* #elif doesn't need a terminator */
+ IfStack[IfIndex] &= ~IFCOND_NEEDTERM;
+ } else {
+ PPError ("Duplicate #else/#elif");
+ }
+ } else {
+ PPError ("Unexpected #elif");
+ }
+ break;
+
+ case PP_ELSE:
+ if (IfIndex >= 0) {
+ if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) {
+ if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) {
+ Skip = !Skip;
+ }
+ IfStack[IfIndex] |= IFCOND_ELSE;
+ } else {
+ PPError ("Duplicate #else");
+ }
+ } else {
+ PPError ("Unexpected `#else'");
+ }
+ break;
+
+ case PP_ENDIF:
+ if (IfIndex >= 0) {
+ /* Remove any clauses on top of stack that do not
+ * need a terminating #endif.
+ */
+ while (IfIndex >= 0 && (IfStack[IfIndex] & IFCOND_NEEDTERM) == 0) {
+ --IfIndex;
+ }
+
+ /* Stack may not be empty here or something is wrong */
+ CHECK (IfIndex >= 0);
+
+ /* Remove the clause that needs a terminator */
+ Skip = (IfStack[IfIndex--] & IFCOND_SKIP) != 0;
+ } else {
+ PPError ("Unexpected `#endif'");
+ }
+ break;
+
+ case PP_ERROR:
+ if (!Skip) {
+ DoError ();
+ }
+ break;
+
+ case PP_IF:
+ Skip = DoIf (Skip);
+ break;
+
+ case PP_IFDEF:
+ Skip = DoIfDef (Skip, 1);
+ break;
+
+ case PP_IFNDEF:
+ Skip = DoIfDef (Skip, 0);
+ break;
+
+ case PP_INCLUDE:
+ if (!Skip) {
+ DoInclude ();
+ }
+ break;
+
+ case PP_LINE:
+ /* Should do something in C99 at least, but we ignore it */
+ if (!Skip) {
+ ClearLine ();
+ }
+ break;
+
+ case PP_PRAGMA:
+ if (!Skip) {
+ DoPragma ();
+ goto Done;
+ }
+ break;
+
+ case PP_UNDEF:
+ if (!Skip) {
+ DoUndef ();
+ }
+ break;
+
+ case PP_WARNING:
+ /* #warning is a non standard extension */
+ if (IS_Get (&Standard) > STD_C99) {
+ if (!Skip) {
+ DoWarning ();
+ }
+ } else {
+ if (!Skip) {
+ PPError ("Preprocessor directive expected");
+ }
+ ClearLine ();
+ }
+ break;
+
+ default:
+ if (!Skip) {
+ PPError ("Preprocessor directive expected");
+ }
+ ClearLine ();
+ }
+ }
+
+ }
+ if (NextLine () == 0) {
+ if (IfIndex >= 0) {
+ PPError ("`#endif' expected");
+ }
+ return;
+ }
+ SkipWhitespace (0);
}
+ PreprocessLine ();
+
Done:
- xlateline ();
- if (Verbose > 1) {
- printf ("line: %s\n", line);
+ if (Verbosity > 1 && SB_NotEmpty (Line)) {
+ printf ("%s(%u): %.*s\n", GetCurrentFile (), GetCurrentLine (),
+ (int) SB_GetLen (Line), SB_GetConstBuf (Line));
}
}