/* */
/* */
/* */
-/* (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 */
#include "xmalloc.h"
/* cc65 */
-#include "asmcode.h"
#include "codegen.h"
#include "error.h"
#include "incpath.h"
-/* 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';
/* List of all active files */
static Collection AFiles = STATIC_COLLECTION_INITIALIZER;
+/* Input stack used when preprocessing. */
+static Collection InputStack = STATIC_COLLECTION_INITIALIZER;
+
/*****************************************************************************/
/* Allocate a new AFile structure for the file */
(void) NewAFile (IF, F);
+
+ /* Allocate the input line buffer */
+ Line = NewStrBuf ();
}
+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 ();
}
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;
{
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)";
- }
+ }
}
}
/* */
/* */
/* */
-/* (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 */
#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];
-
/*****************************************************************************/
/* 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 */
* 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);
}
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);
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]]++;
}
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);
-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;
}
* 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;
}
/* 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);
}
/*
* 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.
*/
-/*****************************************************************************/
-/* Forwards */
-/*****************************************************************************/
-
-
-
-static int Pass1 (const char* From, char* To);
-/* Preprocessor pass 1. Remove whitespace and comments. */
-
-
-
/*****************************************************************************/
/* Data */
/*****************************************************************************/
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. */
/* Types of preprocessor tokens */
typedef enum {
+ PP_ILLEGAL = -1,
PP_DEFINE,
PP_ELIF,
PP_ELSE,
PP_INCLUDE,
PP_LINE,
PP_PRAGMA,
- PP_UNDEF,
- PP_ILLEGAL
+ PP_UNDEF
} pptoken_t;
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.
*/
{
/*****************************************************************************/
-/* 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, '\"');
}
} else {
if (CurC == '/' && NextC == '*') {
PPWarning ("`/*' found inside a comment");
- }
+ }
NextChar ();
}
}
-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 */
/*****************************************************************************/
-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 */
}
/* 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--);
}
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;
}
/* 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 ();
}
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);
}
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);
}
{
ident Ident;
- SkipBlank ();
+ SkipWhitespace ();
if (MacName (Ident)) {
UndefineMacro (Ident);
}
/* 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;
* 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 ();
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);
{
ident Ident;
- SkipBlank ();
+ SkipWhitespace ();
if (MacName (Ident) == 0) {
return 0;
} else {
{
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.
}
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) {
}
/* 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)
*/
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 */
*/
{
/* 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);
}
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;
/* Check for preprocessor lines lines */
if (CurC == '#') {
NextChar ();
- SkipBlank ();
+ SkipWhitespace ();
if (CurC == '\0') {
/* Ignore the empty preprocessor directive */
continue;
}
break;
- case PP_ELIF:
+ case PP_ELIF:
if (IfIndex >= 0) {
if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) {
Skip = DoIf (Skip);
/* #elif doesn't need a terminator */
- IfStack[IfIndex] &= ~IFCOND_NEEDTERM;
+ IfStack[IfIndex] &= ~IFCOND_NEEDTERM;
} else {
PPError ("Duplicate #else/#elif");
}
}
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));
+ }
}