diff --git a/src/cc65/input.c b/src/cc65/input.c index 0e8fc3276..5754b1054 100644 --- a/src/cc65/input.c +++ b/src/cc65/input.c @@ -103,8 +103,8 @@ static Collection IFiles = STATIC_COLLECTION_INITIALIZER; /* List of all active files */ static Collection AFiles = STATIC_COLLECTION_INITIALIZER; -/* Input stack used when preprocessing. */ -static Collection InputStack = STATIC_COLLECTION_INITIALIZER; +/* Input stack used when preprocessing */ +static Collection* CurrentInputStack; /* Counter for the __COUNTER__ macro */ static unsigned MainFileCounter; @@ -394,34 +394,37 @@ static void GetInputChar (void) ** are read by this function. */ { - /* Drop all pushed fragments that don't have data left */ - while (SB_GetIndex (Line) >= SB_GetLen (Line)) { - /* Cannot read more from this line, check next line on stack if any */ - if (CollCount (&InputStack) == 0) { - /* This is THE line */ - break; - } - FreeStrBuf (Line); - Line = CollPop (&InputStack); - } - - /* Now get the next characters from the line */ - if (SB_GetIndex (Line) >= SB_GetLen (Line)) { - CurC = NextC = '\0'; + /* Get the next-next character from the line */ + if (SB_GetIndex (Line) + 1 < SB_GetLen (Line)) { + /* CurC and NextC come from this fragment */ + CurC = SB_AtUnchecked (Line, SB_GetIndex (Line)); + NextC = SB_AtUnchecked (Line, SB_GetIndex (Line) + 1); } else { - CurC = SB_AtUnchecked (Line, SB_GetIndex (Line)); - if (SB_GetIndex (Line) + 1 < SB_GetLen (Line)) { - /* NextC comes from this fragment */ - NextC = SB_AtUnchecked (Line, SB_GetIndex (Line) + 1); - } else { + /* 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 (&InputStack) > 0) { + if (CollCount (CurrentInputStack) > 0) { NextC = ' '; - } else { - NextC = '\0'; } } + + /* Get CurC from the line */ + CurC = SB_LookAt (Line, SB_GetIndex (Line)); } + } @@ -441,16 +444,46 @@ void NextChar (void) +Collection* UseInputStack (Collection* InputStack) +/* Use the provided input stack for incoming input. Return the previously used +** InputStack. +*/ +{ + Collection* OldInputStack = CurrentInputStack; + + CurrentInputStack = InputStack; + return OldInputStack; +} + + + +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 ClearLine (void) /* Clear the current input line */ { - unsigned I; + if (CurrentInputStack != 0) { + unsigned I; - /* Remove all pushed fragments from the input stack */ - for (I = 0; I < CollCount (&InputStack); ++I) { - FreeStrBuf (CollAtUnchecked (&InputStack, I)); + /* Remove all pushed fragments from the input stack */ + for (I = 0; I < CollCount (CurrentInputStack); ++I) { + FreeStrBuf (Line); + Line = CollPop (CurrentInputStack); + } } - CollDeleteAll (&InputStack); /* Clear the contents of Line */ SB_Clear (Line); diff --git a/src/cc65/input.h b/src/cc65/input.h index fb24bbaa8..f9565229b 100644 --- a/src/cc65/input.h +++ b/src/cc65/input.h @@ -70,6 +70,8 @@ extern StrBuf* Line; extern char CurC; extern char NextC; +typedef struct Collection Collection; + /*****************************************************************************/ @@ -95,6 +97,14 @@ void NextChar (void); ** are read by this function. */ +Collection* UseInputStack (Collection* InputStack); +/* Use the provided input stack for incoming input. Return the previously used +** InputStack. +*/ + +void PushLine (StrBuf* L); +/* Save the current input line and use a new one */ + void ClearLine (void); /* Clear the current input line */ diff --git a/src/cc65/macrotab.c b/src/cc65/macrotab.c index 0e80cd638..3bfae0811 100644 --- a/src/cc65/macrotab.c +++ b/src/cc65/macrotab.c @@ -74,19 +74,17 @@ Macro* NewMacro (const char* Name) */ { /* Get the length of the macro name */ - unsigned Len = strlen(Name); + unsigned Len = strlen (Name); /* Allocate the structure */ Macro* M = (Macro*) xmalloc (sizeof(Macro) + Len); /* Initialize the data */ - M->Next = 0; - M->Expanding = 0; - M->ArgCount = -1; /* Flag: Not a function like macro */ - M->MaxArgs = 0; - InitCollection (&M->FormalArgs); + M->Next = 0; + M->ParamCount = -1; /* Flag: Not a function-like macro */ + InitCollection (&M->Params); SB_Init (&M->Replacement); - M->Variadic = 0; + M->Variadic = 0; memcpy (M->Name, Name, Len+1); /* Return the new macro */ @@ -102,10 +100,10 @@ void FreeMacro (Macro* M) { unsigned I; - for (I = 0; I < CollCount (&M->FormalArgs); ++I) { - xfree (CollAtUnchecked (&M->FormalArgs, I)); + for (I = 0; I < CollCount (&M->Params); ++I) { + xfree (CollAtUnchecked (&M->Params, I)); } - DoneCollection (&M->FormalArgs); + DoneCollection (&M->Params); SB_Done (&M->Replacement); xfree (M); } @@ -121,12 +119,12 @@ Macro* CloneMacro (const Macro* M) Macro* New = NewMacro (M->Name); unsigned I; - for (I = 0; I < CollCount (&M->FormalArgs); ++I) { - /* Copy the argument */ - const char* Arg = CollAtUnchecked (&M->FormalArgs, I); - CollAppend (&New->FormalArgs, xstrdup (Arg)); + for (I = 0; I < CollCount (&M->Params); ++I) { + /* Copy the parameter */ + const char* Param = CollAtUnchecked (&M->Params, I); + CollAppend (&New->Params, xstrdup (Param)); } - New->ArgCount = M->ArgCount; + New->ParamCount = M->ParamCount; New->Variadic = M->Variadic; SB_Copy (&New->Replacement, &M->Replacement); @@ -265,14 +263,14 @@ Macro* FindMacro (const char* Name) -int FindMacroArg (Macro* M, const char* Arg) -/* Search for a formal macro argument. If found, return the index of the -** argument. If the argument was not found, return -1. +int FindMacroParam (const Macro* M, const char* Param) +/* Search for a macro parameter. If found, return the index of the parameter. +** If the parameter was not found, return -1. */ { unsigned I; - for (I = 0; I < CollCount (&M->FormalArgs); ++I) { - if (strcmp (CollAtUnchecked (&M->FormalArgs, I), Arg) == 0) { + for (I = 0; I < CollCount (&M->Params); ++I) { + if (strcmp (CollAtUnchecked (&M->Params, I), Param) == 0) { /* Found */ return I; } @@ -284,25 +282,25 @@ int FindMacroArg (Macro* M, const char* Arg) -void AddMacroArg (Macro* M, const char* Arg) -/* Add a formal macro argument. */ +void AddMacroParam (Macro* M, const char* Param) +/* Add a macro parameter. */ { - /* Check if we have a duplicate macro argument, but add it anyway. - ** Beware: Don't use FindMacroArg here, since the actual argument array + /* Check if we have a duplicate macro parameter, but add it anyway. + ** Beware: Don't use FindMacroParam here, since the actual argument array ** may not be initialized. */ unsigned I; - for (I = 0; I < CollCount (&M->FormalArgs); ++I) { - if (strcmp (CollAtUnchecked (&M->FormalArgs, I), Arg) == 0) { + for (I = 0; I < CollCount (&M->Params); ++I) { + if (strcmp (CollAtUnchecked (&M->Params, I), Param) == 0) { /* Found */ - PPError ("Duplicate macro parameter: '%s'", Arg); + PPError ("Duplicate macro parameter: '%s'", Param); break; } } - /* Add the new argument */ - CollAppend (&M->FormalArgs, xstrdup (Arg)); - ++M->ArgCount; + /* Add the new parameter */ + CollAppend (&M->Params, xstrdup (Param)); + ++M->ParamCount; } @@ -313,14 +311,14 @@ int MacroCmp (const Macro* M1, const Macro* M2) int I; /* Argument count must be identical */ - if (M1->ArgCount != M2->ArgCount) { + if (M1->ParamCount != M2->ParamCount) { return 1; } - /* Compare the arguments */ - for (I = 0; I < M1->ArgCount; ++I) { - if (strcmp (CollConstAt (&M1->FormalArgs, I), - CollConstAt (&M2->FormalArgs, I)) != 0) { + /* Compare the parameters */ + for (I = 0; I < M1->ParamCount; ++I) { + if (strcmp (CollConstAt (&M1->Params, I), + CollConstAt (&M2->Params, I)) != 0) { return 1; } } diff --git a/src/cc65/macrotab.h b/src/cc65/macrotab.h index 6a09d7281..52b812b2f 100644 --- a/src/cc65/macrotab.h +++ b/src/cc65/macrotab.h @@ -55,10 +55,8 @@ typedef struct Macro Macro; struct Macro { Macro* Next; /* Next macro with same hash value */ - int Expanding; /* Are we currently expanding this macro? */ - int ArgCount; /* Number of parameters, -1 = no parens */ - unsigned MaxArgs; /* Size of formal argument list */ - Collection FormalArgs; /* Formal argument list (char*) */ + int ParamCount; /* Number of parameters, -1 = no parens */ + Collection Params; /* Parameter list (char*) */ StrBuf Replacement; /* Replacement text */ unsigned char Variadic; /* C99 variadic macro */ char Name[1]; /* Name, dynamically allocated */ @@ -120,13 +118,13 @@ INLINE int IsMacro (const char* Name) # define IsMacro(Name) (FindMacro (Name) != 0) #endif -int FindMacroArg (Macro* M, const char* Arg); -/* Search for a formal macro argument. If found, return the index of the -** argument. If the argument was not found, return -1. +int FindMacroParam (const Macro* M, const char* Param); +/* Search for a macro parameter. If found, return the index of the parameter. +** If the parameter was not found, return -1. */ -void AddMacroArg (Macro* M, const char* Arg); -/* Add a formal macro argument. */ +void AddMacroParam (Macro* M, const char* Param); +/* Add a macro parameter. */ int MacroCmp (const Macro* M1, const Macro* M2); /* Compare two macros and return zero if both are identical. */ diff --git a/src/cc65/preproc.c b/src/cc65/preproc.c index 020df011a..99a8af116 100644 --- a/src/cc65/preproc.c +++ b/src/cc65/preproc.c @@ -74,11 +74,20 @@ #define MSM_IN_DIRECTIVE 0x02U /* In PP directives scan */ #define MSM_IN_ARG_LIST 0x04U /* In macro argument scan */ #define MSM_IN_ARG_EXPANSION 0x08U /* In expansion on arguments */ -#define MSM_OP_DEFINED 0x10U /* Handle the defined operator */ -#define MSM_OP_HAS_INCLUDE 0x20U /* Handle the __has_include operator */ -#define MSM_OP_HAS_C_ATTRIBUTE 0x40U /* Handle the __has_c_attribute operator */ +#define MSM_OP_DEFINED 0x10U /* Handle the "defined" operator */ +#define MSM_OP_HAS_INCLUDE 0x20U /* Handle the "__has_include" operator */ +#define MSM_OP_HAS_C_ATTRIBUTE 0x40U /* Handle the "__has_c_attribute" operator */ #define MSM_TOK_HEADER 0x80U /* Support header tokens */ +/* Macro expansion state flags */ +#define MES_NONE 0x00U /* Nothing */ +#define MES_FIRST_TOKEN 0x01U /* Mark for detecting pp-token count in the sequence */ +#define MES_MULTIPLE_TOKEN 0x02U /* Multiple pp-tokens are detected in the sequence */ +#define MES_BEGIN_WITH_IDENT 0x04U /* The first pp-token of the sequence is an identifier */ +#define MES_HAS_REPLACEMENT 0x10U /* Macro argument has cached replacement result */ +#define MES_NO_VA_COMMA 0x20U /* Variadic macro called w/o the ',' in front of variable argument */ +#define MES_ERROR 0x80U /* Error has occurred in macro expansion */ + /* Management data for #if */ #define IFCOND_NONE 0x00U #define IFCOND_SKIP 0x01U @@ -88,6 +97,17 @@ /* Current PP if stack */ static PPIfStack* PPStack; +/* Struct for rescan */ +typedef struct RescanInputStack RescanInputStack; +struct RescanInputStack { + Collection Lines; + Collection LastTokLens; + StrBuf* PrevTok; +}; + +/* Input backup for rescan */ +static RescanInputStack* CurRescanStack; + /* Intermediate input buffers */ static StrBuf* PLine; /* Buffer for macro expansion */ static StrBuf* MLine; /* Buffer for macro expansion in #pragma */ @@ -100,8 +120,28 @@ static int FileChanged; /* Structure used when expanding macros */ typedef struct MacroExp MacroExp; struct MacroExp { - Collection ActualArgs; /* Actual arguments */ - StrBuf Replacement; /* Replacement with arguments substituted */ + Collection Args; /* Actual arguments (for function-like) */ + Collection HideSets; /* Macros hidden from expansion */ + StrBuf Tokens; /* Originally read sequence */ + unsigned IdentCount; /* Count of identifiers in the pp-token sequence */ + unsigned Flags; /* Macro argument flags */ + MacroExp* Replaced; /* Macro-replaced version of this pp-token sequence */ + unsigned FirstTokLen; /* Length of the first pp-token */ + unsigned LastTokLen; /* Length of the last pp-token */ +}; + +typedef struct HideRange HideRange; +struct HideRange +{ + HideRange* Next; + unsigned Start; + unsigned End; +}; + +typedef struct HiddenMacro HiddenMacro; +struct HiddenMacro { + const Macro* M; + HideRange* HS; }; @@ -125,8 +165,26 @@ static void PreprocessDirective (StrBuf* Source, StrBuf* Target, unsigned ModeFl static int ParseDirectives (unsigned ModeFlags); /* Handle directives. Return 1 if any whitespace or newlines are parsed. */ -static void MacroReplacement (StrBuf* Source, StrBuf* Target, unsigned ModeFlags); -/* Scan for and perform macro replacement */ +static unsigned ReplaceMacros (StrBuf* Source, StrBuf* Target, MacroExp* E, unsigned ModeFlags); +/* Scan for and perform macro replacement. Return the count of identifiers and +** right parentheses in the replacement result. +*/ + +static MacroExp* InitMacroExp (MacroExp* E); +/* Initialize a MacroExp structure */ + +static void DoneMacroExp (MacroExp* E); +/* Cleanup after use of a MacroExp structure */ + +static int CheckPastePPTok (StrBuf* Target, unsigned TokLen, char Next); +/* Return 1 if the last pp-tokens from Source could be concatenated with any +** characters from Appended to form a new valid one. +*/ + +static void LazyCheckNextPPTok (const StrBuf* Prev, unsigned LastTokLen); +/* Memorize the previous pp-token(s) to later check for potential pp-token +** concatenation. +*/ @@ -201,17 +259,451 @@ static ppdirective_t FindPPDirectiveType (const char* Ident) +/*****************************************************************************/ +/* MacroExp helpers */ +/*****************************************************************************/ + + + +static HideRange* NewHideRange (unsigned Start, unsigned Len) +/* Create a hide range */ +{ + HideRange* HS = xmalloc (sizeof (HideRange)); + + HS->Next = 0; + HS->Start = Start; + HS->End = Start + Len; + + return HS; +} + + + +static void FreeHideRange (HideRange* HS) +/* Free a hide range */ +{ + xfree (HS); +} + + + +static HiddenMacro* NewHiddenMacro (const Macro* M) +/* Create a new struct for the hidden macro */ +{ + HiddenMacro* MHS = xmalloc (sizeof (HiddenMacro)); + + MHS->M = M; + MHS->HS = 0; + + return MHS; +} + + + +static void FreeHiddenMacro (HiddenMacro* MHS) +/* Free the struct and all ranges of the hidden macro */ +{ + HideRange* This; + HideRange* Next; + + if (MHS == 0) { + return; + } + + for (This = MHS->HS; This != 0; This = Next) { + Next = This->Next; + FreeHideRange (This); + } + + xfree (MHS); +} + + + /*****************************************************************************/ /* struct MacroExp */ /*****************************************************************************/ +static HiddenMacro* ME_FindHiddenMacro (const MacroExp* E, const Macro* M) +/* Find the macro hide set */ +{ + unsigned I; + + for (I = 0; I < CollCount (&E->HideSets); ++I) { + HiddenMacro* MHS = CollAtUnchecked (&E->HideSets, I); + if (MHS->M == M) { + return MHS; + } + } + + return 0; +} + + + +static void ME_HideMacro (unsigned Idx, unsigned Count, MacroExp* E, const Macro* M) +/* Hide the macro from the Idx'th identifier */ +{ + if (Count > 0) { + /* Find the macro hideset */ + HiddenMacro* MHS = ME_FindHiddenMacro (E, M); + HideRange** This; + + /* New hidden section */ + HideRange* NewHS = NewHideRange (Idx, Count); + + /* New macro to hide */ + if (MHS == 0) { + MHS = NewHiddenMacro (M); + CollAppend (&E->HideSets, MHS); + } + This = &MHS->HS; + + if (*This == 0) { + *This = NewHS; + } else { + /* Insert */ + while (1) { + if (*This == 0 || NewHS->Start <= (*This)->Start) { + /* Insert before */ + NewHS->Next = *This; + *This = NewHS; + break; + } else if (NewHS->Start <= (*This)->End) { + /* Insert after */ + NewHS->Next = (*This)->Next; + (*This)->Next = NewHS; + break; + } + /* Advance */ + This = &(*This)->Next; + } + + /* Merge */ + while (*This != 0) { + HideRange* Next = (*This)->Next; + + if (Next != 0 && (*This)->End >= Next->Start) { + /* Expand this to the next */ + if ((*This)->End < Next->End) { + (*This)->End = Next->End; + } + + /* Remove next */ + (*This)->Next = Next->Next; + FreeHideRange (Next); + + /* Advance */ + This = &(*This)->Next; + } else { + /* No more */ + break; + } + } + } + } +} + + + +static int ME_CanExpand (unsigned Idx, const MacroExp* E, const Macro* M) +/* Return 1 if the macro can be expanded with the Idx'th identifier */ +{ + if (E != 0) { + /* Find the macro hideset */ + HiddenMacro* MHS = ME_FindHiddenMacro (E, M); + if (MHS != 0) { + /* Check if the macro is hidden from this identifier */ + HideRange* HS = MHS->HS; + while (HS != 0) { + /* If the macro name overlaps with the range where the macro is hidden, + ** the macro cannot be expanded. + */ + if (Idx >= HS->Start && Idx < HS->End) { + return 0; + } + HS = HS->Next; + } + } + } + + return 1; +} + + + +static void ME_OffsetHideSets (unsigned Idx, unsigned Offs, MacroExp* E) +/* Adjust all macro hide set ranges for the macro expansion when the identifier +** at Idx is replaced with a count of Offs + 1 (if Offs > 0) of identifiers. +*/ +{ + if (Offs != 0) { + unsigned I; + + for (I = 0; I < CollCount (&E->HideSets); ++I) { + HiddenMacro* MHS = CollAtUnchecked (&E->HideSets, I); + HideRange* This; + + for (This = MHS->HS; This != 0; This = This->Next) { + if (Idx < This->Start) { + This->Start += Offs; + This->End += Offs; + } else if (Idx < This->End) { + This->End += Offs; + } + } + } + } +} + + + +static void ME_RemoveToken (unsigned Idx, unsigned Count, MacroExp* E) +/* Remove the Idx'th identifier token from tracking and offset all hidden +** ranges accordingly. +*/ +{ + unsigned I; + + for (I = 0; I < CollCount (&E->HideSets); ++I) { + HiddenMacro* MHS = CollAtUnchecked (&E->HideSets, I); + HideRange* This; + HideRange** Prev; + + for (Prev = &MHS->HS, This = *Prev; This != 0; This = *Prev) { + if (Idx < This->Start) { + if (This->Start - Idx >= Count) { + This->Start -= Count; + This->End -= Count; + } else { + if (This->End - Idx > Count) { + This->Start = Idx; + This->End -= Count; + } else { + /* Remove */ + (*Prev) = This->Next; + FreeHideRange (This); + continue; + } + } + } else if (Idx < This->End) { + if (This->End - Idx > Count) { + This->End -= Count; + } else { + This->End = Idx; + } + + if (This->End == This->Start) { + /* Remove */ + (*Prev) = This->Next; + FreeHideRange (This); + continue; + } + } + + Prev = &This->Next; + } + } +} + + + +static void ME_HandleSemiNestedMacro (unsigned NameIdx, unsigned LastIdx, MacroExp* E) +/* Unhide the macro name from all hidesets if it was expanded with an unhidden +** right parenthesis. This is unspecified but allowed behavior according to +** ISO/IEC 9899:2018, 6.10.3.4ff. +*/ +{ + unsigned I; + + for (I = 0; I < CollCount (&E->HideSets); ++I) { + HiddenMacro* MHS = CollAtUnchecked (&E->HideSets, I); + HideRange* This; + HideRange** Prev; + + for (Prev = &MHS->HS, This = *Prev; This != 0; This = *Prev) { + if (NameIdx < This->End) { + if (NameIdx >= This->Start && LastIdx >= This->End) { + This->End = NameIdx; + if (This->End == This->Start) { + /* Remove */ + (*Prev) = This->Next; + FreeHideRange (This); + continue; + } + } + Prev = &This->Next; + } else { + break; + } + } + } +} + + + +static void ME_AddArgHideSets (unsigned Idx, const MacroExp* A, MacroExp* Parent) +/* Propagate the macro hide sets of the substituted argument starting as the +** Idx'th identifier of the result. +*/ +{ + unsigned I; + + /* Move the hide set generated with in the argument as it will be freed later */ + for (I = 0; I < CollCount (&A->HideSets); ++I) { + HiddenMacro* MHS = CollAtUnchecked (&A->HideSets, I); + HideRange* HS; + + for (HS = MHS->HS; HS != 0; HS = HS->Next) { + ME_HideMacro (Idx + HS->Start, HS->End - HS->Start, Parent, MHS->M); + } + } +} + + + +static void ME_DoneHideSets (MacroExp* E) +/* Free all of hidden macros for the macro expansion */ +{ + unsigned I; + + for (I = 0; I < CollCount (&E->HideSets); ++I) { + HiddenMacro* MHS = CollAtUnchecked (&E->HideSets, I); + FreeHiddenMacro (MHS); + } + DoneCollection (&E->HideSets); +} + + + +static void ME_SetTokLens (MacroExp* E, unsigned TokLen) +/* Set token lengths and flags for macro expansion struct */ +{ + E->LastTokLen = TokLen; + if ((E->Flags & MES_FIRST_TOKEN) != 0) { + E->Flags &= ~MES_FIRST_TOKEN; + E->FirstTokLen = E->LastTokLen; + } else { + E->Flags |= MES_MULTIPLE_TOKEN; + } +} + + + +static MacroExp* ME_MakeReplaced (MacroExp* A) +/* Make a replaced version of the argument */ +{ + /* Replace the parameter with actual argument tokens */ + if ((A->Flags & MES_HAS_REPLACEMENT) == 0) { + A->Replaced = xmalloc (sizeof (MacroExp)); + + InitMacroExp (A->Replaced); + SB_Reset (&A->Tokens); + + /* Propagate the hide sets */ + ME_AddArgHideSets (0, A, A->Replaced); + + /* Do macro expansion on the argument */ + A->Replaced->IdentCount = ReplaceMacros (&A->Tokens, + &A->Replaced->Tokens, + A->Replaced, + MSM_IN_ARG_EXPANSION); + + A->Flags |= MES_HAS_REPLACEMENT; + } + + return A->Replaced != 0 ? A->Replaced : A; +} + + + +static MacroExp* ME_GetOriginalArg (const MacroExp* E, unsigned Index) +/* Return an actual macro argument with the given index */ +{ + return CollAt (&E->Args, Index); +} + + + +static MacroExp* ME_GetReplacedArg (const MacroExp* E, unsigned Index) +/* Return a replaced macro argument with the given index */ +{ + return ME_MakeReplaced (CollAt (&E->Args, Index)); +} + + + +static MacroExp* ME_AppendArg (MacroExp* E, MacroExp* Arg) +/* Add a copy of Arg to the list of actual macro arguments. +** NOTE: This function will clear the token sequence of Arg! +*/ +{ + MacroExp* A = xmalloc (sizeof (MacroExp)); + + /* Initialize our MacroExp structure */ + InitMacroExp (A); + + /* Copy info about the original strings */ + A->IdentCount = Arg->IdentCount; + A->Flags = Arg->Flags; + A->FirstTokLen = Arg->FirstTokLen; + A->LastTokLen = Arg->LastTokLen; + + /* Move the contents of Arg to A */ + SB_Move (&A->Tokens, &Arg->Tokens); + + /* Add A to the list of actual arguments */ + CollAppend (&E->Args, A); + + return A; +} + + + +static void ME_ClearArgs (MacroExp* E) +/* Clear all read arguments for macro expansion */ +{ + unsigned I; + + /* Delete the list with actual arguments */ + for (I = 0; I < CollCount (&E->Args); ++I) { + MacroExp* A = CollAtUnchecked (&E->Args, I); + + /* Destroy the macro expansion structure and then free memory allocated + ** for it. + */ + DoneMacroExp (A); + xfree (A); + } + + DoneCollection (&E->Args); + InitCollection (&E->Args); +} + + + +static int ME_IsNextArgVariadic (const MacroExp* E, const Macro* M) +/* Return true if the next actual argument we will add is a variadic one */ +{ + return (M->Variadic && + M->ParamCount == (int) CollCount (&E->Args) + 1); +} + + + static MacroExp* InitMacroExp (MacroExp* E) /* Initialize a MacroExp structure */ { - InitCollection (&E->ActualArgs); - SB_Init (&E->Replacement); + InitCollection (&E->Args); + InitCollection (&E->HideSets); + SB_Init (&E->Tokens); + E->IdentCount = 0; + E->Flags = MES_FIRST_TOKEN; + E->Replaced = 0; + E->FirstTokLen = 0; + E->LastTokLen = 0; return E; } @@ -220,48 +712,70 @@ static MacroExp* InitMacroExp (MacroExp* 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)); + ME_ClearArgs (E); + ME_DoneHideSets (E); + SB_Done (&E->Tokens); + if (E->Replaced != 0) { + DoneMacroExp (E->Replaced); } - DoneCollection (&E->ActualArgs); - SB_Done (&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! -*/ +/*****************************************************************************/ +/* Rescan input stack */ +/*****************************************************************************/ + + + +static void PushRescanLine (RescanInputStack* RIS, StrBuf* L, unsigned LastTokLen) +/* Push an input line to the rescan input stack */ { - /* 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); + CollAppend (&RIS->Lines, L); + /* Abuse the pointer to store an unsigned */ + CollAppend (&RIS->LastTokLens, (void*)(uintptr_t)LastTokLen); } -static StrBuf* ME_GetActual (MacroExp* E, unsigned Index) -/* Return an actual macro argument with the given index */ +static void PopRescanLine (void) +/* Pop and free a rescan input line if it reaches the end */ { - return CollAt (&E->ActualArgs, Index); + if (CurC == '\0' && CollCount (&CurRescanStack->Lines) > 1) { + FreeStrBuf (CollPop (&CurRescanStack->Lines)); + InitLine (CollLast (&CurRescanStack->Lines)); + CollPop (&CurRescanStack->LastTokLens); + } } -static int ME_ArgIsVariadic (const MacroExp* E, const Macro* M) -/* Return true if the next actual argument we will add is a variadic one */ +static void InitRescanInputStack (RescanInputStack* RIS) +/* Init a RescanInputStack struct */ { - return (M->Variadic && - M->ArgCount == (int) CollCount (&E->ActualArgs) + 1); + InitCollection (&RIS->Lines); + InitCollection (&RIS->LastTokLens); + RIS->PrevTok = 0; +} + + + +static void DoneRescanInputStack (RescanInputStack* RIS) +/* Free a RescanInputStack struct. RIS must be non-NULL. */ +{ + /* Free pushed input lines */ + while (CollCount (&RIS->Lines) > 1) { + FreeStrBuf (CollPop (&RIS->Lines)); + } + /* Switch back to the old input stack */ + InitLine (CollPop (&RIS->Lines)); + + /* Free any remaining pp-tokens used for concatenation check */ + FreeStrBuf (RIS->PrevTok); + + /* Done */ + DoneCollection (&RIS->Lines); + DoneCollection (&RIS->LastTokLens); } @@ -431,6 +945,8 @@ static int SkipWhitespace (int SkipLines) { int Skipped = 0; int NewLine = 0; + + /* Rescanning */ while (1) { if (IsSpace (CurC)) { NextChar (); @@ -441,14 +957,51 @@ static int SkipWhitespace (int SkipLines) } else if (CurC == '/' && NextC == '/') { NewStyleComment (); Skipped = 1; - } else if (CurC == '\0' && SkipLines) { - /* End of line, read next */ - if (NextLine () != 0) { - ++PendingNewLines; - NewLine = 1; - Skipped = 0; + } else if (CurC == '\0') { + /* End of line */ + if (CurRescanStack != 0 && + CollCount (&CurRescanStack->Lines) > 1 && + Line == CollLast (&CurRescanStack->Lines)) { + + unsigned LastTokLen = (unsigned)(uintptr_t)CollLast (&CurRescanStack->LastTokLens); + + /* Check for potentially merged tokens */ + if (Skipped == 0 && LastTokLen != 0) { + /* Get the following input */ + StrBuf* Next = CollAtUnchecked (&CurRescanStack->Lines, + CollCount (&CurRescanStack->Lines) - 2); + char C = SB_Peek (Next); + + /* We cannot check right now if the next pp-token may be a + ** macro. + */ + if (IsIdent (C)) { + /* Memorize the previous pp-token and check it later */ + LazyCheckNextPPTok (Line, LastTokLen); + } else if (C != '\0' && !IsSpace (C)) { + /* If the two adjacent pp-tokens could be put together + ** to form a new one, we have to separate them with an + ** additional space. + */ + Skipped = CheckPastePPTok (Line, LastTokLen, SB_Peek (Next)); + } + + } + + /* switch back to previous input */ + PopRescanLine (); + + } else if (SkipLines) { + /* Read next line */ + if (NextLine () != 0) { + ++PendingNewLines; + NewLine = 1; + Skipped = 0; + } else { + /* End of input */ + break; + } } else { - /* End of input */ break; } } else { @@ -456,6 +1009,7 @@ static int SkipWhitespace (int SkipLines) break; } } + return Skipped != 0 ? Skipped : -(NewLine != 0); } @@ -490,11 +1044,28 @@ static void CopyHeaderNameToken (StrBuf* Target) +static int IsQuotedString (void) +/* Retrun 1 if the incoming characters indicate a string literal or character +** constant, otherwise return 0. +*/ +{ + return IsQuote (CurC) || IsWideQuoted (CurC, NextC); +} + + + 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; + char Quote; + + if (CurC == 'L') { + SB_AppendChar (Target, CurC); + NextChar (); + } + + Quote = CurC; SB_AppendChar (Target, CurC); NextChar (); @@ -521,6 +1092,384 @@ static void CopyQuotedString (StrBuf* Target) +static int GetPunc (char* S) +/* Parse a punctuator token. Return 1 and store the parsed token string into S +** on success, otherwise just return 0. +*/ +{ + char C; + switch (CurC) { + + case '[': + case ']': + case '(': + case ')': + case '{': + case '}': + case '~': + case '?': + case ':': + case ';': + case ',': + /* C */ + *S++ = CurC; + NextChar (); + break; + + case '=': + case '#': + /* C or CC */ + C = *S++ = CurC; + NextChar (); + if (CurC == C) { + *S++ = C; + NextChar (); + } + break; + + case '*': + case '/': + case '%': + case '^': + case '!': + /* C or C= */ + *S++ = CurC; + NextChar (); + if (CurC == '=') { + *S++ = CurC; + NextChar (); + } + break; + + case '+': + case '&': + case '|': + /* C, CC or C= */ + C = *S++ = CurC; + NextChar (); + if (CurC == C || CurC == '=') { + *S++ = CurC; + NextChar (); + } + break; + + case '<': + case '>': + /* C, CC, C= or CC= */ + C = *S++ = CurC; + NextChar (); + if (CurC == C) { + *S++ = CurC; + if (NextC == '=') { + *S++ = NextC; + NextChar (); + } + NextChar (); + } else if (CurC == '=') { + *S++ = CurC; + NextChar (); + } + break; + + case '-': + /* C, CC, C= or C> */ + *S++ = CurC; + NextChar (); + switch (CurC) { + case '-': + case '=': + case '>': + *S++ = CurC; + NextChar (); + break; + default: + break; + } + break; + + case '.': + /* C or CCC */ + *S++ = CurC; + NextChar (); + if (CurC == '.' && NextC == '.') { + *S++ = CurC; + *S++ = NextC; + NextChar (); + NextChar (); + } + break; + + default: + return 0; + + } + + *S = '\0'; + return 1; +} + + + +static int CheckPastePPTok (StrBuf* Source, unsigned TokLen, char Next) +/* Return 1 if the last pp-tokens from Source could be concatenated with any +** characters from Appended to form a new valid one. +*/ +{ + char C; + unsigned NewTokLen; + StrBuf* OldSource; + StrBuf Src = AUTO_STRBUF_INITIALIZER; + StrBuf Buf = AUTO_STRBUF_INITIALIZER; + + if (TokLen == 0 || IsBlank (SB_LookAtLast (Source))) { + return 0; + } + + PRECONDITION (SB_GetLen (Source) >= TokLen); + + /* Special casing "..", "/ /" and "/ *" that are not pp-tokens but still + ** need be separated. + */ + C = SB_LookAt (Source, SB_GetLen (Source) - TokLen); + if ((C == '.' && Next == '.') || (C == '/' && (Next == '/' || Next == '*'))) { + return 1; + } + + SB_CopyBuf (&Src, SB_GetConstBuf (Source) + SB_GetLen (Source) - TokLen, TokLen); + SB_AppendChar (&Src, Next); + + SB_Reset (&Src); + OldSource = InitLine (&Src); + + if (IsPPNumber (CurC, NextC)) { + /* PP-number */ + CopyPPNumber (&Buf); + } else if (IsQuotedString ()) { + /* Quoted string */ + CopyQuotedString (&Buf); + } else { + ident Ident; + if (GetPunc (Ident)) { + /* Punctuator */ + SB_CopyStr (&Buf, Ident); + } else if (IsSym (Ident)) { + /* Identifier */ + SB_CopyStr (&Buf, Ident); + } + } + + NewTokLen = SB_GetLen (&Buf); + + SB_Done (&Buf); + SB_Done (&Src); + + /* Restore old source */ + InitLine (OldSource); + + /* Return if concatenation succeeded */ + return NewTokLen != TokLen; +} + + + +static int TryPastePPTok (StrBuf* Target, + StrBuf* Appended, + unsigned FirstTokLen, + unsigned SecondTokLen) +/* Paste the whole appened pp-token sequence onto the end of the target +** pp-token sequence. Diagnose if it fails to form a valid pp-token with the +** two pp-tokens pasted together. Return 1 if succeeds. +*/ +{ + unsigned TokLen; + StrBuf* OldSource; + StrBuf Src = AUTO_STRBUF_INITIALIZER; + StrBuf Buf = AUTO_STRBUF_INITIALIZER; + + if (FirstTokLen == 0 || SecondTokLen == 0) { + SB_Append (Target, Appended); + return 1; + } + + /* Since we need to concatenate the token sequences, remove the + ** last whitespace that was added to target, since it must come + ** from the input. + */ + if (IsBlank (SB_LookAtLast (Target))) { + SB_Drop (Target, 1); + } + + PRECONDITION (SB_GetLen (Target) >= FirstTokLen && + SB_GetLen (Appended) >= SecondTokLen); + + /* Special casing "..", "/ /" and "/ *" */ + if (FirstTokLen == 1) { + char C = SB_LookAt (Target, SB_GetLen (Target) - FirstTokLen); + char N = SB_LookAt (Appended, 0); + /* Avoid forming a comment introducer or an ellipsis. Note that an + ** ellipsis pp-token cannot be formed with macros anyway. + */ + if ((C == '.' && N == '.') || (C == '/' && (N == '/' || N == '*'))) { + SB_AppendChar (Target, ' '); + SB_Append (Target, Appended); + PPWarning ("Pasting formed \"%c%c\", an invalid preprocessing token", C, N); + + return 0; + } + } + + SB_CopyBuf (&Src, SB_GetConstBuf (Target) + SB_GetLen (Target) - FirstTokLen, FirstTokLen); + if (SecondTokLen == SB_GetLen (Appended) || IsSpace (SB_LookAt (Appended, SecondTokLen))) { + SB_AppendBuf (&Src, SB_GetConstBuf (Appended), SecondTokLen); + } else { + SB_AppendBuf (&Src, SB_GetConstBuf (Appended), SecondTokLen + 1); + } + + SB_Reset (&Src); + OldSource = InitLine (&Src); + + if (IsPPNumber (CurC, NextC)) { + /* PP-number */ + CopyPPNumber (&Buf); + } else if (IsQuotedString ()) { + /* Quoted string */ + CopyQuotedString (&Buf); + } else { + ident Ident; + if (GetPunc (Ident)) { + /* Punctuator */ + SB_CopyStr (&Buf, Ident); + } else if (IsSym (Ident)) { + /* Identifier */ + SB_CopyStr (&Buf, Ident); + } else { + /* Unknown */ + } + } + + TokLen = SB_GetLen (&Buf); + if (TokLen < FirstTokLen + SecondTokLen) { + /* The pasting doesn't form a valid pp-token */ + while (SB_GetLen (&Buf) < FirstTokLen + SecondTokLen) { + SB_AppendChar (&Buf, CurC); + NextChar (); + } + SB_Terminate (&Buf); + PPWarning ("Pasting formed \"%s\", an invalid preprocessing token", + SB_GetConstBuf (&Buf)); + + /* Add a space between the tokens to avoid problems in rescanning */ + if (TokLen > FirstTokLen) { + SB_AppendChar (Target, ' '); + } + /* Append all remaining tokens */ + SB_Append (Target, Appended); + + /* No concatenation */ + TokLen = FirstTokLen; + } else { + /* Add a space after the merged token if necessary */ + SB_AppendBuf (Target, SB_GetConstBuf (Appended), SecondTokLen); + if (TokLen > FirstTokLen + SecondTokLen) { + SB_AppendChar (Target, ' '); + } + /* Append all remaining tokens */ + SB_AppendBuf (Target, + SB_GetConstBuf (Appended) + SecondTokLen, + SB_GetLen (Appended) - SecondTokLen); + } + + SB_Done (&Buf); + SB_Done (&Src); + + /* Restore old source */ + InitLine (OldSource); + + /* Return if concatenation succeeded */ + return TokLen != FirstTokLen; +} + + + +static void SeparatePPTok (StrBuf* Target, char Next) +/* Add a space to target if the previous pp-token could be concatenated with +** the following character. +*/ +{ + if (CurRescanStack->PrevTok != 0) { + unsigned Len = SB_GetLen (CurRescanStack->PrevTok) - SB_GetIndex (CurRescanStack->PrevTok); + + /* Check for pp-token pasting */ + if (CheckPastePPTok (CurRescanStack->PrevTok, Len, Next)) { + SB_AppendChar (Target, ' '); + } + FreeStrBuf (CurRescanStack->PrevTok); + CurRescanStack->PrevTok = 0; + } +} + + + +static void LazyCheckNextPPTok (const StrBuf* Prev, unsigned LastTokLen) +/* Memorize the previous pp-token(s) to later check for potential pp-token +** concatenation. +*/ +{ + char C; + int CheckEllipsis = 0; + unsigned NewIndex = SB_GetLen (Prev) - LastTokLen; + + PRECONDITION (SB_GetLen (Prev) >= LastTokLen); + + /* Check for some special cases */ + C = SB_AtUnchecked (Prev, NewIndex); + + /* We may exclude certain punctuators for speedups. As newer C standards + ** could add more punctuators such as "[[", "]]", "::" and so on, this + ** check might need changes accordingly. + */ + if (C == '[' || C == ']' || C == '(' || C == ')' || + C == '{' || C == '}' || C == '~' || C == '?' || + C == ':' || C == ';' || C == ',') { + /* These punctuators cannot be concatenated */ + return; + } + + /* Special check for .. */ + if (NewIndex > 0 && + C == '.' && + SB_AtUnchecked (Prev, NewIndex - 1) == '.') { + /* Save the preceding '.' as well */ + CheckEllipsis = 1; + } + + if (CurRescanStack->PrevTok != 0) { + unsigned OldIndex = SB_GetIndex (CurRescanStack->PrevTok); + unsigned OldLen = SB_GetLen (CurRescanStack->PrevTok) - OldIndex; + unsigned NewLen = SB_GetLen (Prev) - NewIndex; + if (OldLen == NewLen && + strncmp (SB_GetConstBuf (CurRescanStack->PrevTok) + OldIndex - CheckEllipsis, + SB_GetConstBuf (Prev) + NewIndex - CheckEllipsis, + OldLen + CheckEllipsis) == 0) { + /* Same pp-token, keep using the old one */ + } else { + /* Logic error */ + SB_Terminate (CurRescanStack->PrevTok); + Internal ("Unchecked pp-token concatenation: \"%s\"", + SB_GetConstBuf (CurRescanStack->PrevTok) + SB_GetIndex (CurRescanStack->PrevTok)); + } + } else { + /* Memorize the current line */ + CurRescanStack->PrevTok = NewStrBuf (); + SB_CopyBuf (CurRescanStack->PrevTok, + SB_GetConstBuf (Prev) + NewIndex - CheckEllipsis, + LastTokLen + CheckEllipsis); + SB_Reset (CurRescanStack->PrevTok); + } +} + + + static int CheckExtraTokens (const char* Name) /* Check for extra tokens at the end of the directive. Return 1 if there are ** extra tokens, otherwise 0. @@ -542,317 +1491,604 @@ static int CheckExtraTokens (const char* Name) -static void ReadMacroArgs (MacroExp* E, const Macro* M, int MultiLine) -/* Identify the arguments to a macro call as-is */ +static unsigned ReadMacroArgs (unsigned NameIdx, MacroExp* E, const Macro* M, int MultiLine) +/* Identify the arguments to a macro call as-is. Return the total count of +** identifiers and right parentheses in the read argument list. +*/ { - int MissingParen = 0; - unsigned Parens; /* Number of open parenthesis */ - StrBuf Arg = AUTO_STRBUF_INITIALIZER; + unsigned Idx = 0; + unsigned CountInArg = 0; + unsigned Parens = 0; /* Number of open parenthesis */ + ident Ident; + MacroExp Arg; + + InitMacroExp (&Arg); /* Eat the left paren */ NextChar (); /* Read the actual macro arguments */ - Parens = 0; while (1) { /* Squeeze runs of blanks within an arg */ int OldPendingNewLines = PendingNewLines; int Skipped = SkipWhitespace (MultiLine); - if (MultiLine && CurC == '#') { + + /* Directives can only be found in an argument list that spans + ** multiple lines. + */ + if (MultiLine && OldPendingNewLines < PendingNewLines && CurC == '#') { int Newlines = 0; - while (CurC == '#') { + while (OldPendingNewLines < PendingNewLines && CurC == '#') { Newlines += PendingNewLines - OldPendingNewLines; PendingNewLines = OldPendingNewLines; OldPendingNewLines = 0; Skipped = ParseDirectives (MSM_IN_ARG_LIST) || Skipped; - Skipped = SkipWhitespace (MultiLine) || Skipped; } PendingNewLines += Newlines; } - if (Skipped && SB_NotEmpty (&Arg)) { - SB_AppendChar (&Arg, ' '); + + /* Append a space as a separator */ + if (Skipped && SB_NotEmpty (&Arg.Tokens)) { + SB_AppendChar (&Arg.Tokens, ' '); + Skipped = 1; + } else { + Skipped = 0; } - if (CurC == '(') { - /* Nested parenthesis */ - SB_AppendChar (&Arg, CurC); - NextChar (); - ++Parens; + /* Finish reading the current argument if we are not inside nested + ** parentheses or a variadic macro argument. + */ + if (Parens == 0 && + ((CurC == ',' && !ME_IsNextArgVariadic (E, M)) || CurC == ')')) { - } 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, M)) { - /* It's a comma, but we're inside a variadic macro argument, so - ** just copy it and proceed. - */ - SB_AppendChar (&Arg, CurC); - NextChar (); - } else { - /* End of actual argument. Remove whitespace from the end. */ - while (IsSpace (SB_LookAtLast (&Arg))) { - SB_Drop (&Arg, 1); - } - - /* If this is not the single empty argument for a macro with - ** an empty argument list, remember it. - */ - if (CurC != ')' || SB_NotEmpty (&Arg) || 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); + /* End of actual argument. Remove whitespace from the end. */ + while (IsBlank (SB_LookAtLast (&Arg.Tokens))) { + SB_Drop (&Arg.Tokens, 1); } + + /* 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) { + MacroExp* A = ME_AppendArg (E, &Arg); + unsigned I; + + /* Copy the hide sets from the argument list */ + for (I = 0; I < CollCount (&E->HideSets); ++I) { + HiddenMacro* MHS = CollAtUnchecked (&E->HideSets, I); + HideRange* HS; + + for (HS = MHS->HS; HS != 0; HS = HS->Next) { + /* Get the correct hide range */ + unsigned Start = NameIdx + 1 + Idx; + unsigned Len; + if (HS->Start < Start) { + if (HS->End > Start) { + Len = HS->End - Start; + } else { + /* Out of the range */ + continue; + } + Start = 0; + } else { + Len = HS->End - HS->Start; + Start = HS->Start - Start; + } + if (Start + Len > Idx + CountInArg) { + if (Idx + CountInArg > Start) { + Len = Idx + CountInArg - Start; + } else { + /* Out of the range */ + break; + } + } + ME_HideMacro (Start, Len, A, MHS->M); + } + } + + /* More argument info */ + A->IdentCount = CountInArg; + } + + Idx += CountInArg; + + /* Check for end of macro param list */ + if (CurC == ')') { + /* Count right parens */ + ++Idx; + NextChar (); + break; + } + + /* Start the next param */ + NextChar (); + DoneMacroExp (&Arg); + InitMacroExp (&Arg); + CountInArg = 0; + + continue; + } else if (CurC == '\0') { + /* End of input inside macro argument list */ PPError ("Unterminated argument list invoking macro '%s'", M->Name); - MissingParen = 1; + Parens = -1; ClearLine (); + E->Flags |= MES_ERROR; + Idx = 0; break; + } else { - /* Just copy the character */ - SB_AppendChar (&Arg, CurC); - NextChar (); + unsigned LastLen = SB_GetLen (&Arg.Tokens); + + if (IsSym (Ident)) { + /* Just copy the identifier */ + SB_AppendStr (&Arg.Tokens, Ident); + + /* Count identifiers */ + ++CountInArg; + + /* Used for concatentation check */ + if ((Arg.Flags & MES_FIRST_TOKEN) != 0) { + Arg.Flags |= MES_BEGIN_WITH_IDENT; + } + } else if (IsPPNumber (CurC, NextC)) { + /* Copy a pp-number */ + CopyPPNumber (&Arg.Tokens); + } else if (IsQuotedString ()) { + /* Quoted string - just copy */ + CopyQuotedString (&Arg.Tokens); + } else if (GetPunc (Ident)) { + /* Check nested parentheses */ + if (Ident[0] == '(') { + /* Opening nested parenthesis */ + ++Parens; + } else if (Ident[0] == ')') { + /* Closing nested parenthesis */ + --Parens; + + /* Count right parens */ + ++CountInArg; + } + /* Just copy the punctuator */ + SB_AppendStr (&Arg.Tokens, Ident); + } else { + /* Just copy the character */ + SB_AppendChar (&Arg.Tokens, CurC); + NextChar (); + + /* But don't count it */ + ++LastLen; + } + + /* Used for concatentation check */ + ME_SetTokLens (&Arg, SB_GetLen (&Arg.Tokens) - LastLen - Skipped); } } /* Compare formal and actual argument count */ - if (CollCount (&E->ActualArgs) != (unsigned) M->ArgCount) { + if (CollCount (&E->Args) != (unsigned) M->ParamCount) { - if (!MissingParen) { + if (Parens == 0) { /* Argument count mismatch */ PPError ("Macro argument count mismatch"); } /* Be sure to make enough empty arguments available */ - SB_Clear (&Arg); - while (CollCount (&E->ActualArgs) < (unsigned) M->ArgCount) { - ME_AppendActual (E, &Arg); + DoneMacroExp (&Arg); + InitMacroExp (&Arg); + Arg.Flags |= MES_HAS_REPLACEMENT; + while (CollCount (&E->Args) < (unsigned) M->ParamCount) { + ME_AppendArg (E, &Arg); } } - /* Deallocate string buf resources */ - SB_Done (&Arg); + /* Deallocate argument resources */ + DoneMacroExp (&Arg); + + /* Return the total count of identifiers and right parentheses in the + ** argument list. + */ + return Idx; } -static void SubstMacroArgs (MacroExp* E, Macro* M) -/* Argument substitution according to ISO/IEC 9899:1999 (E), 6.10.3.1ff */ +static unsigned SubstMacroArgs (unsigned NameIdx, StrBuf* Target, MacroExp* E, Macro* M, unsigned* IdentCount) +/* Argument substitution according to ISO/IEC 9899:1999 (E), 6.10.3.1ff. +** Return the length of the last pp-token in the result and output the count +** of identifiers and right parentheses in the result to *IdentCount. +*/ { + unsigned Idx = NameIdx; ident Ident; - int ArgIdx; + unsigned TokLen = 0; + int ParamIdx; StrBuf* OldSource; - StrBuf* Arg; - int HaveSpace; + int HaveSpace = 0; + StrBuf Buf = AUTO_STRBUF_INITIALIZER; /* Temporary buffer */ + /* Remember the current input stack and disable it for now */ + Collection* OldInputStack = UseInputStack (0); + + /* Remember the current input and switch to the macro replacement */ + unsigned OldIndex = SB_GetIndex (&M->Replacement); - /* Remember the current input and switch to the macro replacement. */ - int OldIndex = SB_GetIndex (&M->Replacement); SB_Reset (&M->Replacement); OldSource = InitLine (&M->Replacement); - /* Argument handling loop */ + /* If the macro expansion replaces an function-like macro with an argument + ** list containing a right parenthesis outside the hidesets of previously + ** replaced macros, stop those hidesets from this replacement. This is not + ** required by the standard but just to match up with other major C + ** compilers. + */ + ME_HandleSemiNestedMacro (NameIdx, NameIdx + *IdentCount, E); + + /* Substitution loop */ while (CurC != '\0') { + int NeedPaste = 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 (M, Ident)) >= 0) { + /* Remember and skip any following whitespace */ + HaveSpace = SkipWhitespace (0); - /* A macro argument. Get the corresponding actual argument. */ - Arg = ME_GetActual (E, ArgIdx); - - /* Copy any following whitespace */ - HaveSpace = SkipWhitespace (0); + /* Check if it's a macro parameter */ + if ((ParamIdx = FindMacroParam (M, Ident)) >= 0) { /* If a ## operator follows, we have to insert the actual - ** argument as is, otherwise it must be macro replaced. + ** argument as-is, otherwise it must be macro-replaced. */ if (CurC == '#' && NextC == '#') { - /* ### Add placemarker if necessary */ - SB_Append (&E->Replacement, Arg); + /* Get the corresponding actual argument */ + const MacroExp* A = ME_GetOriginalArg (E, ParamIdx); + + /* Separate with a white space if necessary */ + if (CheckPastePPTok (Target, TokLen, SB_Peek (&A->Tokens))) { + SB_AppendChar (Target, ' '); + } + + /* For now we need no placemarkers */ + SB_Append (Target, &A->Tokens); + + /* Adjust tracking */ + ME_OffsetHideSets (Idx, A->IdentCount, E); + Idx += A->IdentCount; + + /* This will be used for concatenation */ + TokLen = A->LastTokLen; } else { - /* Replace the formal argument by a macro replaced copy - ** of the actual. - */ - SB_Reset (Arg); - MacroReplacement (Arg, &E->Replacement, 0); + /* Get the corresponding macro-replaced argument */ + const MacroExp* A = ME_GetReplacedArg (E, ParamIdx); - /* If we skipped whitespace before, re-add it now */ - if (HaveSpace) { - SB_AppendChar (&E->Replacement, ' '); + /* Separate with a white space if necessary */ + if (CheckPastePPTok (Target, TokLen, SB_Peek (&A->Tokens))) { + SB_AppendChar (Target, ' '); } - } + /* Append the replaced string */ + SB_Append (Target, &A->Tokens); + + /* Insert the range of identifiers to parent preceding this argument */ + ME_OffsetHideSets (Idx, A->IdentCount, E); + + /* Add hide range */ + ME_AddArgHideSets (Idx, A, E); + + /* Adjust tracking */ + Idx += A->IdentCount; + + /* May be used for later pp-token merge check */ + TokLen = A->LastTokLen; + } } else { /* An identifier, keep it */ - SB_AppendStr (&E->Replacement, Ident); + SB_AppendStr (Target, Ident); + /* Adjust tracking */ + ME_OffsetHideSets (Idx, 1, E); + ++Idx; + + /* May be used for later concatenation */ + TokLen = strlen (Ident); } + /* Special casing for 'L' prefixing '#' */ + if (TokLen == 1 && SB_LookAtLast (Target) == 'L' && CurC == '#') { + HaveSpace = 1; + } + + /* Squeeze and add the skipped whitespace back for consistency */ + if (HaveSpace && !IsBlank (SB_LookAtLast (Target))) { + SB_AppendChar (Target, ' '); + } + + /* Done with this substituted argument */ + continue; + } else if (CurC == '#' && NextC == '#') { - /* ## operator. */ + /* ## operator */ NextChar (); NextChar (); SkipWhitespace (0); - /* Since we need to concatenate the token sequences, remove - ** any whitespace that was added to target, since it must come - ** from the input. - */ - while (IsSpace (SB_LookAtLast (&E->Replacement))) { - SB_Drop (&E->Replacement, 1); - } - /* If the next token is an identifier which is a macro argument, - ** replace it, otherwise do nothing. + ** replace it, otherwise just add it. */ if (IsSym (Ident)) { + unsigned NewCount = 1; - /* Check if it's a macro argument */ - if ((ArgIdx = FindMacroArg (M, Ident)) >= 0) { + /* Check if it's a macro parameter */ + if ((ParamIdx = FindMacroParam (M, Ident)) >= 0) { - /* Get the corresponding actual argument and add it. */ - SB_Append (&E->Replacement, ME_GetActual (E, ArgIdx)); + /* Get the corresponding actual argument */ + MacroExp* A = ME_GetOriginalArg (E, ParamIdx); + + /* Insert the range of identifiers to parent preceding this argument */ + ME_OffsetHideSets (Idx, A->IdentCount, E); + + /* Add hide range */ + ME_AddArgHideSets (Idx, A, E); + + /* Adjust tracking */ + NewCount = A->IdentCount; + + /* If the preceding pp-token is not a placemarker and is + ** concatenated to with an identifier, the count of tracked + ** identifiers is then one less. + */ + if (TryPastePPTok (Target, &A->Tokens, TokLen, A->FirstTokLen)) { + if (TokLen > 0 && (A->Flags & MES_BEGIN_WITH_IDENT) != 0) { + --NewCount; + ME_RemoveToken (Idx, 1, E); + } + if ((A->Flags & MES_MULTIPLE_TOKEN) == 0) { + TokLen += A->FirstTokLen; + } else { + TokLen = A->LastTokLen; + } + } else { + TokLen = A->LastTokLen; + } } else { - /* Just an ordinary identifier - add as is */ - SB_AppendStr (&E->Replacement, Ident); + unsigned Len; + + /* Just an ordinary identifier - add as-is */ + SB_CopyStr (&Buf, Ident); + + /* If the preceding pp-token is not a placemarker and is + ** concatenated to with an identifier, the count of tracked + ** identifiers is then one less. + */ + Len = SB_GetLen (&Buf); + if (TryPastePPTok (Target, &Buf, TokLen, Len)) { + if (TokLen > 0) { + --NewCount; + } + TokLen += Len; + } else { + TokLen = Len; + } + + /* Adjust tracking */ + ME_OffsetHideSets (Idx, NewCount, E); } + + /* Adjust tracking */ + Idx += NewCount; + + /* Keep the whitespace for consistency */ + HaveSpace = SkipWhitespace (0); + if (HaveSpace && !IsBlank (SB_LookAtLast (Target))) { + SB_AppendChar (Target, ' '); + } + + /* Done with this concatenated identifier */ + continue; } - } else if (CurC == '#' && M->ArgCount >= 0) { - - /* A # operator within a macro expansion of a function like - ** macro. Read the following identifier and check if it's a - ** macro parameter. - */ - NextChar (); - SkipWhitespace (0); - if (!IsSym (Ident) || (ArgIdx = FindMacroArg (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); + if (CurC != '\0') { + /* Non-identifiers may still be pasted together */ + NeedPaste = 1; } - } else if (IsQuote (CurC)) { - CopyQuotedString (&E->Replacement); - } else { - SB_AppendChar (&E->Replacement, CurC); - NextChar (); } + + /* Use the temporary buffer */ + SB_Clear (&Buf); + if (IsPPNumber (CurC, NextC)) { + CopyPPNumber (&Buf); + } else if (IsQuotedString ()) { + CopyQuotedString (&Buf); + } else { + if (CurC == '#' && M->ParamCount >= 0) { + /* A # operator within a macro expansion of a function-like + ** macro. Read the following identifier and check if it's a + ** macro parameter. + */ + NextChar (); + SkipWhitespace (0); + if (!IsSym (Ident) || (ParamIdx = FindMacroParam (M, Ident)) < 0) { + /* Should not happen, but still */ + Internal ("'#' is not followed by a macro parameter"); + } else { + /* Make a valid string from Replacement */ + MacroExp* A = ME_GetOriginalArg (E, ParamIdx); + SB_Reset (&A->Tokens); + Stringize (&A->Tokens, &Buf); + } + } else if (GetPunc (Ident)) { + /* Count right parens. This is OK since they cannot be pasted + ** to form different punctuators with others. + */ + if (Ident[0] == ')') { + /* Adjust tracking */ + ME_OffsetHideSets (Idx, 1, E); + ++Idx; + } + SB_AppendStr (&Buf, Ident); + } else if (CurC != '\0') { + SB_AppendChar (&Buf, CurC); + NextChar (); + } + } + + /* Squeeze any whitespace for consistency. Especially, comments must + ** be consumed before fed to the punctuator parser, or their leading + ** '/' characters would be parsed wrongly as division operators. + */ + HaveSpace = SkipWhitespace (0); + + if (NeedPaste) { + unsigned Len = SB_GetLen (&Buf); + + /* Concatenate pp-tokens */ + if (TryPastePPTok (Target, &Buf, TokLen, Len)) { + TokLen += Len; + } else { + TokLen = Len; + } + } else { + /* Just append the token */ + SB_Append (Target, &Buf); + TokLen = SB_GetLen (&Buf); + } + + if (HaveSpace && !IsBlank (SB_LookAtLast (Target))) { + SB_AppendChar (Target, ' '); + } + } -#if 0 - /* Remove whitespace from the end of the line */ - while (IsSpace (SB_LookAtLast (&E->Replacement))) { - SB_Drop (&E->Replacement, 1); - } -#endif + /* Done with the temporary buffer */ + SB_Done (&Buf); + + /* Remove the macro name itself together with the arguments (if any) */ + ME_RemoveToken (Idx, 1 + *IdentCount, E); + + /* Hide this macro for the whole result of this expansion */ + ME_HideMacro (NameIdx, Idx - NameIdx, E, M); /* Switch back the input */ + UseInputStack (OldInputStack); InitLine (OldSource); SB_SetIndex (&M->Replacement, OldIndex); + + /* Set the count of identifiers and right parentheses in the result */ + *IdentCount = Idx - NameIdx; + + /* Return the length of the last pp-token */ + return TokLen; } -static void ExpandMacro (StrBuf* Target, Macro* M, int MultiLine) -/* Expand a macro into Target */ +static unsigned ExpandMacro (unsigned Idx, StrBuf* Target, MacroExp* E, Macro* M, int MultiLine) +/* Expand a macro into Target. Return the length of the last pp-token in the +** result of the expansion. +*/ { - MacroExp E; + unsigned Count = 0; /* Count of identifiers and right parentheses */ + unsigned Len = 0; /* Length of the last pp-token in the result */ -#if 0 + /* Disable previous pp-token spacing checking */ + StrBuf* PrevTok = CurRescanStack->PrevTok; + CurRescanStack->PrevTok = 0; + +#if DEV_CC65_DEBUG static unsigned V = 0; - printf ("Expanding %s(%u)\n", M->Name, ++V); + printf ("Expanding (%u) %s\n", ++V, M->Name); #endif - /* Initialize our MacroExp structure */ - InitMacroExp (&E); - /* Check if this is a function like macro */ - if (M->ArgCount >= 0) { - + if (M->ParamCount >= 0) { /* Read the actual macro arguments (with the enclosing parentheses) */ - ReadMacroArgs (&E, M, MultiLine); - + Count = ReadMacroArgs (Idx, E, M, MultiLine); } - /* Replace macro arguments handling the # and ## operators */ - SubstMacroArgs (&E, M); + if ((E->Flags & MES_ERROR) == 0) { + /* Replace macro parameters with arguments handling the # and ## operators */ + Len = SubstMacroArgs (Idx, Target, E, M, &Count); + } else { + SB_CopyStr (Target, M->Name); + } - /* Forbide repeated expansion of the same macro in use */ - M->Expanding = 1; - MacroReplacement (&E.Replacement, Target, 0); - M->Expanding = 0; + if (CollCount (&E->Args) > 0) { + /* Clear all arguments */ + ME_ClearArgs (E); + } -#if 0 - printf ("Done with %s(%u)\n", E.M->Name, V--); +#if DEV_CC65_DEBUG + printf ("Expanded (%u) %s to %d ident(s) at %u: %s\n", + V--, M->Name, Count, Idx, SB_GetConstBuf (Target)); #endif - /* Free memory allocated for the macro expansion structure */ - DoneMacroExp (&E); + /* Reenable previous pp-token concatenation checking */ + FreeStrBuf (CurRescanStack->PrevTok); + CurRescanStack->PrevTok = PrevTok; + + /* Return the length of the last pp-token in the expansion result */ + return Len; } -static void MacroReplacement (StrBuf* Source, StrBuf* Target, unsigned ModeFlags) -/* Scan for and perform macro replacement */ +static unsigned ReplaceMacros (StrBuf* Source, StrBuf* Target, MacroExp* E, unsigned ModeFlags) +/* Scan for and perform macro replacement. Return the count of identifiers and +** right parentheses in the replacement result. +*/ { - ident Ident; + unsigned Count = 0; + StrBuf* TmpTarget = NewStrBuf (); /* Remember the current input and switch to Source */ - StrBuf* OldSource = InitLine (Source); + StrBuf* OldSource = InitLine (Source); + RescanInputStack RescanStack; + RescanInputStack* OldRescanStack = CurRescanStack; + + InitRescanInputStack (&RescanStack); + PushRescanLine (&RescanStack, Line, 0); + CurRescanStack = &RescanStack; /* Loop substituting macros */ while (CurC != '\0') { + int Skipped = 0; + ident Ident; + /* If we have an identifier, check if it's a macro */ if (IsSym (Ident)) { if ((ModeFlags & MSM_OP_DEFINED) != 0 && strcmp (Ident, "defined") == 0) { /* Handle the "defined" operator */ int HaveParen = 0; + /* Eat the "defined" operator */ + ME_RemoveToken (Count, 1, E); + SkipWhitespace (0); if (CurC == '(') { HaveParen = 1; NextChar (); SkipWhitespace (0); } + + /* Add a space to separate the result if necessary */ + SeparatePPTok (Target, '0'); + if (IsSym (Ident)) { /* Eat the identifier */ + ME_RemoveToken (Count, 1, E); SB_AppendChar (Target, IsMacro (Ident) ? '1' : '0'); if (HaveParen) { SkipWhitespace (0); @@ -860,6 +2096,8 @@ static void MacroReplacement (StrBuf* Source, StrBuf* Target, unsigned ModeFlags PPError ("')' expected"); ClearLine (); } else { + /* Eat the right parenthesis */ + ME_RemoveToken (Count, 1, E); NextChar (); } } @@ -869,25 +2107,48 @@ static void MacroReplacement (StrBuf* Source, StrBuf* Target, unsigned ModeFlags SB_AppendChar (Target, '0'); } } else { - /* Check if it's an expandable macro */ Macro* M = FindMacro (Ident); - if (M != 0 && !M->Expanding) { + + /* Check if it's an expandable macro */ + if (M != 0 && ME_CanExpand (Count, E, M)) { + int MultiLine = (ModeFlags & MSM_MULTILINE) != 0; + unsigned LastTokLen; + /* Check if this is a function-like macro */ - if (M->ArgCount >= 0) { - int MultiLine = (ModeFlags & MSM_MULTILINE) != 0; - int Whitespace = SkipWhitespace (MultiLine); + if (M->ParamCount >= 0) { + int OldPendingNewLines = PendingNewLines; + int HaveSpace = SkipWhitespace (MultiLine) > 0; + + /* A function-like macro name without an immediately + ** following argument list is not subject to expansion. + */ if (CurC != '(') { - /* Function-like macro without an argument list is not replaced */ + /* No expansion */ + ++Count; + + /* Add a space to separate the macro name if necessary */ + SeparatePPTok (Target, M->Name[0]); SB_AppendStr (Target, M->Name); - if (Whitespace > 0) { + + /* Keep tracking pp-token lengths */ + if ((ModeFlags & MSM_IN_ARG_EXPANSION) != 0) { + /* Used for concatentation check */ + if ((E->Flags & MES_FIRST_TOKEN) != 0) { + E->Flags |= MES_BEGIN_WITH_IDENT; + } + ME_SetTokLens (E, strlen (M->Name)); + } + + /* Append back the whitespace */ + if (HaveSpace) { SB_AppendChar (Target, ' '); } - /* Directives can only be found in an argument list - ** that spans multiple lines. + /* Since we have already got on hold of the next + ** line, we have to go on preprocessing them. */ if (MultiLine) { - if (CurC == '#') { + if (OldPendingNewLines < PendingNewLines && CurC == '#') { /* If we were going to support #pragma in ** macro argument list, it would be output ** to OLine. @@ -903,49 +2164,157 @@ static void MacroReplacement (StrBuf* Source, StrBuf* Target, unsigned ModeFlags /* Add the source info to preprocessor output if needed */ AddPreLine (Target); } - } else { - /* Function-like macro */ - if (OLine == 0) { - OLine = Target; - ExpandMacro (Target, M, MultiLine); - OLine = 0; - } else { - ExpandMacro (Target, M, MultiLine); - } + + /* Loop */ + goto Loop; } - } else { - /* Object-like macro */ - ExpandMacro (Target, M, 0); } - } else { - /* An identifier, keep it */ - SB_AppendStr (Target, Ident); + + /* Either an object-like or function-like macro */ + MultiLine = MultiLine && M->ParamCount >= 0; + + /* If we were going to support #pragma in macro argument + ** list, it would be output to OLine. + */ + if (MultiLine && OLine == 0) { + OLine = TmpTarget; + LastTokLen = ExpandMacro (Count, TmpTarget, E, M, MultiLine); + OLine = 0; + } else { + LastTokLen = ExpandMacro (Count, TmpTarget, E, M, MultiLine); + } + + /* Check for errors in expansion */ + if ((E->Flags & MES_ERROR) != 0) { + break; + } + + /* Pop the current line if it is at the end */ + PopRescanLine (); + + if (SB_GetLen (TmpTarget) > 0) { + /* Start rescanning from the temporary result */ + SB_Reset (TmpTarget); + InitLine (TmpTarget); + PushRescanLine (CurRescanStack, TmpTarget, LastTokLen); + + /* Switch the buffers */ + TmpTarget = NewStrBuf (); + } + + /* Since we are rescanning, we needn't add the + ** count of just replaced identifiers right now. + */ + continue; + } + + /* An unexpandable identifier. Keep it. */ + ++Count; + + /* Add a space to separate the macro name if necessary */ + SeparatePPTok (Target, Ident[0]); + SB_AppendStr (Target, Ident); + + /* Keep tracking pp-token lengths */ + if ((ModeFlags & MSM_IN_ARG_EXPANSION) != 0) { + /* Used for concatentation check */ + if ((E->Flags & MES_FIRST_TOKEN) != 0) { + E->Flags |= MES_BEGIN_WITH_IDENT; + } + ME_SetTokLens (E, strlen (Ident)); } } } else { + unsigned LastLen; + + /* Add a space to separate the macro name if necessary */ + SeparatePPTok (Target, CurC); + + LastLen = SB_GetLen (Target); + if ((ModeFlags & MSM_TOK_HEADER) != 0 && (CurC == '<' || CurC == '\"')) { CopyHeaderNameToken (Target); - } else if (IsQuote (CurC)) { + } else if (IsPPNumber (CurC, NextC)) { + CopyPPNumber (Target); + } else if (IsQuotedString ()) { CopyQuotedString (Target); } else { - int Whitespace = SkipWhitespace (0); - if (Whitespace) { - SB_AppendChar (Target, ' '); - } else { - SB_AppendChar (Target, CurC); - NextChar (); + Skipped = SkipWhitespace (0); + + /* Punctuators must be checked after whitespace since comments + ** introducers may be misinterpreted as division operators. + */ + if (!Skipped) { + if (GetPunc (Ident)) { + if (Ident[0] == ')') { + /* Count right parens */ + ++Count; + } + SB_AppendStr (Target, Ident); + + /* If an identifier follows immediately, it could be a macro + ** expanded later that occasionally need a space to separate. + */ + if (IsIdent (CurC)) { + /* Memorize the previous pp-token and check it later */ + LazyCheckNextPPTok (Target, strlen (Ident)); + } + } else { + SB_AppendChar (Target, CurC); + NextChar (); + + /* Don't count this character */ + ++LastLen; + } } } + + /* Keep tracking pp-token lengths */ + if ((ModeFlags & MSM_IN_ARG_EXPANSION) != 0) { + ME_SetTokLens (E, SB_GetLen (Target) - LastLen); + } + } + +Loop: + /* Switch back to the previous input stream if we have finished + ** rescanning the current one. + */ + if (CurC == '\0' && CollCount (&CurRescanStack->Lines) > 1) { + /* Check for rescan sequence end and pp-token pasting */ + Skipped = SkipWhitespace (0) || Skipped; + } + + /* Append a space if there hasn't been one */ + if (Skipped && !IsSpace (SB_LookAtLast (Target))) { + SB_AppendChar (Target, ' '); } } + /* Append the remaining result */ + SB_Append (Target, TmpTarget); + + /* Done with the temporary buffer */ + SB_Done (TmpTarget); + /* Drop whitespace at the end */ if (IsBlank (SB_LookAtLast (Target))) { SB_Drop (Target, 1); } + /* Sanity check */ + if ((E->Flags & MES_ERROR) == 0) { + CHECK (CollCount (&CurRescanStack->Lines) == 1); + } + + /* Done with the current input stack */ + DoneRescanInputStack (CurRescanStack); + CurRescanStack = OldRescanStack; + /* Switch back the input */ InitLine (OldSource); + + /* Return the count of identifiers and right parentheses */ + return Count; } @@ -956,19 +2325,108 @@ static void MacroReplacement (StrBuf* Source, StrBuf* Target, unsigned ModeFlags +static int ParseMacroReplacement (StrBuf* Source, Macro* M) +/* Check correctness of macro definition while squeezing old and new style +** comments and other non-newline whitespace sequences. Return 1 on success +** or 0 on failure. +*/ +{ + /* Switch to the new input source */ + StrBuf* OldSource = InitLine (Source); + int HasWhiteSpace = 0; + unsigned Len; + ident Ident; + + /* Skip whitespace before the macro replacement */ + SkipWhitespace (0); + + /* Check for ## at start */ + if (CurC == '#' && NextC == '#') { + /* Diagnose and bail out */ + PPError ("'##' cannot appear at start of macro expansion"); + goto Error_Handler; + } + + /* Loop removing ws and comments */ + while (CurC != '\0') { + if (HasWhiteSpace) { + SB_AppendChar (&M->Replacement, ' '); + } else if (IsQuotedString ()) { + CopyQuotedString (&M->Replacement); + } else { + if (M->ParamCount >= 0 && GetPunc (Ident)) { + Len = strlen (Ident); + /* Check for # */ + if (Len == 1 && Ident[0] == '#') { + HasWhiteSpace = SkipWhitespace (0); + + /* Check next pp-token */ + if (!IsSym (Ident) || FindMacroParam (M, Ident) < 0) { + PPError ("'#' is not followed by a macro parameter"); + goto Error_Handler; + } + + /* Make the replacement */ + SB_AppendChar (&M->Replacement, '#'); + if (HasWhiteSpace) { + SB_AppendChar (&M->Replacement, ' '); + } + SB_AppendStr (&M->Replacement, Ident); + } else { + SB_AppendBuf (&M->Replacement, Ident, Len); + } + } else { + SB_AppendChar (&M->Replacement, CurC); + NextChar (); + } + } + + HasWhiteSpace = SkipWhitespace (0); + } + + /* Check for ## at end */ + Len = SB_GetLen (&M->Replacement); + if (Len >= 2) { + if (SB_LookAt (&M->Replacement, Len - 1) == '#' && + SB_LookAt (&M->Replacement, Len - 2) == '#') { + /* Diagnose and bail out */ + PPError ("'##' cannot appear at end of macro expansion"); + goto Error_Handler; + } + } + + /* Terminate the new input line */ + SB_Terminate (&M->Replacement); + + /* Switch back to the old source */ + InitLine (OldSource); + + /* Success */ + return 1; + +Error_Handler: + + /* Switch back to the old source */ + InitLine (OldSource); + + /* Failure */ + return 0; +} + + + static void DoDefine (void) /* Process #define directive */ { ident Ident; - Macro* M; + Macro* M = 0; Macro* Existing; int C89; - unsigned Len; /* Read the macro name */ SkipWhitespace (0); if (!MacName (Ident)) { - return; + goto Error_Handler; } /* Remember if we're in C89 mode */ @@ -977,20 +2435,20 @@ static void DoDefine (void) /* Check for forbidden macro names */ if (strcmp (Ident, "defined") == 0) { PPError ("'%s' cannot be used as a macro name", Ident); - return; + goto Error_Handler; } /* Create a new macro definition */ M = NewMacro (Ident); - /* Check if this is a function like macro */ + /* Check if this is a function-like macro */ if (CurC == '(') { /* Skip the left paren */ NextChar (); - /* Set the marker that this is a function like macro */ - M->ArgCount = 0; + /* Set the marker that this is a function-like macro */ + M->ParamCount = 0; /* Read the formal parameter list */ while (1) { @@ -1009,23 +2467,21 @@ static void DoDefine (void) NextChar (); if (CurC != '.' || NextC != '.') { PPError ("'...' expected"); - ClearLine (); - FreeMacro (M); - return; + goto Error_Handler; } NextChar (); NextChar (); /* Remember that the macro is variadic and use __VA_ARGS__ as - ** the argument name. + ** the parameter name. */ - AddMacroArg (M, "__VA_ARGS__"); + AddMacroParam (M, "__VA_ARGS__"); M->Variadic = 1; } else { - /* Must be macro argument name */ + /* Must be macro parameter name */ if (MacName (Ident) == 0) { - return; + goto Error_Handler; } /* __VA_ARGS__ is only allowed in post-C89 mode */ @@ -1034,12 +2490,12 @@ static void DoDefine (void) "of a C99 variadic macro"); } - /* Add the macro argument */ - AddMacroArg (M, Ident); + /* Add the macro parameter */ + AddMacroParam (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. + ** reached the end of the macro parameter list. */ SkipWhitespace (0); if (M->Variadic || CurC != ',') { @@ -1051,47 +2507,22 @@ static void DoDefine (void) /* Check for a right paren and eat it if we find one */ if (CurC != ')') { PPError ("')' expected for macro definition"); - ClearLine (); - FreeMacro (M); - return; + goto Error_Handler; } NextChar (); } - /* Skip whitespace before the macro replacement */ - SkipWhitespace (0); - /* Remove whitespace and comments from the line, store the preprocessed ** line into the macro replacement buffer. */ - TranslationPhase3 (Line, &M->Replacement); - - /* Remove whitespace from the end of the line */ - while (IsSpace (SB_LookAtLast (&M->Replacement))) { - SB_Drop (&M->Replacement, 1); + if (ParseMacroReplacement (Line, M) == 0) { + goto Error_Handler; } + #if 0 printf ("%s: <%.*s>\n", M->Name, SB_GetLen (&M->Replacement), SB_GetConstBuf (&M->Replacement)); #endif - /* Check for ## at start or end */ - Len = SB_GetLen (&M->Replacement); - if (Len >= 2) { - if (SB_LookAt (&M->Replacement, 0) == '#' && - SB_LookAt (&M->Replacement, 1) == '#') { - /* Diagnose and bail out */ - PPError ("'##' cannot appear at start of macro expansion"); - FreeMacro (M); - return; - } else if (SB_LookAt (&M->Replacement, Len - 1) == '#' && - SB_LookAt (&M->Replacement, Len - 2) == '#') { - /* Diagnose and bail out */ - PPError ("'##' cannot appear at end of macro expansion"); - FreeMacro (M); - return; - } - } - /* Get an existing macro definition with this name */ Existing = FindMacro (M->Name); @@ -1108,6 +2539,18 @@ static void DoDefine (void) /* Insert the new macro into the macro table */ InsertMacro (M); + + /* Success */ + return; + +Error_Handler: + + /* Cleanup */ + ClearLine (); + + if (M != 0) { + FreeMacro (M); + } } @@ -1582,6 +3025,7 @@ static int ParseDirectives (unsigned ModeFlags) if (!PPSkip) { if ((ModeFlags & MSM_IN_ARG_LIST) == 0) { DoPragma (); + return Whitespace; } else { PPError ("Embedded #pragma directive within macro arguments is unsupported"); } @@ -1624,7 +3068,7 @@ static int ParseDirectives (unsigned ModeFlags) Whitespace = SkipWhitespace (0) || Whitespace; } - return Whitespace != 0; + return Whitespace; } @@ -1688,7 +3132,7 @@ static void TranslationPhase3 (StrBuf* Source, StrBuf* Target) } if (HasWhiteSpace) { SB_AppendChar (Target, ' '); - } else if (IsQuote (CurC)) { + } else if (IsQuotedString ()) { CopyQuotedString (Target); } else { SB_AppendChar (Target, CurC); @@ -1710,16 +3154,12 @@ static void PreprocessDirective (StrBuf* Source, StrBuf* Target, unsigned ModeFl ** whitespace and comments, then do macro replacement. */ { - int OldIndex = SB_GetIndex (Source); MacroExp E; SkipWhitespace (0); InitMacroExp (&E); - MacroReplacement (Source, Target, ModeFlags | MSM_IN_DIRECTIVE); + ReplaceMacros (Source, Target, &E, ModeFlags | MSM_IN_DIRECTIVE); DoneMacroExp (&E); - - /* Restore the source input index */ - SB_SetIndex (Source, OldIndex); } @@ -1727,6 +3167,8 @@ static void PreprocessDirective (StrBuf* Source, StrBuf* Target, unsigned ModeFl void Preprocess (void) /* Preprocess lines count of which is affected by directives */ { + MacroExp E; + SB_Clear (PLine); /* Add the source info to preprocessor output if needed */ @@ -1741,10 +3183,14 @@ void Preprocess (void) AddPreLine (PLine); /* Add leading whitespace to prettify preprocessor output */ - AppendIndent (PLine, SB_GetIndex (Line)); + if (CurC != '\0') { + AppendIndent (PLine, SB_GetIndex (Line)); + } /* Expand macros if any */ - MacroReplacement (Line, PLine, MSM_MULTILINE); + InitMacroExp (&E); + ReplaceMacros (Line, PLine, &E, MSM_MULTILINE); + DoneMacroExp (&E); /* Add the source info to preprocessor output if needed */ AddPreLine (PLine); diff --git a/src/cc65/scanner.c b/src/cc65/scanner.c index 09dd8fef8..66ce6906e 100644 --- a/src/cc65/scanner.c +++ b/src/cc65/scanner.c @@ -235,10 +235,20 @@ void SymName (char* S) +int IsWideQuoted (char First, char Second) +/* Return 1 if the two successive characters indicate a wide string literal or +** a wide char constant, otherwise return 0. +*/ +{ + return First == 'L' && IsQuote(Second); +} + + + int IsSym (char* S) /* If a symbol follows, read it and return 1, otherwise return 0 */ { - if (IsIdent (CurC)) { + if (IsIdent (CurC) && !IsWideQuoted (CurC, NextC)) { SymName (S); return 1; } else { diff --git a/src/cc65/scanner.h b/src/cc65/scanner.h index 7a67b10ed..338ad6a65 100644 --- a/src/cc65/scanner.h +++ b/src/cc65/scanner.h @@ -282,6 +282,11 @@ void SymName (char* S); ** least of size MAX_IDENTLEN+1. */ +int IsWideQuoted (char First, char Second); +/* Return 1 if the two successive characters indicate a wide string literal or +** a wide char constant, otherwise return 0. +*/ + int IsSym (char* S); /* If a symbol follows, read it and return 1, otherwise return 0 */