From: cuz Date: Thu, 16 Sep 2004 12:42:41 +0000 (+0000) Subject: Introduce a -E flag that activates just the preprocessor. X-Git-Tag: V2.12.0~652 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=06b57e6e7e9b651b9db5020b8cb53fd315f423e5;p=cc65 Introduce a -E flag that activates just the preprocessor. Use variable sized string buffers for input and macro processing instead of the fixed buffers used before. Many changes in the preprocessor to make it more standards compliant (which it is still not). git-svn-id: svn://svn.cc65.org/cc65/trunk@3182 b7a2c559-68d2-44c3-8de9-860c34a00d81 --- diff --git a/src/cc65/compile.c b/src/cc65/compile.c index 4301fb737..8a9c7c3b5 100644 --- a/src/cc65/compile.c +++ b/src/cc65/compile.c @@ -56,6 +56,7 @@ #include "litpool.h" #include "macrotab.h" #include "pragma.h" +#include "preproc.h" #include "standard.h" #include "symtab.h" @@ -178,7 +179,7 @@ static void Parse (void) /* Size is unknown and not an array */ Error ("Variable `%s' has unknown size", Decl.Ident); } - } else if (ANSI) { + } else if (IS_Get (&Standard) != STD_CC65) { /* We cannot declare variables of type void */ Error ("Illegal type for variable `%s'", Decl.Ident); } @@ -332,18 +333,34 @@ void Compile (const char* FileName) /* Open the input file */ OpenMainFile (FileName); - /* Ok, start the ball rolling... */ - Parse (); + /* Are we supposed to compile or just preprocess the input? */ + if (PreprocessOnly) { - /* Dump the literal pool. */ - DumpLiteralPool (); + while (NextLine ()) { + Preprocess (); + printf ("%.*s\n", SB_GetLen (Line), SB_GetConstBuf (Line)); + } + + if (Debug) { + PrintMacroStats (stdout); + } + + } else { - /* Write imported/exported symbols */ - EmitExternals (); + /* Ok, start the ball rolling... */ + Parse (); + + /* Dump the literal pool. */ + DumpLiteralPool (); + + /* Write imported/exported symbols */ + EmitExternals (); + + if (Debug) { + PrintLiteralPoolStats (stdout); + PrintMacroStats (stdout); + } - if (Debug) { - PrintLiteralPoolStats (stdout); - PrintMacroStats (stdout); } /* Leave the main lexical level */ diff --git a/src/cc65/error.c b/src/cc65/error.c index 175852ddb..44825287e 100644 --- a/src/cc65/error.c +++ b/src/cc65/error.c @@ -68,15 +68,15 @@ unsigned WarningCount = 0; -static void IntWarning (const char* Filename, unsigned Line, const char* Msg, va_list ap) +static void IntWarning (const char* Filename, unsigned LineNo, const char* Msg, va_list ap) /* Print warning message - internal function. */ { if (!IS_Get (&WarnDisable)) { - fprintf (stderr, "%s(%u): Warning: ", Filename, Line); + fprintf (stderr, "%s(%u): Warning: ", Filename, LineNo); vfprintf (stderr, Msg, ap); fprintf (stderr, "\n"); - Print (stderr, 1, "Line: %s\n", line); + Print (stderr, 1, "Input: %.*s\n", SB_GetLen (Line), SB_GetConstBuf (Line)); ++WarningCount; } } @@ -105,14 +105,14 @@ void PPWarning (const char* Format, ...) -static void IntError (const char* Filename, unsigned Line, const char* Msg, va_list ap) +static void IntError (const char* Filename, unsigned LineNo, const char* Msg, va_list ap) /* Print an error message - internal function*/ { - fprintf (stderr, "%s(%u): Error: ", Filename, Line); + fprintf (stderr, "%s(%u): Error: ", Filename, LineNo); vfprintf (stderr, Msg, ap); fprintf (stderr, "\n"); - Print (stderr, 1, "Line: %s\n", line); + Print (stderr, 1, "Input: %.*s\n", SB_GetLen (Line), SB_GetConstBuf (Line)); ++ErrorCount; if (ErrorCount > 10) { Fatal ("Too many errors"); @@ -165,7 +165,7 @@ void Fatal (const char* Format, ...) va_end (ap); fprintf (stderr, "\n"); - Print (stderr, 1, "Line: %s\n", line); + Print (stderr, 1, "Input: %.*s\n", SB_GetLen (Line), SB_GetConstBuf (Line)); exit (EXIT_FAILURE); } @@ -192,7 +192,7 @@ void Internal (const char* Format, ...) va_start (ap, Format); vfprintf (stderr, Format, ap); va_end (ap); - fprintf (stderr, "\nLine: %s\n", line); + fprintf (stderr, "\nInput: %.*s\n", SB_GetLen (Line), SB_GetConstBuf (Line)); /* Use abort to create a core dump */ abort (); @@ -203,12 +203,8 @@ void Internal (const char* Format, ...) void ErrorReport (void) /* Report errors (called at end of compile) */ { - if (ErrorCount == 0 && Verbosity > 0) { - printf ("No errors.\n"); - } + Print (stdout, 1, "%u errors, %u warnings\n", ErrorCount, WarningCount); } - - diff --git a/src/cc65/global.c b/src/cc65/global.c index 1d2847d41..1a9d01aab 100644 --- a/src/cc65/global.c +++ b/src/cc65/global.c @@ -46,7 +46,7 @@ unsigned char AddSource = 0; /* Add source lines as comments */ unsigned char DebugInfo = 0; /* Add debug info to the obj */ unsigned char CreateDep = 0; /* Create a dependency file */ -unsigned char ANSI = 0; /* Strict ANSI flag */ +unsigned char PreprocessOnly = 0; /* Just preprocess the input */ unsigned RegisterSpace = 6; /* Space available for register vars */ /* Stackable options */ @@ -64,4 +64,3 @@ IntStack CodeSizeFactor = INTSTACK(100);/* Size factor for generated code */ - diff --git a/src/cc65/global.h b/src/cc65/global.h index 873e63b4e..d7820c715 100644 --- a/src/cc65/global.h +++ b/src/cc65/global.h @@ -53,7 +53,7 @@ extern unsigned char AddSource; /* Add source lines as comments */ extern unsigned char DebugInfo; /* Add debug info to the obj */ extern unsigned char CreateDep; /* Create a dependency file */ -extern unsigned char ANSI; /* Strict ANSI flag */ +extern unsigned char PreprocessOnly; /* Just preprocess the input */ extern unsigned RegisterSpace; /* Space available for register vars */ /* Stackable options */ diff --git a/src/cc65/input.c b/src/cc65/input.c index 0f254f492..6a26869f2 100644 --- a/src/cc65/input.c +++ b/src/cc65/input.c @@ -6,10 +6,10 @@ /* */ /* */ /* */ -/* (C) 2000 Ullrich von Bassewitz */ -/* Wacholderweg 14 */ -/* D-70597 Stuttgart */ -/* EMail: uz@musoftware.de */ +/* (C) 2000-2004 Ullrich von Bassewitz */ +/* Römerstrasse 52 */ +/* D-70794 Filderstadt */ +/* EMail: uz@cc65.org */ /* */ /* */ /* This software is provided 'as-is', without any expressed or implied */ @@ -46,7 +46,6 @@ #include "xmalloc.h" /* cc65 */ -#include "asmcode.h" #include "codegen.h" #include "error.h" #include "incpath.h" @@ -61,10 +60,8 @@ -/* Input line stuff */ -static char LineBuf [LINESIZE]; -char* line = LineBuf; -const char* lptr = LineBuf; +/* The current input line */ +StrBuf* Line; /* Current and next input character */ char CurC = '\0'; @@ -87,6 +84,9 @@ 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; + /*****************************************************************************/ @@ -217,6 +217,9 @@ void OpenMainFile (const char* Name) /* Allocate a new AFile structure for the file */ (void) NewAFile (IF, F); + + /* Allocate the input line buffer */ + Line = NewStrBuf (); } @@ -297,48 +300,101 @@ static void CloseIncludeFile (void) +static void GetInputChar (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 + * 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'; + } 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 comes from next fragment */ + if (CollCount (&InputStack) > 0) { + NextC = ' '; + } else { + NextC = '\0'; + } + } + } +} + + + +void NextChar (void) +/* Skip the current input character and read the next one from the input + * stream. CurC and NextC are valid after the call. If end of line is + * reached, both are set to NUL, no more lines are read by this function. + */ +{ + /* Skip the last character read */ + SB_Skip (Line); + + /* Read the next one */ + GetInputChar (); +} + + + void ClearLine (void) /* Clear the current input line */ { - line[0] = '\0'; - lptr = line; + unsigned I; + + /* Remove all pushed fragments from the input stack */ + for (I = 0; I < CollCount (&InputStack); ++I) { + FreeStrBuf (CollAtUnchecked (&InputStack, I)); + } + CollDeleteAll (&InputStack); + + /* Clear the contents of Line */ + SB_Clear (Line); CurC = '\0'; NextC = '\0'; } -void InitLine (const char* Buf) -/* Initialize lptr from Buf and read CurC and NextC from the new input line */ +StrBuf* InitLine (StrBuf* Buf) +/* Initialize Line from Buf and read CurC and NextC from the new input line. + * The function returns the old input line. + */ { - lptr = Buf; - CurC = lptr[0]; - if (CurC != '\0') { - NextC = lptr[1]; - } else { - NextC = '\0'; - } + StrBuf* OldLine = Line; + Line = Buf; + CurC = SB_LookAt (Buf, SB_GetIndex (Buf)); + NextC = SB_LookAt (Buf, SB_GetIndex (Buf) + 1); + return OldLine; } -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 - * are read by this function. - */ +void PushLine (const StrBuf* Buf) +/* Push a copy of Buf onto the input stack */ { - if (lptr[0] != '\0') { - ++lptr; - CurC = lptr[0]; - if (CurC != '\0') { - NextC = lptr[1]; - } else { - NextC = '\0'; - } - } else { - CurC = NextC = '\0'; - } + CollAppend (&InputStack, Line); + Line = NewStrBuf (); + SB_Copy (Line, Buf); + + /* Make CurC and NextC valid */ + GetInputChar (); } @@ -346,74 +402,83 @@ void NextChar (void) int NextLine (void) /* Get a line from the current input. Returns 0 on end of file. */ { - AFile* Input; - unsigned Len; - unsigned Part; - unsigned Start; - int Done; + AFile* Input; - /* Setup the line */ + /* Clear the current line */ ClearLine (); /* If there is no file open, bail out, otherwise get the current input file */ if (CollCount (&AFiles) == 0) { return 0; } - Input = (AFile*) CollLast (&AFiles); + Input = CollLast (&AFiles); - /* Read lines until we get one with real contents */ - Len = 0; - Done = 0; - while (!Done && Len < LINESIZE) { + /* Read characters until we have one complete line */ + while (1) { - while (fgets (line + Len, LINESIZE - Len, Input->F) == 0) { + /* Read the next character */ + int C = fgetc (Input->F); - /* Assume EOF */ - ClearLine (); + /* Check for EOF */ + if (C == EOF) { /* Leave the current file */ CloseIncludeFile (); - /* If there is no file open, bail out, otherwise get the - * current input file - */ - if (CollCount (&AFiles) == 0) { - return 0; - } - Input = (AFile*) CollLast (&AFiles); - - } - - /* We got a new line */ - ++Input->Line; - - /* Remove the trailing cr/lf if we have one. We will ignore both, cr - * and lf on all systems since this enables us to compile DOS/Windows - * stuff also on unix systems (where fgets does not remove the cr). - */ - Part = strlen (line + Len); - Start = Len; - Len += Part; - while (Len > 0 && (line[Len-1] == '\n' || line[Len-1] == '\r')) { - --Len; - } - line [Len] = '\0'; - - /* Check if we have a line continuation character at the end. If not, - * we're done. - */ - if (Len > 0 && line[Len-1] == '\\') { - line[Len-1] = '\n'; /* Replace by newline */ - } else { - Done = 1; - } + /* Accept files without a newline at the end */ + if (SB_NotEmpty (Line)) { + break; + } + + /* 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; + } + + /* Check for end of line */ + if (C == '\n') { + + /* We got a new line */ + ++Input->Line; + + /* If the \n is preceeded by a \r, remove the \r, so we can read + * DOS/Windows files under *nix. + */ + if (SB_LookAtLast (Line) == '\r') { + SB_Drop (Line, 1); + } + + /* If we don't have a line continuation character at the end, + * we're done with this line. Otherwise replace the character + * by a newline and continue reading. + */ + if (SB_LookAtLast (Line) == '\\') { + Line->Buf[Line->Len-1] = '\n'; + } else { + break; + } + + } else if (C != '\0') { /* Ignore embedded NULs */ + + /* Just some character, add it to the line */ + SB_AppendChar (Line, C); + + } } - /* Got a line. Initialize the current and next characters. */ - InitLine (line); + /* Add a termination character to the string buffer */ + SB_Terminate (Line); + + /* Initialize the current and next characters. */ + InitLine (Line); /* Create line information for this line */ - UpdateLineInfo (Input->Input, Input->Line, line); + UpdateLineInfo (Input->Input, Input->Line, SB_GetConstBuf (Line)); /* Done */ return 1; @@ -426,17 +491,17 @@ const char* GetCurrentFile (void) { unsigned AFileCount = CollCount (&AFiles); if (AFileCount > 0) { - const AFile* AF = (const AFile*) CollAt (&AFiles, AFileCount-1); - return AF->Input->Name; + 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 { + /* 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)"; - } + } } } diff --git a/src/cc65/input.h b/src/cc65/input.h index ad291c0f8..7fc90d247 100644 --- a/src/cc65/input.h +++ b/src/cc65/input.h @@ -6,10 +6,10 @@ /* */ /* */ /* */ -/* (C) 2000 Ullrich von Bassewitz */ -/* Wacholderweg 14 */ -/* D-70597 Stuttgart */ -/* EMail: uz@musoftware.de */ +/* (C) 2000-2004 Ullrich von Bassewitz */ +/* Römerstrasse 52 */ +/* D-70794 Filderstadt */ +/* EMail: uz@cc65.org */ /* */ /* */ /* This software is provided 'as-is', without any expressed or implied */ @@ -40,6 +40,9 @@ #include +/* common */ +#include "strbuf.h" + /*****************************************************************************/ @@ -48,13 +51,8 @@ -/* Maximum length of an input line and the corresponding char array */ -#define LINEMAX 4095 -#define LINESIZE LINEMAX+1 - -/* Input line stuff */ -extern char* line; -extern const char* lptr; /* ### Remove this */ +/* The current input line */ +extern StrBuf* Line; /* Current and next input character */ extern char CurC; @@ -64,7 +62,7 @@ extern char NextC; typedef struct IFile IFile; struct IFile { unsigned Index; /* File index */ - unsigned Usage; /* Usage counter */ + unsigned Usage; /* Usage counter */ unsigned long Size; /* File size */ unsigned long MTime; /* Time of last modification */ char Name[1]; /* Name of file (dynamically allocated) */ @@ -73,7 +71,7 @@ struct IFile { /*****************************************************************************/ -/* Code */ +/* Code */ /*****************************************************************************/ @@ -84,18 +82,20 @@ void OpenMainFile (const char* Name); void OpenIncludeFile (const char* Name, unsigned DirSpec); /* Open an include file and insert it into the tables. */ -void ClearLine (void); -/* Clear the current input line */ - -void InitLine (const char* Buf); -/* Initialize lptr from Buf and read CurC and NextC from the new input line */ - 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 * are read by this function. */ +void ClearLine (void); +/* Clear the current input line */ + +StrBuf* InitLine (StrBuf* Buf); +/* Initialize Line from Buf and read CurC and NextC from the new input line. + * The function returns the old input line. + */ + int NextLine (void); /* Get a line from the current input. Returns 0 on end of file. */ diff --git a/src/cc65/macrotab.c b/src/cc65/macrotab.c index 45a290fe7..20866ef90 100644 --- a/src/cc65/macrotab.c +++ b/src/cc65/macrotab.c @@ -6,10 +6,10 @@ /* */ /* */ /* */ -/* (C) 2000 Ullrich von Bassewitz */ -/* Wacholderweg 14 */ -/* D-70597 Stuttgart */ -/* EMail: uz@musoftware.de */ +/* (C) 2000-2004 Ullrich von Bassewitz */ +/* Römerstraße 52 */ +/* D-70794 Filderstadt */ +/* EMail: uz@cc65.org */ /* */ /* */ /* This software is provided 'as-is', without any expressed or implied */ @@ -56,12 +56,6 @@ #define MACRO_TAB_SIZE 211 static Macro* MacroTab[MACRO_TAB_SIZE]; -/* A table that holds the count of macros that start with a specific character. - * It is used to determine quickly, if an identifier may be a macro or not - * without calculating the hash over the name. - */ -static unsigned short MacroFlagTab[256]; - /*****************************************************************************/ @@ -83,11 +77,11 @@ Macro* NewMacro (const char* Name) /* Initialize the data */ M->Next = 0; + M->Expanding = 0; M->ArgCount = -1; /* Flag: Not a function like macro */ M->MaxArgs = 0; - M->FormalArgs = 0; - M->ActualArgs = 0; - M->Replacement = 0; + InitCollection (&M->FormalArgs); + InitStrBuf (&M->Replacement); memcpy (M->Name, Name, Len+1); /* Return the new macro */ @@ -101,14 +95,13 @@ void FreeMacro (Macro* M) * table, use UndefineMacro for that. */ { - int I; + unsigned I; - for (I = 0; I < M->ArgCount; ++I) { - xfree (M->FormalArgs[I]); + for (I = 0; I < CollCount (&M->FormalArgs); ++I) { + xfree (CollAtUnchecked (&M->FormalArgs, I)); } - xfree (M->FormalArgs); - xfree (M->ActualArgs); - xfree (M->Replacement); + DoneCollection (&M->FormalArgs); + DoneStrBuf (&M->Replacement); xfree (M); } @@ -135,7 +128,7 @@ void DefineTextMacro (const char* Name, const char* Val) Macro* M = NewMacro (Name); /* Set the value as replacement text */ - M->Replacement = xstrdup (Val); + SB_CopyStr (&M->Replacement, Val); /* Insert the macro into the macro table */ InsertMacro (M); @@ -144,26 +137,14 @@ void DefineTextMacro (const char* Name, const char* Val) void InsertMacro (Macro* M) -/* Insert the given macro into the macro table. This call will also allocate - * the ActualArgs parameter array. - */ +/* Insert the given macro into the macro table. */ { - unsigned Hash; - - /* Allocate the ActualArgs parameter array */ - if (M->ArgCount > 0) { - M->ActualArgs = (char const**) xmalloc (M->ArgCount * sizeof(char*)); - } - /* Get the hash value of the macro name */ - Hash = HashStr (M->Name) % MACRO_TAB_SIZE; + unsigned Hash = HashStr (M->Name) % MACRO_TAB_SIZE; /* Insert the macro */ M->Next = MacroTab[Hash]; MacroTab[Hash] = M; - - /* Increment the number of macros starting with this char */ - MacroFlagTab[(unsigned)(unsigned char)M->Name[0]]++; } @@ -191,9 +172,6 @@ int UndefineMacro (const char* Name) L->Next = M->Next; } - /* Decrement the number of macros starting with this char */ - MacroFlagTab[(unsigned)(unsigned char)M->Name[0]]--; - /* Delete the macro */ FreeMacro (M); @@ -236,38 +214,21 @@ Macro* FindMacro (const char* Name) -int IsMacro (const char* Name) -/* Return true if the given name is the name of a macro, return false otherwise */ -{ - return MaybeMacro(Name[0]) && FindMacro(Name) != 0; -} - - - -int MaybeMacro (unsigned char C) -/* Return true if the given character may be the start of the name of an - * existing macro, return false if not. - */ -{ - return (MacroFlagTab[C] > 0); -} - - - -const char* FindMacroArg (Macro* M, const char* Arg) -/* Search for a formal macro argument. If found, return the actual - * (replacement) argument. If the argument was not found, return NULL. +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 I; - for (I = 0; I < M->ArgCount; ++I) { - if (strcmp (M->FormalArgs[I], Arg) == 0) { - /* Found */ - return M->ActualArgs[I]; + unsigned I; + for (I = 0; I < CollCount (&M->FormalArgs); ++I) { + if (strcmp (CollAtUnchecked (&M->FormalArgs, I), Arg) == 0) { + /* Found */ + return I; } } + /* Not found */ - return 0; + return -1; } @@ -279,29 +240,18 @@ void AddMacroArg (Macro* M, const char* Arg) * Beware: Don't use FindMacroArg here, since the actual argument array * may not be initialized. */ - int I; - for (I = 0; I < M->ArgCount; ++I) { - if (strcmp (M->FormalArgs[I], Arg) == 0) { + unsigned I; + for (I = 0; I < CollCount (&M->FormalArgs); ++I) { + if (strcmp (CollAtUnchecked (&M->FormalArgs, I), Arg) == 0) { /* Found */ Error ("Duplicate macro parameter: `%s'", Arg); break; } } - /* Check if we have enough room available, otherwise expand the array - * that holds the formal argument list. - */ - if (M->ArgCount >= (int) M->MaxArgs) { - /* We must expand the array */ - char** OldArgs = M->FormalArgs; - M->MaxArgs += 10; - M->FormalArgs = (char**) xmalloc (M->MaxArgs * sizeof(char*)); - memcpy (M->FormalArgs, OldArgs, M->ArgCount * sizeof (char*)); - xfree (OldArgs); - } - /* Add the new argument */ - M->FormalArgs[M->ArgCount++] = xstrdup (Arg); + CollAppend (&M->FormalArgs, xstrdup (Arg)); + ++M->ArgCount; } @@ -318,13 +268,14 @@ int MacroCmp (const Macro* M1, const Macro* M2) /* Compare the arguments */ for (I = 0; I < M1->ArgCount; ++I) { - if (strcmp (M1->FormalArgs[I], M2->FormalArgs[I]) != 0) { + if (strcmp (CollConstAt (&M1->FormalArgs, I), + CollConstAt (&M2->FormalArgs, I)) != 0) { return 1; } } /* Compare the replacement */ - return strcmp (M1->Replacement, M2->Replacement); + return SB_Compare (&M1->Replacement, &M2->Replacement); } diff --git a/src/cc65/macrotab.h b/src/cc65/macrotab.h index 0a699bbfc..957b8c404 100644 --- a/src/cc65/macrotab.h +++ b/src/cc65/macrotab.h @@ -6,10 +6,10 @@ /* */ /* */ /* */ -/* (C) 2000 Ullrich von Bassewitz */ -/* Wacholderweg 14 */ -/* D-70597 Stuttgart */ -/* EMail: uz@musoftware.de */ +/* (C) 2000-2004 Ullrich von Bassewitz */ +/* Römerstraße 52 */ +/* D-70794 Filderstadt */ +/* EMail: uz@cc65.org */ /* */ /* */ /* This software is provided 'as-is', without any expressed or implied */ @@ -38,27 +38,35 @@ +/* common */ +#include "coll.h" +#include "inline.h" +#include "strbuf.h" + + + /*****************************************************************************/ -/* data */ +/* data */ /*****************************************************************************/ +/* Structure describing a macro */ 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 */ - char** FormalArgs; /* Formal argument list */ - char const** ActualArgs; /* Actual argument list */ - char* Replacement; /* Replacement text */ + Collection FormalArgs; /* Formal argument list (char*) */ + StrBuf Replacement; /* Replacement text */ char Name[1]; /* Name, dynamically allocated */ }; /*****************************************************************************/ -/* code */ +/* Code */ /*****************************************************************************/ @@ -80,9 +88,7 @@ void DefineTextMacro (const char* Name, const char* Val); /* Define a macro for a textual constant */ void InsertMacro (Macro* M); -/* Insert the given macro into the macro table. This call will also allocate - * the ActualArgs parameter array. - */ +/* 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 @@ -93,17 +99,19 @@ int UndefineMacro (const char* Name); Macro* FindMacro (const char* Name); /* Find a macro with the given name. Return the macro definition or NULL */ -int IsMacro (const char* Name); +#if defined(HAVE_INLINE) +INLINE int IsMacro (const char* Name) /* Return true if the given name is the name of a macro, return false otherwise */ +{ + return FindMacro (Name) != 0; +} +#else +# define IsMacro(Name) (FindMacro (Name) != 0) +#endif -int MaybeMacro (unsigned char C); -/* Return true if the given character may be the start of the name of an - * existing macro, return false if not. - */ - -const char* FindMacroArg (Macro* M, const char* Arg); -/* Search for a formal macro argument. If found, return the actual - * (replacement) argument. If the argument was not found, return NULL. +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. */ void AddMacroArg (Macro* M, const char* Arg); diff --git a/src/cc65/main.c b/src/cc65/main.c index 1d6ee180f..63e8e8543 100644 --- a/src/cc65/main.c +++ b/src/cc65/main.c @@ -82,6 +82,7 @@ static void Usage (void) "Short options:\n" " -Cl\t\t\tMake local variables static\n" " -Dsym[=defn]\t\tDefine a symbol\n" + " -E\t\t\tStop after the preprocessing stage\n" " -I dir\t\tSet an include directory search path\n" " -O\t\t\tOptimize code\n" " -Oi\t\t\tOptimize code, inline more code\n" @@ -802,7 +803,11 @@ int main (int argc, char* argv[]) DefineSym (GetArg (&I, 2)); break; - case 'I': + case 'E': + PreprocessOnly = 1; + break; + + case 'I': OptIncludeDir (Arg, GetArg (&I, 2)); break; @@ -908,7 +913,7 @@ int main (int argc, char* argv[]) /* Create dependencies if requested */ if (CreateDep) { DoCreateDep (OutputFile); - Print (stdout, 1, "Creating dependeny file"); + Print (stdout, 1, "Creating dependeny file\n"); } } diff --git a/src/cc65/preproc.c b/src/cc65/preproc.c index 034460598..5f8aaf027 100644 --- a/src/cc65/preproc.c +++ b/src/cc65/preproc.c @@ -1,6 +1,6 @@ /* * C pre-processor functions. - * Portions of this code are copyright (C) 1989 John R. Dunning. + * Small portions of this code are copyright (C) 1989 John R. Dunning. * See copyleft.jrd for license information. */ @@ -32,17 +32,6 @@ -/*****************************************************************************/ -/* Forwards */ -/*****************************************************************************/ - - - -static int Pass1 (const char* From, char* To); -/* Preprocessor pass 1. Remove whitespace and comments. */ - - - /*****************************************************************************/ /* Data */ /*****************************************************************************/ @@ -62,9 +51,31 @@ static unsigned char IfStack[MAX_IFS]; static int IfIndex = -1; /* Buffer for macro expansion */ -static char mlinebuf [LINESIZE]; -static char* mline = mlinebuf; -static char* mptr; +static StrBuf* MLine; + +/* Structure used when expanding macros */ +typedef struct MacroExp MacroExp; +struct MacroExp { + Collection ActualArgs; /* Actual arguments */ + StrBuf Replacement; /* Replacement with arguments substituted */ + Macro* M; /* The macro we're handling */ +}; + + + +/*****************************************************************************/ +/* Forwards */ +/*****************************************************************************/ + + + +static unsigned Pass1 (StrBuf* Source, StrBuf* Target); +/* Preprocessor pass 1. Remove whitespace. Handle old and new style comments + * and the "defined" operator. + */ + +static void MacroReplacement (StrBuf* Source, StrBuf* Target); +/* Perform macro replacement. */ @@ -76,6 +87,7 @@ static char* mptr; /* Types of preprocessor tokens */ typedef enum { + PP_ILLEGAL = -1, PP_DEFINE, PP_ELIF, PP_ELSE, @@ -87,8 +99,7 @@ typedef enum { PP_INCLUDE, PP_LINE, PP_PRAGMA, - PP_UNDEF, - PP_ILLEGAL + PP_UNDEF } pptoken_t; @@ -126,7 +137,7 @@ static int CmpToken (const void* Key, const void* Elem) static pptoken_t FindPPToken (const char* Ident) -/* Find a preprocessor token and return ut. Return PP_ILLEGAL if the identifier +/* Find a preprocessor token and return it. Return PP_ILLEGAL if the identifier * is not a valid preprocessor token. */ { @@ -138,66 +149,95 @@ static pptoken_t FindPPToken (const char* Ident) /*****************************************************************************/ -/* Code */ +/* struct MacroExp */ /*****************************************************************************/ -#ifdef HAVE_INLINE -INLINE void KeepChar (char c) -/* Put character c into translation buffer. */ +static MacroExp* InitMacroExp (MacroExp* E, Macro* M) +/* Initialize a MacroExp structure */ { - *mptr++ = c; + InitCollection (&E->ActualArgs); + InitStrBuf (&E->Replacement); + E->M = M; + return E; } -#else -#define KeepChar(c) *mptr++ = (c) -#endif -static void KeepStr (const char* S) -/* Put string str into translation buffer. */ +static void DoneMacroExp (MacroExp* E) +/* Cleanup after use of a MacroExp structure */ { - unsigned Len = strlen (S); - memcpy (mptr, S, Len); - mptr += Len; + unsigned I; + + /* Delete the list with actual arguments */ + for (I = 0; I < CollCount (&E->ActualArgs); ++I) { + FreeStrBuf (CollAtUnchecked (&E->ActualArgs, I)); + } + DoneCollection (&E->ActualArgs); + DoneStrBuf (&E->Replacement); +} + + + +static void ME_AppendActual (MacroExp* E, StrBuf* Arg) +/* Add a copy of Arg to the list of actual macro arguments. + * NOTE: This function will clear Arg! + */ +{ + /* Create a new string buffer */ + StrBuf* A = NewStrBuf (); + + /* Move the contents of Arg to A */ + SB_Move (A, Arg); + + /* Add A to the actual arguments */ + CollAppend (&E->ActualArgs, A); } -static void Stringize (const char* S) +static StrBuf* ME_GetActual (MacroExp* E, unsigned Index) +/* Return an actual macro argument with the given index */ +{ + return CollAt (&E->ActualArgs, Index); +} + + + +/*****************************************************************************/ +/* Code */ +/*****************************************************************************/ + + + +static void Stringize (StrBuf* Source, StrBuf* Target) /* Stringize the given string: Add double quotes at start and end and preceed * each occurance of " and \ by a backslash. */ { - KeepChar ('\"'); + char C; + + /* Add a starting quote */ + SB_AppendChar (Target, '\"'); + /* Replace any characters inside the string may not be part of a string * unescaped. */ - while (*S) { - switch (*S) { + while ((C = SB_Get (Source)) != '\0') { + switch (C) { case '\"': case '\\': - KeepChar ('\\'); + SB_AppendChar (Target, '\\'); /* FALLTHROUGH */ default: - KeepChar (*S); + SB_AppendChar (Target, C); break; } - ++S; } - KeepChar ('\"'); -} - - -static void SwapLineBuffers (void) -/* Swap both line buffers */ -{ - /* Swap mline and line */ - char* p = line; - line = mline; - mline = p; + /* Add the closing quote */ + SB_AppendChar (Target, '\"'); } @@ -225,7 +265,7 @@ static void OldStyleComment (void) } else { if (CurC == '/' && NextC == '*') { PPWarning ("`/*' found inside a comment"); - } + } NextChar (); } } @@ -255,52 +295,47 @@ static void NewStyleComment (void) -static void SkipBlank (void) -/* Skip blanks and tabs in the input stream. */ +static void SkipWhitespace (void) +/* Skip white space in the input stream. */ { - while (IsBlank (CurC)) { + while (IsSpace (CurC)) { NextChar (); } } -static char* CopyQuotedString (char* Target) -/* Copy a single or double quoted string from the input to Target. Return the - * new target pointer. Target will not be terminated after the copy. - */ +static void CopyQuotedString (StrBuf* Target) +/* Copy a single or double quoted string from the input to Target. */ { /* Remember the quote character, copy it to the target buffer and skip it */ char Quote = CurC; - *Target++ = CurC; + SB_AppendChar (Target, CurC); NextChar (); /* Copy the characters inside the string */ while (CurC != '\0' && CurC != Quote) { /* Keep an escaped char */ if (CurC == '\\') { - *Target++ = CurC; - NextChar (); + SB_AppendChar (Target, CurC); + NextChar (); } /* Copy the character */ - *Target++ = CurC; + SB_AppendChar (Target, CurC); NextChar (); } /* If we had a terminating quote, copy it */ if (CurC != '\0') { - *Target++ = CurC; + SB_AppendChar (Target, CurC); NextChar (); } - - /* Return the new target pointer */ - return Target; } /*****************************************************************************/ -/* Macro stuff */ +/* Macro stuff */ /*****************************************************************************/ @@ -321,107 +356,37 @@ static int MacName (char* Ident) -static void ExpandMacroArgs (Macro* M) -/* Expand the arguments of a macro */ -{ - ident Ident; - const char* Replacement; - const char* SavePtr; - - /* Save the current line pointer and setup the new ones */ - SavePtr = lptr; - InitLine (M->Replacement); - - /* Copy the macro replacement checking for parameters to replace */ - while (CurC != '\0') { - /* If the next token is an identifier, check for a macro arg */ - if (IsIdent (CurC)) { - SymName (Ident); - Replacement = FindMacroArg (M, Ident); - if (Replacement) { - /* Macro arg, keep the replacement */ - KeepStr (Replacement); - } else { - /* No macro argument, keep the original identifier */ - KeepStr (Ident); - } - } else if (CurC == '#' && IsIdent (NextC)) { - NextChar (); - SymName (Ident); - Replacement = FindMacroArg (M, Ident); - if (Replacement) { - /* Make a valid string from Replacement */ - Stringize (Replacement); - } else { - /* No replacement - keep the input */ - KeepChar ('#'); - KeepStr (Ident); - } - } else if (IsQuote (CurC)) { - mptr = CopyQuotedString (mptr); - } else { - KeepChar (CurC); - NextChar (); - } - } - - /* Reset the line pointer */ - InitLine (SavePtr); -} - - - -static int MacroCall (Macro* M) -/* Process a function like macro */ +static void ReadMacroArgs (MacroExp* E) +/* Identify the arguments to a macro call */ { - int ArgCount; /* Macro argument count */ - unsigned ParCount; /* Number of open parenthesis */ - char Buf[LINESIZE]; /* Argument buffer */ - const char* ArgStart; - char* B; - - /* Check for an argument list. If we don't have a list, we won't expand - * the macro. - */ - SkipBlank (); - if (CurC != '(') { - KeepStr (M->Name); - KeepChar (' '); - return 1; - } - - /* Eat the left paren */ - NextChar (); + unsigned Parens; /* Number of open parenthesis */ + StrBuf Arg = STATIC_STRBUF_INITIALIZER; /* Read the actual macro arguments and store pointers to these arguments * into the array of actual arguments in the macro definition. */ - ArgCount = 0; - ParCount = 0; - ArgStart = Buf; - B = Buf; + Parens = 0; while (1) { if (CurC == '(') { - /* Nested parenthesis */ - *B++ = CurC; - NextChar (); - ++ParCount; + /* Nested parenthesis */ + SB_AppendChar (&Arg, CurC); + NextChar (); + ++Parens; } else if (IsQuote (CurC)) { - B = CopyQuotedString (B); + CopyQuotedString (&Arg); } else if (CurC == ',' || CurC == ')') { - if (ParCount == 0) { - /* End of actual argument */ - *B++ = '\0'; - while (IsBlank(*ArgStart)) { - ++ArgStart; - } - if (ArgCount < M->ArgCount) { - M->ActualArgs[ArgCount++] = ArgStart; - } else if (CurC != ')' || *ArgStart != '\0' || M->ArgCount > 0) { - /* Be sure not to count the single empty argument for a - * macro that does not have arguments. - */ - ++ArgCount; + if (Parens == 0) { + + /* End of actual argument. Remove whitespace from the end. */ + while (IsSpace (SB_LookAtLast (&Arg))) { + SB_Drop (&Arg, 1); + } + + /* If this is not the single empty argument for a macro with + * an empty argument list, remember it. + */ + if (CurC != ')' || SB_NotEmpty (&Arg) || E->M->ArgCount > 0) { + ME_AppendActual (E, &Arg); } /* Check for end of macro param list */ @@ -431,69 +396,271 @@ static int MacroCall (Macro* M) } /* Start the next param */ - ArgStart = B; NextChar (); + SB_Clear (&Arg); } else { /* Comma or right paren inside nested parenthesis */ if (CurC == ')') { - --ParCount; + --Parens; } - *B++ = CurC; + SB_AppendChar (&Arg, CurC); NextChar (); } - } else if (IsBlank (CurC)) { - /* Squeeze runs of blanks */ - *B++ = ' '; - SkipBlank (); + } else if (IsSpace (CurC)) { + /* Squeeze runs of blanks within an arg */ + if (SB_NotEmpty (&Arg)) { + SB_AppendChar (&Arg, ' '); + } + SkipWhitespace (); } else if (CurC == '/' && NextC == '*') { - *B++ = ' '; + if (SB_NotEmpty (&Arg)) { + SB_AppendChar (&Arg, ' '); + } OldStyleComment (); } else if (IS_Get (&Standard) >= STD_C99 && CurC == '/' && NextC == '/') { - *B++ = ' '; + if (SB_NotEmpty (&Arg)) { + SB_AppendChar (&Arg, ' '); + } NewStyleComment (); } else if (CurC == '\0') { /* End of line inside macro argument list - read next line */ + if (SB_NotEmpty (&Arg)) { + SB_AppendChar (&Arg, ' '); + } if (NextLine () == 0) { - return 0; + ClearLine (); + break; } } else { /* Just copy the character */ - *B++ = CurC; + SB_AppendChar (&Arg, CurC); + NextChar (); + } + } + + /* Deallocate string buf resources */ + DoneStrBuf (&Arg); +} + + + +static void MacroArgSubst (MacroExp* E) +/* Argument substitution according to ISO/IEC 9899:1999 (E), 6.10.3.1ff */ +{ + ident Ident; + int ArgIdx; + StrBuf* OldSource; + StrBuf* Arg; + int HaveSpace; + + + /* Remember the current input and switch to the macro replacement. */ + SB_Reset (&E->M->Replacement); + OldSource = InitLine (&E->M->Replacement); + + /* Argument handling loop */ + while (CurC != '\0') { + + /* If we have an identifier, check if it's a macro */ + if (IsSym (Ident)) { + + /* Check if it's a macro argument */ + if ((ArgIdx = FindMacroArg (E->M, Ident)) >= 0) { + + /* A macro argument. Get the corresponding actual argument. */ + Arg = ME_GetActual (E, ArgIdx); + + /* Copy any following whitespace */ + HaveSpace = IsSpace (CurC); + if (HaveSpace) { + SkipWhitespace (); + } + + /* If a ## operator follows, we have to insert the actual + * argument as is, otherwise it must be macro replaced. + */ + if (CurC == '#' && NextC == '#') { + + /* ### Add placemarker if necessary */ + SB_Append (&E->Replacement, Arg); + + } else { + + /* Replace the formal argument by a macro replaced copy + * of the actual. + */ + SB_Reset (Arg); + MacroReplacement (Arg, &E->Replacement); + + /* If we skipped whitespace before, re-add it now */ + if (HaveSpace) { + SB_AppendChar (&E->Replacement, ' '); + } + } + + + } else { + + /* An identifier, keep it */ + SB_AppendStr (&E->Replacement, Ident); + + } + + } else if (CurC == '#' && NextC == '#') { + + /* ## operator. */ + NextChar (); + NextChar (); + SkipWhitespace (); + + /* Since we need to concatenate the token sequences, remove + * any whitespace that was added to target, since it must come + * from the input. + */ + while (IsSpace (SB_LookAtLast (&E->Replacement))) { + SB_Drop (&E->Replacement, 1); + } + + /* If the next token is an identifier which is a macro argument, + * replace it, otherwise do nothing. + */ + if (IsSym (Ident)) { + + /* Check if it's a macro argument */ + if ((ArgIdx = FindMacroArg (E->M, Ident)) >= 0) { + + /* Get the corresponding actual argument and add it. */ + SB_Append (&E->Replacement, ME_GetActual (E, ArgIdx)); + + } else { + + /* Just an ordinary identifier - add as is */ + SB_AppendStr (&E->Replacement, Ident); + + } + } + + } else if (CurC == '#' && E->M->ArgCount >= 0) { + + /* A # operator within a macro expansion of a function like + * macro. Read the following identifier and check if it's a + * macro parameter. + */ + NextChar (); + SkipWhitespace (); + if (!IsSym (Ident) || (ArgIdx = FindMacroArg (E->M, Ident)) < 0) { + printf ("<%.*s>\n", SB_GetLen (Line), SB_GetConstBuf (Line)); + PPError ("`#' is not followed by a macro parameter"); + } else { + /* Make a valid string from Replacement */ + Arg = ME_GetActual (E, ArgIdx); + SB_Reset (Arg); + Stringize (Arg, &E->Replacement); + } + + } else if (IsQuote (CurC)) { + CopyQuotedString (&E->Replacement); + } else { + SB_AppendChar (&E->Replacement, CurC); NextChar (); } } - /* Compare formal argument count with actual */ - if (M->ArgCount != ArgCount) { +#if 0 + /* Remove whitespace from the end of the line */ + while (IsSpace (SB_LookAtLast (&E->Replacement))) { + SB_Drop (&E->Replacement, 1); + } +#endif + + /* Switch back the input */ + InitLine (OldSource); +} + + + +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) { PPError ("Macro argument count mismatch"); /* Be sure to make enough empty arguments available */ - while (ArgCount < M->ArgCount) { - M->ActualArgs [ArgCount++] = ""; + StrBuf Arg = STATIC_STRBUF_INITIALIZER; + while (CollCount (&E.ActualArgs) < (unsigned) M->ArgCount) { + ME_AppendActual (&E, &Arg); } } - /* Preprocess the line, replacing macro parameters */ - ExpandMacroArgs (M); + /* 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; - /* Done */ - return 1; + /* Free memory allocated for the macro expansion structure */ + DoneMacroExp (&E); } -static void ExpandMacro (Macro* M) -/* Expand a macro */ +static void ExpandMacro (StrBuf* Target, Macro* M) +/* Expand a macro into Target */ { /* Check if this is a function like macro */ + //printf ("Expanding %s(%u)\n", M->Name, ++V); if (M->ArgCount >= 0) { - /* Function like macro */ - if (MacroCall (M) == 0) { - ClearLine (); - } + + int Whitespace = IsSpace (CurC); + if (Whitespace) { + SkipWhitespace (); + } + if (CurC != '(') { + /* Function like macro but no parameter list */ + SB_AppendStr (Target, M->Name); + if (Whitespace) { + SB_AppendChar (Target, ' '); + } + } else { + /* Function like macro */ + MacroCall (Target, M); + } + } else { - /* Just copy the replacement text */ - KeepStr (M->Replacement); + + MacroExp E; + InitMacroExp (&E, M); + + /* Handle # and ## operators for object like macros */ + MacroArgSubst (&E); + + /* Do macro replacement on the macro that already has the parameters + * substituted. + */ + M->Expanding = 1; + MacroReplacement (&E.Replacement, Target); + M->Expanding = 0; + + /* Free memory allocated for the macro expansion structure */ + DoneMacroExp (&E); + } + //printf ("Done with %s(%u)\n", M->Name, V--); } @@ -501,14 +668,12 @@ static void ExpandMacro (Macro* M) static void DefineMacro (void) /* Handle a macro definition. */ { - char* saveptr; - ident Ident; - char Buf[LINESIZE]; - Macro* M; - Macro* Existing; + ident Ident; + Macro* M; + Macro* Existing; /* Read the macro name */ - SkipBlank (); + SkipWhitespace (); if (!MacName (Ident)) { return; } @@ -530,16 +695,18 @@ static void DefineMacro (void) /* Read the formal parameter list */ while (1) { - SkipBlank (); - if (CurC == ')') - break; + SkipWhitespace (); + if (CurC == ')') { + break; + } if (MacName (Ident) == 0) { - return; + return; } AddMacroArg (M, Ident); - SkipBlank (); - if (CurC != ',') - break; + SkipWhitespace (); + if (CurC != ',') { + break; + } NextChar (); } @@ -552,142 +719,152 @@ static void DefineMacro (void) NextChar (); } + /* Skip whitespace before the macro replacement */ + SkipWhitespace (); + /* Insert the macro into the macro table and allocate the ActualArgs array */ InsertMacro (M); /* Remove whitespace and comments from the line, store the preprocessed - * line into Buf. + * line into the macro replacement buffer. */ - SkipBlank (); - saveptr = mptr; - Pass1 (lptr, Buf); - mptr = saveptr; + Pass1 (Line, &M->Replacement); - /* Create a copy of the replacement */ - M->Replacement = xstrdup (Buf); + /* Remove whitespace from the end of the line */ + while (IsSpace (SB_LookAtLast (&M->Replacement))) { + SB_Drop (&M->Replacement, 1); + } + + //printf ("%s: <%.*s>\n", M->Name, SB_GetLen (&M->Replacement), SB_GetConstBuf (&M->Replacement)); /* If we have an existing macro, check if the redefinition is identical. * Print a diagnostic if not. */ - if (Existing) { - if (MacroCmp (M, Existing) != 0) { - PPError ("Macro redefinition is not identical"); - } + if (Existing && MacroCmp (M, Existing) != 0) { + PPError ("Macro redefinition is not identical"); } } /*****************************************************************************/ -/* Preprocessing */ +/* Preprocessing */ /*****************************************************************************/ -static int Pass1 (const char* From, char* To) -/* Preprocessor pass 1. Remove whitespace and comments. */ +static unsigned Pass1 (StrBuf* Source, StrBuf* Target) +/* Preprocessor pass 1. Remove whitespace. Handle old and new style comments + * and the "defined" operator. + */ { - int done; + unsigned IdentCount; ident Ident; int HaveParen; - /* Initialize reading from "From" */ - InitLine (From); - - /* Target is "To" */ - mptr = To; + /* Switch to the new input source */ + StrBuf* OldSource = InitLine (Source); /* Loop removing ws and comments */ - done = 1; + IdentCount = 0; while (CurC != '\0') { - if (IsBlank (CurC)) { - KeepChar (' '); - SkipBlank (); - } else if (IsIdent (CurC)) { - SymName (Ident); - if (Preprocessing && strcmp(Ident, "defined") == 0) { - /* Handle the "defined" operator */ - SkipBlank(); - HaveParen = 0; - if (CurC == '(') { - HaveParen = 1; - NextChar (); - SkipBlank(); - } - if (!IsIdent (CurC)) { - PPError ("Identifier expected"); - KeepChar ('0'); - } else { - SymName (Ident); - KeepChar (IsMacro (Ident)? '1' : '0'); - if (HaveParen) { - SkipBlank(); - if (CurC != ')') { - PPError ("`)' expected"); - } else { - NextChar (); - } - } - } - } else { - if (MaybeMacro (Ident[0])) { - done = 0; - } - KeepStr (Ident); - } - } else if (IsQuote (CurC)) { - mptr = CopyQuotedString (mptr); - } else if (CurC == '/' && NextC == '*') { - KeepChar (' '); + if (IsSpace (CurC)) { + /* Squeeze runs of blanks */ + if (!IsSpace (SB_LookAtLast (Target))) { + SB_AppendChar (Target, ' '); + } + SkipWhitespace (); + } else if (IsSym (Ident)) { + if (Preprocessing && strcmp (Ident, "defined") == 0) { + /* Handle the "defined" operator */ + SkipWhitespace (); + HaveParen = 0; + if (CurC == '(') { + HaveParen = 1; + NextChar (); + SkipWhitespace (); + } + if (IsSym (Ident)) { + SB_AppendChar (Target, IsMacro (Ident)? '1' : '0'); + if (HaveParen) { + SkipWhitespace (); + if (CurC != ')') { + PPError ("`)' expected"); + } else { + NextChar (); + } + } + } else { + PPError ("Identifier expected"); + SB_AppendChar (Target, '0'); + } + } else { + ++IdentCount; + SB_AppendStr (Target, Ident); + } + } else if (IsQuote (CurC)) { + CopyQuotedString (Target); + } else if (CurC == '/' && NextC == '*') { + if (!IsSpace (SB_LookAtLast (Target))) { + SB_AppendChar (Target, ' '); + } OldStyleComment (); } else if (IS_Get (&Standard) >= STD_C99 && CurC == '/' && NextC == '/') { - KeepChar (' '); - NewStyleComment (); + if (!IsSpace (SB_LookAtLast (Target))) { + SB_AppendChar (Target, ' '); + } + NewStyleComment (); } else { - KeepChar (CurC); - NextChar (); + SB_AppendChar (Target, CurC); + NextChar (); } } - KeepChar ('\0'); - return done; + + /* Switch back to the old source */ + InitLine (OldSource); + + /* Return the number of identifiers found in the line */ + return IdentCount; } -static int Pass2 (const char* From, char* To) -/* Preprocessor pass 2. Perform macro substitution. */ +static void MacroReplacement (StrBuf* Source, StrBuf* Target) +/* Perform macro replacement. */ { - int no_chg; ident Ident; Macro* M; - /* Initialize reading from "From" */ - InitLine (From); - - /* Target is "To" */ - mptr = To; + /* Remember the current input and switch to Source */ + StrBuf* OldSource = InitLine (Source); /* Loop substituting macros */ - no_chg = 1; while (CurC != '\0') { /* If we have an identifier, check if it's a macro */ - if (IsIdent (CurC)) { - SymName (Ident); - M = FindMacro (Ident); - if (M) { - ExpandMacro (M); - no_chg = 0; - } else { - KeepStr (Ident); + if (IsSym (Ident)) { + /* Check if it's a macro */ + if ((M = FindMacro (Ident)) != 0 && !M->Expanding) { + /* It's a macro, expand it */ + ExpandMacro (Target, M); + } else { + /* An identifier, keep it */ + SB_AppendStr (Target, Ident); } } else if (IsQuote (CurC)) { - mptr = CopyQuotedString (mptr); - } else { - KeepChar (CurC); + CopyQuotedString (Target); + } else if (IsSpace (CurC)) { + if (!IsSpace (SB_LookAtLast (Target))) { + SB_AppendChar (Target, CurC); + } + NextChar (); + } else { + SB_AppendChar (Target, CurC); NextChar (); } } - return no_chg; + + /* Switch back the input */ + InitLine (OldSource); } @@ -695,28 +872,20 @@ static int Pass2 (const char* From, char* To) static void PreprocessLine (void) /* Translate one line. */ { - unsigned I; - - /* Trim whitespace and remove comments. The function returns false if no - * identifiers were found that may be macros. If this is the case, no - * macro substitution is performed. + /* Trim whitespace and remove comments. The function returns the number of + * identifiers found. If there were any, we will have to check for macros. */ - int Done = Pass1 (line, mline); - - /* Repeatedly expand macros in the line */ - for (I = 0; I < 256; ++I) { - /* Swap mline and line */ - SwapLineBuffers (); - if (Done) { - break; - } - /* Perform macro expansion */ - Done = Pass2 (line, mline); - KeepChar ('\0'); + SB_Clear (MLine); + if (Pass1 (Line, MLine) > 0) { + MLine = InitLine (MLine); + SB_Reset (Line); + SB_Clear (MLine); + MacroReplacement (Line, MLine); } - /* Reinitialize line parsing */ - InitLine (line); + /* Read from the new line */ + SB_Reset (MLine); + MLine = InitLine (MLine); } @@ -726,7 +895,7 @@ static void DoUndef (void) { ident Ident; - SkipBlank (); + SkipWhitespace (); if (MacName (Ident)) { UndefineMacro (Ident); } @@ -760,35 +929,36 @@ static int DoIf (int Skip) /* Process #if directive */ { ExprDesc Expr; - char* S; /* We're about to abuse the compiler expression parser to evaluate the * #if expression. Save the current tokens to come back here later. * NOTE: Yes, this is a hack, but it saves a complete separate expression * evaluation for the preprocessor. */ - Token sv1 = CurTok; - Token sv2 = NextTok; + Token SavedCurTok = CurTok; + Token SavedNextTok = NextTok; /* Make sure the line infos for the tokens won't get removed */ - if (sv1.LI) { - UseLineInfo (sv1.LI); + if (SavedCurTok.LI) { + UseLineInfo (SavedCurTok.LI); } - if (sv2.LI) { - UseLineInfo (sv2.LI); + if (SavedNextTok.LI) { + UseLineInfo (SavedNextTok.LI); } +#if 0 /* Remove the #if from the line */ - SkipBlank (); + SkipWhitespace (); S = line; while (CurC != '\0') { - *S++ = CurC; - NextChar (); + *S++ = CurC; + NextChar (); } *S = '\0'; /* Start over parsing from line */ InitLine (line); +#endif /* Switch into special preprocessing mode */ Preprocessing = 1; @@ -800,9 +970,9 @@ static int DoIf (int Skip) * expression evaluation will eat these two tokens but nothing from * the following line. */ - strcat (line, ";;"); + SB_AppendStr (Line, ";;"); - /* Prime the token pump (remove old tokens from the stream) */ + /* Load CurTok and NextTok with tokens from the new input */ NextToken (); NextToken (); @@ -813,8 +983,8 @@ static int DoIf (int Skip) Preprocessing = 0; /* Reset the old tokens */ - CurTok = sv1; - NextTok = sv2; + CurTok = SavedCurTok; + NextTok = SavedNextTok; /* Set the #if condition according to the expression result */ return PushIf (Skip, 1, Expr.IVal != 0); @@ -827,7 +997,7 @@ static int DoIfDef (int skip, int flag) { ident Ident; - SkipBlank (); + SkipWhitespace (); if (MacName (Ident) == 0) { return 0; } else { @@ -842,10 +1012,11 @@ static void DoInclude (void) { char RTerm; unsigned DirSpec; + StrBuf Filename = STATIC_STRBUF_INITIALIZER; /* Skip blanks */ - SkipBlank (); + SkipWhitespace (); /* Get the next char and check for a valid file name terminator. Setup * the include directory spec (SYS/USR) by looking at the terminator. @@ -868,15 +1039,12 @@ static void DoInclude (void) } NextChar (); - /* Copy the filename into mline. Since mline has the same size as the - * input line, we don't need to check for an overflow here. - */ - mptr = mline; + /* Get a copy of the filename */ while (CurC != '\0' && CurC != RTerm) { - KeepChar (CurC); + SB_AppendChar (&Filename, CurC); NextChar (); } - *mptr = '\0'; + SB_Terminate (&Filename); /* Check if we got a terminator */ if (CurC != RTerm) { @@ -886,9 +1054,12 @@ static void DoInclude (void) } /* Open the include file */ - OpenIncludeFile (mline, DirSpec); + OpenIncludeFile (SB_GetConstBuf (&Filename), DirSpec); Done: + /* Free the allocated filename data */ + DoneStrBuf (&Filename); + /* Clear the remaining line so the next input will come from the new * file (if open) */ @@ -900,11 +1071,11 @@ Done: static void DoError (void) /* Print an error */ { - SkipBlank (); + SkipWhitespace (); if (CurC == '\0') { PPError ("Invalid #error directive"); } else { - PPError ("#error: %s", lptr); + PPError ("#error: %s", SB_GetConstBuf (Line) + SB_GetIndex (Line)); } /* Clear the rest of line */ @@ -919,20 +1090,21 @@ static void DoPragma (void) */ { /* Skip blanks following the #pragma directive */ - SkipBlank (); + SkipWhitespace (); - /* Copy the remainder of the line into mline removing comments and ws */ - Pass1 (lptr, mline); + /* Copy the remainder of the line into MLine removing comments and ws */ + SB_Clear (MLine); + Pass1 (Line, MLine); /* Convert the directive into the operator */ - mptr = line; - KeepStr ("_Pragma ("); - Stringize (mline); - KeepChar (')'); - *mptr = '\0'; + SB_CopyStr (Line, "_Pragma ("); + SB_Reset (MLine); + Stringize (MLine, Line); + SB_AppendChar (Line, ')'); /* Initialize reading from line */ - InitLine (line); + SB_Reset (Line); + InitLine (Line); } @@ -943,8 +1115,13 @@ void Preprocess (void) int Skip; ident Directive; + /* Create the output buffer if we don't already have one */ + if (MLine == 0) { + MLine = NewStrBuf (); + } + /* Skip white space at the beginning of the line */ - SkipBlank (); + SkipWhitespace (); /* Check for stuff to skip */ Skip = 0; @@ -953,7 +1130,7 @@ void Preprocess (void) /* Check for preprocessor lines lines */ if (CurC == '#') { NextChar (); - SkipBlank (); + SkipWhitespace (); if (CurC == '\0') { /* Ignore the empty preprocessor directive */ continue; @@ -970,7 +1147,7 @@ void Preprocess (void) } break; - case PP_ELIF: + case PP_ELIF: if (IfIndex >= 0) { if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) { @@ -982,7 +1159,7 @@ void Preprocess (void) Skip = DoIf (Skip); /* #elif doesn't need a terminator */ - IfStack[IfIndex] &= ~IFCOND_NEEDTERM; + IfStack[IfIndex] &= ~IFCOND_NEEDTERM; } else { PPError ("Duplicate #else/#elif"); } @@ -1082,12 +1259,15 @@ void Preprocess (void) } return; } - SkipBlank (); + SkipWhitespace (); } PreprocessLine (); Done: - Print (stdout, 2, "line: %s\n", line); + if (Verbosity > 1 && SB_NotEmpty (Line)) { + printf ("%s(%u): %.*s\n", GetCurrentFile (), GetCurrentLine (), + SB_GetLen (Line), SB_GetConstBuf (Line)); + } } diff --git a/src/cc65/scanner.c b/src/cc65/scanner.c index 435875800..840d0b559 100644 --- a/src/cc65/scanner.c +++ b/src/cc65/scanner.c @@ -181,7 +181,7 @@ static int SkipWhite (void) */ { while (1) { - while (CurC == 0) { + while (CurC == '\0') { if (NextLine () == 0) { return 0; } @@ -197,27 +197,30 @@ static int SkipWhite (void) -void SymName (char* s) -/* Get symbol from input stream */ +void SymName (char* S) +/* Read a symbol from the input stream. The first character must have been + * checked before calling this function. The buffer is expected to be at + * least of size MAX_IDENTLEN+1. + */ { - unsigned k = 0; + unsigned Len = 0; do { - if (k != MAX_IDENTLEN) { - ++k; - *s++ = CurC; - } + if (Len < MAX_IDENTLEN) { + ++Len; + *S++ = CurC; + } NextChar (); } while (IsIdent (CurC) || IsDigit (CurC)); - *s = '\0'; + *S = '\0'; } -int IsSym (char *s) -/* Get symbol from input stream or return 0 if not a symbol. */ +int IsSym (char* S) +/* If a symbol follows, read it and return 1, otherwise return 0 */ { if (IsIdent (CurC)) { - SymName (s); + SymName (S); return 1; } else { return 0; @@ -245,7 +248,7 @@ static void SetTok (int tok) static int ParseChar (void) -/* Parse a character. Converts \n into EOL, etc. */ +/* Parse a character. Converts escape chars into character codes. */ { int I; unsigned Val; @@ -292,9 +295,20 @@ static int ParseChar (void) case 'X': /* Hex character constant */ NextChar (); - Val = HexVal (CurC) << 4; - NextChar (); - C = Val | HexVal (CurC); /* Do not translate */ + if (!IsXDigit (CurC)) { + Error ("\\x used with no following hex digits"); + C = ' '; + } + I = 0; + C = 0; + while (IsXDigit (CurC)) { + if (++I <= 2) { + C = (C << 4) | HexVal (CurC); + } else if (I == 3) { + Error ("Too many digits in hex character constant"); + } + NextChar (); + } break; case '0': case '1': @@ -304,16 +318,16 @@ static int ParseChar (void) case '5': case '6': case '7': - /* Octal constant */ + /* Octal constant ### FIXME: Eat all available octal chars! */ I = 0; Val = CurC - '0'; - while (NextC >= '0' && NextC <= '7' && ++I <= 3) { + while (IsODigit (NextC) && ++I <= 3) { NextChar (); Val = (Val << 3) | (CurC - '0'); } C = (int) Val; if (Val >= 256) { - Error ("Character constant out of range"); + Error ("Character constant out of range: %u", Val); C = ' '; } break; diff --git a/src/cc65/scanner.h b/src/cc65/scanner.h index 36fd88081..db297ff01 100644 --- a/src/cc65/scanner.h +++ b/src/cc65/scanner.h @@ -223,11 +223,14 @@ INLINE int TokIsTypeQual (const Token* T) # define TokIsTypeQual(T) ((T)->Tok >= TOK_FIRST_TYPEQUAL && (T)->Tok <= TOK_LAST_TYPEQUAL) #endif -void SymName (char* s); -/* Get symbol from input stream */ +void SymName (char* S); +/* Read a symbol from the input stream. The first character must have been + * checked before calling this function. The buffer is expected to be at + * least of size MAX_IDENTLEN+1. + */ -int IsSym (char* s); -/* Get symbol from input stream or return 0 if not a symbol. */ +int IsSym (char* S); +/* If a symbol follows, read it and return 1, otherwise return 0 */ void NextToken (void); /* Get next token from input stream */