X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fcc65%2Fpreproc.c;h=846d07966b938a1c95cb9bb97a9d3929bd0e8777;hb=a84080febe4bffb7382c763e756d185c3e812646;hp=c95bbb92cb01e14a2288d5778db5b76ef70f9d0c;hpb=5db55826f0592c542bee8d26b16d2052c1d95896;p=cc65 diff --git a/src/cc65/preproc.c b/src/cc65/preproc.c index c95bbb92c..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 @@ -9,6 +41,7 @@ /* common */ #include "chartype.h" #include "check.h" +#include "inline.h" #include "print.h" #include "xmalloc.h" @@ -22,20 +55,9 @@ #include "input.h" #include "lineinfo.h" #include "macrotab.h" -#include "scanner.h" -#include "util.h" #include "preproc.h" - - - -/*****************************************************************************/ -/* Forwards */ -/*****************************************************************************/ - - - -static int Pass1 (const char* From, char* To); -/* Preprocessor pass 1. Remove whitespace and comments. */ +#include "scanner.h" +#include "standard.h" @@ -58,12 +80,31 @@ static unsigned char IfStack[MAX_IFS]; static int IfIndex = -1; /* Buffer for macro expansion */ -static char mlinebuf [LINESIZE]; -static char* mline = mlinebuf; -static char* mptr; +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 */ +}; + + + +/*****************************************************************************/ +/* Forwards */ +/*****************************************************************************/ -/* Flag: Expand macros in this line */ -static int ExpandMacros = 1; + + +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. */ @@ -75,6 +116,7 @@ static int ExpandMacros = 1; /* Types of preprocessor tokens */ typedef enum { + PP_ILLEGAL = -1, PP_DEFINE, PP_ELIF, PP_ELSE, @@ -86,8 +128,7 @@ typedef enum { PP_INCLUDE, PP_LINE, PP_PRAGMA, - PP_UNDEF, - PP_ILLEGAL + PP_UNDEF } pptoken_t; @@ -125,7 +166,7 @@ static int CmpToken (const void* Key, const void* Elem) static pptoken_t FindPPToken (const char* Ident) -/* Find a preprocessor token and return ut. Return PP_ILLEGAL if the identifier +/* Find a preprocessor token and return it. Return PP_ILLEGAL if the identifier * is not a valid preprocessor token. */ { @@ -137,31 +178,110 @@ static pptoken_t FindPPToken (const char* Ident) /*****************************************************************************/ -/* Code */ +/* struct MacroExp */ /*****************************************************************************/ -static void keepch (char c) -/* Put character c into translation buffer. */ +static MacroExp* InitMacroExp (MacroExp* E, Macro* M) +/* Initialize a MacroExp structure */ { - *mptr++ = c; + InitCollection (&E->ActualArgs); + InitStrBuf (&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 */ +{ + 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 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. + */ { - unsigned Len = strlen (S); - memcpy (mptr, S, Len); - mptr += Len; + 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. @@ -176,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 (); } } @@ -195,52 +315,65 @@ 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. */ +{ + /* 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 (IsBlank (CurC)) { + 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 */ /*****************************************************************************/ @@ -261,170 +394,324 @@ static int MacName (char* Ident) -static void ExpandMacroArgs (Macro* M) -/* Expand the arguments of a macro */ +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--); */ } @@ -432,18 +719,20 @@ static void ExpandMacro (Macro* M) 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); @@ -461,16 +750,56 @@ static void DefineMacro (void) /* 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 (); } @@ -483,181 +812,173 @@ static void DefineMacro (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 */ +/* 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); } @@ -667,7 +988,7 @@ static void DoUndef (void) { ident Ident; - SkipBlank (); + SkipWhitespace (); if (MacName (Ident)) { UndefineMacro (Ident); } @@ -700,61 +1021,67 @@ static int PushIf (int Skip, int Invert, int Cond) static int DoIf (int Skip) /* Process #if directive */ { - ExprDesc 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 PushIf (Skip, 1, lval.ConstVal != 0); + return PushIf (Skip, 1, Expr.IVal != 0); } @@ -764,7 +1091,7 @@ static int DoIfDef (int skip, int flag) { ident Ident; - SkipBlank (); + SkipWhitespace (); if (MacName (Ident) == 0) { return 0; } else { @@ -779,10 +1106,11 @@ static void DoInclude (void) { 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. @@ -805,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) { @@ -823,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) */ @@ -837,11 +1165,11 @@ Done: 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 the rest of line */ @@ -850,14 +1178,44 @@ static void DoError (void) +static void DoPragma (void) +/* Handle a #pragma line by converting the #pragma preprocessor directive into + * the _Pragma() compiler operator. + */ +{ + /* 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); +} + + + 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; @@ -866,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; @@ -883,22 +1241,22 @@ void Preprocess (void) } 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"); - } + 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"); } @@ -906,7 +1264,7 @@ void Preprocess (void) case PP_ELSE: if (IfIndex >= 0) { - if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) { + if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) { if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) { Skip = !Skip; } @@ -924,7 +1282,7 @@ void Preprocess (void) /* Remove any clauses on top of stack that do not * need a terminating #endif. */ - while (IfIndex >= 0 && (IfStack[IfIndex] & IFCOND_NEEDTERM) == 0) { + while (IfIndex >= 0 && (IfStack[IfIndex] & IFCOND_NEEDTERM) == 0) { --IfIndex; } @@ -963,20 +1321,17 @@ void Preprocess (void) break; case PP_LINE: - /* Not allowed in strict ANSI mode */ - if (!Skip && ANSI) { - PPError ("Preprocessor directive expected"); + /* Should do something in C99 at least, but we ignore it */ + if (!Skip) { ClearLine (); - } - break; + } + 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: @@ -989,7 +1344,7 @@ void Preprocess (void) PPError ("Preprocessor directive expected"); ClearLine (); } - } + } } if (NextLine () == 0) { @@ -998,11 +1353,15 @@ void Preprocess (void) } 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)); + } }