1
0
mirror of https://github.com/cc65/cc65.git synced 2024-12-24 11:31:31 +00:00

Merge pull request #1868 from acqn/PPFix

[cc65] Fixed and improved C preprocessor
This commit is contained in:
Bob Andrews 2022-10-16 19:02:23 +02:00 committed by GitHub
commit b2238fdcd4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 218 additions and 79 deletions

View File

@ -67,6 +67,9 @@
/* The current input line */
StrBuf* Line;
/* The input line to reuse as the next line */
static StrBuf* CurReusedLine;
/* Current and next input character */
char CurC = '\0';
char NextC = '\0';
@ -403,24 +406,6 @@ static void GetInputChar (void)
/* NextC is '\0' by default */
NextC = '\0';
/* Drop all pushed fragments that don't have data left */
if (CurrentInputStack != 0) {
while (SB_GetIndex (Line) >= SB_GetLen (Line)) {
/* Cannot read more from this line, check next line on stack if any */
if (CollCount (CurrentInputStack) == 0) {
/* This is THE line */
break;
}
FreeStrBuf (Line);
Line = CollPop (CurrentInputStack);
}
/* NextC comes from next fragment */
if (CollCount (CurrentInputStack) > 0) {
NextC = ' ';
}
}
/* Get CurC from the line */
CurC = SB_LookAt (Line, SB_GetIndex (Line));
}
@ -461,13 +446,17 @@ void PushLine (StrBuf* L)
/* Save the current input line and use a new one */
{
PRECONDITION (CurrentInputStack != 0);
if (SB_GetIndex (L) < SB_GetLen (L)) {
CollAppend (CurrentInputStack, Line);
Line = L;
GetInputChar ();
} else {
FreeStrBuf (L);
}
}
void ReuseInputLine (void)
/* Save and reuse the current line as the next line */
{
CurReusedLine = Line;
}
@ -475,16 +464,6 @@ void PushLine (StrBuf* L)
void ClearLine (void)
/* Clear the current input line */
{
if (CurrentInputStack != 0) {
unsigned I;
/* Remove all pushed fragments from the input stack */
for (I = 0; I < CollCount (CurrentInputStack); ++I) {
FreeStrBuf (Line);
Line = CollPop (CurrentInputStack);
}
}
/* Clear the contents of Line */
SB_Clear (Line);
CurC = '\0';
@ -515,12 +494,47 @@ int NextLine (void)
int C;
AFile* Input;
/* Clear the current line */
ClearLine ();
SB_Clear (Line);
/* Overwrite the next input line with the pushed line if there is one */
if (CurReusedLine != 0) {
/* Use data move to resolve the issue that Line may be impersistent */
if (Line != CurReusedLine) {
SB_Move (Line, CurReusedLine);
}
/* Continue with this Line */
InitLine (Line);
CurReusedLine = 0;
/* Must have an input file when called */
if (CollCount(&AFiles) == 0) {
return 1;
}
/* If there are pushed input lines, read from them */
if (CurrentInputStack != 0 && CollCount (CurrentInputStack) > 0) {
/* Drop all pushed fragments that have no data left until one can be
** used as input.
*/
do {
/* Use data move to resolve the issue that Line may be impersistent */
if (Line != CollLast (CurrentInputStack)) {
SB_Move (Line, CollPop (CurrentInputStack));
} else {
CollPop (CurrentInputStack);
}
} while (CollCount (CurrentInputStack) > 0 &&
SB_GetIndex (Line) >= SB_GetLen (Line));
if (SB_GetIndex (Line) < SB_GetLen (Line)) {
InitLine (Line);
/* Successive */
return 1;
}
}
/* Otherwise, clear the current line */
ClearLine ();
/* Must have an input file when going on */
if (CollCount (&AFiles) == 0) {
return 0;
}

View File

@ -104,6 +104,9 @@ Collection* UseInputStack (Collection* InputStack);
void PushLine (StrBuf* L);
/* Save the current input line and use a new one */
void ReuseInputLine (void);
/* Save and reuse the current line as the next line */
void ClearLine (void);
/* Clear the current input line */

View File

@ -806,6 +806,21 @@ static int MacName (char* Ident)
static void CheckForBadIdent (const char* Ident, int Std, const Macro* M)
/* Check for and warning on problematic identifiers */
{
if (Std >= STD_C99 &&
(M == 0 || !M->Variadic) &&
strcmp (Ident, "__VA_ARGS__") == 0) {
/* __VA_ARGS__ cannot be used as a macro parameter name in post-C89
** mode.
*/
PPWarning ("__VA_ARGS__ can only appear in the expansion of a C99 variadic macro");
}
}
static void AddPreLine (StrBuf* Str)
/* Add newlines to the string buffer */
{
@ -878,7 +893,7 @@ static void Stringize (StrBuf* Source, StrBuf* Target)
static void OldStyleComment (void)
/* Remove an old style C comment from line. */
/* 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.
@ -897,6 +912,7 @@ static void OldStyleComment (void)
StartingLine);
return;
}
++PendingNewLines;
} else {
if (CurC == '/' && NextC == '*') {
PPWarning ("'/*' found inside a comment");
@ -913,7 +929,7 @@ static void OldStyleComment (void)
static void NewStyleComment (void)
/* Remove a new style C comment from line. */
/* Remove a new style C comment from line */
{
/* Diagnose if this is unsupported */
if (IS_Get (&Standard) < STD_C99 && !AllowNewComments) {
@ -1552,7 +1568,10 @@ static unsigned ReadMacroArgs (unsigned NameIdx, MacroExp* E, const Macro* M, in
/* If this is not the single empty argument for a macro with an
** empty argument list, remember it.
*/
if (CurC != ')' || SB_NotEmpty (&Arg.Tokens) || M->ParamCount > 0) {
if (CurC != ')' ||
CollCount (&E->Args) > 0 ||
SB_NotEmpty (&Arg.Tokens) ||
M->ParamCount > 0) {
MacroExp* A = ME_AppendArg (E, &Arg);
unsigned I;
@ -1670,11 +1689,23 @@ static unsigned ReadMacroArgs (unsigned NameIdx, MacroExp* E, const Macro* M, in
}
/* Compare formal and actual argument count */
if (CollCount (&E->Args) != (unsigned) M->ParamCount) {
if (CollCount (&E->Args) < (unsigned) M->ParamCount) {
/* Check further only when the parentheses are paired */
if (Parens == 0) {
/* Argument count mismatch */
PPError ("Macro argument count mismatch");
/* Specially casing variable argument */
if (M->Variadic &&
M->ParamCount > 0 &&
CollCount (&E->Args) + 1 == (unsigned) M->ParamCount) {
/* The variable argument is left out entirely */
E->Flags |= MES_NO_VA_COMMA;
if (IS_Get (&Standard) < STD_CC65) {
PPWarning ("ISO C does not permit leaving out the comma before the variable argument");
}
} else {
/* Too few argument */
PPError ("Macro \"%s\" passed only %u arguments, but requires %u",
M->Name, CollCount (&E->Args), (unsigned) M->ParamCount);
}
}
/* Be sure to make enough empty arguments available */
@ -1684,6 +1715,10 @@ static unsigned ReadMacroArgs (unsigned NameIdx, MacroExp* E, const Macro* M, in
while (CollCount (&E->Args) < (unsigned) M->ParamCount) {
ME_AppendArg (E, &Arg);
}
} else if (Parens == 0 && CollCount (&E->Args) > (unsigned) M->ParamCount) {
/* Too many arguments */
PPError ("Macro \"%s\" passed %u arguments, but takes just %u",
M->Name, CollCount (&E->Args), (unsigned) M->ParamCount);
}
/* Deallocate argument resources */
@ -2071,6 +2106,12 @@ static unsigned ReplaceMacros (StrBuf* Source, StrBuf* Target, MacroExp* E, unsi
/* If we have an identifier, check if it's a macro */
if (IsSym (Ident)) {
/* Check for bad identifier names */
if ((ModeFlags & (MSM_MULTILINE | MSM_IN_DIRECTIVE | MSM_IN_ARG_LIST)) != 0 &&
(CollCount (&CurRescanStack->Lines) == 1 || CurC == '\0')) {
CheckForBadIdent (Ident, IS_Get (&Standard), 0);
}
if ((ModeFlags & MSM_OP_DEFINED) != 0 && strcmp (Ident, "defined") == 0) {
/* Handle the "defined" operator */
int HaveParen = 0;
@ -2118,7 +2159,6 @@ static unsigned ReplaceMacros (StrBuf* Source, StrBuf* Target, MacroExp* E, unsi
/* Check if this is a function-like macro */
if (M->ParamCount >= 0) {
int OldPendingNewLines = PendingNewLines;
int HaveSpace = SkipWhitespace (MultiLine) > 0;
/* A function-like macro name without an immediately
@ -2141,32 +2181,50 @@ static unsigned ReplaceMacros (StrBuf* Source, StrBuf* Target, MacroExp* E, unsi
ME_SetTokLens (E, strlen (M->Name));
}
/* Since we have already got on hold of the next
** line, we have to reuse it as the next line
** instead of reading a new line from the source.
*/
if (PendingNewLines > 0 && MultiLine) {
unsigned I = SB_GetIndex (Line);
/* There is no way a function-like macro call
** detection could span multiple lines within
** the range of another just expanded macro.
*/
CHECK (CollCount (&CurRescanStack->Lines) == 1);
/* Revert one newline */
--PendingNewLines;
/* Align indention */
while (I > 0) {
--I;
if (SB_GetBuf (Line)[I] == '\n') {
++I;
break;
}
SB_GetBuf (Line)[I] = ' ';
}
/* Set start index */
SB_SetIndex (Line, I);
/* Add newlines */
AddPreLine (Target);
/* Reuse this line as the next line */
ReuseInputLine ();
/* Quit this loop */
break;
}
/* Append back the whitespace */
if (HaveSpace) {
SB_AppendChar (Target, ' ');
}
/* Since we have already got on hold of the next
** line, we have to go on preprocessing them.
*/
if (MultiLine) {
if (OldPendingNewLines < PendingNewLines && CurC == '#') {
/* If we were going to support #pragma in
** macro argument list, it would be output
** to OLine.
*/
if (OLine == 0) {
OLine = Target;
ParseDirectives (ModeFlags);
OLine = 0;
} else {
ParseDirectives (ModeFlags);
}
}
/* Add the source info to preprocessor output if needed */
AddPreLine (Target);
}
/* Loop */
goto Loop;
}
@ -2200,8 +2258,36 @@ static unsigned ReplaceMacros (StrBuf* Source, StrBuf* Target, MacroExp* E, unsi
InitLine (TmpTarget);
PushRescanLine (CurRescanStack, TmpTarget, LastTokLen);
/* Add a space before a '#' at the beginning of the line */
if (CurC == '#' &&
NextC != '#' &&
(SB_IsEmpty (Target) || SB_LookAtLast (Target) == '\n')) {
SB_AppendChar (Target, ' ');
}
/* Switch the buffers */
TmpTarget = NewStrBuf ();
} else if (PendingNewLines > 0 && MultiLine) {
/* Cancel remaining check for pp-tokens separation
** if there is since ther have been newlines that
** can always separate them.
*/
if (CurRescanStack->PrevTok != 0) {
FreeStrBuf (CurRescanStack->PrevTok);
CurRescanStack->PrevTok = 0;
}
/* Squeeze whitespace */
SkipWhitespace (0);
/* Add indention to preprocessor output if needed */
if (CurC != '\0' && CollCount (&CurRescanStack->Lines) == 1) {
/* Add newlines */
AddPreLine (Target);
/* Align indention */
AppendIndent (Target, SB_GetIndex (Line));
}
}
/* Since we are rescanning, we needn't add the
@ -2241,7 +2327,24 @@ static unsigned ReplaceMacros (StrBuf* Source, StrBuf* Target, MacroExp* E, unsi
} else if (IsQuotedString ()) {
CopyQuotedString (Target);
} else {
/* We want to squeeze whitespace until the end of the current
** input line, so we have to deal with such cases specially.
*/
if (CollCount (&CurRescanStack->Lines) > 1) {
RescanInputStack* RIS = CurRescanStack;
/* Temporarily disable input popping */
CurRescanStack = 0;
Skipped = SkipWhitespace (0);
CurRescanStack = RIS;
if (CurC == '\0') {
/* Now we are at the end of the input line */
goto Loop;
}
} else {
Skipped = SkipWhitespace (0);
}
/* Punctuators must be checked after whitespace since comments
** introducers may be misinterpreted as division operators.
@ -2284,6 +2387,19 @@ Loop:
if (CurC == '\0' && CollCount (&CurRescanStack->Lines) > 1) {
/* Check for rescan sequence end and pp-token pasting */
Skipped = SkipWhitespace (0) || Skipped;
/* Add indention to preprocessor output if needed */
if (CurC != '\0' &&
PendingNewLines > 0 &&
(ModeFlags & MSM_MULTILINE) != 0 &&
CollCount (&CurRescanStack->Lines) == 1) {
/* Add newlines */
AddPreLine (Target);
/* Align indention */
AppendIndent (Target, SB_GetIndex (Line));
Skipped = 0;
}
}
/* Append a space if there hasn't been one */
@ -2338,6 +2454,7 @@ static int ParseMacroReplacement (StrBuf* Source, Macro* M)
int HasWhiteSpace = 0;
unsigned Len;
ident Ident;
int Std = IS_Get (&Standard);
/* Skip whitespace before the macro replacement */
SkipWhitespace (0);
@ -2355,6 +2472,9 @@ static int ParseMacroReplacement (StrBuf* Source, Macro* M)
SB_AppendChar (&M->Replacement, ' ');
} else if (IsQuotedString ()) {
CopyQuotedString (&M->Replacement);
} else if (IsSym (Ident)) {
CheckForBadIdent (Ident, Std, M);
SB_AppendStr (&M->Replacement, Ident);
} else {
if (M->ParamCount >= 0 && GetPunc (Ident)) {
Len = strlen (Ident);
@ -2423,7 +2543,7 @@ static void DoDefine (void)
ident Ident;
Macro* M = 0;
Macro* Existing;
int C89;
int Std;
/* Read the macro name */
SkipWhitespace (0);
@ -2431,8 +2551,8 @@ static void DoDefine (void)
goto Error_Handler;
}
/* Remember if we're in C89 mode */
C89 = (IS_Get (&Standard) == STD_C89);
/* Remember the language standard we are in */
Std = IS_Get (&Standard);
/* Check for forbidden macro names */
if (strcmp (Ident, "defined") == 0) {
@ -2440,6 +2560,9 @@ static void DoDefine (void)
goto Error_Handler;
}
/* Check for and warn on special identifiers */
CheckForBadIdent (Ident, Std, 0);
/* Create a new macro definition */
M = NewMacro (Ident);
@ -2464,7 +2587,7 @@ static void DoDefine (void)
/* The next token must be either an identifier, or - if not in
** C89 mode - the ellipsis.
*/
if (!C89 && CurC == '.') {
if (Std >= STD_C99 && CurC == '.') {
/* Ellipsis */
NextChar ();
if (CurC != '.' || NextC != '.') {
@ -2486,11 +2609,8 @@ static void DoDefine (void)
goto Error_Handler;
}
/* __VA_ARGS__ is only allowed in post-C89 mode */
if (!C89 && strcmp (Ident, "__VA_ARGS__") == 0) {
PPWarning ("'__VA_ARGS__' can only appear in the expansion "
"of a C99 variadic macro");
}
/* Check for and warn on special identifiers */
CheckForBadIdent (Ident, Std, 0);
/* Add the macro parameter */
AddMacroParam (M, Ident);
@ -2665,6 +2785,7 @@ static int DoIfDef (int skip, int flag)
SkipWhitespace (0);
if (MacName (Ident)) {
CheckForBadIdent (Ident, IS_Get (&Standard), 0);
Value = IsMacro (Ident);
/* Check for extra tokens */
CheckExtraTokens (flag ? "ifdef" : "ifndef");
@ -2871,6 +2992,7 @@ static void DoUndef (void)
SkipWhitespace (0);
if (MacName (Ident)) {
CheckForBadIdent (Ident, IS_Get (&Standard), 0);
UndefineMacro (Ident);
}
/* Check for extra tokens */