X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fcc65%2Fpreproc.c;h=846d07966b938a1c95cb9bb97a9d3929bd0e8777;hb=a84080febe4bffb7382c763e756d185c3e812646;hp=1ea8cb81da4d1d1be768ab055a89af3af8a6cffe;hpb=36131c00d0e955d7d75cad7940804f26c9c3eeb6;p=cc65 diff --git a/src/cc65/preproc.c b/src/cc65/preproc.c index 1ea8cb81d..846d07966 100644 --- a/src/cc65/preproc.c +++ b/src/cc65/preproc.c @@ -1,5 +1,37 @@ +/*****************************************************************************/ +/* */ +/* preproc.c */ +/* */ +/* cc65 preprocessor */ +/* */ +/* */ +/* */ +/* (C) 1998-2005, Ullrich von Bassewitz */ +/* Römerstraße 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 #include @@ -8,6 +40,8 @@ /* common */ #include "chartype.h" +#include "check.h" +#include "inline.h" #include "print.h" #include "xmalloc.h" @@ -21,9 +55,40 @@ #include "input.h" #include "lineinfo.h" #include "macrotab.h" -#include "scanner.h" -#include "util.h" #include "preproc.h" +#include "scanner.h" +#include "standard.h" + + + +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* 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 */ +}; @@ -33,61 +98,190 @@ -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. + */ + +static void MacroReplacement (StrBuf* Source, StrBuf* Target); +/* Perform macro replacement. */ /*****************************************************************************/ -/* data */ +/* Low level preprocessor token handling */ /*****************************************************************************/ -/* Set when the pp calls expr() recursively */ -unsigned char Preprocessing = 0; +/* 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 +} pptoken_t; -/* 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; +/* 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 }, +}; + +/* 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 */ +{ + InitCollection (&E->ActualArgs); + InitStrBuf (&E->Replacement); + E->M = M; + return E; +} + + + +static void DoneMacroExp (MacroExp* E) +/* Cleanup after use of a MacroExp structure */ +{ + unsigned I; + + /* Delete the list with actual arguments */ + for (I = 0; I < CollCount (&E->ActualArgs); ++I) { + FreeStrBuf (CollAtUnchecked (&E->ActualArgs, I)); + } + DoneCollection (&E->ActualArgs); + DoneStrBuf (&E->Replacement); +} + + + +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 (*mptr++ = c); + return CollAt (&E->ActualArgs, Index); } -static void keepstr (const char* S) -/* Put string str into translation buffer. */ +static int ME_ArgIsVariadic (const MacroExp* E) +/* Return true if the next actual argument we will add is a variadic one */ { - while (*S) { - keepch (*S++); + 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 Comment (void) -/* Remove a C comment from line. */ +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. @@ -102,14 +296,14 @@ static void Comment (void) while (CurC != '*' || NextC != '/') { if (CurC == '\0') { if (NextLine () == 0) { - PPError ("End-of-file reached in comment starting at line %u", - StartingLine); + PPError ("End-of-file reached in comment starting at line %u", + StartingLine); return; } } else { if (CurC == '/' && NextC == '*') { - PPWarning ("`/*' found inside a comment"); - } + PPWarning ("`/*' found inside a comment"); + } NextChar (); } } @@ -121,63 +315,78 @@ static void Comment (void) -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)) { + /* 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 void SkipWhitespace (void) +/* Skip white space in the input stream. */ +{ + while (IsSpace (CurC)) { 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 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 (); + if (CurC == '\\') { + SB_AppendChar (Target, CurC); + NextChar (); } /* Copy the character */ - *Target++ = CurC; + SB_AppendChar (Target, CurC); NextChar (); } /* If we had a terminating quote, copy it */ if (CurC != '\0') { - *Target++ = CurC; + 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"); + PPError ("Identifier expected"); ClearLine (); - return 0; + return 0; } else { return 1; } @@ -185,189 +394,345 @@ static int MacName (char* Ident) -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 == '(') { - /* 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); + /* 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 { - /* No macro argument, keep the original identifier */ - keepstr (Ident); + /* 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 (CurC == '#' && IsIdent (NextC)) { - NextChar (); - SymName (Ident); - Replacement = FindMacroArg (M, Ident); - if (Replacement) { - keepch ('\"'); - keepstr (Replacement); - keepch ('\"'); - } else { - keepch ('#'); - keepstr (Ident); + } else if (IsSpace (CurC)) { + /* Squeeze runs of blanks within an arg */ + if (SB_NotEmpty (&Arg)) { + SB_AppendChar (&Arg, ' '); + } + SkipWhitespace (); + } 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 line inside macro argument list - read next line */ + if (SB_NotEmpty (&Arg)) { + SB_AppendChar (&Arg, ' '); + } + if (NextLine () == 0) { + ClearLine (); + break; } - } else if (IsQuote (CurC)) { - mptr = CopyQuotedString (mptr); } else { - *mptr++ = CurC; - NextChar (); + /* Just copy the character */ + SB_AppendChar (&Arg, CurC); + NextChar (); } } - /* Reset the line pointer */ - InitLine (SavePtr); + /* Deallocate string buf resources */ + DoneStrBuf (&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; - /* Expect an argument list */ - SkipBlank (); - if (CurC != '(') { - PPError ("Illegal macro call"); - return 0; - } - /* Eat the left paren */ - NextChar (); + /* Remember the current input and switch to the macro replacement. */ + SB_Reset (&E->M->Replacement); + OldSource = InitLine (&E->M->Replacement); - /* Read the actual macro arguments and store pointers to these arguments - * into the array of actual arguments in the macro definition. - */ - ArgCount = 0; - ParCount = 0; - ArgStart = Buf; - B = Buf; - while (1) { - if (CurC == '(') { - /* Nested parenthesis */ - *B++ = CurC; - NextChar (); - ++ParCount; - } else if (IsQuote (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; - } + /* Argument handling loop */ + while (CurC != '\0') { - /* 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; - } + /* 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 = IsSpace (CurC); + if (HaveSpace) { + SkipWhitespace (); + } + + /* 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, ' '); + } + } + + + } else { + + /* An identifier, keep it */ + SB_AppendStr (&E->Replacement, Ident); + + } + + } else if (CurC == '#' && NextC == '#') { + + /* ## operator. */ + NextChar (); + NextChar (); + SkipWhitespace (); + + /* 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 (); + 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 { - /* Just copy the character */ - *B++ = CurC; - NextChar (); + SB_AppendChar (&E->Replacement, 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++] = ""; - } +#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); +} - /* Preprocess the line, replacing macro parameters */ - ExpandMacroArgs (M); - /* Done */ - return 1; + +static void MacroCall (StrBuf* Target, Macro* M) +/* Process a function like macro */ +{ + MacroExp E; + + /* Eat the left paren */ + NextChar (); + + /* 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. + */ + 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 */ { + /* ### printf ("Expanding %s(%u)\n", M->Name, ++V); */ + /* Check if this is a function like macro */ if (M->ArgCount >= 0) { - /* Function like macro */ - if (MacroCall (M) == 0) { - ClearLine (); - } + + int Whitespace = IsSpace (CurC); + if (Whitespace) { + SkipWhitespace (); + } + 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); + } + /* ### printf ("Done with %s(%u)\n", M->Name, V--); */ } -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 (); if (!MacName (Ident)) { 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); @@ -381,24 +746,64 @@ static void addmac (void) NextChar (); /* Set the marker that this is a function like macro */ - M->ArgCount = 0; + M->ArgCount = 0; - /* Read the formal parameter list */ + /* Read the formal parameter list */ while (1) { - SkipBlank (); - if (CurC == ')') - break; - if (MacName (Ident) == 0) { - return; - } - AddMacroArg (M, Ident); - SkipBlank (); - if (CurC != ',') - break; + + /* Skip white space and check for end of parameter list */ + SkipWhitespace (); + 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 (); + if (M->Variadic || CurC != ',') { + break; + } NextChar (); } - /* Check for a right paren and eat it if we find one */ + /* Check for a right paren and eat it if we find one */ if (CurC != ')') { PPError ("`)' expected"); ClearLine (); @@ -407,191 +812,183 @@ static void addmac (void) NextChar (); } + /* Skip whitespace before the macro replacement */ + SkipWhitespace (); + /* 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); + } + + /* ### printf ("%s: <%.*s>\n", M->Name, SB_GetLen (&M->Replacement), SB_GetConstBuf (&M->Replacement)); */ /* 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; + unsigned IdentCount; ident Ident; int HaveParen; - /* Initialize reading from "From" */ - InitLine (From); - - /* 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 (IsQuote (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 (); - } + if (IsSpace (CurC)) { + /* Squeeze runs of blanks */ + if (!IsSpace (SB_LookAtLast (Target))) { + SB_AppendChar (Target, ' '); + } + SkipWhitespace (); + } else if (IsSym (Ident)) { + if (Preprocessing && strcmp (Ident, "defined") == 0) { + /* Handle the "defined" operator */ + SkipWhitespace (); + HaveParen = 0; + if (CurC == '(') { + HaveParen = 1; + NextChar (); + SkipWhitespace (); + } + if (IsSym (Ident)) { + SB_AppendChar (Target, IsMacro (Ident)? '1' : '0'); + if (HaveParen) { + SkipWhitespace (); + 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 { - *mptr++ = CurC; - NextChar (); + SB_AppendChar (Target, CurC); + NextChar (); } } - keepch ('\0'); - return done; + + /* 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); - - /* 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); - } + /* 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)) { - mptr = CopyQuotedString (mptr); - } else { - *mptr++ = CurC; - NextChar (); + CopyQuotedString (Target); + } else if (IsSpace (CurC)) { + if (!IsSpace (SB_LookAtLast (Target))) { + SB_AppendChar (Target, CurC); + } + NextChar (); + } else { + SB_AppendChar (Target, CurC); + NextChar (); } } - return no_chg; + + /* 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 void DoUndef (void) +/* Process the #undef directive */ { ident Ident; - SkipBlank (); + SkipWhitespace (); if (MacName (Ident)) { UndefineMacro (Ident); } @@ -599,106 +996,121 @@ static void doundef (void) -static int setmflag (int skip, int flag, int cond) -/* setmflag( skip, flag, cond ) */ +static int PushIf (int Skip, int Invert, int Cond) +/* Push a new if level onto the if stack */ { - if (skip) { - s_ifdef[++i_ifdef] = 3; - return (1); + /* Check for an overflow of the if stack */ + if (IfIndex >= MAX_IFS-1) { + PPError ("Too many nested #if clauses"); + return 1; + } + + /* Push the #if condition */ + ++IfIndex; + if (Skip) { + IfStack[IfIndex] = IFCOND_SKIP | IFCOND_NEEDTERM; + return 1; } else { - s_ifdef[++i_ifdef] = 6; - return (flag ^ cond); + IfStack[IfIndex] = IFCOND_NONE | IFCOND_NEEDTERM; + return (Invert ^ Cond); } } -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; /* Make sure the line infos for the tokens won't get removed */ - if (sv1.LI) { - UseLineInfo (sv1.LI); + if (SavedCurTok.LI) { + UseLineInfo (SavedCurTok.LI); } - if (sv2.LI) { - UseLineInfo (sv2.LI); + if (SavedNextTok.LI) { + UseLineInfo (SavedNextTok.LI); } - /* Remove the #if from the line and add two semicolons as sentinels */ - SkipBlank (); +#if 0 + /* Remove the #if from the line */ + SkipWhitespace (); S = line; while (CurC != '\0') { - *S++ = CurC; - NextChar (); + *S++ = CurC; + NextChar (); } - *S++ = ';'; - *S++ = ';'; - *S = '\0'; + *S = '\0'; /* Start over parsing from line */ InitLine (line); +#endif /* Switch into special preprocessing mode */ Preprocessing = 1; /* Expand macros in this line */ - xlateline (); + PreprocessLine (); + + /* 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); - /* Prime the token pump (remove old tokens from the stream) */ + /* 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 (); if (MacName (Ident) == 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; + StrBuf Filename = STATIC_STRBUF_INITIALIZER; /* Skip blanks */ - SkipBlank (); + SkipWhitespace (); /* Get the next char and check for a valid file name terminator. Setup * the include directory spec (SYS/USR) by looking at the terminator. @@ -721,15 +1133,12 @@ static void doinclude (void) } 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; + SB_AppendChar (&Filename, CurC); NextChar (); } - *mptr = '\0'; + SB_Terminate (&Filename); /* Check if we got a terminator */ if (CurC != RTerm) { @@ -739,9 +1148,12 @@ static void doinclude (void) } /* Open the include file */ - OpenIncludeFile (mline, DirSpec); + OpenIncludeFile (SB_GetConstBuf (&Filename), DirSpec); Done: + /* Free the allocated filename data */ + DoneStrBuf (&Filename); + /* Clear the remaining line so the next input will come from the new * file (if open) */ @@ -750,63 +1162,43 @@ Done: -static void doerror (void) +static void DoError (void) /* Print an error */ { - SkipBlank (); + SkipWhitespace (); if (CurC == '\0') { PPError ("Invalid #error directive"); } else { - PPError ("#error: %s", lptr); + PPError ("#error: %s", SB_GetConstBuf (Line) + SB_GetIndex (Line)); } - /* clear rest of line */ + /* Clear the rest of line */ ClearLine (); } -/* 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 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 } -}; - - - -static int searchtok (const char *sym, const struct tok_elt *toks) -/* Search a token in a table */ +static void DoPragma (void) +/* Handle a #pragma line by converting the #pragma preprocessor directive into + * the _Pragma() compiler operator. + */ { - while (toks->toknam && strcmp (toks->toknam, sym)) - ++toks; - return (toks->toknbr); + /* Skip blanks following the #pragma directive */ + SkipWhitespace (); + + /* 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); } @@ -814,11 +1206,16 @@ static int searchtok (const char *sym, const struct tok_elt *toks) 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 (); /* Check for stuff to skip */ Skip = 0; @@ -827,7 +1224,7 @@ void Preprocess (void) /* Check for preprocessor lines lines */ if (CurC == '#') { NextChar (); - SkipBlank (); + SkipWhitespace (); if (CurC == '\0') { /* Ignore the empty preprocessor directive */ continue; @@ -836,28 +1233,64 @@ void Preprocess (void) PPError ("Preprocessor directive expected"); ClearLine (); } else { - switch (searchtok (Directive, pre_toks)) { + switch (FindPPToken (Directive)) { case PP_DEFINE: if (!Skip) { - addmac (); + 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 (s_ifdef[i_ifdef] & 2) { - if (s_ifdef[i_ifdef] & 4) { - Skip = !Skip; - } - s_ifdef[i_ifdef] ^= 2; + 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 (i_ifdef >= 0) { - Skip = s_ifdef[i_ifdef--] & 1; + 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'"); } @@ -865,48 +1298,45 @@ void Preprocess (void) case PP_ERROR: if (!Skip) { - doerror (); + DoError (); } break; case PP_IF: - Skip = doiff (Skip); + Skip = DoIf (Skip); break; case PP_IFDEF: - Skip = doifdef (Skip, 1); + Skip = DoIfDef (Skip, 1); break; case PP_IFNDEF: - Skip = doifdef (Skip, 0); + Skip = DoIfDef (Skip, 0); break; case PP_INCLUDE: if (!Skip) { - doinclude (); + DoInclude (); } break; case PP_LINE: - /* Not allowed in strict ANSI mode */ - if (ANSI) { - PPError ("Preprocessor directive expected"); - ClearLine (); - } - break; + /* Should do something in C99 at least, but we ignore it */ + if (!Skip) { + 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; - } + if (!Skip) { + DoPragma (); + goto Done; + } break; case PP_UNDEF: if (!Skip) { - doundef (); + DoUndef (); } break; @@ -914,20 +1344,24 @@ void Preprocess (void) PPError ("Preprocessor directive expected"); ClearLine (); } - } + } } if (NextLine () == 0) { - if (i_ifdef >= 0) { + if (IfIndex >= 0) { PPError ("`#endif' expected"); } return; } - SkipBlank (); + SkipWhitespace (); } + PreprocessLine (); + Done: - xlateline (); - Print (stdout, 2, "line: %s\n", line); + if (Verbosity > 1 && SB_NotEmpty (Line)) { + printf ("%s(%u): %.*s\n", GetCurrentFile (), GetCurrentLine (), + SB_GetLen (Line), SB_GetConstBuf (Line)); + } }