/* common */
#include "chartype.h"
+#include "check.h"
#include "print.h"
#include "xmalloc.h"
/*****************************************************************************/
-/* data */
+/* Data */
/*****************************************************************************/
-/* Set when the pp calls expr() recursively */
+/* Set when the preprocessor calls expr() recursively */
unsigned char Preprocessing = 0;
/* Management data for #if */
-#define N_IFDEF 16
-static int i_ifdef = -1;
-static char s_ifdef[N_IFDEF];
+#define MAX_IFS 64
+#define IFCOND_NONE 0x00U
+#define IFCOND_SKIP 0x01U
+#define IFCOND_ELSE 0x02U
+#define IFCOND_NEEDTERM 0x04U
+static unsigned char IfStack[MAX_IFS];
+static int IfIndex = -1;
/* Buffer for macro expansion */
static char mlinebuf [LINESIZE];
/*****************************************************************************/
-/* code */
+/* Low level preprocessor token handling */
/*****************************************************************************/
-static int keepch (char c)
+/* Types of preprocessor tokens */
+typedef enum {
+ PP_DEFINE,
+ PP_ELIF,
+ PP_ELSE,
+ PP_ENDIF,
+ PP_ERROR,
+ PP_IF,
+ PP_IFDEF,
+ PP_IFNDEF,
+ PP_INCLUDE,
+ PP_LINE,
+ PP_PRAGMA,
+ PP_UNDEF,
+ PP_ILLEGAL
+} pptoken_t;
+
+
+
+/* Preprocessor keyword to token mapping table */
+static const struct PPToken {
+ const char* Key; /* Keyword */
+ pptoken_t Tok; /* Token */
+} PPTokens[] = {
+ { "define", PP_DEFINE },
+ { "elif", PP_ELIF },
+ { "else", PP_ELSE },
+ { "endif", PP_ENDIF },
+ { "error", PP_ERROR },
+ { "if", PP_IF },
+ { "ifdef", PP_IFDEF },
+ { "ifndef", PP_IFNDEF },
+ { "include", PP_INCLUDE },
+ { "line", PP_LINE },
+ { "pragma", PP_PRAGMA },
+ { "undef", PP_UNDEF },
+};
+
+/* Number of preprocessor tokens */
+#define PPTOKEN_COUNT (sizeof(PPTokens) / sizeof(PPTokens[0]))
+
+
+
+static int CmpToken (const void* Key, const void* Elem)
+/* Compare function for bsearch */
+{
+ return strcmp ((const char*) Key, ((const struct PPToken*) Elem)->Key);
+}
+
+
+
+static pptoken_t FindPPToken (const char* Ident)
+/* Find a preprocessor token and return ut. Return PP_ILLEGAL if the identifier
+ * is not a valid preprocessor token.
+ */
+{
+ struct PPToken* P;
+ P = bsearch (Ident, PPTokens, PPTOKEN_COUNT, sizeof (PPTokens[0]), CmpToken);
+ return P? P->Tok : PP_ILLEGAL;
+}
+
+
+
+/*****************************************************************************/
+/* Code */
+/*****************************************************************************/
+
+
+
+static void keepch (char c)
/* Put character c into translation buffer. */
{
- return (*mptr++ = c);
+ *mptr++ = c;
}
static void keepstr (const char* S)
/* Put string str into translation buffer. */
{
- while (*S) {
- keepch (*S++);
- }
+ unsigned Len = strlen (S);
+ memcpy (mptr, S, Len);
+ mptr += Len;
}
/*****************************************************************************/
-/* Macro stuff */
+/* Macro stuff */
/*****************************************************************************/
static int MacName (char* Ident)
-/* Get macro symbol name. If error, print message and clear line. */
+/* Get a macro symbol name into Ident. If we have an error, print a
+ * diagnostic message and clear the line.
+ */
{
if (IsSym (Ident) == 0) {
- PPError ("Identifier expected");
+ PPError ("Identifier expected");
ClearLine ();
- return 0;
+ return 0;
} else {
return 1;
}
static void ExpandMacroArgs (Macro* M)
-/* Preprocessor pass 2. Perform macro substitution. */
+/* Expand the arguments of a macro */
{
ident Ident;
const char* 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 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 */
+ Replacement = FindMacroArg (M, Ident);
+ if (Replacement) {
+ /* Macro arg, keep the replacement */
keepstr (Replacement);
} else {
- /* No macro argument, keep the original identifier */
+ /* No macro argument, keep the original identifier */
keepstr (Ident);
}
} else if (CurC == '#' && IsIdent (NextC)) {
NextChar ();
SymName (Ident);
- Replacement = FindMacroArg (M, Ident);
+ Replacement = FindMacroArg (M, Ident);
if (Replacement) {
keepch ('\"');
keepstr (Replacement);
mptr = CopyQuotedString (mptr);
} else {
*mptr++ = CurC;
- NextChar ();
+ NextChar ();
}
}
static int MacroCall (Macro* M)
/* Process a function like macro */
{
- unsigned ArgCount; /* Macro argument count */
+ int ArgCount; /* Macro argument count */
unsigned ParCount; /* Number of open parenthesis */
char Buf[LINESIZE]; /* Argument buffer */
const char* ArgStart;
B = Buf;
while (1) {
if (CurC == '(') {
- /* Nested parenthesis */
+ /* Nested parenthesis */
*B++ = CurC;
- NextChar ();
+ NextChar ();
++ParCount;
} else if (IsQuote (CurC)) {
B = CopyQuotedString (B);
} else if (CurC == ',' || CurC == ')') {
if (ParCount == 0) {
- /* End of actual argument */
+ /* End of actual argument */
*B++ = '\0';
- while (IsBlank(*ArgStart)) {
- ++ArgStart;
- }
+ 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.
- */
+ /* Be sure not to count the single empty argument for a
+ * macro that does not have arguments.
+ */
++ArgCount;
- }
+ }
- /* Check for end of macro param list */
- if (CurC == ')') {
- NextChar ();
- break;
- }
+ /* Check for end of macro param list */
+ if (CurC == ')') {
+ NextChar ();
+ break;
+ }
/* Start the next param */
ArgStart = B;
- NextChar ();
+ NextChar ();
} else {
- /* Comma or right paren inside nested parenthesis */
+ /* Comma or right paren inside nested parenthesis */
if (CurC == ')') {
--ParCount;
}
*B++ = CurC;
- NextChar ();
+ NextChar ();
}
} else if (IsBlank (CurC)) {
- /* Squeeze runs of blanks */
+ /* Squeeze runs of blanks */
*B++ = ' ';
SkipBlank ();
} else if (CurC == '\0') {
- /* End of line inside macro argument list - read next line */
+ /* End of line inside macro argument list - read next line */
if (NextLine () == 0) {
return 0;
}
} else {
- /* Just copy the character */
+ /* Just copy the character */
*B++ = CurC;
- NextChar ();
+ NextChar ();
}
}
/* Compare formal argument count with actual */
if (M->ArgCount != ArgCount) {
- PPError ("Macro argument count mismatch");
- /* Be sure to make enough empty arguments available */
- while (ArgCount < M->ArgCount) {
- M->ActualArgs [ArgCount++] = "";
- }
+ PPError ("Macro argument count mismatch");
+ /* Be sure to make enough empty arguments available */
+ while (ArgCount < M->ArgCount) {
+ M->ActualArgs [ArgCount++] = "";
+ }
}
/* Preprocess the line, replacing macro parameters */
{
/* Check if this is a function like macro */
if (M->ArgCount >= 0) {
- /* Function like macro */
+ /* Function like macro */
if (MacroCall (M) == 0) {
ClearLine ();
}
} else {
- /* Just copy the replacement text */
+ /* Just copy the replacement text */
keepstr (M->Replacement);
}
}
-static void addmac (void)
-/* Add a macro to the macro table. */
+static void DefineMacro (void)
+/* Handle a macro definition. */
{
char* saveptr;
ident Ident;
NextChar ();
/* Set the marker that this is a function like macro */
- M->ArgCount = 0;
+ M->ArgCount = 0;
- /* Read the formal parameter list */
+ /* Read the formal parameter list */
while (1) {
SkipBlank ();
if (CurC == ')')
if (MacName (Ident) == 0) {
return;
}
- AddMacroArg (M, Ident);
+ AddMacroArg (M, Ident);
SkipBlank ();
if (CurC != ',')
break;
NextChar ();
}
- /* Check for a right paren and eat it if we find one */
+ /* Check for a right paren and eat it if we find one */
if (CurC != ')') {
PPError ("`)' expected");
ClearLine ();
* Print a diagnostic if not.
*/
if (Existing) {
- if (MacroCmp (M, Existing) != 0) {
- PPError ("Macro redefinition is not identical");
- }
+ if (MacroCmp (M, Existing) != 0) {
+ PPError ("Macro redefinition is not identical");
+ }
}
}
/*****************************************************************************/
-
+/* Preprocessing */
/*****************************************************************************/
-static void doundef (void)
-/* Process #undef directive */
+static void DoUndef (void)
+/* Process the #undef directive */
{
ident Ident;
-static int setmflag (int skip, int flag, int cond)
-/* setmflag( skip, flag, cond ) */
+static int PushIf (int Skip, int Invert, int Cond)
+/* Push a new if level onto the if stack */
{
- if (skip) {
- s_ifdef[++i_ifdef] = 3;
- return (1);
+ /* Check for an overflow of the if stack */
+ if (IfIndex >= MAX_IFS-1) {
+ PPError ("Too many nested #if clauses");
+ return 1;
+ }
+
+ /* Push the #if condition */
+ ++IfIndex;
+ if (Skip) {
+ IfStack[IfIndex] = IFCOND_SKIP | IFCOND_NEEDTERM;
+ return 1;
} else {
- s_ifdef[++i_ifdef] = 6;
- return (flag ^ cond);
+ IfStack[IfIndex] = IFCOND_NONE | IFCOND_NEEDTERM;
+ return (Invert ^ Cond);
}
}
-static int doiff (int skip)
+static int DoIf (int Skip)
/* Process #if directive */
{
- struct expent lval;
+ ExprDesc lval;
char* S;
/* We're about to abuse the compiler expression parser to evaluate the
NextTok = sv2;
/* Set the #if condition according to the expression result */
- return (setmflag (skip, 1, lval.e_const != 0));
+ return PushIf (Skip, 1, lval.ConstVal != 0);
}
-static int doifdef (int skip, int flag)
+static int DoIfDef (int skip, int flag)
/* Process #ifdef if flag == 1, or #ifndef if flag == 0. */
{
ident Ident;
if (MacName (Ident) == 0) {
return 0;
} else {
- return setmflag (skip, flag, IsMacro(Ident));
+ return PushIf (skip, flag, IsMacro(Ident));
}
}
-static void doinclude (void)
+static void DoInclude (void)
/* Open an include file. */
{
char RTerm;
-static void doerror (void)
+static void DoError (void)
/* Print an error */
{
SkipBlank ();
PPError ("#error: %s", lptr);
}
- /* clear rest of line */
+ /* Clear the rest of line */
ClearLine ();
}
-/* C preprocessor. */
-
-/* stuff used to bum the keyword dispatching stuff */
-enum {
- PP_DEFINE,
- PP_ELSE,
- PP_ENDIF,
- PP_ERROR,
- PP_IF,
- PP_IFDEF,
- PP_IFNDEF,
- PP_INCLUDE,
- PP_LINE,
- PP_PRAGMA,
- PP_UNDEF,
- PP_ILLEGAL
-};
-
-static const struct tok_elt pre_toks[] = {
- { "define", PP_DEFINE },
- { "else", PP_ELSE },
- { "endif", PP_ENDIF },
- { "error", PP_ERROR },
- { "if", PP_IF },
- { "ifdef", PP_IFDEF },
- { "ifndef", PP_IFNDEF },
- { "include", PP_INCLUDE },
- { "line", PP_LINE },
- { "pragma", PP_PRAGMA },
- { "undef", PP_UNDEF },
- { 0, PP_ILLEGAL }
-};
-
-
-
-static int searchtok (const char *sym, const struct tok_elt *toks)
-/* Search a token in a table */
-{
- while (toks->toknam && strcmp (toks->toknam, sym))
- ++toks;
- return (toks->toknbr);
-}
-
-
-
void Preprocess (void)
/* Preprocess a line */
{
PPError ("Preprocessor directive expected");
ClearLine ();
} else {
- switch (searchtok (Directive, pre_toks)) {
+ switch (FindPPToken (Directive)) {
case PP_DEFINE:
if (!Skip) {
- addmac ();
+ DefineMacro ();
}
break;
+ case PP_ELIF:
+ if (IfIndex >= 0) {
+ if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) {
+
+ /* Handle as #else/#if combination */
+ if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) {
+ Skip = !Skip;
+ }
+ IfStack[IfIndex] |= IFCOND_ELSE;
+ Skip = DoIf (Skip);
+
+ /* #elif doesn't need a terminator */
+ IfStack[IfIndex] &= ~IFCOND_NEEDTERM;
+ } else {
+ PPError ("Duplicate #else/#elif");
+ }
+ } else {
+ PPError ("Unexpected #elif");
+ }
+ break;
+
case PP_ELSE:
- if (s_ifdef[i_ifdef] & 2) {
- if (s_ifdef[i_ifdef] & 4) {
- Skip = !Skip;
- }
- s_ifdef[i_ifdef] ^= 2;
+ if (IfIndex >= 0) {
+ if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) {
+ if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) {
+ Skip = !Skip;
+ }
+ IfStack[IfIndex] |= IFCOND_ELSE;
+ } else {
+ PPError ("Duplicate #else");
+ }
} else {
PPError ("Unexpected `#else'");
}
break;
case PP_ENDIF:
- if (i_ifdef >= 0) {
- Skip = s_ifdef[i_ifdef--] & 1;
+ if (IfIndex >= 0) {
+ /* Remove any clauses on top of stack that do not
+ * need a terminating #endif.
+ */
+ while (IfIndex >= 0 && (IfStack[IfIndex] & IFCOND_NEEDTERM) == 0) {
+ --IfIndex;
+ }
+
+ /* Stack may not be empty here or something is wrong */
+ CHECK (IfIndex >= 0);
+
+ /* Remove the clause that needs a terminator */
+ Skip = (IfStack[IfIndex--] & IFCOND_SKIP) != 0;
} else {
PPError ("Unexpected `#endif'");
}
case PP_ERROR:
if (!Skip) {
- doerror ();
+ DoError ();
}
break;
case PP_IF:
- Skip = doiff (Skip);
+ Skip = DoIf (Skip);
break;
case PP_IFDEF:
- Skip = doifdef (Skip, 1);
+ Skip = DoIfDef (Skip, 1);
break;
case PP_IFNDEF:
- Skip = doifdef (Skip, 0);
+ Skip = DoIfDef (Skip, 0);
break;
case PP_INCLUDE:
if (!Skip) {
- doinclude ();
+ DoInclude ();
}
break;
case PP_LINE:
- /* Not allowed in strict ANSI mode */
- if (ANSI) {
- PPError ("Preprocessor directive expected");
- ClearLine ();
- }
+ /* Not allowed in strict ANSI mode */
+ if (!Skip && ANSI) {
+ PPError ("Preprocessor directive expected");
+ ClearLine ();
+ }
break;
case PP_PRAGMA:
case PP_UNDEF:
if (!Skip) {
- doundef ();
+ DoUndef ();
}
break;
}
if (NextLine () == 0) {
- if (i_ifdef >= 0) {
+ if (IfIndex >= 0) {
PPError ("`#endif' expected");
}
return;