diff --git a/doc/cc65.sgml b/doc/cc65.sgml index 43039f713..683249bda 100644 --- a/doc/cc65.sgml +++ b/doc/cc65.sgml @@ -1202,17 +1202,34 @@ The compiler defines several macros at startup: This macro is defined if the target is the Commodore Plus/4 (-t plus4). - __STDC_HOSTED__ - - This macro is expands to the integer constant 1. - __SIM6502__ This macro is defined if the target is sim65 in 6502 mode (-t sim6502). __SIM65C02__ + This macro is defined if the target is sim65 in 65C02 mode (-t sim65c02). + __STDC_HOSTED__ + + This macro expands to the integer constant 1. + + __STDC_NO_ATOMICS__ + + This macro expands to the integer constant 1 if the language standard is cc65 (--standard cc65). + + __STDC_NO_COMPLEX__ + + This macro expands to the integer constant 1 if the language standard is cc65 (--standard cc65). + + __STDC_NO_THREADS__ + + This macro expands to the integer constant 1 if the language standard is cc65 (--standard cc65). + + __STDC_NO_VLA__ + + This macro expands to the integer constant 1 if the language standard is cc65 (--standard cc65). + __SUPERVISION__ This macro is defined if the target is the Supervision (-t supervision). diff --git a/src/cc65/compile.c b/src/cc65/compile.c index 7f2cd8ba0..75250da0a 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -82,8 +82,11 @@ static void Parse (void) SymEntry* Entry; FuncDesc* FuncDef = 0; - /* Go... */ - NextToken (); + /* Initialization for deferred operations */ + InitDeferredOps (); + + /* Fill up the next token with a bogus semicolon and start the tokenizer */ + NextTok.Tok = TOK_SEMI; NextToken (); /* Parse until end of input */ @@ -338,6 +341,9 @@ static void Parse (void) } } + + /* Done with deferred operations */ + DoneDeferredOps (); } @@ -403,7 +409,13 @@ void Compile (const char* FileName) /* DefineNumericMacro ("__STDC__", 1); <- not now */ DefineNumericMacro ("__STDC_HOSTED__", 1); - InitDeferredOps (); + /* Stuff unsupported */ + if (IS_Get (&Standard) > STD_C99) { + DefineNumericMacro ("__STDC_NO_ATOMICS__", 1); + DefineNumericMacro ("__STDC_NO_COMPLEX__", 1); + DefineNumericMacro ("__STDC_NO_THREADS__", 1); + DefineNumericMacro ("__STDC_NO_VLA__", 1); + } /* Create the base lexical level */ EnterGlobalLevel (); @@ -423,6 +435,9 @@ void Compile (const char* FileName) /* Generate the code generator preamble */ g_preamble (); + /* Init preprocessor */ + InitPreprocess (); + /* Open the input file */ OpenMainFile (FileName); @@ -433,10 +448,8 @@ void Compile (const char* FileName) OpenOutputFile (); /* Preprocess each line and write it to the output file */ - while (NextLine ()) { - Preprocess (); - WriteOutput ("%.*s\n", (int) SB_GetLen (Line), SB_GetConstBuf (Line)); - } + while (PreprocessNextLine ()) + { /* Nothing */ } /* Close the output file */ CloseOutputFile (); @@ -494,9 +507,11 @@ void Compile (const char* FileName) } } } + } - DoneDeferredOps (); + /* Done with preprocessor */ + DonePreprocess (); if (Debug) { PrintMacroStats (stdout); diff --git a/src/cc65/input.c b/src/cc65/input.c index 22a66e1f7..18441f5c8 100644 --- a/src/cc65/input.c +++ b/src/cc65/input.c @@ -54,6 +54,7 @@ #include "input.h" #include "lineinfo.h" #include "output.h" +#include "preproc.h" @@ -91,6 +92,8 @@ struct AFile { FILE* F; /* Input file stream */ IFile* Input; /* Points to corresponding IFile */ int SearchPath; /* True if we've added a path for this file */ + PPIfStack IfStack; /* PP #if stack */ + int MissingNL; /* Last input line was missing a newline */ }; /* List of all input files */ @@ -156,6 +159,8 @@ static AFile* NewAFile (IFile* IF, FILE* F) AF->Line = 0; AF->F = F; AF->Input = IF; + AF->IfStack.Index = -1; + AF->MissingNL = 0; /* Increment the usage counter of the corresponding IFile. If this ** is the first use, set the file data and output debug info if @@ -257,6 +262,12 @@ void OpenMainFile (const char* Name) /* Allocate a new AFile structure for the file */ MainFile = NewAFile (IF, F); + /* Use this file with PP */ + SetPPIfStack (&MainFile->IfStack); + + /* Begin PP for this file */ + PreprocessBegin (); + /* Allocate the input line buffer */ Line = NewStrBuf (); @@ -274,6 +285,7 @@ void OpenIncludeFile (const char* Name, InputType IT) char* N; FILE* F; IFile* IF; + AFile* AF; /* Check for the maximum include nesting */ if (CollCount (&AFiles) > MAX_INC_NESTING) { @@ -311,12 +323,18 @@ void OpenIncludeFile (const char* Name, InputType IT) Print (stdout, 1, "Opened include file '%s'\n", IF->Name); /* Allocate a new AFile structure */ - (void) NewAFile (IF, F); + AF = NewAFile (IF, F); + + /* Use this file with PP */ + SetPPIfStack (&AF->IfStack); + + /* Begin PP for this file */ + PreprocessBegin (); } -static void CloseIncludeFile (void) +void CloseIncludeFile (void) /* Close an include file and switch to the higher level file. Set Input to ** NULL if this was the main file. */ @@ -329,14 +347,18 @@ static void CloseIncludeFile (void) /* Must have an input file when called */ PRECONDITION (AFileCount > 0); + /* End preprocessor in this file */ + PreprocessEnd (); + /* Get the current active input file */ - Input = (AFile*) CollLast (&AFiles); + Input = CollLast (&AFiles); /* Close the current input file (we're just reading so no error check) */ fclose (Input->F); /* Delete the last active file from the active file collection */ - CollDelete (&AFiles, AFileCount-1); + --AFileCount; + CollDelete (&AFiles, AFileCount); /* If we had added an extra search path for this AFile, remove it */ if (Input->SearchPath) { @@ -345,6 +367,12 @@ static void CloseIncludeFile (void) /* Delete the active file structure */ FreeAFile (Input); + + /* Use previous file with PP if it is not the main file */ + if (AFileCount > 0) { + Input = CollLast (&AFiles); + SetPPIfStack (&Input->IfStack); + } } @@ -436,47 +464,49 @@ StrBuf* InitLine (StrBuf* Buf) int NextLine (void) -/* Get a line from the current input. Returns 0 on end of file. */ +/* Get a line from the current input. Returns 0 on end of file with no new +** input bytes. +*/ { + int C; AFile* Input; /* Clear the current line */ ClearLine (); + SB_Clear (Line); - /* If there is no file open, bail out, otherwise get the current input file */ - if (CollCount (&AFiles) == 0) { + /* Must have an input file when called */ + if (CollCount(&AFiles) == 0) { return 0; } + + /* Get the current input file */ Input = CollLast (&AFiles); /* Read characters until we have one complete line */ while (1) { /* Read the next character */ - int C = fgetc (Input->F); + C = fgetc (Input->F); /* Check for EOF */ if (C == EOF) { - /* Accept files without a newline at the end */ - if (SB_NotEmpty (Line)) { + if (!Input->MissingNL || SB_NotEmpty (Line)) { + + /* Accept files without a newline at the end */ ++Input->Line; - break; - } - /* Leave the current file */ - CloseIncludeFile (); + /* Assume no new line */ + Input->MissingNL = 1; - /* If there is no file open, bail out, otherwise get the - ** previous input file and start over. - */ - if (CollCount (&AFiles) == 0) { - return 0; } - Input = CollLast (&AFiles); - continue; + break; } + /* Assume no new line */ + Input->MissingNL = 1; + /* Check for end of line */ if (C == '\n') { @@ -497,6 +527,7 @@ int NextLine (void) if (SB_LookAtLast (Line) == '\\') { Line->Buf[Line->Len-1] = '\n'; } else { + Input->MissingNL = 0; break; } @@ -517,6 +548,38 @@ int NextLine (void) /* Create line information for this line */ UpdateLineInfo (Input->Input, Input->Line, Line); + /* Done */ + return C != EOF || SB_NotEmpty (Line); +} + + + +int PreprocessNextLine (void) +/* Get a line from opened input files and do preprocess. Returns 0 on end of +** main file. +*/ +{ + while (NextLine() == 0) { + + /* If there is no input file open, bail out. Otherwise get the previous + ** input file and start over. + */ + if (CollCount (&AFiles) == 0) { + return 0; + } + + /* Leave the current file */ + CloseIncludeFile (); + } + + /* Do preprocess anyways */ + Preprocess (); + + /* Write it to the output file if in preprocess-only mode */ + if (PreprocessOnly) { + WriteOutput ("%.*s\n", (int) SB_GetLen (Line), SB_GetConstBuf (Line)); + } + /* Done */ return 1; } @@ -539,14 +602,8 @@ const char* GetCurrentFile (void) const AFile* AF = (const AFile*) CollAt (&AFiles, AFileCount-1); return AF->Input->Name; } else { - /* No open file. Use the main file if we have one. */ - unsigned IFileCount = CollCount (&IFiles); - if (IFileCount > 0) { - const IFile* IF = (const IFile*) CollAt (&IFiles, 0); - return IF->Name; - } else { - return "(outside file scope)"; - } + /* No open file */ + return "(outside file scope)"; } } diff --git a/src/cc65/input.h b/src/cc65/input.h index a643800ba..779217b9b 100644 --- a/src/cc65/input.h +++ b/src/cc65/input.h @@ -46,7 +46,7 @@ /*****************************************************************************/ -/* data */ +/* Data */ /*****************************************************************************/ @@ -84,6 +84,11 @@ void OpenMainFile (const char* Name); void OpenIncludeFile (const char* Name, InputType IT); /* Open an include file and insert it into the tables. */ +void CloseIncludeFile (void); +/* Close an include file and switch to the higher level file. Set Input to +** NULL if this was the main file. +*/ + void NextChar (void); /* Read the next character from the input stream and make CurC and NextC ** valid. If end of line is reached, both are set to NUL, no more lines @@ -99,7 +104,14 @@ StrBuf* InitLine (StrBuf* Buf); */ int NextLine (void); -/* Get a line from the current input. Returns 0 on end of file. */ +/* Get a line from the current input. Returns 0 on end of file with no new +** input bytes. +*/ + +int PreprocessNextLine (void); +/* Get a line from opened input files and do preprocess. Returns 0 on end of +** main file. +*/ const char* GetInputFile (const struct IFile* IF); /* Return a filename from an IFile struct */ diff --git a/src/cc65/macrotab.c b/src/cc65/macrotab.c index c04024dc8..37b52351f 100644 --- a/src/cc65/macrotab.c +++ b/src/cc65/macrotab.c @@ -56,6 +56,9 @@ #define MACRO_TAB_SIZE 211 static Macro* MacroTab[MACRO_TAB_SIZE]; +/* The undefined macros list head */ +static Macro* UndefinedMacrosListHead; + /*****************************************************************************/ @@ -108,6 +111,29 @@ void FreeMacro (Macro* M) +Macro* CloneMacro (const Macro* M) +/* Clone a macro definition. The function is not insert the macro into the +** macro table, thus the cloned instance cannot be freed with UndefineMacro. +** Use FreeMacro for that. +*/ +{ + 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)); + } + New->ArgCount = M->ArgCount; + New->Variadic = M->Variadic; + SB_Copy (&New->Replacement, &M->Replacement); + + return New; +} + + + void DefineNumericMacro (const char* Name, long Val) /* Define a macro for a numeric constant */ { @@ -150,10 +176,11 @@ void InsertMacro (Macro* M) -int UndefineMacro (const char* Name) -/* Search for the macro with the given name and remove it from the macro -** table if it exists. Return 1 if a macro was found and deleted, return -** 0 otherwise. +Macro* UndefineMacro (const char* Name) +/* Search for the macro with the given name, if it exists, remove it from +** the defined macro table and insert it to a list for pending deletion. +** Return the macro if it was found and removed, return 0 otherwise. +** To safely free the removed macro, use FreeUndefinedMacros(). */ { /* Get the hash value of the macro name */ @@ -173,11 +200,12 @@ int UndefineMacro (const char* Name) L->Next = M->Next; } - /* Delete the macro */ - FreeMacro (M); + /* Add this macro to pending deletion list */ + M->Next = UndefinedMacrosListHead; + UndefinedMacrosListHead = M; /* Done */ - return 1; + return M; } /* Next macro */ @@ -191,6 +219,23 @@ int UndefineMacro (const char* Name) +void FreeUndefinedMacros (void) +/* Free all undefined macros */ +{ + Macro* Next; + + while (UndefinedMacrosListHead != 0) { + Next = UndefinedMacrosListHead->Next; + + /* Delete the macro */ + FreeMacro (UndefinedMacrosListHead); + + UndefinedMacrosListHead = Next; + } +} + + + Macro* FindMacro (const char* Name) /* Find a macro with the given name. Return the macro definition or NULL */ { diff --git a/src/cc65/macrotab.h b/src/cc65/macrotab.h index c3ff20ceb..6a09d7281 100644 --- a/src/cc65/macrotab.h +++ b/src/cc65/macrotab.h @@ -82,6 +82,12 @@ void FreeMacro (Macro* M); ** table, use UndefineMacro for that. */ +Macro* CloneMacro (const Macro* M); +/* Clone a macro definition. The function is not insert the macro into the +** macro table, thus the cloned instance cannot be freed with UndefineMacro. +** Use FreeMacro for that. +*/ + void DefineNumericMacro (const char* Name, long Val); /* Define a macro for a numeric constant */ @@ -91,12 +97,16 @@ void DefineTextMacro (const char* Name, const char* Val); void InsertMacro (Macro* M); /* Insert the given macro into the macro table. */ -int UndefineMacro (const char* Name); -/* Search for the macro with the given name and remove it from the macro -** table if it exists. Return 1 if a macro was found and deleted, return -** 0 otherwise. +Macro* UndefineMacro (const char* Name); +/* Search for the macro with the given name, if it exists, remove it from +** the defined macro table and insert it to a list for pending deletion. +** Return the macro if it was found and removed, return 0 otherwise. +** To safely free the removed macro, use FreeUndefinedMacros(). */ +void FreeUndefinedMacros (void); +/* Free all undefined macros */ + Macro* FindMacro (const char* Name); /* Find a macro with the given name. Return the macro definition or NULL */ diff --git a/src/cc65/ppexpr.c b/src/cc65/ppexpr.c index da25e12f1..af2c1de3b 100644 --- a/src/cc65/ppexpr.c +++ b/src/cc65/ppexpr.c @@ -90,12 +90,14 @@ static void PPExprInit (PPExpr* Expr) static void PPErrorSkipLine (void) -/* Skip the remain tokens till the end of the line and set the expression -** parser error flag. +/* Set the expression parser error flag, skip the remain tokens till the end +** of the line, clear the current and the next tokens. */ { - SkipTokens (0, 0); PPEvaluationFailed = 1; + SkipTokens (0, 0); + CurTok.Tok = TOK_CEOF; + NextTok.Tok = TOK_CEOF; } @@ -148,6 +150,10 @@ static void PPhiePrimary (PPExpr* Expr) Expr->IVal = 0; break; + case TOK_CEOF: + /* Error recovery */ + break; + default: /* Illegal expression in PP mode */ PPError ("Preprocessor expression expected"); @@ -252,6 +258,10 @@ void PPhie10 (PPExpr* Expr) Expr->IVal = !Expr->IVal; break; + case TOK_CEOF: + /* Error recovery */ + break; + case TOK_STAR: case TOK_AND: case TOK_SIZEOF: @@ -286,7 +296,7 @@ static void PPhie_internal (const token_t* Ops, /* List of generators */ /* Get the right hand side */ hienext (&Rhs); - if (PPEvaluationEnabled) { + if (PPEvaluationEnabled && !PPEvaluationFailed) { /* If either side is unsigned, the result is unsigned */ Expr->Flags |= Rhs.Flags & PPEXPR_UNSIGNED; @@ -407,7 +417,7 @@ static void PPhie_compare (const token_t* Ops, /* List of generators */ /* Get the right hand side */ hienext (&Rhs); - if (PPEvaluationEnabled) { + if (PPEvaluationEnabled && !PPEvaluationFailed) { /* If either side is unsigned, the comparison is unsigned */ Expr->Flags |= Rhs.Flags & PPEXPR_UNSIGNED; @@ -501,7 +511,7 @@ static void PPhie7 (PPExpr* Expr) PPhie8 (&Rhs); /* Evaluate */ - if (PPEvaluationEnabled) { + if (PPEvaluationEnabled && !PPEvaluationFailed) { /* To shift by a negative value is equivalent to shift to the ** opposite direction. */ @@ -761,46 +771,57 @@ static void PPhie1 (PPExpr* Expr) case TOK_ASSIGN: PPError ("Token \"=\" is not valid in preprocessor expressions"); + PPErrorSkipLine (); break; case TOK_PLUS_ASSIGN: PPError ("Token \"+=\" is not valid in preprocessor expressions"); + PPErrorSkipLine (); break; case TOK_MINUS_ASSIGN: PPError ("Token \"-=\" is not valid in preprocessor expressions"); + PPErrorSkipLine (); break; case TOK_MUL_ASSIGN: PPError ("Token \"*=\" is not valid in preprocessor expressions"); + PPErrorSkipLine (); break; case TOK_DIV_ASSIGN: PPError ("Token \"/=\" is not valid in preprocessor expressions"); + PPErrorSkipLine (); break; case TOK_MOD_ASSIGN: PPError ("Token \"%%=\" is not valid in preprocessor expressions"); + PPErrorSkipLine (); break; case TOK_SHL_ASSIGN: PPError ("Token \"<<=\" is not valid in preprocessor expressions"); + PPErrorSkipLine (); break; case TOK_SHR_ASSIGN: PPError ("Token \">>=\" is not valid in preprocessor expressions"); + PPErrorSkipLine (); break; case TOK_AND_ASSIGN: PPError ("Token \"&=\" is not valid in preprocessor expressions"); + PPErrorSkipLine (); break; case TOK_OR_ASSIGN: PPError ("Token \"|=\" is not valid in preprocessor expressions"); + PPErrorSkipLine (); break; case TOK_XOR_ASSIGN: PPError ("Token \"^=\" is not valid in preprocessor expressions"); + PPErrorSkipLine (); break; default: @@ -827,12 +848,13 @@ static void PPhie0 (PPExpr* Expr) -void ParsePPExpr (PPExpr* Expr) +void ParsePPExprInLine (PPExpr* Expr) /* Parse a line for PP expression */ { /* Initialize the parser status */ PPEvaluationFailed = 0; PPEvaluationEnabled = 1; + NextLineDisabled = 1; /* Parse */ PPExprInit (Expr); @@ -841,5 +863,9 @@ void ParsePPExpr (PPExpr* Expr) /* If the evaluation fails, the result is always zero */ if (PPEvaluationFailed) { Expr->IVal = 0; + PPEvaluationFailed = 0; } + + /* Restore parser status */ + NextLineDisabled = 0; } diff --git a/src/cc65/ppexpr.h b/src/cc65/ppexpr.h index 683c6c49d..5e9968a2b 100644 --- a/src/cc65/ppexpr.h +++ b/src/cc65/ppexpr.h @@ -66,7 +66,7 @@ struct PPExpr -void ParsePPExpr (PPExpr* Expr); +void ParsePPExprInLine (PPExpr* Expr); /* Parse a line for PP expression */ diff --git a/src/cc65/preproc.c b/src/cc65/preproc.c index 96bb0bf1c..c545bf889 100644 --- a/src/cc65/preproc.c +++ b/src/cc65/preproc.c @@ -67,27 +67,29 @@ -/* Set when the preprocessor calls expr() recursively */ -static unsigned char Preprocessing = 0; - /* Management data for #if */ -#define MAX_IFS 256 #define IFCOND_NONE 0x00U #define IFCOND_SKIP 0x01U #define IFCOND_ELSE 0x02U #define IFCOND_NEEDTERM 0x04U -static unsigned char IfStack[MAX_IFS]; -static int IfIndex = -1; -/* Buffer for macro expansion */ -static StrBuf* MLine; +/* Current PP if stack */ +static PPIfStack* PPStack; + +/* Intermediate input buffers */ +static StrBuf* PLine; /* Buffer for macro expansion */ +static StrBuf* MLine; /* Buffer for macro expansion in #pragma */ +static StrBuf* OLine; /* Buffer for #pragma output */ + +/* Newlines to be added to preprocessed text */ +static int PendingNewLines; +static int FileChanged; /* Structure used when expanding macros */ typedef struct MacroExp MacroExp; struct MacroExp { Collection ActualArgs; /* Actual arguments */ StrBuf Replacement; /* Replacement with arguments substituted */ - Macro* M; /* The macro we're handling */ }; @@ -103,12 +105,12 @@ static void TranslationPhase3 (StrBuf* Source, StrBuf* Target); ** non-newline whitespace sequences. */ -static unsigned Pass1 (StrBuf* Source, StrBuf* Target); -/* Preprocessor pass 1. Remove whitespace. Handle old and new style comments -** and the "defined" operator. +static int ParseDirectives (int InArgList); +/* Handle directives. Return 1 if there are directives parsed, -1 if new lines +** are read, otherwise 0. */ -static void MacroReplacement (StrBuf* Source, StrBuf* Target); +static void MacroReplacement (StrBuf* Source, StrBuf* Target, int MultiLine); /* Perform macro replacement. */ @@ -190,12 +192,11 @@ static pptoken_t FindPPToken (const char* Ident) -static MacroExp* InitMacroExp (MacroExp* E, Macro* M) +static MacroExp* InitMacroExp (MacroExp* E) /* Initialize a MacroExp structure */ { InitCollection (&E->ActualArgs); SB_Init (&E->Replacement); - E->M = M; return E; } @@ -241,11 +242,11 @@ static StrBuf* ME_GetActual (MacroExp* E, unsigned Index) -static int ME_ArgIsVariadic (const MacroExp* E) +static int ME_ArgIsVariadic (const MacroExp* E, const Macro* M) /* Return true if the next actual argument we will add is a variadic one */ { - return (E->M->Variadic && - E->M->ArgCount == (int) CollCount (&E->ActualArgs) + 1); + return (M->Variadic && + M->ArgCount == (int) CollCount (&E->ActualArgs) + 1); } @@ -256,6 +257,46 @@ static int ME_ArgIsVariadic (const MacroExp* E) +static void AddPreLine (StrBuf* Str) +/* Add newlines to the string buffer */ +{ + if (!PreprocessOnly) { + PendingNewLines = 0; + return; + } + + if (FileChanged || PendingNewLines > 4) { + /* Output #line directives as source info */ + StrBuf Comment = AUTO_STRBUF_INITIALIZER; + if (SB_NotEmpty (Str) && SB_LookAtLast (Str) != '\n') { + SB_AppendChar (Str, '\n'); + } + SB_Printf (&Comment, "#line %u \"%s\"\n", GetCurrentLine (), GetCurrentFile ()); + SB_Append (Str, &Comment); + } else { + /* Output new lines */ + while (PendingNewLines > 0) { + SB_AppendChar (Str, '\n'); + --PendingNewLines; + } + } + FileChanged = 0; + PendingNewLines = 0; +} + + + +static void AppendIndent (StrBuf* Str, int Count) +/* Add Count of spaces ' ' to the string buffer */ +{ + while (Count > 0) { + SB_AppendChar (Str, ' '); + --Count; + } +} + + + static void Stringize (StrBuf* Source, StrBuf* Target) /* Stringize the given string: Add double quotes at start and end and preceed ** each occurance of " and \ by a backslash. @@ -341,12 +382,15 @@ static void NewStyleComment (void) static int SkipWhitespace (int SkipLines) -/* Skip white space and comments in the input stream. Do also skip newlines if -** SkipLines is true. Return zero if nothing was skipped, otherwise return a -** value != zero. +/* Skip white space and comments in the input stream. If skipLines is true, +** also skip newlines and add that count to global PendingNewLines. Return 1 +** if the last skipped character was a white space other than a newline '\n', +** otherwise return -1 if there were any newline characters skipped, otherwise +** return 0 if nothing was skipped. */ { int Skipped = 0; + int NewLine = 0; while (1) { if (IsSpace (CurC)) { NextChar (); @@ -360,7 +404,9 @@ static int SkipWhitespace (int SkipLines) } else if (CurC == '\0' && SkipLines) { /* End of line, read next */ if (NextLine () != 0) { - Skipped = 1; + ++PendingNewLines; + NewLine = 1; + Skipped = 0; } else { /* End of input */ break; @@ -370,7 +416,36 @@ static int SkipWhitespace (int SkipLines) break; } } - return Skipped; + return Skipped != 0 ? Skipped : -(NewLine != 0); +} + + + +static void CopyHeaderNameToken (StrBuf* Target) +/* Copy a header name from the input to Target. */ +{ + /* Remember the quote character, copy it to the target buffer and skip it */ + char Quote = CurC == '"' ? '"' : '>'; + SB_AppendChar (Target, CurC); + NextChar (); + + /* Copy the characters inside the string */ + while (CurC != '\0' && CurC != Quote) { + /* Keep an escaped char */ + if (CurC == '\\') { + SB_AppendChar (Target, CurC); + NextChar (); + } + /* Copy the character */ + SB_AppendChar (Target, CurC); + NextChar (); + } + + /* If we had a terminating quote, copy it */ + if (CurC != '\0') { + SB_AppendChar (Target, CurC); + NextChar (); + } } @@ -399,11 +474,28 @@ static void CopyQuotedString (StrBuf* Target) if (CurC != '\0') { SB_AppendChar (Target, CurC); NextChar (); + } else { + PPWarning ("Missing terminating %c character", Quote); } } +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. +*/ +{ + SkipWhitespace (0); + if (SB_GetIndex (Line) != SB_GetLen (Line)) { + PPWarning ("Extra tokens at end of #%s directive", Name); + return 1; + } + return 0; +} + + + /*****************************************************************************/ /* Macro stuff */ /*****************************************************************************/ @@ -416,7 +508,11 @@ static int MacName (char* Ident) */ { if (IsSym (Ident) == 0) { - PPError ("Identifier expected"); + if (CurC != '\0') { + PPError ("Macro name must be an identifier"); + } else { + PPError ("Missing macro name"); + } ClearLine (); return 0; } else { @@ -426,15 +522,37 @@ static int MacName (char* Ident) -static void ReadMacroArgs (MacroExp* E) -/* Identify the arguments to a macro call */ +static void ReadMacroArgs (MacroExp* E, const Macro* M, int MultiLine) +/* Identify the arguments to a macro call as-is */ { + int MissingParen = 0; unsigned Parens; /* Number of open parenthesis */ - StrBuf Arg = STATIC_STRBUF_INITIALIZER; + StrBuf Arg = AUTO_STRBUF_INITIALIZER; + + /* 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 == '#') { + int Newlines = 0; + + while (CurC == '#') { + Newlines += PendingNewLines - OldPendingNewLines; + PendingNewLines = OldPendingNewLines; + OldPendingNewLines = 0; + Skipped = ParseDirectives (1) || Skipped; + Skipped = SkipWhitespace (MultiLine) || Skipped; + } + PendingNewLines += Newlines; + } + if (Skipped && SB_NotEmpty (&Arg)) { + SB_AppendChar (&Arg, ' '); + } if (CurC == '(') { /* Nested parenthesis */ @@ -456,7 +574,7 @@ static void ReadMacroArgs (MacroExp* E) } SB_AppendChar (&Arg, CurC); NextChar (); - } else if (CurC == ',' && ME_ArgIsVariadic (E)) { + } 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. */ @@ -471,7 +589,7 @@ static void ReadMacroArgs (MacroExp* E) /* If this is not the single empty argument for a macro with ** an empty argument list, remember it. */ - if (CurC != ')' || SB_NotEmpty (&Arg) || E->M->ArgCount > 0) { + if (CurC != ')' || SB_NotEmpty (&Arg) || M->ArgCount > 0) { ME_AppendActual (E, &Arg); } @@ -485,15 +603,10 @@ static void ReadMacroArgs (MacroExp* E) NextChar (); SB_Clear (&Arg); } - } else if (SkipWhitespace (1)) { - /* Squeeze runs of blanks within an arg */ - if (SB_NotEmpty (&Arg)) { - SB_AppendChar (&Arg, ' '); - } } else if (CurC == '\0') { /* End of input inside macro argument list */ - PPError ("Unterminated argument list invoking macro '%s'", E->M->Name); - + PPError ("Unterminated argument list invoking macro '%s'", M->Name); + MissingParen = 1; ClearLine (); break; } else { @@ -503,13 +616,28 @@ static void ReadMacroArgs (MacroExp* E) } } + /* Compare formal and actual argument count */ + if (CollCount (&E->ActualArgs) != (unsigned) M->ArgCount) { + + if (!MissingParen) { + /* 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); + } + } + /* Deallocate string buf resources */ SB_Done (&Arg); } -static void MacroArgSubst (MacroExp* E) +static void MacroArgSubst (MacroExp* E, Macro* M) /* Argument substitution according to ISO/IEC 9899:1999 (E), 6.10.3.1ff */ { ident Ident; @@ -520,9 +648,9 @@ static void MacroArgSubst (MacroExp* E) /* Remember the current input and switch to the macro replacement. */ - int OldIndex = SB_GetIndex (&E->M->Replacement); - SB_Reset (&E->M->Replacement); - OldSource = InitLine (&E->M->Replacement); + int OldIndex = SB_GetIndex (&M->Replacement); + SB_Reset (&M->Replacement); + OldSource = InitLine (&M->Replacement); /* Argument handling loop */ while (CurC != '\0') { @@ -531,7 +659,7 @@ static void MacroArgSubst (MacroExp* E) if (IsSym (Ident)) { /* Check if it's a macro argument */ - if ((ArgIdx = FindMacroArg (E->M, Ident)) >= 0) { + if ((ArgIdx = FindMacroArg (M, Ident)) >= 0) { /* A macro argument. Get the corresponding actual argument. */ Arg = ME_GetActual (E, ArgIdx); @@ -553,7 +681,7 @@ static void MacroArgSubst (MacroExp* E) ** of the actual. */ SB_Reset (Arg); - MacroReplacement (Arg, &E->Replacement); + MacroReplacement (Arg, &E->Replacement, 0); /* If we skipped whitespace before, re-add it now */ if (HaveSpace) { @@ -590,7 +718,7 @@ static void MacroArgSubst (MacroExp* E) if (IsSym (Ident)) { /* Check if it's a macro argument */ - if ((ArgIdx = FindMacroArg (E->M, Ident)) >= 0) { + if ((ArgIdx = FindMacroArg (M, Ident)) >= 0) { /* Get the corresponding actual argument and add it. */ SB_Append (&E->Replacement, ME_GetActual (E, ArgIdx)); @@ -603,7 +731,7 @@ static void MacroArgSubst (MacroExp* E) } } - } else if (CurC == '#' && E->M->ArgCount >= 0) { + } 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 @@ -611,7 +739,7 @@ static void MacroArgSubst (MacroExp* E) */ NextChar (); SkipWhitespace (0); - if (!IsSym (Ident) || (ArgIdx = FindMacroArg (E->M, Ident)) < 0) { + if (!IsSym (Ident) || (ArgIdx = FindMacroArg (M, Ident)) < 0) { PPError ("'#' is not followed by a macro parameter"); } else { /* Make a valid string from Replacement */ @@ -637,100 +765,46 @@ static void MacroArgSubst (MacroExp* E) /* Switch back the input */ InitLine (OldSource); - SB_SetIndex (&E->M->Replacement, OldIndex); + SB_SetIndex (&M->Replacement, OldIndex); } -static void MacroCall (StrBuf* Target, Macro* M) -/* Process a function like macro */ -{ - MacroExp E; - - /* Eat the left paren */ - NextChar (); - - /* Initialize our MacroExp structure */ - InitMacroExp (&E, M); - - /* Read the actual macro arguments */ - ReadMacroArgs (&E); - - /* Compare formal and actual argument count */ - if (CollCount (&E.ActualArgs) != (unsigned) M->ArgCount) { - - StrBuf Arg = STATIC_STRBUF_INITIALIZER; - - /* Argument count mismatch */ - PPError ("Macro argument count mismatch"); - - /* Be sure to make enough empty arguments available */ - while (CollCount (&E.ActualArgs) < (unsigned) M->ArgCount) { - ME_AppendActual (&E, &Arg); - } - } - - /* Replace macro arguments handling the # and ## operators */ - MacroArgSubst (&E); - - /* Do macro replacement on the macro that already has the parameters - ** substituted. - */ - M->Expanding = 1; - MacroReplacement (&E.Replacement, Target); - M->Expanding = 0; - - /* Free memory allocated for the macro expansion structure */ - DoneMacroExp (&E); -} - - - -static void ExpandMacro (StrBuf* Target, Macro* M) +static void ExpandMacro (StrBuf* Target, Macro* M, int MultiLine) /* Expand a macro into Target */ { + MacroExp E; + #if 0 static unsigned V = 0; printf ("Expanding %s(%u)\n", M->Name, ++V); #endif + /* Initialize our MacroExp structure */ + InitMacroExp (&E); + /* Check if this is a function like macro */ if (M->ArgCount >= 0) { - int Whitespace = SkipWhitespace (1); - if (CurC != '(') { - /* Function like macro but no parameter list */ - SB_AppendStr (Target, M->Name); - if (Whitespace) { - SB_AppendChar (Target, ' '); - } - } else { - /* Function like macro */ - MacroCall (Target, M); - } - - } else { - - MacroExp E; - InitMacroExp (&E, M); - - /* Handle # and ## operators for object like macros */ - MacroArgSubst (&E); - - /* Do macro replacement on the macro that already has the parameters - ** substituted. - */ - M->Expanding = 1; - MacroReplacement (&E.Replacement, Target); - M->Expanding = 0; - - /* Free memory allocated for the macro expansion structure */ - DoneMacroExp (&E); + /* Read the actual macro arguments (with the enclosing parentheses) */ + ReadMacroArgs (&E, M, MultiLine); } + + /* Replace macro arguments handling the # and ## operators */ + MacroArgSubst (&E, M); + + /* Forbide repeated expansion of the same macro in use */ + M->Expanding = 1; + MacroReplacement (&E.Replacement, Target, 0); + M->Expanding = 0; + #if 0 - printf ("Done with %s(%u)\n", M->Name, V--); + printf ("Done with %s(%u)\n", E.M->Name, V--); #endif + + /* Free memory allocated for the macro expansion structure */ + DoneMacroExp (&E); } @@ -912,20 +986,25 @@ static void TranslationPhase3 (StrBuf* Source, StrBuf* Target) -static unsigned Pass1 (StrBuf* Source, StrBuf* Target) -/* Preprocessor pass 1. Remove whitespace, old and new style comments. Handle -** the "defined" operator. +static void ProcessSingleLine (StrBuf* Source, + StrBuf* Target, + int HandleDefined, + int HandleHeader) +/* Process a single line. Remove whitespace, old and new style comments. +** Recognize and handle the "defined" operator if HandleDefined is true. +** Recognize and handle header name tokens if HandleHeader is true. */ { - unsigned IdentCount; + int OldIndex = SB_GetIndex (Source); ident Ident; int HaveParen; - /* Switch to the new input source */ + /* Operate on the new input source */ StrBuf* OldSource = InitLine (Source); + SkipWhitespace (0); + /* Loop removing ws and comments */ - IdentCount = 0; while (CurC != '\0') { if (SkipWhitespace (0)) { /* Squeeze runs of blanks */ @@ -933,7 +1012,7 @@ static unsigned Pass1 (StrBuf* Source, StrBuf* Target) SB_AppendChar (Target, ' '); } } else if (IsSym (Ident)) { - if (Preprocessing && strcmp (Ident, "defined") == 0) { + if (HandleDefined && strcmp (Ident, "defined") == 0) { /* Handle the "defined" operator */ SkipWhitespace (0); HaveParen = 0; @@ -957,9 +1036,32 @@ static unsigned Pass1 (StrBuf* Source, StrBuf* Target) SB_AppendChar (Target, '0'); } } else { - ++IdentCount; - SB_AppendStr (Target, Ident); + Macro* M = FindMacro (Ident); + if (M != 0 && !M->Expanding) { + /* Check if this is a function-like macro */ + if (M->ArgCount >= 0) { + int Whitespace = SkipWhitespace (0); + if (CurC != '(') { + /* Function-like macro without an argument list is not replaced */ + SB_AppendStr (Target, M->Name); + if (Whitespace) { + SB_AppendChar (Target, ' '); + } + } else { + /* Function-like macro */ + ExpandMacro (Target, M, 0); + } + } else { + /* Object-like macro */ + ExpandMacro (Target, M, 0); + } + } else { + /* An identifier, keep it */ + SB_AppendStr (Target, Ident); + } } + } else if (HandleHeader && (CurC == '<' || CurC == '\"')) { + CopyHeaderNameToken (Target); } else if (IsQuote (CurC)) { CopyQuotedString (Target); } else { @@ -971,13 +1073,13 @@ static unsigned Pass1 (StrBuf* Source, StrBuf* Target) /* Switch back to the old source */ InitLine (OldSource); - /* Return the number of identifiers found in the line */ - return IdentCount; + /* Restore the source input index */ + SB_SetIndex (Source, OldIndex); } -static void MacroReplacement (StrBuf* Source, StrBuf* Target) +static void MacroReplacement (StrBuf* Source, StrBuf* Target, int MultiLine) /* Perform macro replacement. */ { ident Ident; @@ -992,22 +1094,64 @@ static void MacroReplacement (StrBuf* Source, StrBuf* Target) if (IsSym (Ident)) { /* Check if it's a macro */ if ((M = FindMacro (Ident)) != 0 && !M->Expanding) { - /* It's a macro, expand it */ - ExpandMacro (Target, M); + /* Check if this is a function-like macro */ + if (M->ArgCount >= 0) { + int Whitespace = SkipWhitespace (MultiLine); + if (CurC != '(') { + /* Function-like macro without an argument list is not replaced */ + SB_AppendStr (Target, M->Name); + if (Whitespace > 0) { + SB_AppendChar (Target, ' '); + } + + /* Directives can only be found in an argument list + ** that spans multiple lines. + */ + if (MultiLine) { + if (CurC == '#') { + /* If we were going to support #pragma in + ** macro argument list, it would be output + ** to OLine. + */ + if (OLine == 0) { + OLine = Target; + ParseDirectives (0); + OLine = 0; + } else { + ParseDirectives (0); + } + } + /* 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); + } + } + } else { + /* Object-like macro */ + ExpandMacro (Target, M, 0); + } } else { /* An identifier, keep it */ SB_AppendStr (Target, Ident); } } else if (IsQuote (CurC)) { CopyQuotedString (Target); - } else if (IsSpace (CurC)) { - if (!IsSpace (SB_LookAtLast (Target))) { - SB_AppendChar (Target, CurC); - } - NextChar (); } else { - SB_AppendChar (Target, CurC); - NextChar (); + int Whitespace = SkipWhitespace (0); + if (Whitespace) { + SB_AppendChar (Target, ' '); + } else { + SB_AppendChar (Target, CurC); + NextChar (); + } } } @@ -1017,43 +1161,22 @@ static void MacroReplacement (StrBuf* Source, StrBuf* Target) -static void PreprocessLine (void) -/* Translate one line with defined macros replaced */ -{ - /* Trim whitespace and remove comments. The function returns the number of - ** identifiers found. If there were any, we will have to check for macros. - */ - SB_Clear (MLine); - if (Pass1 (Line, MLine) > 0) { - MLine = InitLine (MLine); - SB_Reset (Line); - SB_Clear (MLine); - MacroReplacement (Line, MLine); - } - - /* Read from the new line */ - SB_Reset (MLine); - MLine = InitLine (MLine); -} - - - static int PushIf (int Skip, int Invert, int Cond) /* Push a new if level onto the if stack */ { /* Check for an overflow of the if stack */ - if (IfIndex >= MAX_IFS-1) { + if (PPStack->Index >= MAX_PP_IFS-1) { PPError ("Too many nested #if clauses"); return 1; } /* Push the #if condition */ - ++IfIndex; + ++PPStack->Index; if (Skip) { - IfStack[IfIndex] = IFCOND_SKIP | IFCOND_NEEDTERM; + PPStack->Stack[PPStack->Index] = IFCOND_SKIP | IFCOND_NEEDTERM; return 1; } else { - IfStack[IfIndex] = IFCOND_NONE | IFCOND_NEEDTERM; + PPStack->Stack[PPStack->Index] = IFCOND_NONE | IFCOND_NEEDTERM; return (Invert ^ Cond); } } @@ -1085,10 +1208,11 @@ static int DoIf (int Skip) PPExpr Expr = AUTO_PPEXPR_INITIALIZER; if (!Skip) { + /* We're about to use a dedicated expression parser to evaluate the #if ** expression. Save the current tokens to come back here later. */ - Token SavedCurTok = CurTok; + Token SavedCurTok = CurTok; Token SavedNextTok = NextTok; /* Make sure the line infos for the tokens won't get removed */ @@ -1099,11 +1223,13 @@ static int DoIf (int Skip) UseLineInfo (SavedNextTok.LI); } - /* Switch into special preprocessing mode */ - Preprocessing = 1; + /* Macro-replace a single line with support for the "defined" operator */ + SB_Clear (MLine); + ProcessSingleLine (Line, MLine, 1, 0); - /* Expand macros in this line */ - PreprocessLine (); + /* Read from the processed line */ + SB_Reset (MLine); + MLine = InitLine (MLine); /* Add two semicolons as sentinels to the line, so the following ** expression evaluation will eat these two tokens but nothing from @@ -1117,10 +1243,10 @@ static int DoIf (int Skip) NextToken (); /* Call the expression parser */ - ParsePPExpr (&Expr); + ParsePPExprInLine (&Expr); - /* End preprocessing mode */ - Preprocessing = 0; + /* Restore input source */ + MLine = InitLine (MLine); /* Reset the old tokens */ CurTok = SavedCurTok; @@ -1144,6 +1270,8 @@ static int DoIfDef (int skip, int flag) SkipWhitespace (0); if (MacName (Ident)) { Value = IsMacro (Ident); + /* Check for extra tokens */ + CheckExtraTokens (flag ? "ifdef" : "ifndef"); } } @@ -1157,14 +1285,15 @@ static void DoInclude (void) { char RTerm; InputType IT; - StrBuf Filename = STATIC_STRBUF_INITIALIZER; + StrBuf Filename = AUTO_STRBUF_INITIALIZER; + /* Macro-replace a single line with special support for */ + SB_Clear (MLine); + ProcessSingleLine (Line, MLine, 0, 1); - /* Preprocess the remainder of the line */ - PreprocessLine (); - - /* Skip blanks */ - SkipWhitespace (0); + /* Read from the processed line */ + SB_Reset (MLine); + MLine = InitLine (MLine); /* Get the next char and check for a valid file name terminator. Setup ** the include directory spec (SYS/USR) by looking at the terminator. @@ -1196,6 +1325,10 @@ static void DoInclude (void) /* Check if we got a terminator */ if (CurC == RTerm) { + /* Skip the terminator */ + NextChar (); + /* Check for extra tokens following the filename */ + CheckExtraTokens ("include"); /* Open the include file */ OpenIncludeFile (SB_GetConstBuf (&Filename), IT); } else if (CurC == '\0') { @@ -1207,6 +1340,9 @@ Done: /* Free the allocated filename data */ SB_Done (&Filename); + /* Restore input source */ + MLine = InitLine (MLine); + /* Clear the remaining line so the next input will come from the new ** file (if open) */ @@ -1220,19 +1356,25 @@ static void DoPragma (void) ** the _Pragma() compiler operator. */ { - /* Copy the remainder of the line into MLine removing comments and ws */ + StrBuf* PragmaLine = OLine; + + PRECONDITION (PragmaLine != 0); + + /* Add the source info to preprocessor output if needed */ + AddPreLine (PragmaLine); + + /* Macro-replace a single line */ SB_Clear (MLine); - Pass1 (Line, MLine); + ProcessSingleLine (Line, MLine, 0, 0); - /* Convert the directive into the operator */ - SB_CopyStr (Line, "_Pragma ("); + /* Convert #pragma to _Pragma () */ + SB_AppendStr (PragmaLine, "_Pragma ("); SB_Reset (MLine); - Stringize (MLine, Line); - SB_AppendChar (Line, ')'); + Stringize (MLine, PragmaLine); + SB_AppendChar (PragmaLine, ')'); - /* Initialize reading from line */ - SB_Reset (Line); - InitLine (Line); + /* End this line */ + SB_SetIndex (PragmaLine, SB_GetLen (PragmaLine)); } @@ -1246,6 +1388,8 @@ static void DoUndef (void) if (MacName (Ident)) { UndefineMacro (Ident); } + /* Check for extra tokens */ + CheckExtraTokens ("undef"); } @@ -1269,23 +1413,17 @@ static void DoWarning (void) -void Preprocess (void) -/* Preprocess a line */ +static int ParseDirectives (int InArgList) +/* Handle directives. Return 1 if any whitespace or newlines are parsed. */ { - int Skip; + int PPSkip = 0; ident Directive; - /* Create the output buffer if we don't already have one */ - if (MLine == 0) { - MLine = NewStrBuf (); - } - - /* Skip white space at the beginning of the line */ - SkipWhitespace (0); + /* Skip white space at the beginning of the first line */ + int Whitespace = SkipWhitespace (0); /* Check for stuff to skip */ - Skip = 0; - while (CurC == '\0' || CurC == '#' || Skip) { + while (CurC == '\0' || CurC == '#' || PPSkip) { /* Check for preprocessor lines lines */ if (CurC == '#') { @@ -1296,7 +1434,7 @@ void Preprocess (void) continue; } if (!IsSym (Directive)) { - if (!Skip) { + if (!PPSkip) { PPError ("Preprocessor directive expected"); } ClearLine (); @@ -1304,24 +1442,23 @@ void Preprocess (void) switch (FindPPToken (Directive)) { case PP_DEFINE: - if (!Skip) { + if (!PPSkip) { DefineMacro (); } break; case PP_ELIF: - if (IfIndex >= 0) { - if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) { - + if (PPStack->Index >= 0) { + if ((PPStack->Stack[PPStack->Index] & IFCOND_ELSE) == 0) { /* Handle as #else/#if combination */ - if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) { - Skip = !Skip; + if ((PPStack->Stack[PPStack->Index] & IFCOND_SKIP) == 0) { + PPSkip = !PPSkip; } - IfStack[IfIndex] |= IFCOND_ELSE; - Skip = DoIf (Skip); + PPStack->Stack[PPStack->Index] |= IFCOND_ELSE; + PPSkip = DoIf (PPSkip); /* #elif doesn't need a terminator */ - IfStack[IfIndex] &= ~IFCOND_NEEDTERM; + PPStack->Stack[PPStack->Index] &= ~IFCOND_NEEDTERM; } else { PPError ("Duplicate #else/#elif"); } @@ -1331,12 +1468,15 @@ void Preprocess (void) break; case PP_ELSE: - if (IfIndex >= 0) { - if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) { - if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) { - Skip = !Skip; + if (PPStack->Index >= 0) { + if ((PPStack->Stack[PPStack->Index] & IFCOND_ELSE) == 0) { + if ((PPStack->Stack[PPStack->Index] & IFCOND_SKIP) == 0) { + PPSkip = !PPSkip; } - IfStack[IfIndex] |= IFCOND_ELSE; + PPStack->Stack[PPStack->Index] |= IFCOND_ELSE; + + /* Check for extra tokens */ + CheckExtraTokens ("else"); } else { PPError ("Duplicate #else"); } @@ -1346,64 +1486,71 @@ void Preprocess (void) break; case PP_ENDIF: - if (IfIndex >= 0) { + if (PPStack->Index >= 0) { /* Remove any clauses on top of stack that do not ** need a terminating #endif. */ - while (IfIndex >= 0 && (IfStack[IfIndex] & IFCOND_NEEDTERM) == 0) { - --IfIndex; + while (PPStack->Index >= 0 && + (PPStack->Stack[PPStack->Index] & IFCOND_NEEDTERM) == 0) { + --PPStack->Index; } /* Stack may not be empty here or something is wrong */ - CHECK (IfIndex >= 0); + CHECK (PPStack->Index >= 0); /* Remove the clause that needs a terminator */ - Skip = (IfStack[IfIndex--] & IFCOND_SKIP) != 0; + PPSkip = (PPStack->Stack[PPStack->Index--] & IFCOND_SKIP) != 0; + + /* Check for extra tokens */ + CheckExtraTokens ("endif"); } else { PPError ("Unexpected '#endif'"); } break; case PP_ERROR: - if (!Skip) { + if (!PPSkip) { DoError (); } break; case PP_IF: - Skip = DoIf (Skip); + PPSkip = DoIf (PPSkip); break; case PP_IFDEF: - Skip = DoIfDef (Skip, 1); + PPSkip = DoIfDef (PPSkip, 1); break; case PP_IFNDEF: - Skip = DoIfDef (Skip, 0); + PPSkip = DoIfDef (PPSkip, 0); break; case PP_INCLUDE: - if (!Skip) { + if (!PPSkip) { DoInclude (); } break; case PP_LINE: /* Should do something in C99 at least, but we ignore it */ - if (!Skip) { + if (!PPSkip) { ClearLine (); } break; case PP_PRAGMA: - if (!Skip) { - DoPragma (); - goto Done; + if (!PPSkip) { + if (!InArgList) { + DoPragma (); + } else { + PPError ("Embedded #pragma directive within macro arguments is unsupported"); + } } break; case PP_UNDEF: - if (!Skip) { + if (!PPSkip) { DoUndef (); } break; @@ -1411,11 +1558,11 @@ void Preprocess (void) case PP_WARNING: /* #warning is a non standard extension */ if (IS_Get (&Standard) > STD_C99) { - if (!Skip) { + if (!PPSkip) { DoWarning (); } } else { - if (!Skip) { + if (!PPSkip) { PPError ("Preprocessor directive expected"); } ClearLine (); @@ -1423,7 +1570,7 @@ void Preprocess (void) break; default: - if (!Skip) { + if (!PPSkip) { PPError ("Preprocessor directive expected"); } ClearLine (); @@ -1432,19 +1579,108 @@ void Preprocess (void) } if (NextLine () == 0) { - if (IfIndex >= 0) { - PPError ("'#endif' expected"); - } - return; + break; } - SkipWhitespace (0); + ++PendingNewLines; + Whitespace = SkipWhitespace (0) || Whitespace; } - PreprocessLine (); + return Whitespace != 0; +} + + + +void Preprocess (void) +/* Preprocess lines count of which is affected by directives */ +{ + SB_Clear (PLine); + + /* Add the source info to preprocessor output if needed */ + AddPreLine (PLine); + + /* Parse any directives */ + OLine = PLine; + ParseDirectives (0); + OLine = 0; + + /* Add the source info to preprocessor output if needed */ + AddPreLine (PLine); + + /* Add leading whitespace to prettify preprocessor output */ + AppendIndent (PLine, SB_GetIndex (Line)); + + /* Expand macros if any */ + MacroReplacement (Line, PLine, 1); + + /* Add the source info to preprocessor output if needed */ + AddPreLine (PLine); + + /* Read from the new line */ + SB_Reset (PLine); + PLine = InitLine (PLine); -Done: if (Verbosity > 1 && SB_NotEmpty (Line)) { printf ("%s:%u: %.*s\n", GetCurrentFile (), GetCurrentLine (), (int) SB_GetLen (Line), SB_GetConstBuf (Line)); } + + /* Free all undefined macros */ + FreeUndefinedMacros (); +} + + + +void InitPreprocess (void) +/* Init preprocessor */ +{ + /* Create the output buffers */ + MLine = NewStrBuf (); + PLine = NewStrBuf (); +} + + + +void DonePreprocess (void) +/* Done with preprocessor */ +{ + /* Done with the output buffers */ + SB_Done (MLine); + SB_Done (PLine); +} + + + +void SetPPIfStack (PPIfStack* Stack) +/* Specify which PP #if stack to use */ +{ + PPStack = Stack; +} + + + +void PreprocessBegin (void) +/* Initialize preprocessor with current file */ +{ + /* Reset #if depth */ + PPStack->Index = -1; + + /* Remember to update source file location in preprocess-only mode */ + FileChanged = 1; +} + + + +void PreprocessEnd (void) +/* Preprocessor done with current file */ +{ + /* Check for missing #endif */ + while (PPStack->Index >= 0) { + if ((PPStack->Stack[PPStack->Index] & IFCOND_NEEDTERM) != 0) { + PPError ("#endif expected"); + } + --PPStack->Index; + } + + /* Remember to update source file location in preprocess-only mode */ + FileChanged = 1; } diff --git a/src/cc65/preproc.h b/src/cc65/preproc.h index 34f62c114..f543b05b5 100644 --- a/src/cc65/preproc.h +++ b/src/cc65/preproc.h @@ -39,7 +39,25 @@ /*****************************************************************************/ -/* code */ +/* Data */ +/*****************************************************************************/ + + + +/* Maximum #if depth per file */ +#define MAX_PP_IFS 256 + +/* Data struct used for per-file-directive handling */ +typedef struct PPIfStack PPIfStack; +struct PPIfStack { + unsigned char Stack[MAX_PP_IFS]; + int Index; +}; + + + +/*****************************************************************************/ +/* Code */ /*****************************************************************************/ @@ -47,6 +65,21 @@ void Preprocess (void); /* Preprocess a line */ +void SetPPIfStack (PPIfStack* Stack); +/* Specify which PP #if stack to use */ + +void PreprocessBegin (void); +/* Initialize preprocessor with current file */ + +void PreprocessEnd (void); +/* Preprocessor done with current file */ + +void InitPreprocess (void); +/* Init preprocessor */ + +void DonePreprocess (void); +/* Done with preprocessor */ + /* End of preproc.h */ diff --git a/src/cc65/scanner.c b/src/cc65/scanner.c index 70203d027..c7e9bb6c2 100644 --- a/src/cc65/scanner.c +++ b/src/cc65/scanner.c @@ -69,6 +69,7 @@ Token CurTok; /* The current token */ Token NextTok; /* The next token */ +int NextLineDisabled; /* Disabled to read next line */ @@ -188,10 +189,10 @@ static int SkipWhite (void) { while (1) { while (CurC == '\0') { - if (NextLine () == 0) { + /* If reading next line fails or is forbidden, bail out */ + if (NextLineDisabled || PreprocessNextLine () == 0) { return 0; } - Preprocess (); } if (IsSpace (CurC)) { NextChar (); diff --git a/src/cc65/scanner.h b/src/cc65/scanner.h index e6a362bf3..cd34cbbe8 100644 --- a/src/cc65/scanner.h +++ b/src/cc65/scanner.h @@ -210,6 +210,7 @@ struct Token { extern Token CurTok; /* The current token */ extern Token NextTok; /* The next token */ +extern int NextLineDisabled; /* Disabled to read next line */ diff --git a/test/misc/bug1357.c b/test/val/bug1357.c similarity index 100% rename from test/misc/bug1357.c rename to test/val/bug1357.c diff --git a/test/val/bug1643.c b/test/val/bug1643.c new file mode 100644 index 000000000..eba733511 --- /dev/null +++ b/test/val/bug1643.c @@ -0,0 +1,12 @@ +/* bug #1643, macro expansion in #include */ + +#define MKSTR(a) MKSTR_IMPL(a) +#define MKSTR_IMPL(a) #a +#define BUG1643_H bug1643.h + +#include MKSTR(BUG1643_H) + +int main(void) +{ + return BUG1643_RESULT; +} diff --git a/test/val/bug1643.h b/test/val/bug1643.h new file mode 100644 index 000000000..fe0423688 --- /dev/null +++ b/test/val/bug1643.h @@ -0,0 +1,13 @@ +/* bug #1643, macro expansion in #include */ + +#define STDIO_H +#include STDIO_H + +#ifdef string +#undef string +#endif + +#define string 0!%^&*/_= +#include + +#define BUG1643_RESULT 0 diff --git a/test/misc/bug760.c b/test/val/bug760.c similarity index 100% rename from test/misc/bug760.c rename to test/val/bug760.c