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:
commit
b2238fdcd4
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user