2005-07-26 22:30:35 +00:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/* */
|
|
|
|
|
/* preproc.c */
|
|
|
|
|
/* */
|
|
|
|
|
/* cc65 preprocessor */
|
|
|
|
|
/* */
|
|
|
|
|
/* */
|
|
|
|
|
/* */
|
|
|
|
|
/* (C) 1998-2005, Ullrich von Bassewitz */
|
|
|
|
|
/* R<>merstra<72>e 52 */
|
|
|
|
|
/* D-70794 Filderstadt */
|
|
|
|
|
/* EMail: uz@cc65.org */
|
|
|
|
|
/* */
|
|
|
|
|
/* */
|
|
|
|
|
/* This software is provided 'as-is', without any expressed or implied */
|
|
|
|
|
/* warranty. In no event will the authors be held liable for any damages */
|
|
|
|
|
/* arising from the use of this software. */
|
|
|
|
|
/* */
|
|
|
|
|
/* Permission is granted to anyone to use this software for any purpose, */
|
|
|
|
|
/* including commercial applications, and to alter it and redistribute it */
|
|
|
|
|
/* freely, subject to the following restrictions: */
|
|
|
|
|
/* */
|
|
|
|
|
/* 1. The origin of this software must not be misrepresented; you must not */
|
|
|
|
|
/* claim that you wrote the original software. If you use this software */
|
|
|
|
|
/* in a product, an acknowledgment in the product documentation would be */
|
|
|
|
|
/* appreciated but is not required. */
|
|
|
|
|
/* 2. Altered source versions must be plainly marked as such, and must not */
|
|
|
|
|
/* be misrepresented as being the original software. */
|
|
|
|
|
/* 3. This notice may not be removed or altered from any source */
|
|
|
|
|
/* distribution. */
|
|
|
|
|
/* */
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
2004-06-29 20:37:18 +00:00
|
|
|
|
|
2000-05-28 13:40:48 +00:00
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
2001-01-05 19:24:47 +00:00
|
|
|
|
/* common */
|
|
|
|
|
#include "chartype.h"
|
2001-09-07 09:54:33 +00:00
|
|
|
|
#include "check.h"
|
2002-09-29 21:09:47 +00:00
|
|
|
|
#include "inline.h"
|
2001-03-10 10:21:03 +00:00
|
|
|
|
#include "print.h"
|
2001-01-05 19:24:47 +00:00
|
|
|
|
#include "xmalloc.h"
|
2000-06-14 21:01:37 +00:00
|
|
|
|
|
2001-01-05 19:24:47 +00:00
|
|
|
|
/* cc65 */
|
2000-05-28 13:40:48 +00:00
|
|
|
|
#include "codegen.h"
|
|
|
|
|
#include "error.h"
|
|
|
|
|
#include "expr.h"
|
|
|
|
|
#include "global.h"
|
|
|
|
|
#include "ident.h"
|
2000-06-14 08:18:19 +00:00
|
|
|
|
#include "incpath.h"
|
2000-06-14 21:01:37 +00:00
|
|
|
|
#include "input.h"
|
2001-07-14 15:56:17 +00:00
|
|
|
|
#include "lineinfo.h"
|
2000-05-28 13:40:48 +00:00
|
|
|
|
#include "macrotab.h"
|
2004-06-21 22:22:11 +00:00
|
|
|
|
#include "preproc.h"
|
2000-05-28 13:40:48 +00:00
|
|
|
|
#include "scanner.h"
|
2004-06-21 22:22:11 +00:00
|
|
|
|
#include "standard.h"
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2001-09-06 10:17:52 +00:00
|
|
|
|
/* Data */
|
2000-05-28 13:40:48 +00:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2001-09-07 09:54:33 +00:00
|
|
|
|
/* Set when the preprocessor calls expr() recursively */
|
2000-05-28 13:40:48 +00:00
|
|
|
|
unsigned char Preprocessing = 0;
|
|
|
|
|
|
|
|
|
|
/* Management data for #if */
|
2001-09-07 09:54:33 +00:00
|
|
|
|
#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;
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
|
|
/* Buffer for macro expansion */
|
2004-09-16 12:42:41 +00:00
|
|
|
|
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. */
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2001-09-06 10:17:52 +00:00
|
|
|
|
/* Low level preprocessor token handling */
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Types of preprocessor tokens */
|
|
|
|
|
typedef enum {
|
2004-09-16 12:42:41 +00:00
|
|
|
|
PP_ILLEGAL = -1,
|
2001-09-06 10:17:52 +00:00
|
|
|
|
PP_DEFINE,
|
2001-09-07 09:54:33 +00:00
|
|
|
|
PP_ELIF,
|
2001-09-06 10:17:52 +00:00
|
|
|
|
PP_ELSE,
|
|
|
|
|
PP_ENDIF,
|
|
|
|
|
PP_ERROR,
|
|
|
|
|
PP_IF,
|
|
|
|
|
PP_IFDEF,
|
|
|
|
|
PP_IFNDEF,
|
|
|
|
|
PP_INCLUDE,
|
|
|
|
|
PP_LINE,
|
|
|
|
|
PP_PRAGMA,
|
2004-09-16 12:42:41 +00:00
|
|
|
|
PP_UNDEF
|
2001-09-06 10:17:52 +00:00
|
|
|
|
} pptoken_t;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Preprocessor keyword to token mapping table */
|
|
|
|
|
static const struct PPToken {
|
|
|
|
|
const char* Key; /* Keyword */
|
|
|
|
|
pptoken_t Tok; /* Token */
|
|
|
|
|
} PPTokens[] = {
|
|
|
|
|
{ "define", PP_DEFINE },
|
2001-09-07 09:54:33 +00:00
|
|
|
|
{ "elif", PP_ELIF },
|
2001-09-06 10:17:52 +00:00
|
|
|
|
{ "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)
|
2004-09-16 12:42:41 +00:00
|
|
|
|
/* Find a preprocessor token and return it. Return PP_ILLEGAL if the identifier
|
2001-09-06 10:17:52 +00:00
|
|
|
|
* 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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2004-09-16 12:42:41 +00:00
|
|
|
|
/* struct MacroExp */
|
2000-05-28 13:40:48 +00:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-09-16 12:42:41 +00:00
|
|
|
|
static MacroExp* InitMacroExp (MacroExp* E, Macro* M)
|
|
|
|
|
/* Initialize a MacroExp structure */
|
2000-05-28 13:40:48 +00:00
|
|
|
|
{
|
2004-09-16 12:42:41 +00:00
|
|
|
|
InitCollection (&E->ActualArgs);
|
|
|
|
|
InitStrBuf (&E->Replacement);
|
|
|
|
|
E->M = M;
|
|
|
|
|
return E;
|
2000-05-28 13:40:48 +00:00
|
|
|
|
}
|
2002-02-18 08:40:11 +00:00
|
|
|
|
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
|
|
|
2004-09-16 12:42:41 +00:00
|
|
|
|
static void DoneMacroExp (MacroExp* E)
|
|
|
|
|
/* Cleanup after use of a MacroExp structure */
|
2000-05-28 13:40:48 +00:00
|
|
|
|
{
|
2004-09-16 12:42:41 +00:00
|
|
|
|
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);
|
2000-05-28 13:40:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-09-16 12:42:41 +00:00
|
|
|
|
static StrBuf* ME_GetActual (MacroExp* E, unsigned Index)
|
|
|
|
|
/* Return an actual macro argument with the given index */
|
|
|
|
|
{
|
|
|
|
|
return CollAt (&E->ActualArgs, Index);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2005-07-24 22:14:41 +00:00
|
|
|
|
static int ME_ArgIsVariadic (const MacroExp* E)
|
|
|
|
|
/* Return true if the next actual argument we will add is a variadic one */
|
|
|
|
|
{
|
|
|
|
|
return (E->M->Variadic &&
|
|
|
|
|
E->M->ArgCount == (int) CollCount (&E->ActualArgs) + 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-09-16 12:42:41 +00:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/* Code */
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void Stringize (StrBuf* Source, StrBuf* Target)
|
2002-09-29 21:09:47 +00:00
|
|
|
|
/* Stringize the given string: Add double quotes at start and end and preceed
|
|
|
|
|
* each occurance of " and \ by a backslash.
|
|
|
|
|
*/
|
|
|
|
|
{
|
2004-09-16 12:42:41 +00:00
|
|
|
|
char C;
|
|
|
|
|
|
|
|
|
|
/* Add a starting quote */
|
|
|
|
|
SB_AppendChar (Target, '\"');
|
|
|
|
|
|
2002-09-29 21:09:47 +00:00
|
|
|
|
/* Replace any characters inside the string may not be part of a string
|
|
|
|
|
* unescaped.
|
|
|
|
|
*/
|
2004-09-16 12:42:41 +00:00
|
|
|
|
while ((C = SB_Get (Source)) != '\0') {
|
|
|
|
|
switch (C) {
|
2002-09-29 21:09:47 +00:00
|
|
|
|
case '\"':
|
|
|
|
|
case '\\':
|
2004-09-16 12:42:41 +00:00
|
|
|
|
SB_AppendChar (Target, '\\');
|
2002-09-29 21:09:47 +00:00
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
|
default:
|
2004-09-16 12:42:41 +00:00
|
|
|
|
SB_AppendChar (Target, C);
|
2002-09-29 21:09:47 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-09-16 12:42:41 +00:00
|
|
|
|
/* Add the closing quote */
|
|
|
|
|
SB_AppendChar (Target, '\"');
|
2002-09-29 21:09:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2001-12-12 21:45:05 +00:00
|
|
|
|
static void OldStyleComment (void)
|
|
|
|
|
/* Remove an old style C comment from line. */
|
2000-05-28 13:40:48 +00:00
|
|
|
|
{
|
2000-06-21 21:02:44 +00:00
|
|
|
|
/* Remember the current line number, so we can output better error
|
|
|
|
|
* messages if the comment is not terminated in the current file.
|
|
|
|
|
*/
|
2000-06-14 21:01:37 +00:00
|
|
|
|
unsigned StartingLine = GetCurrentLine();
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
2000-06-21 21:02:44 +00:00
|
|
|
|
/* Skip the start of comment chars */
|
|
|
|
|
NextChar ();
|
|
|
|
|
NextChar ();
|
|
|
|
|
|
|
|
|
|
/* Skip the comment */
|
|
|
|
|
while (CurC != '*' || NextC != '/') {
|
|
|
|
|
if (CurC == '\0') {
|
2000-06-14 08:18:19 +00:00
|
|
|
|
if (NextLine () == 0) {
|
2001-12-12 21:45:05 +00:00
|
|
|
|
PPError ("End-of-file reached in comment starting at line %u",
|
|
|
|
|
StartingLine);
|
2000-05-28 13:40:48 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2000-06-21 21:02:44 +00:00
|
|
|
|
if (CurC == '/' && NextC == '*') {
|
2001-12-12 21:45:05 +00:00
|
|
|
|
PPWarning ("`/*' found inside a comment");
|
2004-09-16 12:42:41 +00:00
|
|
|
|
}
|
2000-06-21 21:02:44 +00:00
|
|
|
|
NextChar ();
|
2000-05-28 13:40:48 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2000-06-21 21:02:44 +00:00
|
|
|
|
|
|
|
|
|
/* Skip the end of comment chars */
|
|
|
|
|
NextChar ();
|
|
|
|
|
NextChar ();
|
2000-05-28 13:40:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2001-12-12 21:45:05 +00:00
|
|
|
|
static void NewStyleComment (void)
|
|
|
|
|
/* Remove a new style C comment from line. */
|
|
|
|
|
{
|
|
|
|
|
/* Beware: Because line continuation chars are handled when reading
|
|
|
|
|
* lines, we may only skip til the end of the source line, which
|
|
|
|
|
* may not be the same as the end of the input line. The end of the
|
|
|
|
|
* source line is denoted by a lf (\n) character.
|
|
|
|
|
*/
|
|
|
|
|
do {
|
|
|
|
|
NextChar ();
|
|
|
|
|
} while (CurC != '\n' && CurC != '\0');
|
|
|
|
|
if (CurC == '\n') {
|
|
|
|
|
NextChar ();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-09-16 12:42:41 +00:00
|
|
|
|
static void SkipWhitespace (void)
|
|
|
|
|
/* Skip white space in the input stream. */
|
2000-05-28 13:40:48 +00:00
|
|
|
|
{
|
2004-09-16 12:42:41 +00:00
|
|
|
|
while (IsSpace (CurC)) {
|
2000-06-21 21:02:44 +00:00
|
|
|
|
NextChar ();
|
2000-05-28 13:40:48 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-09-16 12:42:41 +00:00
|
|
|
|
static void CopyQuotedString (StrBuf* Target)
|
|
|
|
|
/* Copy a single or double quoted string from the input to Target. */
|
2000-05-28 13:40:48 +00:00
|
|
|
|
{
|
2000-06-21 21:02:44 +00:00
|
|
|
|
/* Remember the quote character, copy it to the target buffer and skip it */
|
|
|
|
|
char Quote = CurC;
|
2004-09-16 12:42:41 +00:00
|
|
|
|
SB_AppendChar (Target, CurC);
|
2000-06-21 21:02:44 +00:00
|
|
|
|
NextChar ();
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
|
|
/* Copy the characters inside the string */
|
2000-06-21 21:02:44 +00:00
|
|
|
|
while (CurC != '\0' && CurC != Quote) {
|
2000-05-28 13:40:48 +00:00
|
|
|
|
/* Keep an escaped char */
|
2002-09-29 21:09:47 +00:00
|
|
|
|
if (CurC == '\\') {
|
2004-09-16 12:42:41 +00:00
|
|
|
|
SB_AppendChar (Target, CurC);
|
|
|
|
|
NextChar ();
|
2000-05-28 13:40:48 +00:00
|
|
|
|
}
|
|
|
|
|
/* Copy the character */
|
2004-09-16 12:42:41 +00:00
|
|
|
|
SB_AppendChar (Target, CurC);
|
2000-06-21 21:02:44 +00:00
|
|
|
|
NextChar ();
|
2000-05-28 13:40:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If we had a terminating quote, copy it */
|
2000-06-21 21:02:44 +00:00
|
|
|
|
if (CurC != '\0') {
|
2004-09-16 12:42:41 +00:00
|
|
|
|
SB_AppendChar (Target, CurC);
|
2000-06-21 21:02:44 +00:00
|
|
|
|
NextChar ();
|
2000-05-28 13:40:48 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2004-09-16 12:42:41 +00:00
|
|
|
|
/* Macro stuff */
|
2000-05-28 13:40:48 +00:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2000-06-21 21:02:44 +00:00
|
|
|
|
static int MacName (char* Ident)
|
2001-09-07 09:54:33 +00:00
|
|
|
|
/* Get a macro symbol name into Ident. If we have an error, print a
|
|
|
|
|
* diagnostic message and clear the line.
|
2001-09-06 10:17:52 +00:00
|
|
|
|
*/
|
2000-05-28 13:40:48 +00:00
|
|
|
|
{
|
2000-06-21 21:02:44 +00:00
|
|
|
|
if (IsSym (Ident) == 0) {
|
2001-09-06 10:17:52 +00:00
|
|
|
|
PPError ("Identifier expected");
|
2000-06-21 21:02:44 +00:00
|
|
|
|
ClearLine ();
|
2001-09-06 10:17:52 +00:00
|
|
|
|
return 0;
|
2000-05-28 13:40:48 +00:00
|
|
|
|
} else {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-09-16 12:42:41 +00:00
|
|
|
|
static void ReadMacroArgs (MacroExp* E)
|
|
|
|
|
/* Identify the arguments to a macro call */
|
2000-05-28 13:40:48 +00:00
|
|
|
|
{
|
2004-09-16 12:42:41 +00:00
|
|
|
|
unsigned Parens; /* Number of open parenthesis */
|
|
|
|
|
StrBuf Arg = STATIC_STRBUF_INITIALIZER;
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
2005-07-24 22:14:41 +00:00
|
|
|
|
/* Read the actual macro arguments */
|
|
|
|
|
Parens = 0;
|
2000-05-28 13:40:48 +00:00
|
|
|
|
while (1) {
|
2000-06-21 21:02:44 +00:00
|
|
|
|
if (CurC == '(') {
|
2005-07-24 22:14:41 +00:00
|
|
|
|
|
2004-09-16 12:42:41 +00:00
|
|
|
|
/* Nested parenthesis */
|
|
|
|
|
SB_AppendChar (&Arg, CurC);
|
|
|
|
|
NextChar ();
|
|
|
|
|
++Parens;
|
2005-07-24 22:14:41 +00:00
|
|
|
|
|
2001-01-05 19:26:25 +00:00
|
|
|
|
} else if (IsQuote (CurC)) {
|
2005-07-24 22:14:41 +00:00
|
|
|
|
|
|
|
|
|
/* Quoted string - just copy */
|
2004-09-16 12:42:41 +00:00
|
|
|
|
CopyQuotedString (&Arg);
|
2005-07-24 22:14:41 +00:00
|
|
|
|
|
2000-06-21 21:02:44 +00:00
|
|
|
|
} else if (CurC == ',' || CurC == ')') {
|
2004-09-16 12:42:41 +00:00
|
|
|
|
|
2005-07-24 22:14:41 +00:00
|
|
|
|
if (Parens) {
|
|
|
|
|
/* Comma or right paren inside nested parenthesis */
|
|
|
|
|
if (CurC == ')') {
|
|
|
|
|
--Parens;
|
|
|
|
|
}
|
|
|
|
|
SB_AppendChar (&Arg, CurC);
|
|
|
|
|
NextChar ();
|
|
|
|
|
} else if (CurC == ',' && ME_ArgIsVariadic (E)) {
|
|
|
|
|
/* It's a comma, but we're inside a variadic macro argument, so
|
|
|
|
|
* just copy it and proceed.
|
|
|
|
|
*/
|
|
|
|
|
SB_AppendChar (&Arg, CurC);
|
|
|
|
|
NextChar ();
|
|
|
|
|
} else {
|
2004-09-16 12:42:41 +00:00
|
|
|
|
/* 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);
|
2003-10-08 16:58:54 +00:00
|
|
|
|
}
|
2000-06-21 21:02:44 +00:00
|
|
|
|
|
2003-10-08 16:58:54 +00:00
|
|
|
|
/* Check for end of macro param list */
|
2001-12-12 21:45:05 +00:00
|
|
|
|
if (CurC == ')') {
|
2003-10-08 16:58:54 +00:00
|
|
|
|
NextChar ();
|
|
|
|
|
break;
|
2001-12-12 21:45:05 +00:00
|
|
|
|
}
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
2000-06-21 21:02:44 +00:00
|
|
|
|
/* Start the next param */
|
2003-10-08 16:58:54 +00:00
|
|
|
|
NextChar ();
|
2004-09-16 12:42:41 +00:00
|
|
|
|
SB_Clear (&Arg);
|
2000-05-28 13:40:48 +00:00
|
|
|
|
}
|
2004-09-16 12:42:41 +00:00
|
|
|
|
} else if (IsSpace (CurC)) {
|
|
|
|
|
/* Squeeze runs of blanks within an arg */
|
|
|
|
|
if (SB_NotEmpty (&Arg)) {
|
|
|
|
|
SB_AppendChar (&Arg, ' ');
|
|
|
|
|
}
|
|
|
|
|
SkipWhitespace ();
|
2003-10-08 16:58:54 +00:00
|
|
|
|
} else if (CurC == '/' && NextC == '*') {
|
2004-09-16 12:42:41 +00:00
|
|
|
|
if (SB_NotEmpty (&Arg)) {
|
|
|
|
|
SB_AppendChar (&Arg, ' ');
|
|
|
|
|
}
|
2001-12-12 21:45:05 +00:00
|
|
|
|
OldStyleComment ();
|
2004-06-21 22:22:11 +00:00
|
|
|
|
} else if (IS_Get (&Standard) >= STD_C99 && CurC == '/' && NextC == '/') {
|
2004-09-16 12:42:41 +00:00
|
|
|
|
if (SB_NotEmpty (&Arg)) {
|
|
|
|
|
SB_AppendChar (&Arg, ' ');
|
|
|
|
|
}
|
2003-10-08 16:58:54 +00:00
|
|
|
|
NewStyleComment ();
|
2000-06-21 21:02:44 +00:00
|
|
|
|
} else if (CurC == '\0') {
|
2001-12-12 21:45:05 +00:00
|
|
|
|
/* End of line inside macro argument list - read next line */
|
2004-09-16 12:42:41 +00:00
|
|
|
|
if (SB_NotEmpty (&Arg)) {
|
|
|
|
|
SB_AppendChar (&Arg, ' ');
|
|
|
|
|
}
|
2000-06-14 08:18:19 +00:00
|
|
|
|
if (NextLine () == 0) {
|
2004-09-16 12:42:41 +00:00
|
|
|
|
ClearLine ();
|
|
|
|
|
break;
|
2000-05-28 13:40:48 +00:00
|
|
|
|
}
|
|
|
|
|
} else {
|
2001-12-12 21:45:05 +00:00
|
|
|
|
/* Just copy the character */
|
2004-09-16 12:42:41 +00:00
|
|
|
|
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) {
|
|
|
|
|
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);
|
2001-12-12 21:45:05 +00:00
|
|
|
|
NextChar ();
|
2000-05-28 13:40:48 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-09-16 12:42:41 +00:00
|
|
|
|
#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) {
|
2004-09-16 14:31:40 +00:00
|
|
|
|
|
|
|
|
|
StrBuf Arg = STATIC_STRBUF_INITIALIZER;
|
|
|
|
|
|
|
|
|
|
/* Argument count mismatch */
|
2001-12-12 21:45:05 +00:00
|
|
|
|
PPError ("Macro argument count mismatch");
|
2004-09-16 14:31:40 +00:00
|
|
|
|
|
2001-12-12 21:45:05 +00:00
|
|
|
|
/* Be sure to make enough empty arguments available */
|
2004-09-16 12:42:41 +00:00
|
|
|
|
while (CollCount (&E.ActualArgs) < (unsigned) M->ArgCount) {
|
|
|
|
|
ME_AppendActual (&E, &Arg);
|
2001-12-12 21:45:05 +00:00
|
|
|
|
}
|
2000-05-28 13:40:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
2004-09-16 12:42:41 +00:00
|
|
|
|
/* 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;
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
2004-09-16 12:42:41 +00:00
|
|
|
|
/* Free memory allocated for the macro expansion structure */
|
|
|
|
|
DoneMacroExp (&E);
|
2000-05-28 13:40:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-09-16 12:42:41 +00:00
|
|
|
|
static void ExpandMacro (StrBuf* Target, Macro* M)
|
|
|
|
|
/* Expand a macro into Target */
|
2000-05-28 13:40:48 +00:00
|
|
|
|
{
|
|
|
|
|
/* Check if this is a function like macro */
|
2004-09-16 12:42:41 +00:00
|
|
|
|
//printf ("Expanding %s(%u)\n", M->Name, ++V);
|
2000-05-28 13:40:48 +00:00
|
|
|
|
if (M->ArgCount >= 0) {
|
2004-09-16 12:42:41 +00:00
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2000-05-28 13:40:48 +00:00
|
|
|
|
} else {
|
2004-09-16 12:42:41 +00:00
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
2000-05-28 13:40:48 +00:00
|
|
|
|
}
|
2004-09-16 12:42:41 +00:00
|
|
|
|
//printf ("Done with %s(%u)\n", M->Name, V--);
|
2000-05-28 13:40:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2001-09-06 10:17:52 +00:00
|
|
|
|
static void DefineMacro (void)
|
|
|
|
|
/* Handle a macro definition. */
|
2000-05-28 13:40:48 +00:00
|
|
|
|
{
|
2004-09-16 12:42:41 +00:00
|
|
|
|
ident Ident;
|
|
|
|
|
Macro* M;
|
|
|
|
|
Macro* Existing;
|
2005-07-24 22:14:41 +00:00
|
|
|
|
int C89;
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
|
|
/* Read the macro name */
|
2004-09-16 12:42:41 +00:00
|
|
|
|
SkipWhitespace ();
|
2000-06-21 21:02:44 +00:00
|
|
|
|
if (!MacName (Ident)) {
|
2000-05-28 13:40:48 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2005-07-24 22:14:41 +00:00
|
|
|
|
/* Remember if we're in C89 mode */
|
|
|
|
|
C89 = (IS_Get (&Standard) == STD_C89);
|
|
|
|
|
|
2000-10-10 21:20:34 +00:00
|
|
|
|
/* Get an existing macro definition with this name */
|
|
|
|
|
Existing = FindMacro (Ident);
|
|
|
|
|
|
2000-05-28 13:40:48 +00:00
|
|
|
|
/* Create a new macro definition */
|
|
|
|
|
M = NewMacro (Ident);
|
|
|
|
|
|
|
|
|
|
/* Check if this is a function like macro */
|
2000-06-21 21:02:44 +00:00
|
|
|
|
if (CurC == '(') {
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
|
|
/* Skip the left paren */
|
2000-06-21 21:02:44 +00:00
|
|
|
|
NextChar ();
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
|
|
/* Set the marker that this is a function like macro */
|
2001-09-06 10:17:52 +00:00
|
|
|
|
M->ArgCount = 0;
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
2001-09-06 10:17:52 +00:00
|
|
|
|
/* Read the formal parameter list */
|
2000-05-28 13:40:48 +00:00
|
|
|
|
while (1) {
|
2005-07-26 22:30:35 +00:00
|
|
|
|
|
|
|
|
|
/* Skip white space and check for end of parameter list */
|
2004-09-16 12:42:41 +00:00
|
|
|
|
SkipWhitespace ();
|
|
|
|
|
if (CurC == ')') {
|
|
|
|
|
break;
|
|
|
|
|
}
|
2005-07-24 22:14:41 +00:00
|
|
|
|
|
|
|
|
|
/* The next token must be either an identifier, or - if not in
|
|
|
|
|
* C89 mode - the ellipsis.
|
|
|
|
|
*/
|
|
|
|
|
if (!C89 && CurC == '.') {
|
|
|
|
|
/* Ellipsis */
|
|
|
|
|
NextChar ();
|
|
|
|
|
if (CurC != '.' || NextC != '.') {
|
|
|
|
|
PPError ("`...' expected");
|
|
|
|
|
ClearLine ();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
NextChar ();
|
|
|
|
|
NextChar ();
|
2005-07-26 22:30:35 +00:00
|
|
|
|
|
|
|
|
|
/* Remember that the macro is variadic and use __VA_ARGS__ as
|
|
|
|
|
* the argument name.
|
|
|
|
|
*/
|
|
|
|
|
AddMacroArg (M, "__VA_ARGS__");
|
2005-07-24 22:14:41 +00:00
|
|
|
|
M->Variadic = 1;
|
2005-07-26 22:30:35 +00:00
|
|
|
|
|
2005-07-24 22:14:41 +00:00
|
|
|
|
} else {
|
|
|
|
|
/* Must be macro argument name */
|
|
|
|
|
if (MacName (Ident) == 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* __VA_ARGS__ is only allowed in C89 mode */
|
|
|
|
|
if (!C89 && strcmp (Ident, "__VA_ARGS__") == 0) {
|
|
|
|
|
PPWarning ("`__VA_ARGS__' can only appear in the expansion "
|
|
|
|
|
"of a C99 variadic macro");
|
|
|
|
|
}
|
2005-07-26 22:30:35 +00:00
|
|
|
|
|
|
|
|
|
/* Add the macro argument */
|
|
|
|
|
AddMacroArg (M, Ident);
|
2005-07-24 22:14:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
2005-07-26 22:30:35 +00:00
|
|
|
|
/* If we had an ellipsis, or the next char is not a comma, we've
|
|
|
|
|
* reached the end of the macro argument list.
|
|
|
|
|
*/
|
2004-09-16 12:42:41 +00:00
|
|
|
|
SkipWhitespace ();
|
2005-07-24 22:14:41 +00:00
|
|
|
|
if (M->Variadic || CurC != ',') {
|
2005-07-26 22:30:35 +00:00
|
|
|
|
break;
|
2004-09-16 12:42:41 +00:00
|
|
|
|
}
|
2000-06-21 21:02:44 +00:00
|
|
|
|
NextChar ();
|
2000-05-28 13:40:48 +00:00
|
|
|
|
}
|
2000-06-21 21:02:44 +00:00
|
|
|
|
|
2001-09-06 10:17:52 +00:00
|
|
|
|
/* Check for a right paren and eat it if we find one */
|
2000-06-21 21:02:44 +00:00
|
|
|
|
if (CurC != ')') {
|
2000-10-15 19:52:01 +00:00
|
|
|
|
PPError ("`)' expected");
|
2000-06-15 19:03:01 +00:00
|
|
|
|
ClearLine ();
|
2000-05-28 13:40:48 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
2000-06-21 21:02:44 +00:00
|
|
|
|
NextChar ();
|
2000-05-28 13:40:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
2004-09-16 12:42:41 +00:00
|
|
|
|
/* Skip whitespace before the macro replacement */
|
|
|
|
|
SkipWhitespace ();
|
|
|
|
|
|
2000-05-28 13:40:48 +00:00
|
|
|
|
/* Insert the macro into the macro table and allocate the ActualArgs array */
|
|
|
|
|
InsertMacro (M);
|
|
|
|
|
|
|
|
|
|
/* Remove whitespace and comments from the line, store the preprocessed
|
2004-09-16 12:42:41 +00:00
|
|
|
|
* line into the macro replacement buffer.
|
2000-05-28 13:40:48 +00:00
|
|
|
|
*/
|
2004-09-16 12:42:41 +00:00
|
|
|
|
Pass1 (Line, &M->Replacement);
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
2004-09-16 12:42:41 +00:00
|
|
|
|
/* 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));
|
2000-10-10 21:20:34 +00:00
|
|
|
|
|
|
|
|
|
/* If we have an existing macro, check if the redefinition is identical.
|
|
|
|
|
* Print a diagnostic if not.
|
|
|
|
|
*/
|
2004-09-16 12:42:41 +00:00
|
|
|
|
if (Existing && MacroCmp (M, Existing) != 0) {
|
|
|
|
|
PPError ("Macro redefinition is not identical");
|
2000-10-10 21:20:34 +00:00
|
|
|
|
}
|
2000-05-28 13:40:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
2004-09-16 12:42:41 +00:00
|
|
|
|
/* Preprocessing */
|
2000-05-28 13:40:48 +00:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-09-16 12:42:41 +00:00
|
|
|
|
static unsigned Pass1 (StrBuf* Source, StrBuf* Target)
|
|
|
|
|
/* Preprocessor pass 1. Remove whitespace. Handle old and new style comments
|
|
|
|
|
* and the "defined" operator.
|
|
|
|
|
*/
|
2000-05-28 13:40:48 +00:00
|
|
|
|
{
|
2004-09-16 12:42:41 +00:00
|
|
|
|
unsigned IdentCount;
|
2000-06-21 21:02:44 +00:00
|
|
|
|
ident Ident;
|
|
|
|
|
int HaveParen;
|
|
|
|
|
|
2004-09-16 12:42:41 +00:00
|
|
|
|
/* Switch to the new input source */
|
|
|
|
|
StrBuf* OldSource = InitLine (Source);
|
2000-06-21 21:02:44 +00:00
|
|
|
|
|
|
|
|
|
/* Loop removing ws and comments */
|
2004-09-16 12:42:41 +00:00
|
|
|
|
IdentCount = 0;
|
2000-06-21 21:02:44 +00:00
|
|
|
|
while (CurC != '\0') {
|
2004-09-16 12:42:41 +00:00
|
|
|
|
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, ' ');
|
|
|
|
|
}
|
2001-12-12 21:45:05 +00:00
|
|
|
|
OldStyleComment ();
|
2004-06-21 22:22:11 +00:00
|
|
|
|
} else if (IS_Get (&Standard) >= STD_C99 && CurC == '/' && NextC == '/') {
|
2004-09-16 12:42:41 +00:00
|
|
|
|
if (!IsSpace (SB_LookAtLast (Target))) {
|
|
|
|
|
SB_AppendChar (Target, ' ');
|
|
|
|
|
}
|
|
|
|
|
NewStyleComment ();
|
2000-05-28 13:40:48 +00:00
|
|
|
|
} else {
|
2004-09-16 12:42:41 +00:00
|
|
|
|
SB_AppendChar (Target, CurC);
|
|
|
|
|
NextChar ();
|
2000-05-28 13:40:48 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2004-09-16 12:42:41 +00:00
|
|
|
|
|
|
|
|
|
/* Switch back to the old source */
|
|
|
|
|
InitLine (OldSource);
|
|
|
|
|
|
|
|
|
|
/* Return the number of identifiers found in the line */
|
|
|
|
|
return IdentCount;
|
2000-05-28 13:40:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-09-16 12:42:41 +00:00
|
|
|
|
static void MacroReplacement (StrBuf* Source, StrBuf* Target)
|
|
|
|
|
/* Perform macro replacement. */
|
2000-05-28 13:40:48 +00:00
|
|
|
|
{
|
2000-06-21 21:02:44 +00:00
|
|
|
|
ident Ident;
|
|
|
|
|
Macro* M;
|
|
|
|
|
|
2004-09-16 12:42:41 +00:00
|
|
|
|
/* Remember the current input and switch to Source */
|
|
|
|
|
StrBuf* OldSource = InitLine (Source);
|
2000-06-21 21:02:44 +00:00
|
|
|
|
|
|
|
|
|
/* Loop substituting macros */
|
|
|
|
|
while (CurC != '\0') {
|
2002-09-29 21:09:47 +00:00
|
|
|
|
/* If we have an identifier, check if it's a macro */
|
2004-09-16 12:42:41 +00:00
|
|
|
|
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);
|
2003-10-08 16:58:54 +00:00
|
|
|
|
}
|
2001-01-05 19:26:25 +00:00
|
|
|
|
} else if (IsQuote (CurC)) {
|
2004-09-16 12:42:41 +00:00
|
|
|
|
CopyQuotedString (Target);
|
|
|
|
|
} else if (IsSpace (CurC)) {
|
|
|
|
|
if (!IsSpace (SB_LookAtLast (Target))) {
|
|
|
|
|
SB_AppendChar (Target, CurC);
|
|
|
|
|
}
|
|
|
|
|
NextChar ();
|
|
|
|
|
} else {
|
|
|
|
|
SB_AppendChar (Target, CurC);
|
2003-10-08 16:58:54 +00:00
|
|
|
|
NextChar ();
|
2000-05-28 13:40:48 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2004-09-16 12:42:41 +00:00
|
|
|
|
|
|
|
|
|
/* Switch back the input */
|
|
|
|
|
InitLine (OldSource);
|
2000-05-28 13:40:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2002-09-29 21:09:47 +00:00
|
|
|
|
static void PreprocessLine (void)
|
2000-05-28 13:40:48 +00:00
|
|
|
|
/* Translate one line. */
|
|
|
|
|
{
|
2004-09-16 12:42:41 +00:00
|
|
|
|
/* Trim whitespace and remove comments. The function returns the number of
|
|
|
|
|
* identifiers found. If there were any, we will have to check for macros.
|
2002-09-29 21:09:47 +00:00
|
|
|
|
*/
|
2004-09-16 12:42:41 +00:00
|
|
|
|
SB_Clear (MLine);
|
|
|
|
|
if (Pass1 (Line, MLine) > 0) {
|
|
|
|
|
MLine = InitLine (MLine);
|
|
|
|
|
SB_Reset (Line);
|
|
|
|
|
SB_Clear (MLine);
|
|
|
|
|
MacroReplacement (Line, MLine);
|
2002-09-29 21:09:47 +00:00
|
|
|
|
}
|
2000-06-21 21:02:44 +00:00
|
|
|
|
|
2004-09-16 12:42:41 +00:00
|
|
|
|
/* Read from the new line */
|
|
|
|
|
SB_Reset (MLine);
|
|
|
|
|
MLine = InitLine (MLine);
|
2000-05-28 13:40:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2001-09-07 09:54:33 +00:00
|
|
|
|
static void DoUndef (void)
|
|
|
|
|
/* Process the #undef directive */
|
2000-05-28 13:40:48 +00:00
|
|
|
|
{
|
|
|
|
|
ident Ident;
|
|
|
|
|
|
2004-09-16 12:42:41 +00:00
|
|
|
|
SkipWhitespace ();
|
2000-06-21 21:02:44 +00:00
|
|
|
|
if (MacName (Ident)) {
|
2000-05-28 13:40:48 +00:00
|
|
|
|
UndefineMacro (Ident);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2001-09-07 09:54:33 +00:00
|
|
|
|
static int PushIf (int Skip, int Invert, int Cond)
|
|
|
|
|
/* Push a new if level onto the if stack */
|
2000-05-28 13:40:48 +00:00
|
|
|
|
{
|
2001-09-07 09:54:33 +00:00
|
|
|
|
/* 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;
|
2000-05-28 13:40:48 +00:00
|
|
|
|
} else {
|
2001-09-07 09:54:33 +00:00
|
|
|
|
IfStack[IfIndex] = IFCOND_NONE | IFCOND_NEEDTERM;
|
|
|
|
|
return (Invert ^ Cond);
|
2000-05-28 13:40:48 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2001-09-07 09:54:33 +00:00
|
|
|
|
static int DoIf (int Skip)
|
2000-05-28 13:40:48 +00:00
|
|
|
|
/* Process #if directive */
|
2004-06-21 22:22:11 +00:00
|
|
|
|
{
|
2004-06-06 14:48:59 +00:00
|
|
|
|
ExprDesc Expr;
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
|
|
/* We're about to abuse the compiler expression parser to evaluate the
|
|
|
|
|
* #if expression. Save the current tokens to come back here later.
|
2001-07-14 15:56:17 +00:00
|
|
|
|
* NOTE: Yes, this is a hack, but it saves a complete separate expression
|
|
|
|
|
* evaluation for the preprocessor.
|
2000-05-28 13:40:48 +00:00
|
|
|
|
*/
|
2004-09-16 12:42:41 +00:00
|
|
|
|
Token SavedCurTok = CurTok;
|
|
|
|
|
Token SavedNextTok = NextTok;
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
2001-07-14 15:56:17 +00:00
|
|
|
|
/* Make sure the line infos for the tokens won't get removed */
|
2004-09-16 12:42:41 +00:00
|
|
|
|
if (SavedCurTok.LI) {
|
|
|
|
|
UseLineInfo (SavedCurTok.LI);
|
2001-07-14 15:56:17 +00:00
|
|
|
|
}
|
2004-09-16 12:42:41 +00:00
|
|
|
|
if (SavedNextTok.LI) {
|
|
|
|
|
UseLineInfo (SavedNextTok.LI);
|
2001-07-14 15:56:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
2004-09-16 12:42:41 +00:00
|
|
|
|
#if 0
|
2002-12-11 15:28:09 +00:00
|
|
|
|
/* Remove the #if from the line */
|
2004-09-16 12:42:41 +00:00
|
|
|
|
SkipWhitespace ();
|
2000-05-28 13:40:48 +00:00
|
|
|
|
S = line;
|
2000-06-21 21:02:44 +00:00
|
|
|
|
while (CurC != '\0') {
|
2004-09-16 12:42:41 +00:00
|
|
|
|
*S++ = CurC;
|
|
|
|
|
NextChar ();
|
2000-06-21 21:02:44 +00:00
|
|
|
|
}
|
2002-12-11 15:28:09 +00:00
|
|
|
|
*S = '\0';
|
2000-06-21 21:02:44 +00:00
|
|
|
|
|
|
|
|
|
/* Start over parsing from line */
|
|
|
|
|
InitLine (line);
|
2004-09-16 12:42:41 +00:00
|
|
|
|
#endif
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
|
|
/* Switch into special preprocessing mode */
|
|
|
|
|
Preprocessing = 1;
|
|
|
|
|
|
|
|
|
|
/* Expand macros in this line */
|
2002-09-29 21:09:47 +00:00
|
|
|
|
PreprocessLine ();
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
2002-12-11 15:28:09 +00:00
|
|
|
|
/* Add two semicolons as sentinels to the line, so the following
|
|
|
|
|
* expression evaluation will eat these two tokens but nothing from
|
|
|
|
|
* the following line.
|
|
|
|
|
*/
|
2004-09-16 12:42:41 +00:00
|
|
|
|
SB_AppendStr (Line, ";;");
|
2002-12-11 15:28:09 +00:00
|
|
|
|
|
2004-09-16 12:42:41 +00:00
|
|
|
|
/* Load CurTok and NextTok with tokens from the new input */
|
2000-06-12 18:31:40 +00:00
|
|
|
|
NextToken ();
|
|
|
|
|
NextToken ();
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
|
|
/* Call the expression parser */
|
2004-06-06 14:48:59 +00:00
|
|
|
|
ConstExpr (hie1, &Expr);
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
|
|
/* End preprocessing mode */
|
|
|
|
|
Preprocessing = 0;
|
|
|
|
|
|
|
|
|
|
/* Reset the old tokens */
|
2004-09-16 12:42:41 +00:00
|
|
|
|
CurTok = SavedCurTok;
|
|
|
|
|
NextTok = SavedNextTok;
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
|
|
/* Set the #if condition according to the expression result */
|
2004-06-06 14:48:59 +00:00
|
|
|
|
return PushIf (Skip, 1, Expr.IVal != 0);
|
2000-05-28 13:40:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2001-09-07 09:54:33 +00:00
|
|
|
|
static int DoIfDef (int skip, int flag)
|
2000-05-28 13:40:48 +00:00
|
|
|
|
/* Process #ifdef if flag == 1, or #ifndef if flag == 0. */
|
|
|
|
|
{
|
|
|
|
|
ident Ident;
|
|
|
|
|
|
2004-09-16 12:42:41 +00:00
|
|
|
|
SkipWhitespace ();
|
2000-06-21 21:02:44 +00:00
|
|
|
|
if (MacName (Ident) == 0) {
|
2000-05-28 13:40:48 +00:00
|
|
|
|
return 0;
|
|
|
|
|
} else {
|
2001-09-07 09:54:33 +00:00
|
|
|
|
return PushIf (skip, flag, IsMacro(Ident));
|
2000-05-28 13:40:48 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2001-09-07 09:54:33 +00:00
|
|
|
|
static void DoInclude (void)
|
2000-05-28 13:40:48 +00:00
|
|
|
|
/* Open an include file. */
|
|
|
|
|
{
|
2000-06-14 21:01:37 +00:00
|
|
|
|
char RTerm;
|
|
|
|
|
unsigned DirSpec;
|
2004-09-16 12:42:41 +00:00
|
|
|
|
StrBuf Filename = STATIC_STRBUF_INITIALIZER;
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
2000-06-14 21:01:37 +00:00
|
|
|
|
|
|
|
|
|
/* Skip blanks */
|
2004-09-16 12:42:41 +00:00
|
|
|
|
SkipWhitespace ();
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
2000-06-14 21:01:37 +00:00
|
|
|
|
/* Get the next char and check for a valid file name terminator. Setup
|
|
|
|
|
* the include directory spec (SYS/USR) by looking at the terminator.
|
|
|
|
|
*/
|
2000-06-21 21:02:44 +00:00
|
|
|
|
switch (CurC) {
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
2000-06-21 21:02:44 +00:00
|
|
|
|
case '\"':
|
|
|
|
|
RTerm = '\"';
|
|
|
|
|
DirSpec = INC_USER;
|
|
|
|
|
break;
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
2000-06-21 21:02:44 +00:00
|
|
|
|
case '<':
|
|
|
|
|
RTerm = '>';
|
|
|
|
|
DirSpec = INC_SYS;
|
|
|
|
|
break;
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
2000-06-21 21:02:44 +00:00
|
|
|
|
default:
|
2000-10-15 19:52:01 +00:00
|
|
|
|
PPError ("`\"' or `<' expected");
|
2000-06-21 21:02:44 +00:00
|
|
|
|
goto Done;
|
2000-05-28 13:40:48 +00:00
|
|
|
|
}
|
2000-06-21 21:02:44 +00:00
|
|
|
|
NextChar ();
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
2004-09-16 12:42:41 +00:00
|
|
|
|
/* Get a copy of the filename */
|
2000-06-21 21:02:44 +00:00
|
|
|
|
while (CurC != '\0' && CurC != RTerm) {
|
2004-09-16 12:42:41 +00:00
|
|
|
|
SB_AppendChar (&Filename, CurC);
|
2000-06-21 21:02:44 +00:00
|
|
|
|
NextChar ();
|
2000-05-28 13:40:48 +00:00
|
|
|
|
}
|
2004-09-16 12:42:41 +00:00
|
|
|
|
SB_Terminate (&Filename);
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
2000-06-21 21:02:44 +00:00
|
|
|
|
/* Check if we got a terminator */
|
|
|
|
|
if (CurC != RTerm) {
|
|
|
|
|
/* No terminator found */
|
2000-10-15 19:52:01 +00:00
|
|
|
|
PPError ("Missing terminator or file name too long");
|
2000-06-21 21:02:44 +00:00
|
|
|
|
goto Done;
|
|
|
|
|
}
|
2000-06-14 21:01:37 +00:00
|
|
|
|
|
|
|
|
|
/* Open the include file */
|
2004-09-16 12:42:41 +00:00
|
|
|
|
OpenIncludeFile (SB_GetConstBuf (&Filename), DirSpec);
|
2000-06-14 21:01:37 +00:00
|
|
|
|
|
|
|
|
|
Done:
|
2004-09-16 12:42:41 +00:00
|
|
|
|
/* Free the allocated filename data */
|
|
|
|
|
DoneStrBuf (&Filename);
|
|
|
|
|
|
2000-06-21 21:02:44 +00:00
|
|
|
|
/* Clear the remaining line so the next input will come from the new
|
|
|
|
|
* file (if open)
|
2000-06-15 19:03:01 +00:00
|
|
|
|
*/
|
|
|
|
|
ClearLine ();
|
2000-05-28 13:40:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2001-09-07 09:54:33 +00:00
|
|
|
|
static void DoError (void)
|
2000-05-28 13:40:48 +00:00
|
|
|
|
/* Print an error */
|
|
|
|
|
{
|
2004-09-16 12:42:41 +00:00
|
|
|
|
SkipWhitespace ();
|
2000-06-21 21:02:44 +00:00
|
|
|
|
if (CurC == '\0') {
|
2000-10-15 19:52:01 +00:00
|
|
|
|
PPError ("Invalid #error directive");
|
2000-05-28 13:40:48 +00:00
|
|
|
|
} else {
|
2004-09-16 12:42:41 +00:00
|
|
|
|
PPError ("#error: %s", SB_GetConstBuf (Line) + SB_GetIndex (Line));
|
2000-05-28 13:40:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
2001-09-07 09:54:33 +00:00
|
|
|
|
/* Clear the rest of line */
|
2000-06-15 19:03:01 +00:00
|
|
|
|
ClearLine ();
|
2000-05-28 13:40:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2002-09-29 21:09:47 +00:00
|
|
|
|
static void DoPragma (void)
|
|
|
|
|
/* Handle a #pragma line by converting the #pragma preprocessor directive into
|
|
|
|
|
* the _Pragma() compiler operator.
|
|
|
|
|
*/
|
|
|
|
|
{
|
|
|
|
|
/* Skip blanks following the #pragma directive */
|
2004-09-16 12:42:41 +00:00
|
|
|
|
SkipWhitespace ();
|
2002-09-29 21:09:47 +00:00
|
|
|
|
|
2004-09-16 12:42:41 +00:00
|
|
|
|
/* Copy the remainder of the line into MLine removing comments and ws */
|
|
|
|
|
SB_Clear (MLine);
|
|
|
|
|
Pass1 (Line, MLine);
|
2002-09-29 21:09:47 +00:00
|
|
|
|
|
|
|
|
|
/* Convert the directive into the operator */
|
2004-09-16 12:42:41 +00:00
|
|
|
|
SB_CopyStr (Line, "_Pragma (");
|
|
|
|
|
SB_Reset (MLine);
|
|
|
|
|
Stringize (MLine, Line);
|
|
|
|
|
SB_AppendChar (Line, ')');
|
2002-09-29 21:09:47 +00:00
|
|
|
|
|
|
|
|
|
/* Initialize reading from line */
|
2004-09-16 12:42:41 +00:00
|
|
|
|
SB_Reset (Line);
|
|
|
|
|
InitLine (Line);
|
2002-09-29 21:09:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2000-06-21 21:02:44 +00:00
|
|
|
|
void Preprocess (void)
|
2000-05-28 13:40:48 +00:00
|
|
|
|
/* Preprocess a line */
|
|
|
|
|
{
|
2002-09-29 21:09:47 +00:00
|
|
|
|
int Skip;
|
|
|
|
|
ident Directive;
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
2004-09-16 12:42:41 +00:00
|
|
|
|
/* Create the output buffer if we don't already have one */
|
|
|
|
|
if (MLine == 0) {
|
|
|
|
|
MLine = NewStrBuf ();
|
|
|
|
|
}
|
|
|
|
|
|
2000-05-28 13:40:48 +00:00
|
|
|
|
/* Skip white space at the beginning of the line */
|
2004-09-16 12:42:41 +00:00
|
|
|
|
SkipWhitespace ();
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
|
|
/* Check for stuff to skip */
|
|
|
|
|
Skip = 0;
|
2000-06-21 21:02:44 +00:00
|
|
|
|
while (CurC == '\0' || CurC == '#' || Skip) {
|
|
|
|
|
|
|
|
|
|
/* Check for preprocessor lines lines */
|
|
|
|
|
if (CurC == '#') {
|
|
|
|
|
NextChar ();
|
2004-09-16 12:42:41 +00:00
|
|
|
|
SkipWhitespace ();
|
2000-06-21 21:02:44 +00:00
|
|
|
|
if (CurC == '\0') {
|
|
|
|
|
/* Ignore the empty preprocessor directive */
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (!IsSym (Directive)) {
|
2000-10-15 19:52:01 +00:00
|
|
|
|
PPError ("Preprocessor directive expected");
|
2000-06-21 21:02:44 +00:00
|
|
|
|
ClearLine ();
|
|
|
|
|
} else {
|
2001-09-06 10:17:52 +00:00
|
|
|
|
switch (FindPPToken (Directive)) {
|
2000-06-21 21:02:44 +00:00
|
|
|
|
|
|
|
|
|
case PP_DEFINE:
|
|
|
|
|
if (!Skip) {
|
2001-09-06 10:17:52 +00:00
|
|
|
|
DefineMacro ();
|
2000-06-21 21:02:44 +00:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2004-09-16 12:42:41 +00:00
|
|
|
|
case PP_ELIF:
|
2002-09-29 21:09:47 +00:00
|
|
|
|
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 */
|
2004-09-16 12:42:41 +00:00
|
|
|
|
IfStack[IfIndex] &= ~IFCOND_NEEDTERM;
|
2002-09-29 21:09:47 +00:00
|
|
|
|
} else {
|
|
|
|
|
PPError ("Duplicate #else/#elif");
|
|
|
|
|
}
|
2001-09-07 09:54:33 +00:00
|
|
|
|
} else {
|
|
|
|
|
PPError ("Unexpected #elif");
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2000-06-21 21:02:44 +00:00
|
|
|
|
case PP_ELSE:
|
2001-09-07 09:54:33 +00:00
|
|
|
|
if (IfIndex >= 0) {
|
2001-12-12 21:45:05 +00:00
|
|
|
|
if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) {
|
2001-09-07 09:54:33 +00:00
|
|
|
|
if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) {
|
|
|
|
|
Skip = !Skip;
|
|
|
|
|
}
|
|
|
|
|
IfStack[IfIndex] |= IFCOND_ELSE;
|
|
|
|
|
} else {
|
|
|
|
|
PPError ("Duplicate #else");
|
|
|
|
|
}
|
2000-06-21 21:02:44 +00:00
|
|
|
|
} else {
|
2000-10-15 19:52:01 +00:00
|
|
|
|
PPError ("Unexpected `#else'");
|
2000-06-21 21:02:44 +00:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PP_ENDIF:
|
2001-09-07 09:54:33 +00:00
|
|
|
|
if (IfIndex >= 0) {
|
|
|
|
|
/* Remove any clauses on top of stack that do not
|
|
|
|
|
* need a terminating #endif.
|
|
|
|
|
*/
|
2002-09-29 21:09:47 +00:00
|
|
|
|
while (IfIndex >= 0 && (IfStack[IfIndex] & IFCOND_NEEDTERM) == 0) {
|
2001-09-07 09:54:33 +00:00
|
|
|
|
--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;
|
2000-06-21 21:02:44 +00:00
|
|
|
|
} else {
|
2000-10-15 19:52:01 +00:00
|
|
|
|
PPError ("Unexpected `#endif'");
|
2000-06-21 21:02:44 +00:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PP_ERROR:
|
|
|
|
|
if (!Skip) {
|
2001-09-07 09:54:33 +00:00
|
|
|
|
DoError ();
|
2000-06-21 21:02:44 +00:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PP_IF:
|
2001-09-07 09:54:33 +00:00
|
|
|
|
Skip = DoIf (Skip);
|
2000-06-21 21:02:44 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PP_IFDEF:
|
2001-09-07 09:54:33 +00:00
|
|
|
|
Skip = DoIfDef (Skip, 1);
|
2000-06-21 21:02:44 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PP_IFNDEF:
|
2001-09-07 09:54:33 +00:00
|
|
|
|
Skip = DoIfDef (Skip, 0);
|
2000-06-21 21:02:44 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PP_INCLUDE:
|
|
|
|
|
if (!Skip) {
|
2001-09-07 09:54:33 +00:00
|
|
|
|
DoInclude ();
|
2000-06-21 21:02:44 +00:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PP_LINE:
|
2004-06-21 22:22:11 +00:00
|
|
|
|
/* Should do something in C99 at least, but we ignore it */
|
|
|
|
|
if (!Skip) {
|
2001-09-07 09:54:33 +00:00
|
|
|
|
ClearLine ();
|
2002-09-29 21:09:47 +00:00
|
|
|
|
}
|
|
|
|
|
break;
|
2000-06-21 21:02:44 +00:00
|
|
|
|
|
|
|
|
|
case PP_PRAGMA:
|
2002-09-29 21:09:47 +00:00
|
|
|
|
if (!Skip) {
|
|
|
|
|
DoPragma ();
|
|
|
|
|
goto Done;
|
|
|
|
|
}
|
2000-06-21 21:02:44 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PP_UNDEF:
|
|
|
|
|
if (!Skip) {
|
2001-09-07 09:54:33 +00:00
|
|
|
|
DoUndef ();
|
2000-06-21 21:02:44 +00:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
2000-10-15 19:52:01 +00:00
|
|
|
|
PPError ("Preprocessor directive expected");
|
2000-06-21 21:02:44 +00:00
|
|
|
|
ClearLine ();
|
|
|
|
|
}
|
2002-09-29 21:09:47 +00:00
|
|
|
|
}
|
2000-05-28 13:40:48 +00:00
|
|
|
|
|
|
|
|
|
}
|
2000-06-14 08:18:19 +00:00
|
|
|
|
if (NextLine () == 0) {
|
2001-09-07 09:54:33 +00:00
|
|
|
|
if (IfIndex >= 0) {
|
2000-10-15 19:52:01 +00:00
|
|
|
|
PPError ("`#endif' expected");
|
2000-05-28 13:40:48 +00:00
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
2004-09-16 12:42:41 +00:00
|
|
|
|
SkipWhitespace ();
|
2000-05-28 13:40:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
2002-09-29 21:09:47 +00:00
|
|
|
|
PreprocessLine ();
|
|
|
|
|
|
2000-05-28 13:40:48 +00:00
|
|
|
|
Done:
|
2004-09-16 12:42:41 +00:00
|
|
|
|
if (Verbosity > 1 && SB_NotEmpty (Line)) {
|
|
|
|
|
printf ("%s(%u): %.*s\n", GetCurrentFile (), GetCurrentLine (),
|
|
|
|
|
SB_GetLen (Line), SB_GetConstBuf (Line));
|
|
|
|
|
}
|
2000-05-28 13:40:48 +00:00
|
|
|
|
}
|
|
|
|
|
|