VBoxCPP.cpp revision 947b3a585d8ba92c2bf1003fb31744be5448819b
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync/* $Id$ */
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync/** @file
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * VBox Build Tool - A mini C Preprocessor.
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync *
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * Purposes to which this preprocessor will be put:
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * - Preprocessig vm.h into dtrace/lib/vm.d so we can access the VM
82391de567696f10b21a762fde6a06fe3c266d28vboxsync * structure (as well as substructures) from DTrace without having
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * to handcraft it all.
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * - Removing \#ifdefs relating to a new feature that has become
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * stable and no longer needs \#ifdef'ing.
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * - Pretty printing preprocessor directives. This will be used by
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * SCM.
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync */
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync/*
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * Copyright (C) 2012 Oracle Corporation
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync *
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
772269936494ffaddd0750ba9e28e805ba81398cvboxsync * available from http://www.virtualbox.org. This file is free software;
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * you can redistribute it and/or modify it under the terms of the GNU
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * General Public License (GPL) as published by the Free Software
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync */
d08f5326f55acc77563fb83341ebf7587242c9b4vboxsync
d08f5326f55acc77563fb83341ebf7587242c9b4vboxsync
d08f5326f55acc77563fb83341ebf7587242c9b4vboxsync/*******************************************************************************
d08f5326f55acc77563fb83341ebf7587242c9b4vboxsync* Header Files *
a58cdee05d01bbbe3d9b08b1c34bf72b6d1b1195vboxsync*******************************************************************************/
a58cdee05d01bbbe3d9b08b1c34bf72b6d1b1195vboxsync#include <VBox/VBoxTpG.h>
a58cdee05d01bbbe3d9b08b1c34bf72b6d1b1195vboxsync
a58cdee05d01bbbe3d9b08b1c34bf72b6d1b1195vboxsync#include <iprt/alloca.h>
82391de567696f10b21a762fde6a06fe3c266d28vboxsync#include <iprt/assert.h>
82391de567696f10b21a762fde6a06fe3c266d28vboxsync#include <iprt/asm.h>
82391de567696f10b21a762fde6a06fe3c266d28vboxsync#include <iprt/ctype.h>
82391de567696f10b21a762fde6a06fe3c266d28vboxsync#include <iprt/err.h>
772269936494ffaddd0750ba9e28e805ba81398cvboxsync#include <iprt/file.h>
e0e0c19eefceaf5d4ec40f9466b58a771f50e799vboxsync#include <iprt/getopt.h>
#include <iprt/initterm.h>
#include <iprt/list.h>
#include <iprt/mem.h>
#include <iprt/message.h>
#include <iprt/path.h>
#include <iprt/stream.h>
#include <iprt/string.h>
#include "scmstream.h"
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** The bitmap type. */
#define VBCPP_BITMAP_TYPE uint64_t
/** The bitmap size as a multiple of VBCPP_BITMAP_TYPE. */
#define VBCPP_BITMAP_SIZE (128 / 64)
/** Checks if a bit is set. */
#define VBCPP_BITMAP_IS_SET(a_bm, a_ch) ASMBitTest(a_bm, (a_ch) & 0x7f)
/** Sets a bit. */
#define VBCPP_BITMAP_SET(a_bm, a_ch) ASMBitSet(a_bm, (a_ch) & 0x7f)
/** Empties the bitmap. */
#define VBCPP_BITMAP_EMPTY(a_bm) do { (a_bm)[0] = 0; (a_bm)[1] = 0; } while (0)
/** Joins to bitmaps by OR'ing their values.. */
#define VBCPP_BITMAP_OR(a_bm1, a_bm2) do { (a_bm1)[0] |= (a_bm2)[0]; (a_bm1)[1] |= (a_bm2)[1]; } while (0)
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/** Pointer to the C preprocessor instance data. */
typedef struct VBCPP *PVBCPP;
/**
* Variable string buffer (very simple version of SCMSTREAM).
*/
typedef struct VBCPPSTRBUF
{
/** The preprocessor instance (for error reporting). */
struct VBCPP *pThis;
/** The length of the string in the buffer. */
size_t cchBuf;
/** The string storage. */
char *pszBuf;
/** Allocated buffer space. */
size_t cbBufAllocated;
} VBCPPSTRBUF;
/** Pointer to a variable string buffer. */
typedef VBCPPSTRBUF *PVBCPPSTRBUF;
/**
* The preprocessor mode.
*/
typedef enum VBCPPMODE
{
kVBCppMode_Invalid = 0,
kVBCppMode_Standard,
kVBCppMode_Selective,
kVBCppMode_SelectiveD,
kVBCppMode_End
} VBCPPMODE;
/**
* A macro (aka define).
*/
typedef struct VBCPPMACRO
{
/** The string space core. */
RTSTRSPACECORE Core;
#if 0
/** For linking macros that have the fExpanding flag set. */
struct VBCPPMACRO *pUpExpanding;
#endif
/** Whether it's a function. */
bool fFunction;
/** Variable argument count. */
bool fVarArg;
/** Set if originating on the command line. */
bool fCmdLine;
/** Set if this macro is currently being expanded and should not be
* recursively applied. */
bool fExpanding;
/** The number of known arguments. */
uint32_t cArgs;
/** Pointer to a list of argument names. */
const char **papszArgs;
/** Lead character bitmap for the argument names. */
VBCPP_BITMAP_TYPE bmArgs[VBCPP_BITMAP_SIZE];
/** The value length. */
size_t cchValue;
/** The define value. (This is followed by the name and arguments.) */
char szValue[1];
} VBCPPMACRO;
/** Pointer to a macro. */
typedef VBCPPMACRO *PVBCPPMACRO;
/**
* Macro expansion data.
*/
typedef struct VBCPPMACROEXP
{
/** The expansion buffer. */
VBCPPSTRBUF StrBuf;
#if 0
/** List of expanding macros (Stack). */
PVBCPPMACRO pMacroStack;
#endif
/** The input stream (in case we want to look for parameter lists). */
PSCMSTREAM pStrmInput;
/** Array of argument values. Used when expanding function style macros. */
char **papszArgs;
/** The number of argument values current in papszArgs. */
uint32_t cArgs;
/** The number of argument values papszArgs can currently hold */
uint32_t cArgsAlloced;
} VBCPPMACROEXP;
/** Pointer to macro expansion data. */
typedef VBCPPMACROEXP *PVBCPPMACROEXP;
/**
* The vbcppMacroExpandReScan mode of operation.
*/
typedef enum VBCPPMACRORESCANMODE
{
/** Invalid mode. */
kMacroReScanMode_Invalid = 0,
/** Normal expansion mode. */
kMacroReScanMode_Normal,
/** Replaces known macros and heeds the 'defined' operator. */
kMacroReScanMode_Expression,
/** End of valid modes. */
kMacroReScanMode_End
} VBCPPMACRORESCANMODE;
/**
* Expression node type.
*/
typedef enum VBCPPEXPRKIND
{
kVBCppExprKind_Invalid = 0,
kVBCppExprKind_Unary,
kVBCppExprKind_Binary,
kVBCppExprKind_Ternary,
kVBCppExprKind_SignedValue,
kVBCppExprKind_UnsignedValue,
kVBCppExprKind_End
} VBCPPEXPRKIND;
/** Macro used for the precedence field. */
#define VBCPPOP_PRECEDENCE(a_iPrecedence) ((a_iPrecedence) << 8)
/** Mask for getting the precedence field value. */
#define VBCPPOP_PRECEDENCE_MASK 0xff00
/** Operator associativity - Left to right. */
#define VBCPPOP_L2R (1 << 16)
/** Operator associativity - Right to left. */
#define VBCPPOP_R2L (2 << 16)
/**
* Unary operators.
*/
typedef enum VBCPPUNARYOP
{
kVBCppUnaryOp_Invalid = 0,
kVBCppUnaryOp_Pluss = VBCPPOP_R2L | VBCPPOP_PRECEDENCE( 3) | 5,
kVBCppUnaryOp_Minus = VBCPPOP_R2L | VBCPPOP_PRECEDENCE( 3) | 6,
kVBCppUnaryOp_LogicalNot = VBCPPOP_R2L | VBCPPOP_PRECEDENCE( 3) | 7,
kVBCppUnaryOp_BitwiseNot = VBCPPOP_R2L | VBCPPOP_PRECEDENCE( 3) | 8,
kVBCppUnaryOp_Parenthesis = VBCPPOP_R2L | VBCPPOP_PRECEDENCE(15) | 9,
kVBCppUnaryOp_End
} VBCPPUNARYOP;
/**
* Binary operators.
*/
typedef enum VBCPPBINARYOP
{
kVBCppBinary_Invalid = 0,
kVBCppBinary_Multiplication = VBCPPOP_L2R | VBCPPOP_PRECEDENCE( 5) | 2,
kVBCppBinary_Division = VBCPPOP_L2R | VBCPPOP_PRECEDENCE( 5) | 4,
kVBCppBinary_Modulo = VBCPPOP_L2R | VBCPPOP_PRECEDENCE( 5) | 5,
kVBCppBinary_Addition = VBCPPOP_L2R | VBCPPOP_PRECEDENCE( 6) | 6,
kVBCppBinary_Subtraction = VBCPPOP_L2R | VBCPPOP_PRECEDENCE( 6) | 7,
kVBCppBinary_LeftShift = VBCPPOP_L2R | VBCPPOP_PRECEDENCE( 7) | 8,
kVBCppBinary_RightShift = VBCPPOP_L2R | VBCPPOP_PRECEDENCE( 7) | 9,
kVBCppBinary_LessThan = VBCPPOP_L2R | VBCPPOP_PRECEDENCE( 8) | 10,
kVBCppBinary_LessThanOrEqual = VBCPPOP_L2R | VBCPPOP_PRECEDENCE( 8) | 11,
kVBCppBinary_GreaterThan = VBCPPOP_L2R | VBCPPOP_PRECEDENCE( 8) | 12,
kVBCppBinary_GreaterThanOrEqual = VBCPPOP_L2R | VBCPPOP_PRECEDENCE( 8) | 13,
kVBCppBinary_EqualTo = VBCPPOP_L2R | VBCPPOP_PRECEDENCE( 9) | 14,
kVBCppBinary_NotEqualTo = VBCPPOP_L2R | VBCPPOP_PRECEDENCE( 9) | 15,
kVBCppBinary_BitwiseAnd = VBCPPOP_L2R | VBCPPOP_PRECEDENCE(10) | 16,
kVBCppBinary_BitwiseXor = VBCPPOP_L2R | VBCPPOP_PRECEDENCE(11) | 17,
kVBCppBinary_BitwiseOr = VBCPPOP_L2R | VBCPPOP_PRECEDENCE(12) | 18,
kVBCppBinary_LogicalAnd = VBCPPOP_L2R | VBCPPOP_PRECEDENCE(13) | 19,
kVBCppBinary_LogicalOr = VBCPPOP_L2R | VBCPPOP_PRECEDENCE(14) | 20,
kVBCppBinary_End
} VBCPPBINARYOP;
/** The precedence of the ternary operator (expr ? true : false). */
#define VBCPPTERNAROP_PRECEDENCE VBCPPOP_PRECEDENCE(16)
/** Pointer to an expression parsing node. */
typedef struct VBCPPEXPR *PVBCPPEXPR;
/**
* Expression parsing node.
*/
typedef struct VBCPPEXPR
{
/** Parent expression. */
PVBCPPEXPR pParent;
/** Whether the expression is complete or not. */
bool fComplete;
/** The kind of expression. */
VBCPPEXPRKIND enmKind;
/** Kind specific content. */
union
{
/** kVBCppExprKind_Unary */
struct
{
VBCPPUNARYOP enmOperator;
PVBCPPEXPR pArg;
} Unary;
/** kVBCppExprKind_Binary */
struct
{
VBCPPBINARYOP enmOperator;
PVBCPPEXPR pLeft;
PVBCPPEXPR pRight;
} Binary;
/** kVBCppExprKind_Ternary */
struct
{
PVBCPPEXPR pExpr;
PVBCPPEXPR pTrue;
PVBCPPEXPR pFalse;
} Ternary;
/** kVBCppExprKind_SignedValue */
struct
{
int64_t s64;
} SignedValue;
/** kVBCppExprKind_UnsignedValue */
struct
{
uint64_t u64;
} UnsignedValue;
} u;
} VBCPPEXPR;
/**
* Operator return statuses.
*/
typedef enum VBCPPEXPRRET
{
kExprRet_Error = -1,
kExprRet_Ok = 0,
kExprRet_UnaryOperator,
kExprRet_Value,
kExprRet_EndOfExpr,
kExprRet_End
} VBCPPEXPRRET;
/**
* Expression parser context.
*/
typedef struct VBCPPEXPRPARSER
{
/** The current expression posistion. */
const char *pszCur;
/** The root node. */
PVBCPPEXPR pRoot;
/** The current expression node. */
PVBCPPEXPR pCur;
/** Where to insert the next expression. */
PVBCPPEXPR *ppCur;
/** The expression. */
const char *pszExpr;
/** The number of undefined macros we've encountered while parsing. */
size_t cUndefined;
/** Pointer to the C preprocessor instance. */
PVBCPP pThis;
} VBCPPEXPRPARSER;
/** Pointer to an expression parser context. */
typedef VBCPPEXPRPARSER *PVBCPPEXPRPARSER;
/**
* Evaluation result.
*/
typedef enum VBCPPEVAL
{
kVBCppEval_Invalid = 0,
kVBCppEval_True,
kVBCppEval_False,
kVBCppEval_Undecided,
kVBCppEval_End
} VBCPPEVAL;
/**
* The condition kind.
*/
typedef enum VBCPPCONDKIND
{
kVBCppCondKind_Invalid = 0,
/** \#if expr */
kVBCppCondKind_If,
/** \#ifdef define */
kVBCppCondKind_IfDef,
/** \#ifndef define */
kVBCppCondKind_IfNDef,
/** \#elif expr */
kVBCppCondKind_ElIf,
/** The end of valid values. */
kVBCppCondKind_End
} VBCPPCONDKIND;
/**
* Conditional stack entry.
*/
typedef struct VBCPPCOND
{
/** The next conditional on the stack. */
struct VBCPPCOND *pUp;
/** The kind of conditional. This changes on encountering \#elif. */
VBCPPCONDKIND enmKind;
/** Evaluation result. */
VBCPPEVAL enmResult;
/** The evaluation result of the whole stack. */
VBCPPEVAL enmStackResult;
/** Whether we've seen the last else. */
bool fSeenElse;
/** Set if we have an else if which has already been decided. */
bool fElIfDecided;
/** The nesting level of this condition. */
uint16_t iLevel;
/** The nesting level of this condition wrt the ones we keep. */
uint16_t iKeepLevel;
/** The condition string. (Points within the stream buffer.) */
const char *pchCond;
/** The condition length. */
size_t cchCond;
} VBCPPCOND;
/** Pointer to a conditional stack entry. */
typedef VBCPPCOND *PVBCPPCOND;
/**
* Input buffer stack entry.
*/
typedef struct VBCPPINPUT
{
/** Pointer to the next input on the stack. */
struct VBCPPINPUT *pUp;
/** The input stream. */
SCMSTREAM StrmInput;
/** Pointer into szName to the part which was specified. */
const char *pszSpecified;
/** The input file name with include path. */
char szName[1];
} VBCPPINPUT;
/** Pointer to a input buffer stack entry */
typedef VBCPPINPUT *PVBCPPINPUT;
/**
* The action to take with \#include.
*/
typedef enum VBCPPINCLUDEACTION
{
kVBCppIncludeAction_Invalid = 0,
kVBCppIncludeAction_Include,
kVBCppIncludeAction_PassThru,
kVBCppIncludeAction_Drop,
kVBCppIncludeAction_End
} VBCPPINCLUDEACTION;
/**
* C Preprocessor instance data.
*/
typedef struct VBCPP
{
/** @name Options
* @{ */
/** The preprocessing mode. */
VBCPPMODE enmMode;
/** Whether to keep comments. */
bool fKeepComments;
/** Whether to respect source defines. */
bool fRespectSourceDefines;
/** Whether to let source defines overrides the ones on the command
* line. */
bool fAllowRedefiningCmdLineDefines;
/** Whether to pass thru defines. */
bool fPassThruDefines;
/** Whether to allow undecided conditionals. */
bool fUndecidedConditionals;
/** Whether to pass thru D pragmas. */
bool fPassThruPragmaD;
/** Whether to pass thru STD pragmas. */
bool fPassThruPragmaSTD;
/** Whether to pass thru other pragmas. */
bool fPassThruPragmaOther;
/** Whether to remove dropped lines from the output. */
bool fRemoveDroppedLines;
/** Whether to preforme line splicing.
* @todo implement line splicing */
bool fLineSplicing;
/** What to do about include files. */
VBCPPINCLUDEACTION enmIncludeAction;
/** The number of include directories. */
uint32_t cIncludes;
/** Array of directories to search for include files. */
char **papszIncludes;
/** The name of the input file. */
const char *pszInput;
/** The name of the output file. NULL if stdout. */
const char *pszOutput;
/** @} */
/** The define string space. */
RTSTRSPACE StrSpace;
/** The string space holding explicitly undefined macros for selective
* preprocessing runs. */
RTSTRSPACE UndefStrSpace;
/** Indicates whether a C-word might need expansion.
* The bitmap is indexed by C-word lead character. Bits that are set
* indicates that the lead character is used in a \#define that we know and
* should expand. */
VBCPP_BITMAP_TYPE bmDefined[VBCPP_BITMAP_SIZE];
/** The current depth of the conditional stack. */
uint32_t cCondStackDepth;
/** Conditional stack. */
PVBCPPCOND pCondStack;
/** The current condition evaluates to kVBCppEval_False, don't output. */
bool fIf0Mode;
/** Just dropped a line and should maybe drop the current line. */
bool fJustDroppedLine;
/** Whether the current line could be a preprocessor line.
* This is set when EOL is encountered and cleared again when a
* non-comment-or-space character is encountered. See vbcppPreprocess. */
bool fMaybePreprocessorLine;
/** The input stack depth */
uint32_t cInputStackDepth;
/** The input buffer stack. */
PVBCPPINPUT pInputStack;
/** The output stream. */
SCMSTREAM StrmOutput;
/** The status of the whole job, as far as we know. */
RTEXITCODE rcExit;
/** Whether StrmOutput is valid (for vbcppTerm). */
bool fStrmOutputValid;
} VBCPP;
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
static PVBCPPMACRO vbcppMacroLookup(PVBCPP pThis, const char *pszDefine, size_t cchDefine);
static RTEXITCODE vbcppMacroExpandIt(PVBCPP pThis, PVBCPPMACROEXP pExp, size_t offMacro, PVBCPPMACRO pMacro, size_t offParameters);
static RTEXITCODE vbcppMacroExpandReScan(PVBCPP pThis, PVBCPPMACROEXP pExp, VBCPPMACRORESCANMODE enmMode, size_t *pcReplacements);
static void vbcppMacroExpandCleanup(PVBCPPMACROEXP pExp);
/*
*
*
* Message Handling.
* Message Handling.
* Message Handling.
* Message Handling.
* Message Handling.
*
*
*/
/**
* Displays an error message.
*
* @returns RTEXITCODE_FAILURE
* @param pThis The C preprocessor instance.
* @param pszMsg The message.
* @param va Message arguments.
*/
static RTEXITCODE vbcppErrorV(PVBCPP pThis, const char *pszMsg, va_list va)
{
NOREF(pThis);
if (pThis->pInputStack)
{
PSCMSTREAM pStrm = &pThis->pInputStack->StrmInput;
size_t const off = ScmStreamTell(pStrm);
size_t const iLine = ScmStreamTellLine(pStrm);
ScmStreamSeekByLine(pStrm, iLine);
size_t const offLine = ScmStreamTell(pStrm);
RTPrintf("%s:%d:%zd: error: %N.\n", pThis->pInputStack->szName, iLine + 1, off - offLine + 1, pszMsg, va);
size_t cchLine;
SCMEOL enmEof;
const char *pszLine = ScmStreamGetLineByNo(pStrm, iLine, &cchLine, &enmEof);
if (pszLine)
RTPrintf(" %.*s\n"
" %*s^\n",
cchLine, pszLine, off - offLine, "");
ScmStreamSeekAbsolute(pStrm, off);
}
else
RTMsgErrorV(pszMsg, va);
return pThis->rcExit = RTEXITCODE_FAILURE;
}
/**
* Displays an error message.
*
* @returns RTEXITCODE_FAILURE
* @param pThis The C preprocessor instance.
* @param pszMsg The message.
* @param ... Message arguments.
*/
static RTEXITCODE vbcppError(PVBCPP pThis, const char *pszMsg, ...)
{
va_list va;
va_start(va, pszMsg);
RTEXITCODE rcExit = vbcppErrorV(pThis, pszMsg, va);
va_end(va);
return rcExit;
}
/**
* Displays an error message.
*
* @returns RTEXITCODE_FAILURE
* @param pThis The C preprocessor instance.
* @param pszPos Pointer to the offending character.
* @param pszMsg The message.
* @param ... Message arguments.
*/
static RTEXITCODE vbcppErrorPos(PVBCPP pThis, const char *pszPos, const char *pszMsg, ...)
{
NOREF(pszPos); NOREF(pThis);
va_list va;
va_start(va, pszMsg);
RTMsgErrorV(pszMsg, va);
va_end(va);
return pThis->rcExit = RTEXITCODE_FAILURE;
}
/*
*
*
* Variable String Buffers.
* Variable String Buffers.
* Variable String Buffers.
* Variable String Buffers.
* Variable String Buffers.
*
*
*/
/**
* Initializes a string buffer.
*
* @param pStrBuf The buffer structure to initialize.
* @param pThis The C preprocessor instance.
*/
static void vbcppStrBufInit(PVBCPPSTRBUF pStrBuf, PVBCPP pThis)
{
pStrBuf->pThis = pThis;
pStrBuf->cchBuf = 0;
pStrBuf->cbBufAllocated = 0;
pStrBuf->pszBuf = NULL;
}
/**
* Deletes a string buffer.
*
* @param pStrBuf Pointer to the string buffer.
*/
static void vbcppStrBufDelete(PVBCPPSTRBUF pStrBuf)
{
RTMemFree(pStrBuf->pszBuf);
pStrBuf->pszBuf = NULL;
}
/**
* Ensures that sufficient bufferspace is available, growing the buffer if
* necessary.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pStrBuf Pointer to the string buffer.
* @param cbMin The minimum buffer size.
*/
static RTEXITCODE vbcppStrBufGrow(PVBCPPSTRBUF pStrBuf, size_t cbMin)
{
if (pStrBuf->cbBufAllocated >= cbMin)
return RTEXITCODE_SUCCESS;
size_t cbNew = pStrBuf->cbBufAllocated * 2;
if (cbNew < cbMin)
cbNew = RT_ALIGN_Z(cbMin, _1K);
void *pv = RTMemRealloc(pStrBuf->pszBuf, cbNew);
if (!pv)
return vbcppError(pStrBuf->pThis, "out of memory (%zu bytes)", cbNew);
pStrBuf->pszBuf = (char *)pv;
pStrBuf->cbBufAllocated = cbNew;
return RTEXITCODE_SUCCESS;
}
/**
* Appends a substring.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pStrBuf Pointer to the string buffer.
* @param pchSrc Pointer to the first character in the substring.
* @param cchSrc The length of the substring.
*/
static RTEXITCODE vbcppStrBufAppendN(PVBCPPSTRBUF pStrBuf, const char *pchSrc, size_t cchSrc)
{
size_t cchBuf = pStrBuf->cchBuf;
if (cchBuf + cchSrc + 1 > pStrBuf->cbBufAllocated)
{
RTEXITCODE rcExit = vbcppStrBufGrow(pStrBuf, cchBuf + cchSrc + 1);
if (rcExit != RTEXITCODE_SUCCESS)
return rcExit;
}
memcpy(&pStrBuf->pszBuf[cchBuf], pchSrc, cchSrc);
cchBuf += cchSrc;
pStrBuf->pszBuf[cchBuf] = '\0';
pStrBuf->cchBuf = cchBuf;
return RTEXITCODE_SUCCESS;
}
/**
* Appends a character.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pStrBuf Pointer to the string buffer.
* @param ch The charater to append.
*/
static RTEXITCODE vbcppStrBufAppendCh(PVBCPPSTRBUF pStrBuf, char ch)
{
size_t cchBuf = pStrBuf->cchBuf;
if (cchBuf + 2 > pStrBuf->cbBufAllocated)
{
RTEXITCODE rcExit = vbcppStrBufGrow(pStrBuf, cchBuf + 2);
if (rcExit != RTEXITCODE_SUCCESS)
return rcExit;
}
pStrBuf->pszBuf[cchBuf++] = ch;
pStrBuf->pszBuf[cchBuf] = '\0';
pStrBuf->cchBuf = cchBuf;
return RTEXITCODE_SUCCESS;
}
/**
* Appends a string to the buffer.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pStrBuf Pointer to the string buffer.
* @param psz The string to append.
*/
static RTEXITCODE vbcppStrBufAppend(PVBCPPSTRBUF pStrBuf, const char *psz)
{
return vbcppStrBufAppendN(pStrBuf, psz, strlen(psz));
}
/**
* Gets the last char in the buffer.
*
* @returns Last character, 0 if empty.
* @param pStrBuf Pointer to the string buffer.
*/
static char vbcppStrBufLastCh(PVBCPPSTRBUF pStrBuf)
{
if (!pStrBuf->cchBuf)
return '\0';
return pStrBuf->pszBuf[pStrBuf->cchBuf - 1];
}
/*
*
*
* C Identifier/Word Parsing.
* C Identifier/Word Parsing.
* C Identifier/Word Parsing.
* C Identifier/Word Parsing.
* C Identifier/Word Parsing.
*
*
*/
/**
* Checks if the given character is a valid C identifier lead character.
*
* @returns true / false.
* @param ch The character to inspect.
*/
DECLINLINE(bool) vbcppIsCIdentifierLeadChar(char ch)
{
return RT_C_IS_ALPHA(ch)
|| ch == '_';
}
/**
* Checks if the given character is a valid C identifier character.
*
* @returns true / false.
* @param ch The character to inspect.
*/
DECLINLINE(bool) vbcppIsCIdentifierChar(char ch)
{
return RT_C_IS_ALNUM(ch)
|| ch == '_';
}
/**
*
* @returns @c true if valid, @c false if not. Error message already displayed
* on failure.
* @param pThis The C preprocessor instance.
* @param pchIdentifier The start of the identifier to validate.
* @param cchIdentifier The length of the identifier. RTSTR_MAX if not
* known.
*/
static bool vbcppValidateCIdentifier(PVBCPP pThis, const char *pchIdentifier, size_t cchIdentifier)
{
if (cchIdentifier == RTSTR_MAX)
cchIdentifier = strlen(pchIdentifier);
if (cchIdentifier == 0)
{
vbcppErrorPos(pThis, pchIdentifier, "Zero length identifier");
return false;
}
if (!vbcppIsCIdentifierLeadChar(*pchIdentifier))
{
vbcppErrorPos(pThis, pchIdentifier, "Bad lead chararacter in identifier: '%.*s'", cchIdentifier, pchIdentifier);
return false;
}
for (size_t off = 1; off < cchIdentifier; off++)
{
if (!vbcppIsCIdentifierChar(pchIdentifier[off]))
{
vbcppErrorPos(pThis, pchIdentifier + off, "Illegal chararacter in identifier: '%.*s' (#%zu)", cchIdentifier, pchIdentifier, off + 1);
return false;
}
}
return true;
}
#if 0
/**
* Checks if the given character is valid C punctuation.
*
* @returns true / false.
* @param ch The character to inspect.
*/
DECLINLINE(bool) vbcppIsCPunctuationLeadChar(char ch)
{
switch (ch)
{
case '!':
case '#':
case '%':
case '&':
case '(':
case ')':
case '*':
case '+':
case ',':
case '-':
case '.':
case '/':
case ':':
case ';':
case '<':
case '=':
case '>':
case '?':
case '[':
case ']':
case '^':
case '{':
case '|':
case '}':
case '~':
return true;
default:
return false;
}
}
/**
* Checks if the given string start with valid C punctuation.
*
* @returns 0 if not, otherwise the length of the punctuation.
* @param pch The which start we should evaluate.
* @param cchMax The maximum string length.
*/
static size_t vbcppIsCPunctuationLeadChar(const char *psz, size_t cchMax)
{
if (!cchMax)
return 0;
switch (psz[0])
{
case '!':
case '*':
case '/':
case '=':
case '^':
if (cchMax >= 2 && psz[1] == '=')
return 2;
return 1;
case '#':
if (cchMax >= 2 && psz[1] == '#')
return 2;
return 1;
case '%':
if (cchMax >= 2 && (psz[1] == '=' || psz[1] == '>'))
return 2;
if (cchMax >= 2 && psz[1] == ':')
{
if (cchMax >= 4 && psz[2] == '%' && psz[3] == ':')
return 4;
return 2;
}
return 1;
case '&':
if (cchMax >= 2 && (psz[1] == '=' || psz[1] == '&'))
return 2;
return 1;
case '(':
case ')':
case ',':
case '?':
case '[':
case ']':
case '{':
case '}':
return 1;
case '+':
if (cchMax >= 2 && (psz[1] == '=' || psz[1] == '+'))
return 2;
return 1;
case '-':
if (cchMax >= 2 && (psz[1] == '=' || psz[1] == '-' || psz[1] == '>'))
return 2;
return 1;
case ':':
if (cchMax >= 2 && psz[1] == '>')
return 2;
return 1;
case ';':
return 1;
case '<':
if (cchMax >= 2 && psz[1] == '<')
{
if (cchMax >= 3 && psz[2] == '=')
return 3;
return 2;
}
if (cchMax >= 2 && (psz[1] == '=' || psz[1] == ':' || psz[1] == '%'))
return 2;
return 1;
case '.':
if (cchMax >= 3 && psz[1] == '.' && psz[2] == '.')
return 3;
return 1;
case '>':
if (cchMax >= 2 && psz[1] == '>')
{
if (cchMax >= 3 && psz[2] == '=')
return 3;
return 2;
}
if (cchMax >= 2 && psz[1] == '=')
return 2;
return 1;
case '|':
if (cchMax >= 2 && (psz[1] == '=' || psz[1] == '|'))
return 2;
return 1;
case '~':
return 1;
default:
return 0;
}
}
#endif
/*
*
*
* Output
* Output
* Output
* Output
* Output
*
*
*/
/**
* Outputs a character.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param ch The character to output.
*/
static RTEXITCODE vbcppOutputCh(PVBCPP pThis, char ch)
{
int rc = ScmStreamPutCh(&pThis->StrmOutput, ch);
if (RT_SUCCESS(rc))
return RTEXITCODE_SUCCESS;
return vbcppError(pThis, "Output error: %Rrc", rc);
}
/**
* Outputs a string.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pch The string.
* @param cch The number of characters to write.
*/
static RTEXITCODE vbcppOutputWrite(PVBCPP pThis, const char *pch, size_t cch)
{
int rc = ScmStreamWrite(&pThis->StrmOutput, pch, cch);
if (RT_SUCCESS(rc))
return RTEXITCODE_SUCCESS;
return vbcppError(pThis, "Output error: %Rrc", rc);
}
static RTEXITCODE vbcppOutputComment(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart, size_t cchOutputted,
size_t cchMinIndent)
{
size_t offCur = ScmStreamTell(pStrmInput);
if (offStart < offCur)
{
int rc = ScmStreamSeekAbsolute(pStrmInput, offStart);
AssertRCReturn(rc, vbcppError(pThis, "Input seek error: %Rrc", rc));
/*
* Use the same indent, if possible.
*/
size_t cchIndent = offStart - ScmStreamTellOffsetOfLine(pStrmInput, ScmStreamTellLine(pStrmInput));
if (cchOutputted < cchIndent)
rc = ScmStreamPrintf(&pThis->StrmOutput, "%*s", cchIndent - cchOutputted, "");
else
rc = ScmStreamPutCh(&pThis->StrmOutput, ' ');
if (RT_FAILURE(rc))
return vbcppError(pThis, "Output error: %Rrc", rc);
/*
* Copy the bytes.
*/
while (ScmStreamTell(pStrmInput) < offCur)
{
unsigned ch = ScmStreamGetCh(pStrmInput);
if (ch == ~(unsigned)0)
return vbcppError(pThis, "Input error: %Rrc", rc);
rc = ScmStreamPutCh(&pThis->StrmOutput, ch);
if (RT_FAILURE(rc))
return vbcppError(pThis, "Output error: %Rrc", rc);
}
}
return RTEXITCODE_SUCCESS;
}
/*
*
*
* Input
* Input
* Input
* Input
* Input
*
*
*/
/**
* Skips white spaces, including escaped new-lines.
*
* @param pStrmInput The input stream.
*/
static void vbcppProcessSkipWhiteAndEscapedEol(PSCMSTREAM pStrmInput)
{
unsigned chPrev = ~(unsigned)0;
unsigned ch;
while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
{
if (ch == '\r' || ch == '\n')
{
if (chPrev != '\\')
break;
chPrev = ch;
ScmStreamSeekByLine(pStrmInput, ScmStreamTellLine(pStrmInput) + 1);
}
else if (RT_C_IS_SPACE(ch))
{
ch = chPrev;
ch = ScmStreamGetCh(pStrmInput);
Assert(ch == chPrev);
}
else
break;
}
}
/**
* Skips white spaces, escaped new-lines and multi line comments.
*
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
*/
static RTEXITCODE vbcppProcessSkipWhiteEscapedEolAndComments(PVBCPP pThis, PSCMSTREAM pStrmInput)
{
unsigned chPrev = ~(unsigned)0;
unsigned ch;
while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
{
if (!RT_C_IS_SPACE(ch))
{
/* Multi-line Comment? */
if (ch != '/')
break; /* most definitely, not. */
size_t offSaved = ScmStreamTell(pStrmInput);
ScmStreamGetCh(pStrmInput);
if (ScmStreamPeekCh(pStrmInput) != '*')
{
ScmStreamSeekAbsolute(pStrmInput, offSaved);
break; /* no */
}
/* Skip to the end of the comment. */
while ((ch = ScmStreamGetCh(pStrmInput)) != ~(unsigned)0)
{
if (ch == '*')
{
ch = ScmStreamGetCh(pStrmInput);
if (ch == '/')
break;
if (ch == ~(unsigned)0)
break;
}
}
if (ch == ~(unsigned)0)
return vbcppError(pThis, "unterminated multi-line comment");
chPrev = '/';
}
/* New line (also matched by RT_C_IS_SPACE). */
else if (ch == '\r' || ch == '\n')
{
/* Stop if not escaped. */
if (chPrev != '\\')
break;
chPrev = ch;
ScmStreamSeekByLine(pStrmInput, ScmStreamTellLine(pStrmInput) + 1);
}
/* Real space char. */
else
{
chPrev = ch;
ch = ScmStreamGetCh(pStrmInput);
Assert(ch == chPrev);
}
}
return RTEXITCODE_SUCCESS;
}
/**
* Skips white spaces, escaped new-lines, and multi line comments, then checking
* that we're at the end of a line.
*
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
*/
static RTEXITCODE vbcppProcessSkipWhiteEscapedEolAndCommentsCheckEol(PVBCPP pThis, PSCMSTREAM pStrmInput)
{
RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
if (rcExit == RTEXITCODE_SUCCESS)
{
unsigned ch = ScmStreamPeekCh(pStrmInput);
if ( ch != ~(unsigned)0
&& ch != '\r'
&& ch != '\n')
rcExit = vbcppError(pThis, "Did not expected anything more on this line");
}
return rcExit;
}
/**
* Skips white spaces.
*
* @returns The current location upon return.
* @param pStrmInput The input stream.
*/
static size_t vbcppProcessSkipWhite(PSCMSTREAM pStrmInput)
{
unsigned ch;
while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
{
if (!RT_C_IS_SPACE(ch) || ch == '\r' || ch == '\n')
break;
unsigned chCheck = ScmStreamGetCh(pStrmInput);
AssertBreak(chCheck == ch);
}
return ScmStreamTell(pStrmInput);
}
/**
* Looks for a left parenthesis in the input stream.
*
* Used during macro expansion. Will ignore comments, newlines and other
* whitespace.
*
* @retval true if found. The stream position at opening parenthesis.
* @retval false if not found. The stream position is unchanged.
*
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
*/
static bool vbcppInputLookForLeftParenthesis(PVBCPP pThis, PSCMSTREAM pStrmInput)
{
size_t offSaved = ScmStreamTell(pStrmInput);
RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
unsigned ch = ScmStreamPeekCh(pStrmInput);
if (ch == '(')
return true;
int rc = ScmStreamSeekAbsolute(pStrmInput, offSaved);
AssertFatalRC(rc);
return false;
}
/**
* Skips input until the real end of the current directive line has been
* reached.
*
* This includes multiline comments starting on the same line
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
* @param poffComment Where to note down the position of the final
* comment. Optional.
*/
static RTEXITCODE vbcppInputSkipToEndOfDirectiveLine(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t *poffComment)
{
if (poffComment)
*poffComment = ~(size_t)0;
RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
bool fInComment = false;
unsigned chPrev = 0;
unsigned ch;
while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
{
if (ch == '\r' || ch == '\n')
{
if (chPrev == '\\')
{
ScmStreamSeekByLine(pStrmInput, ScmStreamTellLine(pStrmInput) + 1);
continue;
}
if (!fInComment)
break;
/* The expression continues after multi-line comments. Cool. :-) */
}
else if (!fInComment)
{
if (chPrev == '/' && ch == '*' )
{
fInComment = true;
if (poffComment)
*poffComment = ScmStreamTell(pStrmInput) - 1;
}
else if (chPrev == '/' && ch == '/')
{
if (poffComment)
*poffComment = ScmStreamTell(pStrmInput) - 1;
rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
break; /* done */
}
}
else if (ch == '/' && chPrev == '*')
fInComment = false;
/* advance */
chPrev = ch;
ch = ScmStreamGetCh(pStrmInput); Assert(ch == chPrev);
}
return rcExit;
}
/**
* Processes a multi-line comment.
*
* Must either string the comment or keep it. If the latter, we must refrain
* from replacing C-words in it.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
*/
static RTEXITCODE vbcppProcessMultiLineComment(PVBCPP pThis, PSCMSTREAM pStrmInput)
{
/* The open comment sequence. */
ScmStreamGetCh(pStrmInput); /* '*' */
RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
if ( pThis->fKeepComments
&& !pThis->fIf0Mode)
rcExit = vbcppOutputWrite(pThis, "/*", 2);
/* The comment.*/
unsigned ch;
while ( rcExit == RTEXITCODE_SUCCESS
&& (ch = ScmStreamGetCh(pStrmInput)) != ~(unsigned)0 )
{
if (ch == '*')
{
/* Closing sequence? */
unsigned ch2 = ScmStreamPeekCh(pStrmInput);
if (ch2 == '/')
{
ScmStreamGetCh(pStrmInput);
if ( pThis->fKeepComments
&& !pThis->fIf0Mode)
rcExit = vbcppOutputWrite(pThis, "*/", 2);
break;
}
}
if (ch == '\r' || ch == '\n')
{
if ( ( pThis->fKeepComments
&& !pThis->fIf0Mode)
|| !pThis->fRemoveDroppedLines
|| !ScmStreamIsAtStartOfLine(&pThis->StrmOutput))
rcExit = vbcppOutputCh(pThis, ch);
pThis->fJustDroppedLine = false;
pThis->fMaybePreprocessorLine = true;
}
else if ( pThis->fKeepComments
&& !pThis->fIf0Mode)
rcExit = vbcppOutputCh(pThis, ch);
if (rcExit != RTEXITCODE_SUCCESS)
break;
}
return rcExit;
}
/**
* Processes a single line comment.
*
* Must either string the comment or keep it. If the latter, we must refrain
* from replacing C-words in it.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
*/
static RTEXITCODE vbcppProcessOneLineComment(PVBCPP pThis, PSCMSTREAM pStrmInput)
{
RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
SCMEOL enmEol;
size_t cchLine;
const char *pszLine = ScmStreamGetLine(pStrmInput, &cchLine, &enmEol); Assert(pszLine);
pszLine--; cchLine++; /* unfetching the first slash. */
for (;;)
{
if ( pThis->fKeepComments
&& !pThis->fIf0Mode)
rcExit = vbcppOutputWrite(pThis, pszLine, cchLine + enmEol);
else if ( !pThis->fIf0Mode
|| !pThis->fRemoveDroppedLines
|| !ScmStreamIsAtStartOfLine(&pThis->StrmOutput) )
rcExit = vbcppOutputWrite(pThis, pszLine + cchLine, enmEol);
if (rcExit != RTEXITCODE_SUCCESS)
break;
if ( cchLine == 0
|| pszLine[cchLine - 1] != '\\')
break;
pszLine = ScmStreamGetLine(pStrmInput, &cchLine, &enmEol);
if (!pszLine)
break;
}
pThis->fJustDroppedLine = false;
pThis->fMaybePreprocessorLine = true;
return rcExit;
}
/**
* Processes a double quoted string.
*
* Must not replace any C-words in strings.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
*/
static RTEXITCODE vbcppProcessStringLitteral(PVBCPP pThis, PSCMSTREAM pStrmInput)
{
RTEXITCODE rcExit = vbcppOutputCh(pThis, '"');
if (rcExit == RTEXITCODE_SUCCESS)
{
bool fEscaped = false;
for (;;)
{
unsigned ch = ScmStreamGetCh(pStrmInput);
if (ch == ~(unsigned)0)
{
rcExit = vbcppError(pThis, "Unterminated double quoted string");
break;
}
rcExit = vbcppOutputCh(pThis, ch);
if (rcExit != RTEXITCODE_SUCCESS)
break;
if (ch == '"' && !fEscaped)
break;
fEscaped = !fEscaped && ch == '\\';
}
}
return rcExit;
}
/**
* Processes a single quoted constant.
*
* Must not replace any C-words in character constants.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
*/
static RTEXITCODE vbcppProcessCharacterConstant(PVBCPP pThis, PSCMSTREAM pStrmInput)
{
RTEXITCODE rcExit = vbcppOutputCh(pThis, '\'');
if (rcExit == RTEXITCODE_SUCCESS)
{
bool fEscaped = false;
for (;;)
{
unsigned ch = ScmStreamGetCh(pStrmInput);
if (ch == ~(unsigned)0)
{
rcExit = vbcppError(pThis, "Unterminated singled quoted string");
break;
}
rcExit = vbcppOutputCh(pThis, ch);
if (rcExit != RTEXITCODE_SUCCESS)
break;
if (ch == '\'' && !fEscaped)
break;
fEscaped = !fEscaped && ch == '\\';
}
}
return rcExit;
}
/**
* Processes a integer or floating point number constant.
*
* Must not replace the type suffix.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
* @param chFirst The first character.
*/
static RTEXITCODE vbcppProcessNumber(PVBCPP pThis, PSCMSTREAM pStrmInput, char chFirst)
{
RTEXITCODE rcExit = vbcppOutputCh(pThis, chFirst);
unsigned ch;
while ( rcExit == RTEXITCODE_SUCCESS
&& (ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
{
if ( !vbcppIsCIdentifierChar(ch)
&& ch != '.')
break;
unsigned ch2 = ScmStreamGetCh(pStrmInput);
AssertBreakStmt(ch2 == ch, rcExit = vbcppError(pThis, "internal error"));
rcExit = vbcppOutputCh(pThis, ch);
}
return rcExit;
}
/**
* Processes a identifier, possibly replacing it with a definition.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
* @param ch The first character.
*/
static RTEXITCODE vbcppProcessIdentifier(PVBCPP pThis, PSCMSTREAM pStrmInput, char ch)
{
RTEXITCODE rcExit;
size_t cchDefine;
const char *pchDefine = ScmStreamCGetWordM1(pStrmInput, &cchDefine);
AssertReturn(pchDefine, vbcppError(pThis, "Internal error in ScmStreamCGetWordM1"));
/*
* Does this look like a define we know?
*/
PVBCPPMACRO pMacro = vbcppMacroLookup(pThis, pchDefine, cchDefine);
if ( pMacro
&& ( !pMacro->fFunction
|| vbcppInputLookForLeftParenthesis(pThis, pStrmInput)) )
{
/*
* Expand it.
*/
VBCPPMACROEXP ExpCtx;
#if 0
ExpCtx.pMacroStack = NULL;
#endif
ExpCtx.pStrmInput = pStrmInput;
ExpCtx.papszArgs = NULL;
ExpCtx.cArgs = 0;
ExpCtx.cArgsAlloced = 0;
vbcppStrBufInit(&ExpCtx.StrBuf, pThis);
rcExit = vbcppStrBufAppendN(&ExpCtx.StrBuf, pchDefine, cchDefine);
if (rcExit == RTEXITCODE_SUCCESS)
rcExit = vbcppMacroExpandIt(pThis, &ExpCtx, 0 /* offset */, pMacro, cchDefine);
if (rcExit == RTEXITCODE_SUCCESS)
rcExit = vbcppMacroExpandReScan(pThis, &ExpCtx, kMacroReScanMode_Normal, NULL);
if (rcExit == RTEXITCODE_SUCCESS)
{
/*
* Insert it into the output stream. Make sure there is a
* whitespace following it.
*/
int rc = ScmStreamWrite(&pThis->StrmOutput, ExpCtx.StrBuf.pszBuf, ExpCtx.StrBuf.cchBuf);
if (RT_SUCCESS(rc))
{
unsigned chAfter = ScmStreamPeekCh(pStrmInput);
if (chAfter != ~(unsigned)0 && !RT_C_IS_SPACE(chAfter))
rcExit = vbcppOutputCh(pThis, ' ');
}
else
rcExit = vbcppError(pThis, "Output error: %Rrc", rc);
}
vbcppMacroExpandCleanup(&ExpCtx);
}
else
{
/*
* Not a macro or a function-macro name match but no invocation, just
* output the text unchanged.
*/
int rc = ScmStreamWrite(&pThis->StrmOutput, pchDefine, cchDefine);
if (RT_SUCCESS(rc))
rcExit = RTEXITCODE_SUCCESS;
else
rcExit = vbcppError(pThis, "Output error: %Rrc", rc);
}
return rcExit;
}
/*
*
*
* D E F I N E S / M A C R O S
* D E F I N E S / M A C R O S
* D E F I N E S / M A C R O S
* D E F I N E S / M A C R O S
* D E F I N E S / M A C R O S
*
*
*/
/**
* Checks if a define exists.
*
* @returns true or false.
* @param pThis The C preprocessor instance.
* @param pszDefine The define name and optionally the argument
* list.
* @param cchDefine The length of the name. RTSTR_MAX is ok.
*/
static bool vbcppMacroExists(PVBCPP pThis, const char *pszDefine, size_t cchDefine)
{
return cchDefine > 0
&& VBCPP_BITMAP_IS_SET(pThis->bmDefined, *pszDefine)
&& RTStrSpaceGetN(&pThis->StrSpace, pszDefine, cchDefine) != NULL;
}
/**
* Looks up a define.
*
* @returns Pointer to the define if found, NULL if not.
* @param pThis The C preprocessor instance.
* @param pszDefine The define name and optionally the argument
* list.
* @param cchDefine The length of the name. RTSTR_MAX is ok.
*/
static PVBCPPMACRO vbcppMacroLookup(PVBCPP pThis, const char *pszDefine, size_t cchDefine)
{
if (!cchDefine)
return NULL;
if (!VBCPP_BITMAP_IS_SET(pThis->bmDefined, *pszDefine))
return NULL;
return (PVBCPPMACRO)RTStrSpaceGetN(&pThis->StrSpace, pszDefine, cchDefine);
}
static uint32_t vbcppMacroLookupArg(PVBCPPMACRO pMacro, const char *pchName, size_t cchName)
{
Assert(cchName > 0);
char const ch = *pchName;
for (uint32_t i = 0; i < pMacro->cArgs; i++)
if ( pMacro->papszArgs[i][0] == ch
&& !strncmp(pMacro->papszArgs[i], pchName, cchName)
&& pMacro->papszArgs[i][cchName] == '\0')
return i;
if ( pMacro->fVarArg
&& cchName == sizeof("__VA_ARGS__") - 1
&& !strncmp(pchName, "__VA_ARGS__", sizeof("__VA_ARGS__") - 1) )
return pMacro->cArgs;
return UINT32_MAX;
}
static RTEXITCODE vbcppMacroExpandReplace(PVBCPP pThis, PVBCPPMACROEXP pExp, size_t off, size_t cchToReplace,
const char *pchReplacement, size_t cchReplacement)
{
/*
* Figure how much space we actually need.
* (Hope this whitespace stuff is correct...)
*/
bool const fLeadingSpace = off > 0
&& !RT_C_IS_SPACE(pExp->StrBuf.pszBuf[off - 1]);
bool const fTrailingSpace = off + cchToReplace < pExp->StrBuf.cchBuf
&& !RT_C_IS_SPACE(pExp->StrBuf.pszBuf[off + cchToReplace]);
size_t const cchActualReplacement = fLeadingSpace + cchReplacement + fTrailingSpace;
/*
* Adjust the buffer size and contents.
*/
if (cchActualReplacement > cchToReplace)
{
size_t const offMore = cchActualReplacement - cchToReplace;
/* Ensure enough buffer space. */
size_t cbMinBuf = offMore + pExp->StrBuf.cchBuf + 1;
RTEXITCODE rcExit = vbcppStrBufGrow(&pExp->StrBuf, cbMinBuf);
if (rcExit != RTEXITCODE_SUCCESS)
return rcExit;
/* Push the chars following the replacement area down to make room. */
memmove(&pExp->StrBuf.pszBuf[off + cchToReplace + offMore],
&pExp->StrBuf.pszBuf[off + cchToReplace],
pExp->StrBuf.cchBuf - off - cchToReplace + 1);
pExp->StrBuf.cchBuf += offMore;
}
else if (cchActualReplacement < cchToReplace)
{
size_t const offLess = cchToReplace - cchActualReplacement;
/* Pull the chars following the replacement area up. */
memmove(&pExp->StrBuf.pszBuf[off + cchToReplace - offLess],
&pExp->StrBuf.pszBuf[off + cchToReplace],
pExp->StrBuf.cchBuf - off - cchToReplace + 1);
pExp->StrBuf.cchBuf -= offLess;
}
/*
* Insert the replacement string.
*/
char *pszCur = &pExp->StrBuf.pszBuf[off];
if (fLeadingSpace)
*pszCur++ = ' ';
memcpy(pszCur, pchReplacement, cchReplacement);
if (fTrailingSpace)
*pszCur++ = ' ';
Assert(strlen(pExp->StrBuf.pszBuf) == pExp->StrBuf.cchBuf);
return RTEXITCODE_SUCCESS;
}
static unsigned vbcppMacroExpandPeekCh(PVBCPPMACROEXP pExp, size_t *poff)
{
size_t off = *poff;
if (off >= pExp->StrBuf.cchBuf)
return pExp->pStrmInput ? ScmStreamPeekCh(pExp->pStrmInput) : ~(unsigned)0;
return pExp->StrBuf.pszBuf[off];
}
static unsigned vbcppMacroExpandGetCh(PVBCPPMACROEXP pExp, size_t *poff)
{
size_t off = *poff;
if (off >= pExp->StrBuf.cchBuf)
return pExp->pStrmInput ? ScmStreamGetCh(pExp->pStrmInput) : ~(unsigned)0;
*poff = off + 1;
return pExp->StrBuf.pszBuf[off];
}
static RTEXITCODE vbcppMacroExpandSkipEolEx(PVBCPP pThis, PVBCPPMACROEXP pExp, size_t *poff, unsigned chFirst)
{
if (chFirst == '\r')
{
unsigned ch2 = vbcppMacroExpandPeekCh(pExp, poff);
if (ch2 == '\n')
{
ch2 = ScmStreamGetCh(pExp->pStrmInput);
AssertReturn(ch2 == '\n', vbcppError(pThis, "internal error"));
}
}
return RTEXITCODE_SUCCESS;
}
static RTEXITCODE vbcppMacroExpandSkipEol(PVBCPP pThis, PVBCPPMACROEXP pExp, size_t *poff)
{
unsigned ch = vbcppMacroExpandGetCh(pExp, poff);
AssertReturn(ch == '\r' || ch == '\n', vbcppError(pThis, "internal error"));
return vbcppMacroExpandSkipEolEx(pThis, pExp, poff, ch);
}
static RTEXITCODE vbcppMacroExpandSkipCommentLine(PVBCPP pThis, PVBCPPMACROEXP pExp, size_t *poff)
{
unsigned ch = vbcppMacroExpandGetCh(pExp, poff);
AssertReturn(ch == '/', vbcppError(pThis, "Internal error - expected '/' got '%c'", ch));
unsigned chPrev = 0;
while ((ch = vbcppMacroExpandGetCh(pExp, poff)) != ~(unsigned)0)
{
if (ch == '\r' || ch == '\n')
{
RTEXITCODE rcExit = vbcppMacroExpandSkipEolEx(pThis, pExp, poff, ch);
if (rcExit != RTEXITCODE_SUCCESS)
return rcExit;
if (chPrev != '\\')
break;
}
chPrev = ch;
}
return RTEXITCODE_SUCCESS;
}
static RTEXITCODE vbcppMacroExpandSkipComment(PVBCPP pThis, PVBCPPMACROEXP pExp, size_t *poff)
{
unsigned ch = vbcppMacroExpandGetCh(pExp, poff);
AssertReturn(ch == '*', vbcppError(pThis, "Internal error - expected '*' got '%c'", ch));
unsigned chPrev2 = 0;
unsigned chPrev = 0;
while ((ch = vbcppMacroExpandGetCh(pExp, poff)) != ~(unsigned)0)
{
if (ch == '/' && chPrev == '*')
break;
if (ch == '\r' || ch == '\n')
{
RTEXITCODE rcExit = vbcppMacroExpandSkipEolEx(pThis, pExp, poff, ch);
if (rcExit != RTEXITCODE_SUCCESS)
return rcExit;
if (chPrev == '\\')
{
chPrev = chPrev2; /* for line splicing */
continue;
}
}
chPrev2 = chPrev;
chPrev = ch;
}
return RTEXITCODE_SUCCESS;
}
static RTEXITCODE vbcppMacroExpandGrowArgArray(PVBCPP pThis, PVBCPPMACROEXP pExp, uint32_t cMinArgs)
{
if (cMinArgs > pExp->cArgsAlloced)
{
void *pv = RTMemRealloc(pExp->papszArgs, cMinArgs * sizeof(char *));
if (!pv)
return vbcppError(pThis, "out of memory");
pExp->papszArgs = (char **)pv;
pExp->cArgsAlloced = cMinArgs;
}
return RTEXITCODE_SUCCESS;
}
static RTEXITCODE vbcppMacroExpandAddEmptyParameter(PVBCPP pThis, PVBCPPMACROEXP pExp)
{
RTEXITCODE rcExit = vbcppMacroExpandGrowArgArray(pThis, pExp, pExp->cArgs + 1);
if (rcExit == RTEXITCODE_SUCCESS)
{
char *pszArg = (char *)RTMemAllocZ(1);
if (pszArg)
pExp->papszArgs[pExp->cArgs++] = pszArg;
else
rcExit = vbcppError(pThis, "out of memory");
}
return rcExit;
}
static RTEXITCODE vbcppMacroExpandGatherParameters(PVBCPP pThis, PVBCPPMACROEXP pExp, size_t *poff, uint32_t cArgsHint)
{
RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
/*
* Free previous argument values.
*/
while (pExp->cArgs > 0)
{
RTMemFree(pExp->papszArgs[--pExp->cArgs]);
pExp->papszArgs[pExp->cArgs] = NULL;
}
/*
* The current character should be an opening parenthsis.
*/
unsigned ch = vbcppMacroExpandGetCh(pExp, poff);
if (ch != '(')
return vbcppError(pThis, "Internal error - expected '(', found '%c' (#x)", ch, ch);
/*
* Parse the argument list.
*/
char chQuote = 0;
size_t cbArgAlloc = 0;
size_t cchArg = 0;
char *pszArg = NULL;
size_t cParentheses = 1;
unsigned chPrev = 0;
while ((ch = vbcppMacroExpandGetCh(pExp, poff)) != ~(unsigned)0)
{
/** @todo check for '#directives'! */
if (ch == ')' && !chQuote)
{
Assert(cParentheses >= 1);
cParentheses--;
/* The end? */
if (!cParentheses)
{
if (cchArg)
while (cchArg > 0 && RT_C_IS_SPACE(pszArg[cchArg - 1]))
pszArg[--cchArg] = '\0';
else if (pExp->cArgs || cArgsHint > 0)
rcExit = vbcppMacroExpandAddEmptyParameter(pThis, pExp);
break;
}
}
else if (ch == '(' && !chQuote)
cParentheses++;
else if (ch == ',' && cParentheses == 1 && !chQuote)
{
/* End of one argument, start of the next. */
if (cchArg)
while (cchArg > 0 && RT_C_IS_SPACE(pszArg[cchArg - 1]))
pszArg[--cchArg] = '\0';
else
{
rcExit = vbcppMacroExpandAddEmptyParameter(pThis, pExp);
if (rcExit != RTEXITCODE_SUCCESS)
break;
}
cbArgAlloc = 0;
cchArg = 0;
pszArg = NULL;
continue;
}
else if (ch == '/' && !chQuote)
{
/* Comment? */
unsigned ch2 = vbcppMacroExpandPeekCh(pExp, poff);
/** @todo This ain't right wrt line splicing. */
if (ch2 == '/' || ch == '*')
{
if (ch2 == '/')
rcExit = vbcppMacroExpandSkipCommentLine(pThis, pExp, poff);
else
rcExit = vbcppMacroExpandSkipComment(pThis, pExp, poff);
if (rcExit != RTEXITCODE_SUCCESS)
break;
continue;
}
}
else if (ch == '"')
{
if (!chQuote)
chQuote = '"';
else if (chPrev != '\\')
chQuote = 0;
}
else if (ch == '\'')
{
if (!chQuote)
chQuote = '\'';
else if (chPrev != '\\')
chQuote = 0;
}
else if (ch == '\\')
{
/* Splice lines? */
unsigned ch2 = vbcppMacroExpandPeekCh(pExp, poff);
if (ch2 == '\r' || ch2 == '\n')
{
rcExit = vbcppMacroExpandSkipEol(pThis, pExp, poff);
if (rcExit != RTEXITCODE_SUCCESS)
break;
continue;
}
}
else if (cchArg == 0 && RT_C_IS_SPACE(ch))
continue; /* ignore spaces leading up to an argument value */
/* Append the character to the argument value, adding the argument
to the output array if it's first character in it. */
if (cchArg + 1 >= cbArgAlloc)
{
/* Add argument to the vector. */
if (!cchArg)
{
rcExit = vbcppMacroExpandGrowArgArray(pThis, pExp, RT_MAX(pExp->cArgs + 1, cArgsHint));
if (rcExit != RTEXITCODE_SUCCESS)
break;
pExp->papszArgs[pExp->cArgs++] = pszArg;
}
/* Resize the argument value buffer. */
cbArgAlloc = cbArgAlloc ? cbArgAlloc * 2 : 16;
pszArg = (char *)RTMemRealloc(pszArg, cbArgAlloc);
if (!pszArg)
{
rcExit = vbcppError(pThis, "out of memory");
break;
}
pExp->papszArgs[pExp->cArgs - 1] = pszArg;
}
pszArg[cchArg++] = ch;
pszArg[cchArg] = '\0';
}
/*
* Check that we're leaving on good terms.
*/
if (rcExit == RTEXITCODE_SUCCESS)
{
if (cParentheses)
rcExit = vbcppError(pThis, "Missing ')'");
}
return rcExit;
}
/**
* Expands the arguments referenced in the macro value.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
* @param pThis The C preprocessor instance.
* @param pExp The expansion context.
* @param pMacro The macro. Must be a function macro.
* @param pStrBuf String buffer containing the result. The caller
* should initialize and destroy this!
*/
static RTEXITCODE vbcppMacroExpandValueWithArguments(PVBCPP pThis, PVBCPPMACROEXP pExp, PVBCPPMACRO pMacro,
PVBCPPSTRBUF pStrBuf)
{
Assert(pMacro->fFunction);
/*
* Empty?
*/
if ( !pMacro->cchValue
|| (pMacro->cchValue == 1 && pMacro->szValue[0] == '#'))
return RTEXITCODE_SUCCESS;
/*
* Parse the value.
*/
RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
const char *pszSrc = pMacro->szValue;
const char *pszSrcSeq;
char ch;
while ((ch = *pszSrc++) != '\0')
{
Assert(ch != '\r'); Assert(ch != '\n'); /* probably not true atm. */
if (ch == '#')
{
if (*pszSrc == '#')
{
/* Concatenate operator. */
rcExit = vbcppError(pThis, "The '##' operatore is not yet implemented");
}
else
{
/* Stringify macro argument. */
rcExit = vbcppError(pThis, "The '#' operatore is not yet implemented");
}
return rcExit;
}
else if (ch == '"')
{
/* String litteral. */
pszSrcSeq = pszSrc - 1;
while ((ch = *pszSrc++) != '"')
{
if (ch == '\\')
ch = *pszSrc++;
if (ch == '\0')
{
rcExit = vbcppError(pThis, "String litteral is missing closing quote (\").");
break;
}
}
rcExit = vbcppStrBufAppendN(pStrBuf, pszSrcSeq, pszSrc - pszSrcSeq);
}
else if (ch == '\'')
{
/* Character constant. */
pszSrcSeq = pszSrc - 1;
while ((ch = *pszSrc++) != '\'')
{
if (ch == '\\')
ch = *pszSrc++;
if (ch == '\0')
{
rcExit = vbcppError(pThis, "Character constant is missing closing quote (').");
break;
}
}
rcExit = vbcppStrBufAppendN(pStrBuf, pszSrcSeq, pszSrc - pszSrcSeq);
}
else if (RT_C_IS_DIGIT(ch))
{
/* Process numerical constants correctly (i.e. don't mess with the suffix). */
pszSrcSeq = pszSrc - 1;
while ( (ch = *pszSrc) != '\0'
&& ( vbcppIsCIdentifierChar(ch)
|| ch == '.') )
pszSrc++;
rcExit = vbcppStrBufAppendN(pStrBuf, pszSrcSeq, pszSrc - pszSrcSeq);
}
else if (RT_C_IS_SPACE(ch))
{
/* join spaces */
if (RT_C_IS_SPACE(vbcppStrBufLastCh(pStrBuf)))
continue;
rcExit = vbcppStrBufAppendCh(pStrBuf, ch);
}
else if (vbcppIsCIdentifierLeadChar(ch))
{
/* Something we should replace? */
pszSrcSeq = pszSrc - 1;
while ( (ch = *pszSrc) != '\0'
&& vbcppIsCIdentifierChar(ch))
pszSrc++;
size_t cchDefine = pszSrc - pszSrcSeq;
uint32_t iArg;
if ( VBCPP_BITMAP_IS_SET(pMacro->bmArgs, *pszSrcSeq)
&& (iArg = vbcppMacroLookupArg(pMacro, pszSrcSeq, cchDefine)) != UINT32_MAX)
{
/** @todo check out spaces here! */
if (iArg < pMacro->cArgs)
{
Assert(iArg < pExp->cArgs);
rcExit = vbcppStrBufAppend(pStrBuf, pExp->papszArgs[iArg]);
if (*pExp->papszArgs[iArg] != '\0' && rcExit == RTEXITCODE_SUCCESS)
rcExit = vbcppStrBufAppendCh(pStrBuf, ' ');
}
else
{
/* __VA_ARGS__ */
if (iArg < pExp->cArgs)
{
for (;;)
{
rcExit = vbcppStrBufAppend(pStrBuf, pExp->papszArgs[iArg]);
if (rcExit != RTEXITCODE_SUCCESS)
break;
iArg++;
if (iArg >= pExp->cArgs)
break;
rcExit = vbcppStrBufAppendCh(pStrBuf, ',');
if (rcExit != RTEXITCODE_SUCCESS)
break;
}
}
if (rcExit == RTEXITCODE_SUCCESS)
rcExit = vbcppStrBufAppendCh(pStrBuf, ' ');
}
}
/* Not an argument needing replacing. */
else
rcExit = vbcppStrBufAppendN(pStrBuf, pszSrcSeq, cchDefine);
}
else
{
rcExit = vbcppStrBufAppendCh(pStrBuf, ch);
}
}
return rcExit;
}
/**
* Expands the given macro.
*
* Caller already checked if a function macro should be expanded, i.e. whether
* there is a parameter list.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
* @param pThis The C preprocessor instance.
* @param pExp The expansion context.
* @param offMacro Offset into the expansion buffer of the macro
* invocation.
* @param pMacro The macro.
* @param offParameters The start of the parameter list if applicable.
* Ignored if not function macro. If the
* parameter list starts at the current stream
* position shall be at the end of the expansion
* buffer.
*/
static RTEXITCODE vbcppMacroExpandIt(PVBCPP pThis, PVBCPPMACROEXP pExp, size_t offMacro, PVBCPPMACRO pMacro,
size_t offParameters)
{
RTEXITCODE rcExit;
Assert(offMacro + pMacro->Core.cchString <= pExp->StrBuf.cchBuf);
Assert(!pMacro->fExpanding);
/*
* Function macros are kind of difficult...
*/
if (pMacro->fFunction)
{
rcExit = vbcppMacroExpandGatherParameters(pThis, pExp, &offParameters, pMacro->cArgs + pMacro->fVarArg);
if (rcExit == RTEXITCODE_SUCCESS)
{
if (pExp->cArgs > pMacro->cArgs && !pMacro->fVarArg)
rcExit = vbcppError(pThis, "Too many arguments to macro '%s' - found %u, expected %u",
pMacro->Core.pszString, pExp->cArgs, pMacro->cArgs);
else if (pExp->cArgs < pMacro->cArgs)
rcExit = vbcppError(pThis, "Too few arguments to macro '%s' - found %u, expected %u",
pMacro->Core.pszString, pExp->cArgs, pMacro->cArgs);
}
if (rcExit == RTEXITCODE_SUCCESS)
{
VBCPPSTRBUF ValueBuf;
vbcppStrBufInit(&ValueBuf, pThis);
rcExit = vbcppMacroExpandValueWithArguments(pThis, pExp, pMacro, &ValueBuf);
if (rcExit == RTEXITCODE_SUCCESS)
rcExit = vbcppMacroExpandReplace(pThis, pExp, offMacro, offParameters - offMacro,
ValueBuf.pszBuf, ValueBuf.cchBuf);
vbcppStrBufDelete(&ValueBuf);
}
}
/*
* Object-like macros are easy. :-)
*/
else
rcExit = vbcppMacroExpandReplace(pThis, pExp, offMacro, pMacro->Core.cchString, pMacro->szValue, pMacro->cchValue);
if (rcExit == RTEXITCODE_SUCCESS)
{
#if 0 /* wrong */
/*
* Push the macro onto the stack.
*/
pMacro->fExpanding = true;
pMacro->pUpExpanding = pExp->pMacroStack;
pExp->pMacroStack = pMacro;
#endif
}
return rcExit;
}
/**
* Looks for a left parenthesis in the macro expansion buffer and the input
* stream.
*
* @retval true if found. The stream position at opening parenthesis.
* @retval false if not found. The stream position is unchanged.
*
* @param pThis The C preprocessor instance.
* @param pExp The expansion context.
* @param poff The current offset in the expansion context.
* Will be updated on success.
*
* @sa vbcppInputLookForLeftParenthesis
*/
static bool vbcppMacroExpandLookForLeftParenthesis(PVBCPP pThis, PVBCPPMACROEXP pExp, size_t *poff)
{
/*
* Search the buffer first. (No comments there.)
*/
size_t off = *poff;
while (off < pExp->StrBuf.cchBuf)
{
char ch = pExp->StrBuf.pszBuf[off];
if (!RT_C_IS_SPACE(ch))
{
if (ch == '(')
{
*poff = off;
return true;
}
return false;
}
off++;
}
/*
* Reached the end of the buffer, continue searching in the stream.
*/
PSCMSTREAM pStrmInput = pExp->pStrmInput;
size_t offSaved = ScmStreamTell(pStrmInput);
RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
unsigned ch = ScmStreamPeekCh(pStrmInput);
if (ch == '(')
{
*poff = pExp->StrBuf.cchBuf;
return true;
}
int rc = ScmStreamSeekAbsolute(pStrmInput, offSaved);
AssertFatalRC(rc);
return false;
}
/**
* Implements the 'defined' unary operator for \#if and \#elif expressions.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
* @param pThis The C preprocessor instance.
* @param pExp The expansion context.
* @param offStart The expansion buffer offset where the 'defined'
* occurs.
* @param poff Where to store the offset at which the re-scan
* shall resume upon return.
*/
static RTEXITCODE vbcppMacroExpandDefinedOperator(PVBCPP pThis, PVBCPPMACROEXP pExp, size_t offStart, size_t *poff)
{
Assert(!pExp->pStrmInput); /* offset usage below. */
/*
* Skip white space.
*/
unsigned ch;
while ((ch = vbcppMacroExpandGetCh(pExp, poff)) != ~(unsigned)0)
if (!RT_C_IS_SPACE(ch))
break;
bool const fWithParenthesis = ch == '(';
if (fWithParenthesis)
while ((ch = vbcppMacroExpandGetCh(pExp, poff)) != ~(unsigned)0)
if (!RT_C_IS_SPACE(ch))
break;
/*
* Macro identifier.
*/
if (!vbcppIsCIdentifierLeadChar(ch))
return vbcppError(pThis, "Expected macro name after 'defined' operator");
size_t const offDefine = *poff - 1;
while ((ch = vbcppMacroExpandGetCh(pExp, poff)) != ~(unsigned)0)
if (!vbcppIsCIdentifierChar(ch))
break;
size_t const cchDefine = *poff - offDefine;
/*
* Check for closing parenthesis.
*/
if (fWithParenthesis)
{
while (RT_C_IS_SPACE(ch))
ch = vbcppMacroExpandGetCh(pExp, poff);
if (ch != ')')
return vbcppError(pThis, "Expected closing parenthesis after macro name");
}
/*
* Do the job.
*/
const char *pszResult = vbcppMacroExists(pThis, &pExp->StrBuf.pszBuf[offDefine], cchDefine)
? "1" : "0";
RTEXITCODE rcExit = vbcppMacroExpandReplace(pThis, pExp, offStart, *poff - offStart, pszResult, 1);
*poff = offStart + 1;
return rcExit;
}
/**
* Re-scan the expanded macro.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
* @param pThis The C preprocessor instance.
* @param pExp The expansion context.
* @param enmMode The re-scan mode.
* @param pcReplacements Where to return the number of replacements
* performed. Optional.
*/
static RTEXITCODE vbcppMacroExpandReScan(PVBCPP pThis, PVBCPPMACROEXP pExp, VBCPPMACRORESCANMODE enmMode, size_t *pcReplacements)
{
RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
size_t cReplacements = 0;
size_t off = 0;
unsigned ch;
while ( off < pExp->StrBuf.cchBuf
&& (ch = vbcppMacroExpandGetCh(pExp, &off)) != ~(unsigned)0)
{
/*
* String litteral or character constant.
*/
if (ch == '\'' || ch == '"')
{
unsigned const chEndQuote = ch;
while ( off < pExp->StrBuf.cchBuf
&& (ch = vbcppMacroExpandGetCh(pExp, &off)) != ~(unsigned)0)
{
if (ch == '\\')
{
ch = vbcppMacroExpandGetCh(pExp, &off);
if (ch == ~(unsigned)0)
break;
}
else if (ch == chEndQuote)
break;
}
if (ch == ~(unsigned)0)
return vbcppError(pThis, "Missing end quote (%c)", chEndQuote);
}
/*
* Number constant.
*/
else if ( RT_C_IS_DIGIT(ch)
|| ( ch == '.'
&& off + 1 < pExp->StrBuf.cchBuf
&& RT_C_IS_DIGIT(vbcppMacroExpandPeekCh(pExp, &off))
)
)
{
while ( off < pExp->StrBuf.cchBuf
&& (ch = vbcppMacroExpandPeekCh(pExp, &off)) != ~(unsigned)0
&& vbcppIsCIdentifierChar(ch) )
vbcppMacroExpandGetCh(pExp, &off);
}
/*
* Something that can be replaced?
*/
else if (vbcppIsCIdentifierLeadChar(ch))
{
size_t offDefine = off - 1;
while ( off < pExp->StrBuf.cchBuf
&& (ch = vbcppMacroExpandPeekCh(pExp, &off)) != ~(unsigned)0
&& vbcppIsCIdentifierChar(ch) )
vbcppMacroExpandGetCh(pExp, &off);
size_t cchDefine = off - offDefine;
PVBCPPMACRO pMacro = vbcppMacroLookup(pThis, &pExp->StrBuf.pszBuf[offDefine], cchDefine);
if ( pMacro
&& ( !pMacro->fFunction
|| vbcppMacroExpandLookForLeftParenthesis(pThis, pExp, &off)) )
{
rcExit = vbcppMacroExpandIt(pThis, pExp, offDefine, pMacro, off);
off = offDefine;
}
else
{
if ( !pMacro
&& enmMode == kMacroReScanMode_Expression
&& cchDefine == sizeof("defined") - 1
&& !strncmp(&pExp->StrBuf.pszBuf[offDefine], "defined", cchDefine))
rcExit = vbcppMacroExpandDefinedOperator(pThis, pExp, offDefine, &off);
else
off = offDefine + cchDefine;
}
}
else
{
Assert(RT_C_IS_SPACE(ch) || RT_C_IS_PUNCT(ch));
Assert(ch != '\r' && ch != '\n');
}
}
return rcExit;
}
/**
* Cleans up the expansion context.
*
* This involves clearing VBCPPMACRO::fExpanding and VBCPPMACRO::pUpExpanding,
* and freeing the memory resources associated with the expansion context.
*
* @param pExp The expansion context.
*/
static void vbcppMacroExpandCleanup(PVBCPPMACROEXP pExp)
{
#if 0
while (pExp->pMacroStack)
{
PVBCPPMACRO pMacro = pExp->pMacroStack;
pExp->pMacroStack = pMacro->pUpExpanding;
pMacro->fExpanding = false;
pMacro->pUpExpanding = NULL;
}
#endif
while (pExp->cArgs > 0)
{
RTMemFree(pExp->papszArgs[--pExp->cArgs]);
pExp->papszArgs[pExp->cArgs] = NULL;
}
RTMemFree(pExp->papszArgs);
pExp->papszArgs = NULL;
vbcppStrBufDelete(&pExp->StrBuf);
}
/**
* Frees a define.
*
* @returns VINF_SUCCESS (used when called by RTStrSpaceDestroy)
* @param pStr Pointer to the VBCPPMACRO::Core member.
* @param pvUser Unused.
*/
static DECLCALLBACK(int) vbcppMacroFree(PRTSTRSPACECORE pStr, void *pvUser)
{
RTMemFree(pStr);
NOREF(pvUser);
return VINF_SUCCESS;
}
/**
* Removes a define.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
* @param pThis The C preprocessor instance.
* @param pszDefine The define name, no argument list or anything.
* @param cchDefine The length of the name. RTSTR_MAX is ok.
* @param fExplicitUndef Explicit undefinition, that is, in a selective
* preprocessing run it will evaluate to undefined.
*/
static RTEXITCODE vbcppMacroUndef(PVBCPP pThis, const char *pszDefine, size_t cchDefine, bool fExplicitUndef)
{
PRTSTRSPACECORE pHit = RTStrSpaceGetN(&pThis->StrSpace, pszDefine, cchDefine);
if (pHit)
{
RTStrSpaceRemove(&pThis->StrSpace, pHit->pszString);
vbcppMacroFree(pHit, NULL);
}
if (fExplicitUndef)
{
if (cchDefine == RTSTR_MAX)
cchDefine = strlen(pszDefine);
PRTSTRSPACECORE pStr = (PRTSTRSPACECORE)RTMemAlloc(sizeof(*pStr) + cchDefine + 1);
if (!pStr)
return vbcppError(pThis, "out of memory");
char *pszDst = (char *)(pStr + 1);
pStr->pszString = pszDst;
memcpy(pszDst, pszDefine, cchDefine);
pszDst[cchDefine] = '\0';
if (!RTStrSpaceInsert(&pThis->UndefStrSpace, pStr))
RTMemFree(pStr);
}
return RTEXITCODE_SUCCESS;
}
/**
* Inserts a define (rejecting and freeing it in some case).
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
* @param pThis The C preprocessor instance.
* @param pMacro The define to insert.
*/
static RTEXITCODE vbcppMacroInsert(PVBCPP pThis, PVBCPPMACRO pMacro)
{
/*
* Reject illegal macro names.
*/
if (!strcmp(pMacro->Core.pszString, "defined"))
{
RTEXITCODE rcExit = vbcppError(pThis, "Cannot use '%s' as a macro name", pMacro->Core.pszString);
vbcppMacroFree(&pMacro->Core, NULL);
return rcExit;
}
/*
* Ignore in source-file defines when doing selective preprocessing.
*/
if ( !pThis->fRespectSourceDefines
&& !pMacro->fCmdLine)
{
/* Ignore*/
vbcppMacroFree(&pMacro->Core, NULL);
return RTEXITCODE_SUCCESS;
}
/*
* Insert it and update the lead character hint bitmap.
*/
if (RTStrSpaceInsert(&pThis->StrSpace, &pMacro->Core))
VBCPP_BITMAP_SET(pThis->bmDefined, *pMacro->Core.pszString);
else
{
/*
* Duplicate. When doing selective D preprocessing, let the command
* line take precendece.
*/
PVBCPPMACRO pOld = (PVBCPPMACRO)RTStrSpaceGet(&pThis->StrSpace, pMacro->Core.pszString); Assert(pOld);
if ( pThis->fAllowRedefiningCmdLineDefines
|| pMacro->fCmdLine == pOld->fCmdLine)
{
if (pMacro->fCmdLine)
RTMsgWarning("Redefining '%s'", pMacro->Core.pszString);
RTStrSpaceRemove(&pThis->StrSpace, pOld->Core.pszString);
vbcppMacroFree(&pOld->Core, NULL);
bool fRc = RTStrSpaceInsert(&pThis->StrSpace, &pMacro->Core);
Assert(fRc);
}
else
{
RTMsgWarning("Ignoring redefinition of '%s'", pMacro->Core.pszString);
vbcppMacroFree(&pMacro->Core, NULL);
}
}
return RTEXITCODE_SUCCESS;
}
/**
* Adds a define.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
* @param pThis The C preprocessor instance.
* @param pszDefine The define name, no parameter list.
* @param cchDefine The length of the name.
* @param pszParams The parameter list.
* @param cchParams The length of the parameter list.
* @param pszValue The value.
* @param cchDefine The length of the value.
* @param fCmdLine Set if originating on the command line.
*/
static RTEXITCODE vbcppMacroAddFn(PVBCPP pThis, const char *pszDefine, size_t cchDefine,
const char *pszParams, size_t cchParams,
const char *pszValue, size_t cchValue,
bool fCmdLine)
{
Assert(RTStrNLen(pszDefine, cchDefine) == cchDefine);
Assert(RTStrNLen(pszParams, cchParams) == cchParams);
Assert(RTStrNLen(pszValue, cchValue) == cchValue);
/*
* Determin the number of arguments and how much space their names
* requires. Performing syntax validation while parsing.
*/
uint32_t cchArgNames = 0;
uint32_t cArgs = 0;
for (size_t off = 0; off < cchParams; off++)
{
/* Skip blanks and maybe one comma. */
bool fIgnoreComma = cArgs != 0;
while (off < cchParams)
{
if (!RT_C_IS_SPACE(pszParams[off]))
{
if (pszParams[off] != ',' || !fIgnoreComma)
{
if (vbcppIsCIdentifierLeadChar(pszParams[off]))
break;
/** @todo variadic macros. */
return vbcppErrorPos(pThis, &pszParams[off], "Unexpected character");
}
fIgnoreComma = false;
}
off++;
}
if (off >= cchParams)
break;
/* Found and argument. First character is already validated. */
cArgs++;
cchArgNames += 2;
off++;
while ( off < cchParams
&& vbcppIsCIdentifierChar(pszParams[off]))
off++, cchArgNames++;
}
/*
* Allocate a structure.
*/
size_t cbDef = RT_OFFSETOF(VBCPPMACRO, szValue[cchValue + 1 + cchDefine + 1 + cchArgNames])
+ sizeof(const char *) * cArgs;
cbDef = RT_ALIGN_Z(cbDef, sizeof(const char *));
PVBCPPMACRO pMacro = (PVBCPPMACRO)RTMemAlloc(cbDef);
if (!pMacro)
return RTMsgErrorExit(RTEXITCODE_FAILURE, "out of memory");
char *pszDst = &pMacro->szValue[cchValue + 1];
pMacro->Core.pszString = pszDst;
memcpy(pszDst, pszDefine, cchDefine);
pszDst += cchDefine;
*pszDst++ = '\0';
pMacro->fFunction = true;
pMacro->fVarArg = false;
pMacro->fCmdLine = fCmdLine;
pMacro->fExpanding = false;
pMacro->cArgs = cArgs;
pMacro->papszArgs = (const char **)((uintptr_t)pMacro + cbDef - sizeof(const char *) * cArgs);
VBCPP_BITMAP_EMPTY(pMacro->bmArgs);
pMacro->cchValue = cchValue;
memcpy(pMacro->szValue, pszValue, cchValue);
pMacro->szValue[cchValue] = '\0';
/*
* Set up the arguments.
*/
uint32_t iArg = 0;
for (size_t off = 0; off < cchParams; off++)
{
/* Skip blanks and maybe one comma. */
bool fIgnoreComma = cArgs != 0;
while (off < cchParams)
{
if (!RT_C_IS_SPACE(pszParams[off]))
{
if (pszParams[off] != ',' || !fIgnoreComma)
break;
fIgnoreComma = false;
}
off++;
}
if (off >= cchParams)
break;
/* Found and argument. First character is already validated. */
VBCPP_BITMAP_SET(pMacro->bmArgs, pszParams[off]);
pMacro->papszArgs[iArg] = pszDst;
do
{
*pszDst++ = pszParams[off++];
} while ( off < cchParams
&& vbcppIsCIdentifierChar(pszParams[off]));
*pszDst++ = '\0';
iArg++;
}
Assert((uintptr_t)pszDst <= (uintptr_t)pMacro->papszArgs);
return vbcppMacroInsert(pThis, pMacro);
}
/**
* Adds a define.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE + msg.
* @param pThis The C preprocessor instance.
* @param pszDefine The define name and optionally the argument
* list.
* @param cchDefine The length of the name. RTSTR_MAX is ok.
* @param pszValue The value.
* @param cchDefine The length of the value. RTSTR_MAX is ok.
* @param fCmdLine Set if originating on the command line.
*/
static RTEXITCODE vbcppMacroAdd(PVBCPP pThis, const char *pszDefine, size_t cchDefine,
const char *pszValue, size_t cchValue, bool fCmdLine)
{
/*
* We need the lengths. Trim the input.
*/
if (cchDefine == RTSTR_MAX)
cchDefine = strlen(pszDefine);
while (cchDefine > 0 && RT_C_IS_SPACE(*pszDefine))
pszDefine++, cchDefine--;
while (cchDefine > 0 && RT_C_IS_SPACE(pszDefine[cchDefine - 1]))
cchDefine--;
if (!cchDefine)
return vbcppErrorPos(pThis, pszDefine, "The define has no name");
if (cchValue == RTSTR_MAX)
cchValue = strlen(pszValue);
while (cchValue > 0 && RT_C_IS_SPACE(*pszValue))
pszValue++, cchValue--;
while (cchValue > 0 && RT_C_IS_SPACE(pszValue[cchValue - 1]))
cchValue--;
/*
* Arguments make the job a bit more annoying. Handle that elsewhere
*/
const char *pszParams = (const char *)memchr(pszDefine, '(', cchDefine);
if (pszParams)
{
size_t cchParams = pszDefine + cchDefine - pszParams;
cchDefine -= cchParams;
if (!vbcppValidateCIdentifier(pThis, pszDefine, cchDefine))
return RTEXITCODE_FAILURE;
if (pszParams[cchParams - 1] != ')')
return vbcppErrorPos(pThis, pszParams + cchParams - 1, "Missing closing parenthesis");
pszParams++;
cchParams -= 2;
return vbcppMacroAddFn(pThis, pszDefine, cchDefine, pszParams, cchParams, pszValue, cchValue, fCmdLine);
}
/*
* Simple define, no arguments.
*/
if (!vbcppValidateCIdentifier(pThis, pszDefine, cchDefine))
return RTEXITCODE_FAILURE;
PVBCPPMACRO pMacro = (PVBCPPMACRO)RTMemAlloc(RT_OFFSETOF(VBCPPMACRO, szValue[cchValue + 1 + cchDefine + 1]));
if (!pMacro)
return RTMsgErrorExit(RTEXITCODE_FAILURE, "out of memory");
pMacro->Core.pszString = &pMacro->szValue[cchValue + 1];
memcpy((char *)pMacro->Core.pszString, pszDefine, cchDefine);
((char *)pMacro->Core.pszString)[cchDefine] = '\0';
pMacro->fFunction = false;
pMacro->fVarArg = false;
pMacro->fCmdLine = fCmdLine;
pMacro->fExpanding = false;
pMacro->cArgs = 0;
pMacro->papszArgs = NULL;
VBCPP_BITMAP_EMPTY(pMacro->bmArgs);
pMacro->cchValue = cchValue;
memcpy(pMacro->szValue, pszValue, cchValue);
pMacro->szValue[cchValue] = '\0';
return vbcppMacroInsert(pThis, pMacro);
}
/**
* Tries to convert a define into an inline D constant.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pMacro The macro.
*/
static RTEXITCODE vbcppMacroTryConvertToInlineD(PVBCPP pThis, PVBCPPMACRO pMacro)
{
AssertReturn(pMacro, vbcppError(pThis, "Internal error"));
if (pMacro->fFunction)
return RTEXITCODE_SUCCESS;
/*
* Do some simple macro resolving. (Mostly to make x86.h work.)
*/
const char *pszDefine = pMacro->Core.pszString;
const char *pszValue = pMacro->szValue;
size_t cchValue = pMacro->cchValue;
unsigned i = 0;
PVBCPPMACRO pMacro2;
while ( i < 10
&& cchValue > 0
&& vbcppIsCIdentifierLeadChar(*pszValue)
&& (pMacro2 = vbcppMacroLookup(pThis, pszValue, cchValue)) != NULL
&& !pMacro2->fFunction )
{
pszValue = pMacro2->szValue;
cchValue = pMacro2->cchValue;
i++;
}
if (!pMacro->cchValue)
return RTEXITCODE_SUCCESS;
/*
* A lone value?
*/
ssize_t cch = 0;
uint64_t u64;
char *pszNext;
int rc = RTStrToUInt64Ex(pszValue, &pszNext, 0, &u64);
if (RT_SUCCESS(rc))
{
if ( rc == VWRN_TRAILING_SPACES
|| rc == VWRN_NEGATIVE_UNSIGNED
|| rc == VWRN_NUMBER_TOO_BIG)
return RTEXITCODE_SUCCESS;
const char *pszType;
if (rc == VWRN_TRAILING_CHARS)
{
if (!strcmp(pszNext, "u") || !strcmp(pszNext, "U"))
pszType = "uint32_t";
else if (!strcmp(pszNext, "ul") || !strcmp(pszNext, "UL"))
pszType = "uintptr_t";
else if (!strcmp(pszNext, "ull") || !strcmp(pszNext, "ULL"))
pszType = "uint64_t";
else
pszType = NULL;
}
else if (u64 <= UINT8_MAX)
pszType = "uint8_t";
else if (u64 <= UINT16_MAX)
pszType = "uint16_t";
else if (u64 <= UINT32_MAX)
pszType = "uint32_t";
else
pszType = "uint64_t";
if (!pszType)
return RTEXITCODE_SUCCESS;
cch = ScmStreamPrintf(&pThis->StrmOutput, "inline %s %s = %.*s;\n",
pszType, pszDefine, pszNext - pszValue, pszValue);
}
/*
* A value wrapped in a constant macro?
*/
else if ( (pszNext = (char *)strchr(pszValue, '(')) != NULL
&& pszValue[cchValue - 1] == ')' )
{
size_t cchPrefix = pszNext - pszValue;
size_t cchInnerValue = cchValue - cchPrefix - 2;
const char *pchInnerValue = &pszValue[cchPrefix + 1];
while (cchInnerValue > 0 && RT_C_IS_SPACE(*pchInnerValue))
cchInnerValue--, pchInnerValue++;
while (cchInnerValue > 0 && RT_C_IS_SPACE(pchInnerValue[cchInnerValue - 1]))
cchInnerValue--;
if (!cchInnerValue || !RT_C_IS_XDIGIT(*pchInnerValue))
return RTEXITCODE_SUCCESS;
rc = RTStrToUInt64Ex(pchInnerValue, &pszNext, 0, &u64);
if ( RT_FAILURE(rc)
|| rc == VWRN_TRAILING_SPACES
|| rc == VWRN_NEGATIVE_UNSIGNED
|| rc == VWRN_NUMBER_TOO_BIG)
return RTEXITCODE_SUCCESS;
const char *pszType;
#define MY_MATCH_STR(a_sz) (sizeof(a_sz) - 1 == cchPrefix && !strncmp(pszValue, a_sz, sizeof(a_sz) - 1))
if (MY_MATCH_STR("UINT8_C"))
pszType = "uint8_t";
else if (MY_MATCH_STR("UINT16_C"))
pszType = "uint16_t";
else if (MY_MATCH_STR("UINT32_C"))
pszType = "uint32_t";
else if (MY_MATCH_STR("UINT64_C"))
pszType = "uint64_t";
else
pszType = NULL;
if (pszType)
cch = ScmStreamPrintf(&pThis->StrmOutput, "inline %s %s = %.*s;\n",
pszType, pszDefine, cchInnerValue, pchInnerValue);
else if (MY_MATCH_STR("RT_BIT") || MY_MATCH_STR("RT_BIT_32"))
cch = ScmStreamPrintf(&pThis->StrmOutput, "inline uint32_t %s = 1U << %llu;\n",
pszDefine, u64);
else if (MY_MATCH_STR("RT_BIT_64"))
cch = ScmStreamPrintf(&pThis->StrmOutput, "inline uint64_t %s = 1ULL << %llu;\n",
pszDefine, u64);
else
return RTEXITCODE_SUCCESS;
#undef MY_MATCH_STR
}
/* Dunno what this is... */
else
return RTEXITCODE_SUCCESS;
/*
* Check for output error and clear the output suppression indicator.
*/
if (cch < 0)
return vbcppError(pThis, "Output error");
pThis->fJustDroppedLine = false;
return RTEXITCODE_SUCCESS;
}
/**
* Processes a abbreviated line number directive.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
* @param offStart The stream position where the directive
* started (for pass thru).
*/
static RTEXITCODE vbcppDirectiveDefine(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
{
/*
* Parse it.
*/
RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
if (rcExit == RTEXITCODE_SUCCESS)
{
size_t cchDefine;
const char *pchDefine = ScmStreamCGetWord(pStrmInput, &cchDefine);
if (pchDefine)
{
/* If it's a function style define, parse out the parameter list. */
size_t cchParams = 0;
const char *pchParams = NULL;
unsigned ch = ScmStreamPeekCh(pStrmInput);
if (ch == '(')
{
ScmStreamGetCh(pStrmInput);
pchParams = ScmStreamGetCur(pStrmInput);
unsigned chPrev = ch;
while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
{
if (ch == '\r' || ch == '\n')
{
if (chPrev != '\\')
{
rcExit = vbcppError(pThis, "Missing ')'");
break;
}
ScmStreamSeekByLine(pStrmInput, ScmStreamTellLine(pStrmInput) + 1);
}
if (ch == ')')
{
cchParams = ScmStreamGetCur(pStrmInput) - pchParams;
ScmStreamGetCh(pStrmInput);
break;
}
ScmStreamGetCh(pStrmInput);
}
}
/* The simple kind. */
else if (!RT_C_IS_SPACE(ch) && ch != ~(unsigned)0)
rcExit = vbcppError(pThis, "Expected whitespace after macro name");
/* Parse out the value. */
if (rcExit == RTEXITCODE_SUCCESS)
rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
if (rcExit == RTEXITCODE_SUCCESS)
{
size_t offValue = ScmStreamTell(pStrmInput);
const char *pchValue = ScmStreamGetCur(pStrmInput);
unsigned chPrev = ch;
while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
{
if (ch == '\r' || ch == '\n')
{
if (chPrev != '\\')
break;
ScmStreamSeekByLine(pStrmInput, ScmStreamTellLine(pStrmInput) + 1);
}
chPrev = ScmStreamGetCh(pStrmInput);
}
size_t cchValue = ScmStreamGetCur(pStrmInput) - pchValue;
/*
* Execute.
*/
if (pchParams)
rcExit = vbcppMacroAddFn(pThis, pchDefine, cchDefine, pchParams, cchParams, pchValue, cchValue, false);
else
rcExit = vbcppMacroAdd(pThis, pchDefine, cchDefine, pchValue, cchValue, false);
/*
* Pass thru?
*/
if ( rcExit == RTEXITCODE_SUCCESS
&& pThis->fPassThruDefines)
{
unsigned cchIndent = pThis->pCondStack ? pThis->pCondStack->iKeepLevel : 0;
ssize_t cch;
if (pchParams)
cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sdefine %.*s(%.*s)",
cchIndent, "", cchDefine, pchDefine, cchParams, pchParams);
else
cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sdefine %.*s",
cchIndent, "", cchDefine, pchDefine);
if (cch > 0)
vbcppOutputComment(pThis, pStrmInput, offValue, cch, 1);
else
rcExit = vbcppError(pThis, "output error");
}
else if ( rcExit == RTEXITCODE_SUCCESS
&& pThis->enmMode == kVBCppMode_SelectiveD)
rcExit = vbcppMacroTryConvertToInlineD(pThis, vbcppMacroLookup(pThis, pchDefine, cchDefine));
else
pThis->fJustDroppedLine = true;
}
}
}
return rcExit;
}
/**
* Processes a abbreviated line number directive.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
* @param offStart The stream position where the directive
* started (for pass thru).
*/
static RTEXITCODE vbcppDirectiveUndef(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
{
/*
* Parse it.
*/
RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
if (rcExit == RTEXITCODE_SUCCESS)
{
size_t cchDefine;
const char *pchDefine = ScmStreamCGetWord(pStrmInput, &cchDefine);
if (pchDefine)
{
size_t offMaybeComment = vbcppProcessSkipWhite(pStrmInput);
rcExit = vbcppProcessSkipWhiteEscapedEolAndCommentsCheckEol(pThis, pStrmInput);
if (rcExit == RTEXITCODE_SUCCESS)
{
/*
* Take action.
*/
PVBCPPMACRO pMacro = vbcppMacroLookup(pThis, pchDefine, cchDefine);
if ( pMacro
&& pThis->fRespectSourceDefines
&& ( !pMacro->fCmdLine
|| pThis->fAllowRedefiningCmdLineDefines ) )
{
RTStrSpaceRemove(&pThis->StrSpace, pMacro->Core.pszString);
vbcppMacroFree(&pMacro->Core, NULL);
}
/*
* Pass thru.
*/
if ( rcExit == RTEXITCODE_SUCCESS
&& pThis->fPassThruDefines)
{
unsigned cchIndent = pThis->pCondStack ? pThis->pCondStack->iKeepLevel : 0;
ssize_t cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sundef %.*s",
cchIndent, "", cchDefine, pchDefine);
if (cch > 0)
vbcppOutputComment(pThis, pStrmInput, offMaybeComment, cch, 1);
else
rcExit = vbcppError(pThis, "output error");
}
}
}
else
rcExit = vbcppError(pThis, "Malformed #ifndef");
}
return rcExit;
}
/*
*
*
* C O N D I T I O N A L S
* C O N D I T I O N A L S
* C O N D I T I O N A L S
* C O N D I T I O N A L S
* C O N D I T I O N A L S
*
*
*/
/**
* Combines current stack result with the one being pushed.
*
* @returns Combined result.
* @param enmEvalPush The result of the condition being pushed.
* @param enmEvalStack The current stack result.
*/
static VBCPPEVAL vbcppCondCombine(VBCPPEVAL enmEvalPush, VBCPPEVAL enmEvalStack)
{
if (enmEvalStack == kVBCppEval_False)
return kVBCppEval_False;
return enmEvalPush;
}
/**
* Pushes an conditional onto the stack.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The current input stream.
* @param offStart Not currently used, using @a pchCondition and
* @a cchCondition instead.
* @param enmKind The kind of conditional.
* @param enmResult The result of the evaluation.
* @param pchCondition The raw condition.
* @param cchCondition The length of @a pchCondition.
*/
static RTEXITCODE vbcppCondPush(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart,
VBCPPCONDKIND enmKind, VBCPPEVAL enmResult,
const char *pchCondition, size_t cchCondition)
{
if (pThis->cCondStackDepth >= _64K)
return vbcppError(pThis, "Too many nested #if/#ifdef/#ifndef statements");
/*
* Allocate a new entry and push it.
*/
PVBCPPCOND pCond = (PVBCPPCOND)RTMemAlloc(sizeof(*pCond));
if (!pCond)
return vbcppError(pThis, "out of memory");
PVBCPPCOND pUp = pThis->pCondStack;
pCond->enmKind = enmKind;
pCond->enmResult = enmResult;
pCond->enmStackResult = pUp ? vbcppCondCombine(enmResult, pUp->enmStackResult) : enmResult;
pCond->fSeenElse = false;
pCond->fElIfDecided = enmResult == kVBCppEval_True;
pCond->iLevel = pThis->cCondStackDepth;
pCond->iKeepLevel = (pUp ? pUp->iKeepLevel : 0) + enmResult == kVBCppEval_Undecided;
pCond->pchCond = pchCondition;
pCond->cchCond = cchCondition;
pCond->pUp = pThis->pCondStack;
pThis->pCondStack = pCond;
pThis->fIf0Mode = pCond->enmStackResult == kVBCppEval_False;
/*
* Do pass thru.
*/
if ( !pThis->fIf0Mode
&& enmResult == kVBCppEval_Undecided)
{
/** @todo this is stripping comments of \#ifdef and \#ifndef atm. */
const char *pszDirective;
switch (enmKind)
{
case kVBCppCondKind_If: pszDirective = "if"; break;
case kVBCppCondKind_IfDef: pszDirective = "ifdef"; break;
case kVBCppCondKind_IfNDef: pszDirective = "ifndef"; break;
case kVBCppCondKind_ElIf: pszDirective = "elif"; break;
default: AssertFailedReturn(RTEXITCODE_FAILURE);
}
ssize_t cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*s%s %.*s",
pCond->iKeepLevel - 1, "", pszDirective, cchCondition, pchCondition);
if (cch < 0)
return vbcppError(pThis, "Output error %Rrc", (int)cch);
}
else
pThis->fJustDroppedLine = true;
return RTEXITCODE_SUCCESS;
}
/**
* Recursively destroys the expression tree.
*
* @param pExpr The root of the expression tree to destroy.
*/
static void vbcppExprDestoryTree(PVBCPPEXPR pExpr)
{
if (!pExpr)
return;
switch (pExpr->enmKind)
{
case kVBCppExprKind_Unary:
vbcppExprDestoryTree(pExpr->u.Unary.pArg);
break;
case kVBCppExprKind_Binary:
vbcppExprDestoryTree(pExpr->u.Binary.pLeft);
vbcppExprDestoryTree(pExpr->u.Binary.pRight);
break;
case kVBCppExprKind_Ternary:
vbcppExprDestoryTree(pExpr->u.Ternary.pExpr);
vbcppExprDestoryTree(pExpr->u.Ternary.pExpr);
vbcppExprDestoryTree(pExpr->u.Ternary.pFalse);
break;
case kVBCppExprKind_SignedValue:
case kVBCppExprKind_UnsignedValue:
break;
default:
AssertFailed();
return;
}
RTMemFree(pExpr);
}
/**
* Report error during expression parsing.
*
* @returns kExprRet_Error
* @param pParser The parser instance.
* @param pszMsg The error message.
* @param ... Format arguments.
*/
static VBCPPEXPRRET vbcppExprParseError(PVBCPPEXPRPARSER pParser, const char *pszMsg, ...)
{
va_list va;
va_start(va, pszMsg);
vbcppErrorV(pParser->pThis, pszMsg, va);
va_end(va);
return kExprRet_Error;
}
/**
* Skip white space.
*
* @param pParser The parser instance.
*/
static void vbcppExprParseSkipWhiteSpace(PVBCPPEXPRPARSER pParser)
{
while (RT_C_IS_SPACE(*pParser->pszCur))
pParser->pszCur++;
}
/**
* Allocate a new
*
* @returns Pointer to the node. NULL+msg on failure.
* @param pParser The parser instance.
*/
static PVBCPPEXPR vbcppExprParseAllocNode(PVBCPPEXPRPARSER pParser)
{
PVBCPPEXPR pExpr = (PVBCPPEXPR)RTMemAllocZ(sizeof(*pExpr));
if (!pExpr)
vbcppExprParseError(pParser, "out of memory (expression node)");
return pExpr;
}
/**
* Looks for right parentheses and/or end of expression.
*
* @returns Expression status.
* @retval kExprRet_Ok
* @retval kExprRet_Error with msg.
* @retval kExprRet_EndOfExpr
* @param pParser The parser instance.
*/
static VBCPPEXPRRET vbcppExprParseMaybeRParenOrEoe(PVBCPPEXPRPARSER pParser)
{
Assert(!pParser->ppCur);
for (;;)
{
vbcppExprParseSkipWhiteSpace(pParser);
char ch = *pParser->pszCur;
if (ch == '\0')
return kExprRet_EndOfExpr;
if (ch != ')')
break;
pParser->pszCur++;
PVBCPPEXPR pCur = pParser->pCur;
while ( pCur
&& ( pCur->enmKind != kVBCppExprKind_Unary
|| pCur->u.Unary.enmOperator != kVBCppUnaryOp_Parenthesis))
{
switch (pCur->enmKind)
{
case kVBCppExprKind_SignedValue:
case kVBCppExprKind_UnsignedValue:
Assert(pCur->fComplete);
break;
case kVBCppExprKind_Unary:
AssertReturn(pCur->u.Unary.pArg, vbcppExprParseError(pParser, "internal error"));
pCur->fComplete = true;
break;
case kVBCppExprKind_Binary:
AssertReturn(pCur->u.Binary.pLeft, vbcppExprParseError(pParser, "internal error"));
AssertReturn(pCur->u.Binary.pRight, vbcppExprParseError(pParser, "internal error"));
pCur->fComplete = true;
break;
case kVBCppExprKind_Ternary:
#if 1 /** @todo Check out the ternary operator implementation. */
return vbcppExprParseError(pParser, "The ternary operator is not implemented");
#else
Assert(pCur->u.Ternary.pExpr);
if (!pCur->u.Ternary.pTrue)
return vbcppExprParseError(pParser, "?!?!?");
if (!pCur->u.Ternary.pFalse)
return vbcppExprParseError(pParser, "?!?!?!?");
pCur->fComplete = true;
#endif
break;
default:
return vbcppExprParseError(pParser, "Internal error (enmKind=%d)", pCur->enmKind);
}
pCur = pCur->pParent;
}
if (!pCur)
return vbcppExprParseError(pParser, "Right parenthesis without a left one");
pCur->fComplete = true;
while ( pCur->enmKind == kVBCppExprKind_Unary
&& pCur->u.Unary.enmOperator != kVBCppUnaryOp_Parenthesis
&& pCur->pParent)
{
AssertReturn(pCur->u.Unary.pArg, vbcppExprParseError(pParser, "internal error"));
pCur->fComplete = true;
pCur = pCur->pParent;
}
}
return kExprRet_Ok;
}
/**
* Parses an binary operator.
*
* @returns Expression status.
* @retval kExprRet_Ok
* @retval kExprRet_Error with msg.
* @param pParser The parser instance.
*/
static VBCPPEXPRRET vbcppExprParseBinaryOperator(PVBCPPEXPRPARSER pParser)
{
/*
* Binary or ternary operator should follow now.
*/
VBCPPBINARYOP enmOp;
char ch = *pParser->pszCur;
switch (ch)
{
case '*':
if (pParser->pszCur[1] == '=')
return vbcppExprParseError(pParser, "The assignment by product operator is not valid in a preprocessor expression");
enmOp = kVBCppBinary_Multiplication;
break;
case '/':
if (pParser->pszCur[1] == '=')
return vbcppExprParseError(pParser, "The assignment by quotient operator is not valid in a preprocessor expression");
enmOp = kVBCppBinary_Division;
break;
case '%':
if (pParser->pszCur[1] == '=')
return vbcppExprParseError(pParser, "The assignment by remainder operator is not valid in a preprocessor expression");
enmOp = kVBCppBinary_Modulo;
break;
case '+':
if (pParser->pszCur[1] == '=')
return vbcppExprParseError(pParser, "The assignment by sum operator is not valid in a preprocessor expression");
enmOp = kVBCppBinary_Addition;
break;
case '-':
if (pParser->pszCur[1] == '=')
return vbcppExprParseError(pParser, "The assignment by difference operator is not valid in a preprocessor expression");
enmOp = kVBCppBinary_Subtraction;
break;
case '<':
enmOp = kVBCppBinary_LessThan;
if (pParser->pszCur[1] == '=')
{
pParser->pszCur++;
enmOp = kVBCppBinary_LessThanOrEqual;
}
else if (pParser->pszCur[1] == '<')
{
pParser->pszCur++;
if (pParser->pszCur[1] == '=')
return vbcppExprParseError(pParser, "The assignment by bitwise left shift operator is not valid in a preprocessor expression");
enmOp = kVBCppBinary_LeftShift;
}
break;
case '>':
enmOp = kVBCppBinary_GreaterThan; break;
if (pParser->pszCur[1] == '=')
{
pParser->pszCur++;
enmOp = kVBCppBinary_GreaterThanOrEqual;
}
else if (pParser->pszCur[1] == '<')
{
pParser->pszCur++;
if (pParser->pszCur[1] == '=')
return vbcppExprParseError(pParser, "The assignment by bitwise right shift operator is not valid in a preprocessor expression");
enmOp = kVBCppBinary_LeftShift;
}
break;
case '=':
if (pParser->pszCur[1] != '=')
return vbcppExprParseError(pParser, "The assignment operator is not valid in a preprocessor expression");
pParser->pszCur++;
enmOp = kVBCppBinary_EqualTo;
break;
case '!':
if (pParser->pszCur[1] != '=')
return vbcppExprParseError(pParser, "Expected binary operator, found the unary operator logical NOT");
pParser->pszCur++;
enmOp = kVBCppBinary_NotEqualTo;
break;
case '&':
if (pParser->pszCur[1] == '=')
return vbcppExprParseError(pParser, "The assignment by bitwise AND operator is not valid in a preprocessor expression");
if (pParser->pszCur[1] == '&')
{
pParser->pszCur++;
enmOp = kVBCppBinary_LogicalAnd;
}
else
enmOp = kVBCppBinary_BitwiseAnd;
break;
case '^':
if (pParser->pszCur[1] == '=')
return vbcppExprParseError(pParser, "The assignment by bitwise XOR operator is not valid in a preprocessor expression");
enmOp = kVBCppBinary_BitwiseXor;
break;
case '|':
if (pParser->pszCur[1] == '=')
return vbcppExprParseError(pParser, "The assignment by bitwise AND operator is not valid in a preprocessor expression");
if (pParser->pszCur[1] == '|')
{
pParser->pszCur++;
enmOp = kVBCppBinary_LogicalOr;
}
else
enmOp = kVBCppBinary_BitwiseOr;
break;
case '~':
return vbcppExprParseError(pParser, "Expected binary operator, found the unary operator bitwise NOT");
case ':':
case '?':
return vbcppExprParseError(pParser, "The ternary operator is not yet implemented");
default:
return vbcppExprParseError(pParser, "Expected binary operator, found '%.20s'", pParser->pszCur);
}
pParser->pszCur++;
/*
* Create a binary operator node.
*/
PVBCPPEXPR pExpr = vbcppExprParseAllocNode(pParser);
if (!pExpr)
return kExprRet_Error;
pExpr->fComplete = true;
pExpr->enmKind = kVBCppExprKind_Binary;
pExpr->u.Binary.enmOperator = enmOp;
pExpr->u.Binary.pLeft = NULL;
pExpr->u.Binary.pRight = NULL;
/*
* Back up the tree until we find our spot.
*/
PVBCPPEXPR *ppPlace = NULL;
PVBCPPEXPR pChild = NULL;
PVBCPPEXPR pParent = pParser->pCur;
while (pParent)
{
if (pParent->enmKind == kVBCppExprKind_Unary)
{
if (pParent->u.Unary.enmOperator == kVBCppUnaryOp_Parenthesis)
{
ppPlace = &pParent->u.Unary.pArg;
break;
}
AssertReturn(pParent->u.Unary.pArg, vbcppExprParseError(pParser, "internal error"));
pParent->fComplete = true;
}
else if (pParent->enmKind == kVBCppExprKind_Binary)
{
AssertReturn(pParent->u.Binary.pLeft, vbcppExprParseError(pParser, "internal error"));
AssertReturn(pParent->u.Binary.pRight, vbcppExprParseError(pParser, "internal error"));
if ((pParent->u.Binary.enmOperator & VBCPPOP_PRECEDENCE_MASK) >= (enmOp & VBCPPOP_PRECEDENCE_MASK))
{
AssertReturn(pChild, vbcppExprParseError(pParser, "internal error"));
if (pParent->u.Binary.pRight == pChild)
ppPlace = &pParent->u.Binary.pRight;
else
ppPlace = &pParent->u.Binary.pLeft;
AssertReturn(*ppPlace == pChild, vbcppExprParseError(pParser, "internal error"));
break;
}
pParent->fComplete = true;
}
else if (pParent->enmKind == kVBCppExprKind_Ternary)
{
return vbcppExprParseError(pParser, "The ternary operator is not implemented");
}
else
AssertReturn( pParent->enmKind == kVBCppExprKind_SignedValue
|| pParent->enmKind == kVBCppExprKind_UnsignedValue,
vbcppExprParseError(pParser, "internal error"));
/* Up on level */
pChild = pParent;
pParent = pParent->pParent;
}
/*
* Do the rotation.
*/
Assert(pChild);
Assert(pChild->pParent == pParent);
pChild->pParent = pExpr;
pExpr->u.Binary.pLeft = pChild;
pExpr->pParent = pParent;
if (!pParent)
pParser->pRoot = pExpr;
else
*ppPlace = pExpr;
pParser->ppCur = &pExpr->u.Binary.pRight;
pParser->pCur = pExpr;
return kExprRet_Ok;
}
/**
* Deals with right paretheses or/and end of expression, looks for binary
* operators.
*
* @returns Expression status.
* @retval kExprRet_Ok if binary operator was found processed.
* @retval kExprRet_Error with msg.
* @retval kExprRet_EndOfExpr
* @param pParser The parser instance.
*/
static VBCPPEXPRRET vbcppExprParseBinaryOrEoeOrRparen(PVBCPPEXPRPARSER pParser)
{
VBCPPEXPRRET enmRet = vbcppExprParseMaybeRParenOrEoe(pParser);
if (enmRet != kExprRet_Ok)
return enmRet;
return vbcppExprParseBinaryOperator(pParser);
}
/**
* Parses an identifier in the expression, replacing it by 0.
*
* All known identifiers has already been replaced by their macro values, so
* what's left are unknown macros. These are replaced by 0.
*
* @returns Expression status.
* @retval kExprRet_Value
* @retval kExprRet_Error with msg.
* @param pParser The parser instance.
*/
static VBCPPEXPRRET vbcppExprParseIdentifier(PVBCPPEXPRPARSER pParser)
{
/** @todo don't increment if it's an actively undefined macro. Need to revise
* the expression related code wrt selective preprocessing. */
pParser->cUndefined++;
/* Find the end. */
const char *pszMacro = pParser->pszCur;
const char *pszNext = pszMacro + 1;
while (vbcppIsCIdentifierChar(*pszNext))
pszNext++;
size_t cchMacro = pszNext - pszMacro;
/* Create a signed value node. */
PVBCPPEXPR pExpr = vbcppExprParseAllocNode(pParser);
if (!pExpr)
return kExprRet_Error;
pExpr->fComplete = true;
pExpr->enmKind = kVBCppExprKind_UnsignedValue;
pExpr->u.UnsignedValue.u64 = 0;
/* Link it. */
pExpr->pParent = pParser->pCur;
pParser->pCur = pExpr;
*pParser->ppCur = pExpr;
pParser->ppCur = NULL;
/* Skip spaces and check for parenthesis. */
pParser->pszCur = pszNext;
vbcppExprParseSkipWhiteSpace(pParser);
if (*pParser->pszCur == '(')
return vbcppExprParseError(pParser, "Unknown unary operator '%.*s'", cchMacro, pszMacro);
return kExprRet_Value;
}
/**
* Parses an numeric constant in the expression.
*
* @returns Expression status.
* @retval kExprRet_Value
* @retval kExprRet_Error with msg.
* @param pParser The parser instance.
*/
static VBCPPEXPRRET vbcppExprParseNumber(PVBCPPEXPRPARSER pParser)
{
bool fSigned;
char *pszNext;
uint64_t u64;
char ch = *pParser->pszCur++;
char ch2 = *pParser->pszCur;
if ( ch == '0'
&& (ch == 'x' || ch == 'X'))
{
ch2 = *++pParser->pszCur;
if (!RT_C_IS_XDIGIT(ch2))
return vbcppExprParseError(pParser, "Expected hex digit following '0x'");
int rc = RTStrToUInt64Ex(pParser->pszCur, &pszNext, 16, &u64);
if ( RT_FAILURE(rc)
|| rc == VWRN_NUMBER_TOO_BIG)
return vbcppExprParseError(pParser, "Invalid hex value '%.20s...' (%Rrc)", pParser->pszCur, rc);
fSigned = false;
}
else if (ch == '0')
{
int rc = RTStrToUInt64Ex(pParser->pszCur - 1, &pszNext, 8, &u64);
if ( RT_FAILURE(rc)
|| rc == VWRN_NUMBER_TOO_BIG)
return vbcppExprParseError(pParser, "Invalid octal value '%.20s...' (%Rrc)", pParser->pszCur, rc);
fSigned = u64 > (uint64_t)INT64_MAX ? false : true;
}
else
{
int rc = RTStrToUInt64Ex(pParser->pszCur - 1, &pszNext, 10, &u64);
if ( RT_FAILURE(rc)
|| rc == VWRN_NUMBER_TOO_BIG)
return vbcppExprParseError(pParser, "Invalid decimal value '%.20s...' (%Rrc)", pParser->pszCur, rc);
fSigned = u64 > (uint64_t)INT64_MAX ? false : true;
}
/* suffix. */
if (vbcppIsCIdentifierLeadChar(*pszNext))
{
size_t cchSuffix = 1;
while (vbcppIsCIdentifierLeadChar(pszNext[cchSuffix]))
cchSuffix++;
if (cchSuffix == '1' && (*pszNext == 'u' || *pszNext == 'U'))
fSigned = false;
else if ( cchSuffix == '1'
&& (*pszNext == 'l' || *pszNext == 'L'))
fSigned = true;
else if ( cchSuffix == '2'
&& (!strncmp(pszNext, "ul", 2) || !strncmp(pszNext, "UL", 2)))
fSigned = false;
else if ( cchSuffix == '2'
&& (!strncmp(pszNext, "ll", 2) || !strncmp(pszNext, "LL", 2)))
fSigned = true;
else if ( cchSuffix == '3'
&& (!strncmp(pszNext, "ull", 3) || !strncmp(pszNext, "ULL", 3)))
fSigned = false;
else
return vbcppExprParseError(pParser, "Invalid number suffix '%.*s'", cchSuffix, pszNext);
pszNext += cchSuffix;
}
pParser->pszCur = pszNext;
/* Create a signed value node. */
PVBCPPEXPR pExpr = vbcppExprParseAllocNode(pParser);
if (!pExpr)
return kExprRet_Error;
pExpr->fComplete = true;
if (fSigned)
{
pExpr->enmKind = kVBCppExprKind_SignedValue;
pExpr->u.SignedValue.s64 = (int64_t)u64;
}
else
{
pExpr->enmKind = kVBCppExprKind_UnsignedValue;
pExpr->u.UnsignedValue.u64 = u64;
}
/* Link it. */
pExpr->pParent = pParser->pCur;
pParser->pCur = pExpr;
*pParser->ppCur = pExpr;
pParser->ppCur = NULL;
return kExprRet_Value;
}
/**
* Parses an character constant in the expression.
*
* @returns Expression status.
* @retval kExprRet_Value
* @retval kExprRet_Error with msg.
* @param pParser The parser instance.
*/
static VBCPPEXPRRET vbcppExprParseCharacterConstant(PVBCPPEXPRPARSER pParser)
{
char ch = *pParser->pszCur++;
char ch2 = *pParser->pszCur++;
if (ch2 == '\'')
return vbcppExprParseError(pParser, "Empty character constant");
int64_t s64;
if (ch2 == '\\')
{
ch2 = *pParser->pszCur++;
switch (ch2)
{
case '0': s64 = 0x00; break;
case 'n': s64 = 0x0d; break;
case 'r': s64 = 0x0a; break;
case 't': s64 = 0x09; break;
default:
return vbcppExprParseError(pParser, "Escape character '%c' is not implemented", ch2);
}
}
else
s64 = ch2;
if (*pParser->pszCur != '\'')
return vbcppExprParseError(pParser, "Character constant contains more than one character");
/* Create a signed value node. */
PVBCPPEXPR pExpr = vbcppExprParseAllocNode(pParser);
if (!pExpr)
return kExprRet_Error;
pExpr->fComplete = true;
pExpr->enmKind = kVBCppExprKind_SignedValue;
pExpr->u.SignedValue.s64 = s64;
/* Link it. */
pExpr->pParent = pParser->pCur;
pParser->pCur = pExpr;
*pParser->ppCur = pExpr;
pParser->ppCur = NULL;
return kExprRet_Value;
}
/**
* Parses a unary operator or a value.
*
* @returns Expression status.
* @retval kExprRet_Value if value was found and processed.
* @retval kExprRet_UnaryOperator if an unary operator was found and processed.
* @retval kExprRet_Error with msg.
* @param pParser The parser instance.
*/
static VBCPPEXPRRET vbcppExprParseUnaryOrValue(PVBCPPEXPRPARSER pParser)
{
vbcppExprParseSkipWhiteSpace(pParser);
char ch = *pParser->pszCur;
if (ch == '\0')
return vbcppExprParseError(pParser, "Premature end of expression");
/*
* Value?
*/
if (ch == '\'')
return vbcppExprParseCharacterConstant(pParser);
if (RT_C_IS_DIGIT(ch))
return vbcppExprParseNumber(pParser);
if (ch == '"')
return vbcppExprParseError(pParser, "String litteral");
if (vbcppIsCIdentifierLeadChar(ch))
return vbcppExprParseIdentifier(pParser);
/*
* Operator?
*/
VBCPPUNARYOP enmOperator;
if (ch == '+')
{
enmOperator = kVBCppUnaryOp_Pluss;
if (pParser->pszCur[1] == '+')
return vbcppExprParseError(pParser, "The prefix increment operator is not valid in a preprocessor expression");
}
else if (ch == '-')
{
enmOperator = kVBCppUnaryOp_Minus;
if (pParser->pszCur[1] == '-')
return vbcppExprParseError(pParser, "The prefix decrement operator is not valid in a preprocessor expression");
}
else if (ch == '!')
enmOperator = kVBCppUnaryOp_LogicalNot;
else if (ch == '~')
enmOperator = kVBCppUnaryOp_BitwiseNot;
else if (ch == '(')
enmOperator = kVBCppUnaryOp_Parenthesis;
else
return vbcppExprParseError(pParser, "Unknown token '%.*s'", 32, pParser->pszCur - 1);
pParser->pszCur++;
/* Create an operator node. */
PVBCPPEXPR pExpr = vbcppExprParseAllocNode(pParser);
if (!pExpr)
return kExprRet_Error;
pExpr->fComplete = false;
pExpr->enmKind = kVBCppExprKind_Unary;
pExpr->u.Unary.enmOperator = enmOperator;
pExpr->u.Unary.pArg = NULL;
/* Link it into the tree. */
pExpr->pParent = pParser->pCur;
pParser->pCur = pExpr;
*pParser->ppCur = pExpr;
pParser->ppCur = &pExpr->u.Unary.pArg;
return kExprRet_UnaryOperator;
}
/**
* Parses an expanded preprocessor expression.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pszExpr The expression to parse.
* @param cchExpr The length of the expression in case we need it.
* @param ppExprTree Where to return the parse tree.
* @param pcUndefined Where to return the number of unknown undefined
* macros. Optional.
*/
static RTEXITCODE vbcppExprParse(PVBCPP pThis, char *pszExpr, size_t cchExpr, PVBCPPEXPR *ppExprTree, size_t *pcUndefined)
{
RTEXITCODE rcExit = RTEXITCODE_FAILURE;
NOREF(cchExpr);
/*
* Initialize the parser context structure.
*/
VBCPPEXPRPARSER Parser;
Parser.pszCur = pszExpr;
Parser.pRoot = NULL;
Parser.pCur = NULL;
Parser.ppCur = &Parser.pRoot;
Parser.pszExpr = pszExpr;
Parser.cUndefined = 0;
Parser.pThis = pThis;
/*
* Do the parsing.
*/
VBCPPEXPRRET enmRet;
for (;;)
{
/*
* Eat unary operators until we hit a value.
*/
do
enmRet = vbcppExprParseUnaryOrValue(&Parser);
while (enmRet == kExprRet_UnaryOperator);
if (enmRet == kExprRet_Error)
break;
AssertBreakStmt(enmRet == kExprRet_Value, enmRet = vbcppExprParseError(&Parser, "Expected value (enmRet=%d)", enmRet));
/*
* Non-unary operator, right parenthesis or end of expression is up next.
*/
enmRet = vbcppExprParseBinaryOrEoeOrRparen(&Parser);
if (enmRet == kExprRet_Error)
break;
if (enmRet == kExprRet_EndOfExpr)
{
/** @todo check if there are any open parentheses. */
rcExit = RTEXITCODE_SUCCESS;
break;
}
AssertBreakStmt(enmRet == kExprRet_Ok, enmRet = vbcppExprParseError(&Parser, "Expected value (enmRet=%d)", enmRet));
}
if (rcExit != RTEXITCODE_SUCCESS)
{
vbcppExprDestoryTree(Parser.pRoot);
return rcExit;
}
if (pcUndefined)
*pcUndefined = Parser.cUndefined;
*ppExprTree = Parser.pRoot;
return rcExit;
}
/**
* Checks if an expression value value is evaluates to @c true or @c false.
*
* @returns @c true or @c false.
* @param pExpr The value expression.
*/
static bool vbcppExprIsExprTrue(PVBCPPEXPR pExpr)
{
Assert(pExpr->enmKind == kVBCppExprKind_SignedValue || pExpr->enmKind == kVBCppExprKind_UnsignedValue);
return pExpr->enmKind == kVBCppExprKind_SignedValue
? pExpr->u.SignedValue.s64 != 0
: pExpr->u.UnsignedValue.u64 != 0;
}
/**
* Evalutes a parse (sub-)tree.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pRoot The root of the parse (sub-)tree.
* @param pResult Where to store the result value.
*/
static RTEXITCODE vbcppExprEvaluteTree(PVBCPP pThis, PVBCPPEXPR pRoot, PVBCPPEXPR pResult)
{
RTEXITCODE rcExit;
switch (pRoot->enmKind)
{
case kVBCppExprKind_SignedValue:
pResult->enmKind = kVBCppExprKind_SignedValue;
pResult->u.SignedValue.s64 = pRoot->u.SignedValue.s64;
return RTEXITCODE_SUCCESS;
case kVBCppExprKind_UnsignedValue:
pResult->enmKind = kVBCppExprKind_UnsignedValue;
pResult->u.UnsignedValue.u64 = pRoot->u.UnsignedValue.u64;
return RTEXITCODE_SUCCESS;
case kVBCppExprKind_Unary:
rcExit = vbcppExprEvaluteTree(pThis, pRoot->u.Unary.pArg, pResult);
if (rcExit != RTEXITCODE_SUCCESS)
return rcExit;
/* Apply the unary operator to the value */
switch (pRoot->u.Unary.enmOperator)
{
case kVBCppUnaryOp_Minus:
if (pResult->enmKind == kVBCppExprKind_SignedValue)
pResult->u.SignedValue.s64 = -pResult->u.SignedValue.s64;
else
pResult->u.UnsignedValue.u64 = (uint64_t)-(int64_t)pResult->u.UnsignedValue.u64;
break;
case kVBCppUnaryOp_LogicalNot:
if (pResult->enmKind == kVBCppExprKind_SignedValue)
pResult->u.SignedValue.s64 = !pResult->u.SignedValue.s64;
else
pResult->u.UnsignedValue.u64 = !pResult->u.UnsignedValue.u64;
break;
case kVBCppUnaryOp_BitwiseNot:
if (pResult->enmKind == kVBCppExprKind_SignedValue)
pResult->u.SignedValue.s64 = ~pResult->u.SignedValue.s64;
else
pResult->u.UnsignedValue.u64 = ~pResult->u.UnsignedValue.u64;
break;
case kVBCppUnaryOp_Pluss:
case kVBCppUnaryOp_Parenthesis:
/* do nothing. */
break;
default:
return vbcppError(pThis, "Internal error: u.Unary.enmOperator=%d", pRoot->u.Unary.enmOperator);
}
return RTEXITCODE_SUCCESS;
case kVBCppExprKind_Binary:
{
/* Always evalute the left side. */
rcExit = vbcppExprEvaluteTree(pThis, pRoot->u.Binary.pLeft, pResult);
if (rcExit != RTEXITCODE_SUCCESS)
return rcExit;
/* If logical AND or OR we can sometimes skip evaluting the right side. */
if ( pRoot->u.Binary.enmOperator == kVBCppBinary_LogicalAnd
&& !vbcppExprIsExprTrue(pResult))
return RTEXITCODE_SUCCESS;
if ( pRoot->u.Binary.enmOperator == kVBCppBinary_LogicalOr
&& vbcppExprIsExprTrue(pResult))
return RTEXITCODE_SUCCESS;
/* Evalute the right side. */
VBCPPEXPR Result2;
rcExit = vbcppExprEvaluteTree(pThis, pRoot->u.Binary.pRight, &Result2);
if (rcExit != RTEXITCODE_SUCCESS)
return rcExit;
/* If one of them is unsigned, promote the other to unsigned as well. */
if ( pResult->enmKind == kVBCppExprKind_UnsignedValue
&& Result2.enmKind == kVBCppExprKind_SignedValue)
{
Result2.enmKind = kVBCppExprKind_UnsignedValue;
Result2.u.UnsignedValue.u64 = Result2.u.SignedValue.s64;
}
else if ( pResult->enmKind == kVBCppExprKind_SignedValue
&& Result2.enmKind == kVBCppExprKind_UnsignedValue)
{
pResult->enmKind = kVBCppExprKind_UnsignedValue;
pResult->u.UnsignedValue.u64 = pResult->u.SignedValue.s64;
}
/* Perform the operation. */
if (pResult->enmKind == kVBCppExprKind_UnsignedValue)
{
switch (pRoot->u.Binary.enmOperator)
{
case kVBCppBinary_Multiplication:
pResult->u.UnsignedValue.u64 *= Result2.u.UnsignedValue.u64;
break;
case kVBCppBinary_Division:
if (!Result2.u.UnsignedValue.u64)
return vbcppError(pThis, "Divide by zero");
pResult->u.UnsignedValue.u64 /= Result2.u.UnsignedValue.u64;
break;
case kVBCppBinary_Modulo:
if (!Result2.u.UnsignedValue.u64)
return vbcppError(pThis, "Divide by zero");
pResult->u.UnsignedValue.u64 %= Result2.u.UnsignedValue.u64;
break;
case kVBCppBinary_Addition:
pResult->u.UnsignedValue.u64 += Result2.u.UnsignedValue.u64;
break;
case kVBCppBinary_Subtraction:
pResult->u.UnsignedValue.u64 -= Result2.u.UnsignedValue.u64;
break;
case kVBCppBinary_LeftShift:
pResult->u.UnsignedValue.u64 <<= Result2.u.UnsignedValue.u64;
break;
case kVBCppBinary_RightShift:
pResult->u.UnsignedValue.u64 >>= Result2.u.UnsignedValue.u64;
break;
case kVBCppBinary_LessThan:
pResult->u.UnsignedValue.u64 = pResult->u.UnsignedValue.u64 < Result2.u.UnsignedValue.u64;
break;
case kVBCppBinary_LessThanOrEqual:
pResult->u.UnsignedValue.u64 = pResult->u.UnsignedValue.u64 <= Result2.u.UnsignedValue.u64;
break;
case kVBCppBinary_GreaterThan:
pResult->u.UnsignedValue.u64 = pResult->u.UnsignedValue.u64 > Result2.u.UnsignedValue.u64;
break;
case kVBCppBinary_GreaterThanOrEqual:
pResult->u.UnsignedValue.u64 = pResult->u.UnsignedValue.u64 >= Result2.u.UnsignedValue.u64;
break;
case kVBCppBinary_EqualTo:
pResult->u.UnsignedValue.u64 = pResult->u.UnsignedValue.u64 == Result2.u.UnsignedValue.u64;
break;
case kVBCppBinary_NotEqualTo:
pResult->u.UnsignedValue.u64 = pResult->u.UnsignedValue.u64 != Result2.u.UnsignedValue.u64;
break;
case kVBCppBinary_BitwiseAnd:
pResult->u.UnsignedValue.u64 &= Result2.u.UnsignedValue.u64;
break;
case kVBCppBinary_BitwiseXor:
pResult->u.UnsignedValue.u64 ^= Result2.u.UnsignedValue.u64;
break;
case kVBCppBinary_BitwiseOr:
pResult->u.UnsignedValue.u64 |= Result2.u.UnsignedValue.u64;
break;
case kVBCppBinary_LogicalAnd:
pResult->u.UnsignedValue.u64 = pResult->u.UnsignedValue.u64 && Result2.u.UnsignedValue.u64;
break;
case kVBCppBinary_LogicalOr:
pResult->u.UnsignedValue.u64 = pResult->u.UnsignedValue.u64 || Result2.u.UnsignedValue.u64;
break;
default:
return vbcppError(pThis, "Internal error: u.Binary.enmOperator=%d", pRoot->u.Binary.enmOperator);
}
}
else
{
switch (pRoot->u.Binary.enmOperator)
{
case kVBCppBinary_Multiplication:
pResult->u.SignedValue.s64 *= Result2.u.SignedValue.s64;
break;
case kVBCppBinary_Division:
if (!Result2.u.SignedValue.s64)
return vbcppError(pThis, "Divide by zero");
pResult->u.SignedValue.s64 /= Result2.u.SignedValue.s64;
break;
case kVBCppBinary_Modulo:
if (!Result2.u.SignedValue.s64)
return vbcppError(pThis, "Divide by zero");
pResult->u.SignedValue.s64 %= Result2.u.SignedValue.s64;
break;
case kVBCppBinary_Addition:
pResult->u.SignedValue.s64 += Result2.u.SignedValue.s64;
break;
case kVBCppBinary_Subtraction:
pResult->u.SignedValue.s64 -= Result2.u.SignedValue.s64;
break;
case kVBCppBinary_LeftShift:
pResult->u.SignedValue.s64 <<= Result2.u.SignedValue.s64;
break;
case kVBCppBinary_RightShift:
pResult->u.SignedValue.s64 >>= Result2.u.SignedValue.s64;
break;
case kVBCppBinary_LessThan:
pResult->u.SignedValue.s64 = pResult->u.SignedValue.s64 < Result2.u.SignedValue.s64;
break;
case kVBCppBinary_LessThanOrEqual:
pResult->u.SignedValue.s64 = pResult->u.SignedValue.s64 <= Result2.u.SignedValue.s64;
break;
case kVBCppBinary_GreaterThan:
pResult->u.SignedValue.s64 = pResult->u.SignedValue.s64 > Result2.u.SignedValue.s64;
break;
case kVBCppBinary_GreaterThanOrEqual:
pResult->u.SignedValue.s64 = pResult->u.SignedValue.s64 >= Result2.u.SignedValue.s64;
break;
case kVBCppBinary_EqualTo:
pResult->u.SignedValue.s64 = pResult->u.SignedValue.s64 == Result2.u.SignedValue.s64;
break;
case kVBCppBinary_NotEqualTo:
pResult->u.SignedValue.s64 = pResult->u.SignedValue.s64 != Result2.u.SignedValue.s64;
break;
case kVBCppBinary_BitwiseAnd:
pResult->u.SignedValue.s64 &= Result2.u.SignedValue.s64;
break;
case kVBCppBinary_BitwiseXor:
pResult->u.SignedValue.s64 ^= Result2.u.SignedValue.s64;
break;
case kVBCppBinary_BitwiseOr:
pResult->u.SignedValue.s64 |= Result2.u.SignedValue.s64;
break;
case kVBCppBinary_LogicalAnd:
pResult->u.SignedValue.s64 = pResult->u.SignedValue.s64 && Result2.u.SignedValue.s64;
break;
case kVBCppBinary_LogicalOr:
pResult->u.SignedValue.s64 = pResult->u.SignedValue.s64 || Result2.u.SignedValue.s64;
break;
default:
return vbcppError(pThis, "Internal error: u.Binary.enmOperator=%d", pRoot->u.Binary.enmOperator);
}
}
return rcExit;
}
case kVBCppExprKind_Ternary:
rcExit = vbcppExprEvaluteTree(pThis, pRoot->u.Ternary.pExpr, pResult);
if (rcExit != RTEXITCODE_SUCCESS)
return rcExit;
if (vbcppExprIsExprTrue(pResult))
return vbcppExprEvaluteTree(pThis, pRoot->u.Ternary.pTrue, pResult);
return vbcppExprEvaluteTree(pThis, pRoot->u.Ternary.pFalse, pResult);
default:
return vbcppError(pThis, "Internal error: enmKind=%d", pRoot->enmKind);
}
}
/**
* Evalutes the expression.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pszExpr The expression.
* @param cchExpr The length of the expression.
* @param penmResult Where to store the result.
*/
static RTEXITCODE vbcppExprEval(PVBCPP pThis, char *pszExpr, size_t cchExpr, size_t cReplacements, VBCPPEVAL *penmResult)
{
Assert(strlen(pszExpr) == cchExpr);
size_t cUndefined;
PVBCPPEXPR pExprTree;
RTEXITCODE rcExit = vbcppExprParse(pThis, pszExpr, cchExpr, &pExprTree, &cUndefined);
if (rcExit == RTEXITCODE_SUCCESS)
{
if ( !cUndefined
|| pThis->enmMode == kVBCppMode_SelectiveD
|| pThis->enmMode == kVBCppMode_Standard)
{
VBCPPEXPR Result;
rcExit = vbcppExprEvaluteTree(pThis, pExprTree, &Result);
if (rcExit == RTEXITCODE_SUCCESS)
{
if (vbcppExprIsExprTrue(&Result))
*penmResult = kVBCppEval_True;
else
*penmResult = kVBCppEval_False;
}
}
else
*penmResult = kVBCppEval_Undecided;
}
return rcExit;
}
static RTEXITCODE vbcppExtractSkipCommentLine(PVBCPP pThis, PSCMSTREAM pStrmInput)
{
unsigned chPrev = ScmStreamGetCh(pStrmInput); Assert(chPrev == '/');
unsigned ch;
while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
{
if (ch == '\r' || ch == '\n')
{
if (chPrev != '\\')
break;
ScmStreamSeekByLine(pStrmInput, ScmStreamTellLine(pStrmInput) + 1);
chPrev = ch;
}
else
{
chPrev = ScmStreamGetCh(pStrmInput);
Assert(chPrev == ch);
}
}
return RTEXITCODE_SUCCESS;
}
static RTEXITCODE vbcppExtractSkipComment(PVBCPP pThis, PSCMSTREAM pStrmInput)
{
unsigned ch = ScmStreamGetCh(pStrmInput); Assert(ch == '*');
while ((ch = ScmStreamGetCh(pStrmInput)) != ~(unsigned)0)
{
if (ch == '*')
{
ch = ScmStreamGetCh(pStrmInput);
if (ch == '/')
return RTEXITCODE_SUCCESS;
}
}
return vbcppError(pThis, "Expected '*/'");
}
static RTEXITCODE vbcppExtractQuotedString(PVBCPP pThis, PSCMSTREAM pStrmInput, PVBCPPSTRBUF pStrBuf,
char chOpen, char chClose)
{
unsigned ch = ScmStreamGetCh(pStrmInput);
Assert(ch == (unsigned)chOpen);
RTEXITCODE rcExit = vbcppStrBufAppendCh(pStrBuf, chOpen);
if (rcExit != RTEXITCODE_SUCCESS)
return rcExit;
for (;;)
{
ch = ScmStreamGetCh(pStrmInput);
if (ch == '\\')
{
ch = ScmStreamGetCh(pStrmInput);
if (ch == ~(unsigned)0)
break;
rcExit = vbcppStrBufAppendCh(pStrBuf, '\\');
if (rcExit == RTEXITCODE_SUCCESS)
rcExit = vbcppStrBufAppendCh(pStrBuf, ch);
if (rcExit != RTEXITCODE_SUCCESS)
return rcExit;
}
else if (ch != ~(unsigned)0)
{
rcExit = vbcppStrBufAppendCh(pStrBuf, ch);
if (rcExit != RTEXITCODE_SUCCESS)
return rcExit;
if (ch == (unsigned)chClose)
return RTEXITCODE_SUCCESS;
}
else
break;
}
return vbcppError(pThis, "File ended with an open character constant");
}
/**
* Extracts a line from the stream, stripping it for comments and maybe
* optimzing some of the whitespace.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
* @param pStrBuf Where to store the extracted line. Caller must
* initialize this prior to the call an delete it
* after use (even on failure).
* @param poffComment Where to note down the position of the final
* comment. Optional.
*/
static RTEXITCODE vbcppExtractDirectiveLine(PVBCPP pThis, PSCMSTREAM pStrmInput, PVBCPPSTRBUF pStrBuf, size_t *poffComment)
{
size_t offComment = ~(size_t)0;
unsigned ch;
while ((ch = ScmStreamPeekCh(pStrmInput)) != ~(unsigned)0)
{
RTEXITCODE rcExit;
if (ch == '/')
{
/* Comment? */
unsigned ch2 = ScmStreamGetCh(pStrmInput); Assert(ch == ch2);
ch = ScmStreamPeekCh(pStrmInput);
if (ch == '*')
{
offComment = ScmStreamTell(pStrmInput) - 1;
rcExit = vbcppExtractSkipComment(pThis, pStrmInput);
}
else if (ch == '/')
{
offComment = ScmStreamTell(pStrmInput) - 1;
rcExit = vbcppExtractSkipCommentLine(pThis, pStrmInput);
}
else
rcExit = vbcppStrBufAppendCh(pStrBuf, '/');
}
else if (ch == '\'')
{
offComment = ~(size_t)0;
rcExit = vbcppExtractQuotedString(pThis, pStrmInput, pStrBuf, '\'', '\'');
}
else if (ch == '"')
{
offComment = ~(size_t)0;
rcExit = vbcppExtractQuotedString(pThis, pStrmInput, pStrBuf, '"', '"');
}
else if (ch == '\r' || ch == '\n')
break; /* done */
else if ( RT_C_IS_SPACE(ch)
&& ( RT_C_IS_SPACE(vbcppStrBufLastCh(pStrBuf))
|| vbcppStrBufLastCh(pStrBuf) == '\0') )
{
unsigned ch2 = ScmStreamGetCh(pStrmInput);
Assert(ch == ch2);
rcExit = RTEXITCODE_SUCCESS;
}
else
{
unsigned ch2 = ScmStreamGetCh(pStrmInput); Assert(ch == ch2);
/* Escaped newline? */
if ( ch == '\\'
&& ( (ch2 = ScmStreamPeekCh(pStrmInput)) == '\r'
|| ch2 == '\n'))
{
ScmStreamSeekByLine(pStrmInput, ScmStreamTellLine(pStrmInput) + 1);
rcExit = RTEXITCODE_SUCCESS;
}
else
{
offComment = ~(size_t)0;
rcExit = vbcppStrBufAppendCh(pStrBuf, ch);
}
}
if (rcExit != RTEXITCODE_SUCCESS)
return rcExit;
}
if (poffComment)
*poffComment = offComment;
return RTEXITCODE_SUCCESS;
}
/**
* Processes a abbreviated line number directive.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
* @param offStart The stream position where the directive
* started (for pass thru).
* @param enmKind The kind of directive we're processing.
*/
static RTEXITCODE vbcppDirectiveIfOrElif(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart,
VBCPPCONDKIND enmKind)
{
/*
* Check for missing #if if #elif.
*/
if ( enmKind == kVBCppCondKind_ElIf
&& !pThis->pCondStack )
return vbcppError(pThis, "#elif without #if");
/*
* Extract the expression string.
*/
const char *pchCondition = ScmStreamGetCur(pStrmInput);
size_t offComment;
VBCPPMACROEXP ExpCtx;
#if 0
ExpCtx.pMacroStack = NULL;
#endif
ExpCtx.pStrmInput = NULL;
ExpCtx.papszArgs = NULL;
ExpCtx.cArgs = 0;
ExpCtx.cArgsAlloced = 0;
vbcppStrBufInit(&ExpCtx.StrBuf, pThis);
RTEXITCODE rcExit = vbcppExtractDirectiveLine(pThis, pStrmInput, &ExpCtx.StrBuf, &offComment);
if (rcExit == RTEXITCODE_SUCCESS)
{
size_t const cchCondition = ScmStreamGetCur(pStrmInput) - pchCondition;
/*
* Expand known macros in it.
*/
size_t cReplacements;
rcExit = vbcppMacroExpandReScan(pThis, &ExpCtx, kMacroReScanMode_Expression, &cReplacements);
if (rcExit == RTEXITCODE_SUCCESS)
{
/*
* Strip it and check that it's not empty.
*/
char *pszExpr = ExpCtx.StrBuf.pszBuf;
size_t cchExpr = ExpCtx.StrBuf.cchBuf;
while (cchExpr > 0 && RT_C_IS_SPACE(*pszExpr))
pszExpr++, cchExpr--;
while (cchExpr > 0 && RT_C_IS_SPACE(pszExpr[cchExpr - 1]))
{
pszExpr[--cchExpr] = '\0';
ExpCtx.StrBuf.cchBuf--;
}
if (cchExpr)
{
/*
* Now, evalute the expression.
*/
VBCPPEVAL enmResult;
rcExit = vbcppExprEval(pThis, pszExpr, cchExpr, cReplacements, &enmResult);
if (rcExit == RTEXITCODE_SUCCESS)
{
/*
* Take action.
*/
if (enmKind != kVBCppCondKind_ElIf)
rcExit = vbcppCondPush(pThis, pStrmInput, offComment, enmKind, enmResult,
pchCondition, cchCondition);
else
{
PVBCPPCOND pCond = pThis->pCondStack;
if ( pCond->enmResult != kVBCppEval_Undecided
&& ( !pCond->pUp
|| pCond->pUp->enmStackResult == kVBCppEval_True))
{
Assert(enmResult == kVBCppEval_True || enmResult == kVBCppEval_False);
if ( pCond->enmResult == kVBCppEval_False
&& enmResult == kVBCppEval_True
&& !pCond->fElIfDecided)
{
pCond->enmStackResult = kVBCppEval_True;
pCond->fElIfDecided = true;
}
else
pCond->enmStackResult = kVBCppEval_False;
pThis->fIf0Mode = pCond->enmStackResult == kVBCppEval_False;
}
pCond->enmKind = kVBCppCondKind_ElIf;
pCond->enmResult = enmResult;
pCond->pchCond = pchCondition;
pCond->cchCond = cchCondition;
/*
* Do #elif pass thru.
*/
if ( !pThis->fIf0Mode
&& pCond->enmResult == kVBCppEval_Undecided)
{
ssize_t cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*selif", pCond->iKeepLevel - 1, "");
if (cch > 0)
rcExit = vbcppOutputComment(pThis, pStrmInput, offStart, cch, 2);
else
rcExit = vbcppError(pThis, "Output error %Rrc", (int)cch);
}
else
pThis->fJustDroppedLine = true;
}
}
}
else
rcExit = vbcppError(pThis, "Empty #if expression");
}
}
vbcppMacroExpandCleanup(&ExpCtx);
return rcExit;
}
/**
* Processes a abbreviated line number directive.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
* @param offStart The stream position where the directive
* started (for pass thru).
*/
static RTEXITCODE vbcppDirectiveIfDef(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
{
/*
* Parse it.
*/
RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
if (rcExit == RTEXITCODE_SUCCESS)
{
size_t cchDefine;
const char *pchDefine = ScmStreamCGetWord(pStrmInput, &cchDefine);
if (pchDefine)
{
rcExit = vbcppProcessSkipWhiteEscapedEolAndCommentsCheckEol(pThis, pStrmInput);
if (rcExit == RTEXITCODE_SUCCESS)
{
/*
* Evaluate it.
*/
VBCPPEVAL enmEval;
if (vbcppMacroExists(pThis, pchDefine, cchDefine))
enmEval = kVBCppEval_True;
else if ( !pThis->fUndecidedConditionals
|| RTStrSpaceGetN(&pThis->UndefStrSpace, pchDefine, cchDefine) != NULL)
enmEval = kVBCppEval_False;
else
enmEval = kVBCppEval_Undecided;
rcExit = vbcppCondPush(pThis, pStrmInput, offStart, kVBCppCondKind_IfDef, enmEval,
pchDefine, cchDefine);
}
}
else
rcExit = vbcppError(pThis, "Malformed #ifdef");
}
return rcExit;
}
/**
* Processes a abbreviated line number directive.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
* @param offStart The stream position where the directive
* started (for pass thru).
*/
static RTEXITCODE vbcppDirectiveIfNDef(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
{
/*
* Parse it.
*/
RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
if (rcExit == RTEXITCODE_SUCCESS)
{
size_t cchDefine;
const char *pchDefine = ScmStreamCGetWord(pStrmInput, &cchDefine);
if (pchDefine)
{
rcExit = vbcppProcessSkipWhiteEscapedEolAndCommentsCheckEol(pThis, pStrmInput);
if (rcExit == RTEXITCODE_SUCCESS)
{
/*
* Evaluate it.
*/
VBCPPEVAL enmEval;
if (vbcppMacroExists(pThis, pchDefine, cchDefine))
enmEval = kVBCppEval_False;
else if ( !pThis->fUndecidedConditionals
|| RTStrSpaceGetN(&pThis->UndefStrSpace, pchDefine, cchDefine) != NULL)
enmEval = kVBCppEval_True;
else
enmEval = kVBCppEval_Undecided;
rcExit = vbcppCondPush(pThis, pStrmInput, offStart, kVBCppCondKind_IfNDef, enmEval,
pchDefine, cchDefine);
}
}
else
rcExit = vbcppError(pThis, "Malformed #ifndef");
}
return rcExit;
}
/**
* Processes a abbreviated line number directive.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
* @param offStart The stream position where the directive
* started (for pass thru).
*/
static RTEXITCODE vbcppDirectiveElse(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
{
/*
* Nothing to parse, just comment positions to find and note down.
*/
offStart = vbcppProcessSkipWhite(pStrmInput);
RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndCommentsCheckEol(pThis, pStrmInput);
if (rcExit == RTEXITCODE_SUCCESS)
{
/*
* Execute.
*/
PVBCPPCOND pCond = pThis->pCondStack;
if (pCond)
{
if (!pCond->fSeenElse)
{
pCond->fSeenElse = true;
if ( pCond->enmResult != kVBCppEval_Undecided
&& ( !pCond->pUp
|| pCond->pUp->enmStackResult == kVBCppEval_True))
{
if ( pCond->enmResult == kVBCppEval_True
|| pCond->fElIfDecided)
pCond->enmStackResult = kVBCppEval_False;
else
pCond->enmStackResult = kVBCppEval_True;
pThis->fIf0Mode = pCond->enmStackResult == kVBCppEval_False;
}
/*
* Do pass thru.
*/
if ( !pThis->fIf0Mode
&& pCond->enmResult == kVBCppEval_Undecided)
{
ssize_t cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*selse", pCond->iKeepLevel - 1, "");
if (cch > 0)
rcExit = vbcppOutputComment(pThis, pStrmInput, offStart, cch, 2);
else
rcExit = vbcppError(pThis, "Output error %Rrc", (int)cch);
}
else
pThis->fJustDroppedLine = true;
}
else
rcExit = vbcppError(pThis, "Double #else or/and missing #endif");
}
else
rcExit = vbcppError(pThis, "#else without #if");
}
return rcExit;
}
/**
* Processes a abbreviated line number directive.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
* @param offStart The stream position where the directive
* started (for pass thru).
*/
static RTEXITCODE vbcppDirectiveEndif(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
{
/*
* Nothing to parse, just comment positions to find and note down.
*/
offStart = vbcppProcessSkipWhite(pStrmInput);
RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndCommentsCheckEol(pThis, pStrmInput);
if (rcExit == RTEXITCODE_SUCCESS)
{
/*
* Execute.
*/
PVBCPPCOND pCond = pThis->pCondStack;
if (pCond)
{
pThis->pCondStack = pCond->pUp;
pThis->fIf0Mode = pCond->pUp && pCond->pUp->enmStackResult == kVBCppEval_False;
/*
* Do pass thru.
*/
if ( !pThis->fIf0Mode
&& pCond->enmResult == kVBCppEval_Undecided)
{
ssize_t cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sendif", pCond->iKeepLevel - 1, "");
if (cch > 0)
rcExit = vbcppOutputComment(pThis, pStrmInput, offStart, cch, 1);
else
rcExit = vbcppError(pThis, "Output error %Rrc", (int)cch);
}
else
pThis->fJustDroppedLine = true;
}
else
rcExit = vbcppError(pThis, "#endif without #if");
}
return rcExit;
}
/*
*
*
* Misc Directives
* Misc Directives
* Misc Directives
* Misc Directives
*
*
*/
/**
* Adds an include directory.
*
* @returns Program exit code, with error message on failure.
* @param pThis The C preprocessor instance.
* @param pszDir The directory to add.
*/
static RTEXITCODE vbcppAddInclude(PVBCPP pThis, const char *pszDir)
{
uint32_t cIncludes = pThis->cIncludes;
if (cIncludes >= _64K)
return vbcppError(pThis, "Too many include directories");
void *pv = RTMemRealloc(pThis->papszIncludes, (cIncludes + 1) * sizeof(char **));
if (!pv)
return vbcppError(pThis, "No memory for include directories");
pThis->papszIncludes = (char **)pv;
int rc = RTStrDupEx(&pThis->papszIncludes[cIncludes], pszDir);
if (RT_FAILURE(rc))
return vbcppError(pThis, "No string memory for include directories");
pThis->cIncludes = cIncludes + 1;
return RTEXITCODE_SUCCESS;
}
/**
* Processes a abbreviated line number directive.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
* @param offStart The stream position where the directive
* started (for pass thru).
*/
static RTEXITCODE vbcppDirectiveInclude(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
{
/*
* Parse it.
*/
RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
if (rcExit == RTEXITCODE_SUCCESS)
{
size_t cchFileSpec = 0;
const char *pchFileSpec = NULL;
size_t cchFilename = 0;
const char *pchFilename = NULL;
unsigned ch = ScmStreamPeekCh(pStrmInput);
unsigned chType = ch;
if (ch == '"' || ch == '<')
{
ScmStreamGetCh(pStrmInput);
pchFileSpec = pchFilename = ScmStreamGetCur(pStrmInput);
unsigned chEnd = chType == '<' ? '>' : '"';
unsigned chPrev = ch;
while ( (ch = ScmStreamGetCh(pStrmInput)) != ~(unsigned)0
&& ch != chEnd)
{
if (ch == '\r' || ch == '\n')
{
rcExit = vbcppError(pThis, "Multi-line include file specfications are not supported");
break;
}
}
if (rcExit == RTEXITCODE_SUCCESS)
{
if (ch != ~(unsigned)0)
cchFileSpec = cchFilename = ScmStreamGetCur(pStrmInput) - pchFilename - 1;
else
rcExit = vbcppError(pThis, "Expected '%c'", chType);
}
}
else if (vbcppIsCIdentifierLeadChar(ch))
{
//pchFileSpec = ScmStreamCGetWord(pStrmInput, &cchFileSpec);
rcExit = vbcppError(pThis, "Including via a define is not implemented yet");
}
else
rcExit = vbcppError(pThis, "Malformed include directive");
/*
* Take down the location of the next non-white space, in case we need
* to pass thru the directive further down. Then skip to the end of the
* line.
*/
size_t const offIncEnd = vbcppProcessSkipWhite(pStrmInput);
if (rcExit == RTEXITCODE_SUCCESS)
rcExit = vbcppProcessSkipWhiteEscapedEolAndCommentsCheckEol(pThis, pStrmInput);
if (rcExit == RTEXITCODE_SUCCESS)
{
/*
* Execute it.
*/
if (pThis->enmIncludeAction == kVBCppIncludeAction_Include)
{
/** @todo Search for the include file and push it onto the input stack.
* Not difficult, just unnecessary rigth now. */
rcExit = vbcppError(pThis, "Includes are fully implemented");
}
else if (pThis->enmIncludeAction == kVBCppIncludeAction_PassThru)
{
/* Pretty print the passthru. */
unsigned cchIndent = pThis->pCondStack ? pThis->pCondStack->iKeepLevel : 0;
ssize_t cch;
if (chType == '<')
cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sinclude <%.*s>",
cchIndent, "", cchFileSpec, pchFileSpec);
else if (chType == '"')
cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sinclude \"%.*s\"",
cchIndent, "", cchFileSpec, pchFileSpec);
else
cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*sinclude %.*s",
cchIndent, "", cchFileSpec, pchFileSpec);
if (cch > 0)
rcExit = vbcppOutputComment(pThis, pStrmInput, offIncEnd, cch, 1);
else
rcExit = vbcppError(pThis, "Output error %Rrc", (int)cch);
}
else
{
Assert(pThis->enmIncludeAction == kVBCppIncludeAction_Drop);
pThis->fJustDroppedLine = true;
}
}
}
return rcExit;
}
/**
* Processes a abbreviated line number directive.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
* @param offStart The stream position where the directive
* started (for pass thru).
*/
static RTEXITCODE vbcppDirectivePragma(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
{
/*
* Parse out the first word.
*/
RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
if (rcExit == RTEXITCODE_SUCCESS)
{
size_t cchPragma;
const char *pchPragma = ScmStreamCGetWord(pStrmInput, &cchPragma);
if (pchPragma)
{
size_t const off2nd = vbcppProcessSkipWhite(pStrmInput);
size_t offComment;
rcExit = vbcppInputSkipToEndOfDirectiveLine(pThis, pStrmInput, &offComment);
if (rcExit == RTEXITCODE_SUCCESS)
{
/*
* What to do about this
*/
bool fPassThru = false;
if ( cchPragma == 1
&& *pchPragma == 'D')
fPassThru = pThis->fPassThruPragmaD;
else if ( cchPragma == 3
&& !strncmp(pchPragma, "STD", 3))
fPassThru = pThis->fPassThruPragmaSTD;
else
fPassThru = pThis->fPassThruPragmaOther;
if (fPassThru)
{
unsigned cchIndent = pThis->pCondStack ? pThis->pCondStack->iKeepLevel : 0;
ssize_t cch = ScmStreamPrintf(&pThis->StrmOutput, "#%*spragma %.*s",
cchIndent, "", cchPragma, pchPragma);
if (cch > 0)
rcExit = vbcppOutputComment(pThis, pStrmInput, off2nd, cch, 1);
else
rcExit = vbcppError(pThis, "output error");
}
else
pThis->fJustDroppedLine = true;
}
}
else
rcExit = vbcppError(pThis, "Malformed #pragma");
}
return rcExit;
}
/**
* Processes an error directive.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
* @param offStart The stream position where the directive
* started (for pass thru).
*/
static RTEXITCODE vbcppDirectiveError(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
{
return vbcppError(pThis, "Hit an #error");
}
/**
* Processes a abbreviated line number directive.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
* @param offStart The stream position where the directive
* started (for pass thru).
*/
static RTEXITCODE vbcppDirectiveLineNo(PVBCPP pThis, PSCMSTREAM pStrmInput, size_t offStart)
{
return vbcppError(pThis, "Not implemented: %s", __FUNCTION__);
}
/**
* Processes a abbreviated line number directive.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
*/
static RTEXITCODE vbcppDirectiveLineNoShort(PVBCPP pThis, PSCMSTREAM pStrmInput)
{
return vbcppError(pThis, "Not implemented: %s", __FUNCTION__);
}
/**
* Handles a preprocessor directive.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
* @param pThis The C preprocessor instance.
* @param pStrmInput The input stream.
*/
static RTEXITCODE vbcppProcessDirective(PVBCPP pThis, PSCMSTREAM pStrmInput)
{
/*
* Get the directive and do a string switch on it.
*/
RTEXITCODE rcExit = vbcppProcessSkipWhiteEscapedEolAndComments(pThis, pStrmInput);
if (rcExit != RTEXITCODE_SUCCESS)
return rcExit;
size_t cchDirective;
const char *pchDirective = ScmStreamCGetWord(pStrmInput, &cchDirective);
if (pchDirective)
{
size_t const offStart = ScmStreamTell(pStrmInput);
#define IS_DIRECTIVE(a_sz) ( sizeof(a_sz) - 1 == cchDirective && strncmp(pchDirective, a_sz, sizeof(a_sz) - 1) == 0)
if (IS_DIRECTIVE("if"))
rcExit = vbcppDirectiveIfOrElif(pThis, pStrmInput, offStart, kVBCppCondKind_If);
else if (IS_DIRECTIVE("elif"))
rcExit = vbcppDirectiveIfOrElif(pThis, pStrmInput, offStart, kVBCppCondKind_ElIf);
else if (IS_DIRECTIVE("ifdef"))
rcExit = vbcppDirectiveIfDef(pThis, pStrmInput, offStart);
else if (IS_DIRECTIVE("ifndef"))
rcExit = vbcppDirectiveIfNDef(pThis, pStrmInput, offStart);
else if (IS_DIRECTIVE("else"))
rcExit = vbcppDirectiveElse(pThis, pStrmInput, offStart);
else if (IS_DIRECTIVE("endif"))
rcExit = vbcppDirectiveEndif(pThis, pStrmInput, offStart);
else if (!pThis->fIf0Mode)
{
if (IS_DIRECTIVE("include"))
rcExit = vbcppDirectiveInclude(pThis, pStrmInput, offStart);
else if (IS_DIRECTIVE("define"))
rcExit = vbcppDirectiveDefine(pThis, pStrmInput, offStart);
else if (IS_DIRECTIVE("undef"))
rcExit = vbcppDirectiveUndef(pThis, pStrmInput, offStart);
else if (IS_DIRECTIVE("pragma"))
rcExit = vbcppDirectivePragma(pThis, pStrmInput, offStart);
else if (IS_DIRECTIVE("error"))
rcExit = vbcppDirectiveError(pThis, pStrmInput, offStart);
else if (IS_DIRECTIVE("line"))
rcExit = vbcppDirectiveLineNo(pThis, pStrmInput, offStart);
else
rcExit = vbcppError(pThis, "Unknown preprocessor directive '#%.*s'", cchDirective, pchDirective);
}
#undef IS_DIRECTIVE
}
else if (!pThis->fIf0Mode)
{
/* Could it be a # <num> "file" directive? */
unsigned ch = ScmStreamPeekCh(pStrmInput);
if (RT_C_IS_DIGIT(ch))
rcExit = vbcppDirectiveLineNoShort(pThis, pStrmInput);
else
rcExit = vbcppError(pThis, "Malformed preprocessor directive");
}
return rcExit;
}
/*
*
*
* M a i n b o d y.
* M a i n b o d y.
* M a i n b o d y.
* M a i n b o d y.
* M a i n b o d y.
*
*
*/
/**
* Does the actually preprocessing of the input file.
*
* @returns Exit code.
* @param pThis The C preprocessor instance.
*/
static RTEXITCODE vbcppPreprocess(PVBCPP pThis)
{
RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
/*
* Parse.
*/
while (pThis->pInputStack)
{
pThis->fMaybePreprocessorLine = true;
PSCMSTREAM pStrmInput = &pThis->pInputStack->StrmInput;
unsigned ch;
while ((ch = ScmStreamGetCh(pStrmInput)) != ~(unsigned)0)
{
if (ch == '/')
{
ch = ScmStreamPeekCh(pStrmInput);
if (ch == '*')
rcExit = vbcppProcessMultiLineComment(pThis, pStrmInput);
else if (ch == '/')
rcExit = vbcppProcessOneLineComment(pThis, pStrmInput);
else
{
pThis->fMaybePreprocessorLine = false;
if (!pThis->fIf0Mode)
rcExit = vbcppOutputCh(pThis, '/');
}
}
else if (ch == '#' && pThis->fMaybePreprocessorLine)
{
rcExit = vbcppProcessDirective(pThis, pStrmInput);
pStrmInput = &pThis->pInputStack->StrmInput;
}
else if (ch == '\r' || ch == '\n')
{
if ( ( !pThis->fIf0Mode
&& !pThis->fJustDroppedLine)
|| !pThis->fRemoveDroppedLines
|| !ScmStreamIsAtStartOfLine(&pThis->StrmOutput))
rcExit = vbcppOutputCh(pThis, ch);
pThis->fJustDroppedLine = false;
pThis->fMaybePreprocessorLine = true;
}
else if (RT_C_IS_SPACE(ch))
{
if (!pThis->fIf0Mode)
rcExit = vbcppOutputCh(pThis, ch);
}
else
{
pThis->fMaybePreprocessorLine = false;
if (!pThis->fIf0Mode)
{
if (ch == '"')
rcExit = vbcppProcessStringLitteral(pThis, pStrmInput);
else if (ch == '\'')
rcExit = vbcppProcessCharacterConstant(pThis, pStrmInput);
else if (vbcppIsCIdentifierLeadChar(ch))
rcExit = vbcppProcessIdentifier(pThis, pStrmInput, ch);
else if (RT_C_IS_DIGIT(ch))
rcExit = vbcppProcessNumber(pThis, pStrmInput, ch);
else
rcExit = vbcppOutputCh(pThis, ch);
}
}
if (rcExit != RTEXITCODE_SUCCESS)
break;
}
/*
* Check for errors.
*/
if (rcExit != RTEXITCODE_SUCCESS)
break;
/*
* Pop the input stack.
*/
PVBCPPINPUT pPopped = pThis->pInputStack;
pThis->pInputStack = pPopped->pUp;
RTMemFree(pPopped);
}
return rcExit;
}
/**
* Opens the input and output streams.
*
* @returns Exit code.
* @param pThis The C preprocessor instance.
*/
static RTEXITCODE vbcppOpenStreams(PVBCPP pThis)
{
if (!pThis->pszInput)
return vbcppError(pThis, "Preprocessing the standard input stream is currently not supported");
size_t cchName = strlen(pThis->pszInput);
PVBCPPINPUT pInput = (PVBCPPINPUT)RTMemAlloc(RT_OFFSETOF(VBCPPINPUT, szName[cchName + 1]));
if (!pInput)
return vbcppError(pThis, "out of memory");
pInput->pUp = pThis->pInputStack;
pInput->pszSpecified = pInput->szName;
memcpy(pInput->szName, pThis->pszInput, cchName + 1);
pThis->pInputStack = pInput;
int rc = ScmStreamInitForReading(&pInput->StrmInput, pThis->pszInput);
if (RT_FAILURE(rc))
return vbcppError(pThis, "ScmStreamInitForReading returned %Rrc when opening input file (%s)",
rc, pThis->pszInput);
rc = ScmStreamInitForWriting(&pThis->StrmOutput, &pInput->StrmInput);
if (RT_FAILURE(rc))
return vbcppError(pThis, "ScmStreamInitForWriting returned %Rrc", rc);
pThis->fStrmOutputValid = true;
return RTEXITCODE_SUCCESS;
}
/**
* Changes the preprocessing mode.
*
* @param pThis The C preprocessor instance.
* @param enmMode The new mode.
*/
static void vbcppSetMode(PVBCPP pThis, VBCPPMODE enmMode)
{
switch (enmMode)
{
case kVBCppMode_Standard:
pThis->fKeepComments = false;
pThis->fRespectSourceDefines = true;
pThis->fAllowRedefiningCmdLineDefines = true;
pThis->fPassThruDefines = false;
pThis->fUndecidedConditionals = false;
pThis->fPassThruPragmaD = false;
pThis->fPassThruPragmaSTD = true;
pThis->fPassThruPragmaOther = true;
pThis->fRemoveDroppedLines = false;
pThis->fLineSplicing = true;
pThis->enmIncludeAction = kVBCppIncludeAction_Include;
break;
case kVBCppMode_Selective:
pThis->fKeepComments = true;
pThis->fRespectSourceDefines = false;
pThis->fAllowRedefiningCmdLineDefines = false;
pThis->fPassThruDefines = true;
pThis->fUndecidedConditionals = true;
pThis->fPassThruPragmaD = true;
pThis->fPassThruPragmaSTD = true;
pThis->fPassThruPragmaOther = true;
pThis->fRemoveDroppedLines = true;
pThis->fLineSplicing = false;
pThis->enmIncludeAction = kVBCppIncludeAction_PassThru;
break;
case kVBCppMode_SelectiveD:
pThis->fKeepComments = true;
pThis->fRespectSourceDefines = true;
pThis->fAllowRedefiningCmdLineDefines = false;
pThis->fPassThruDefines = false;
pThis->fUndecidedConditionals = false;
pThis->fPassThruPragmaD = true;
pThis->fPassThruPragmaSTD = false;
pThis->fPassThruPragmaOther = false;
pThis->fRemoveDroppedLines = true;
pThis->fLineSplicing = false;
pThis->enmIncludeAction = kVBCppIncludeAction_Drop;
break;
default:
AssertFailedReturnVoid();
}
pThis->enmMode = enmMode;
}
/**
* Parses the command line options.
*
* @returns Program exit code. Exit on non-success or if *pfExit is set.
* @param pThis The C preprocessor instance.
* @param argc The argument count.
* @param argv The argument vector.
* @param pfExit Pointer to the exit indicator.
*/
static RTEXITCODE vbcppParseOptions(PVBCPP pThis, int argc, char **argv, bool *pfExit)
{
RTEXITCODE rcExit;
*pfExit = false;
/*
* Option config.
*/
static RTGETOPTDEF const s_aOpts[] =
{
{ "--define", 'D', RTGETOPT_REQ_STRING },
{ "--include-dir", 'I', RTGETOPT_REQ_STRING },
{ "--undefine", 'U', RTGETOPT_REQ_STRING },
{ "--keep-comments", 'C', RTGETOPT_REQ_NOTHING },
{ "--strip-comments", 'c', RTGETOPT_REQ_NOTHING },
{ "--D-strip", 'd', RTGETOPT_REQ_NOTHING },
};
RTGETOPTUNION ValueUnion;
RTGETOPTSTATE GetOptState;
int rc = RTGetOptInit(&GetOptState, argc, argv, &s_aOpts[0], RT_ELEMENTS(s_aOpts), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
AssertReleaseRCReturn(rc, RTEXITCODE_FAILURE);
/*
* Process the options.
*/
while ((rc = RTGetOpt(&GetOptState, &ValueUnion)) != 0)
{
switch (rc)
{
case 'c':
pThis->fKeepComments = false;
break;
case 'C':
pThis->fKeepComments = false;
break;
case 'd':
vbcppSetMode(pThis, kVBCppMode_SelectiveD);
break;
case 'D':
{
const char *pszEqual = strchr(ValueUnion.psz, '=');
if (pszEqual)
rcExit = vbcppMacroAdd(pThis, ValueUnion.psz, pszEqual - ValueUnion.psz, pszEqual + 1, RTSTR_MAX, true);
else
rcExit = vbcppMacroAdd(pThis, ValueUnion.psz, RTSTR_MAX, "1", 1, true);
if (rcExit != RTEXITCODE_SUCCESS)
return rcExit;
break;
}
case 'I':
rcExit = vbcppAddInclude(pThis, ValueUnion.psz);
if (rcExit != RTEXITCODE_SUCCESS)
return rcExit;
break;
case 'U':
rcExit = vbcppMacroUndef(pThis, ValueUnion.psz, RTSTR_MAX, true);
break;
case 'h':
RTPrintf("No help yet, sorry\n");
*pfExit = true;
return RTEXITCODE_SUCCESS;
case 'V':
{
/* The following is assuming that svn does it's job here. */
static const char s_szRev[] = "$Revision$";
const char *psz = RTStrStripL(strchr(s_szRev, ' '));
RTPrintf("r%.*s\n", strchr(psz, ' ') - psz, psz);
*pfExit = true;
return RTEXITCODE_SUCCESS;
}
case VINF_GETOPT_NOT_OPTION:
if (!pThis->pszInput)
pThis->pszInput = ValueUnion.psz;
else if (!pThis->pszOutput)
pThis->pszOutput = ValueUnion.psz;
else
return RTMsgErrorExit(RTEXITCODE_SYNTAX, "too many file arguments");
break;
/*
* Errors and bugs.
*/
default:
return RTGetOptPrintError(rc, &ValueUnion);
}
}
return RTEXITCODE_SUCCESS;
}
/**
* Terminates the preprocessor.
*
* This may return failure if an error was delayed.
*
* @returns Exit code.
* @param pThis The C preprocessor instance.
*/
static RTEXITCODE vbcppTerm(PVBCPP pThis)
{
/*
* Flush the output first.
*/
if (pThis->fStrmOutputValid)
{
if (pThis->pszOutput)
{
int rc = ScmStreamWriteToFile(&pThis->StrmOutput, "%s", pThis->pszOutput);
if (RT_FAILURE(rc))
vbcppError(pThis, "ScmStreamWriteToFile failed with %Rrc when writing '%s'", rc, pThis->pszOutput);
}
else
{
int rc = ScmStreamWriteToStdOut(&pThis->StrmOutput);
if (RT_FAILURE(rc))
vbcppError(pThis, "ScmStreamWriteToStdOut failed with %Rrc", rc);
}
}
/*
* Cleanup.
*/
while (pThis->pInputStack)
{
ScmStreamDelete(&pThis->pInputStack->StrmInput);
void *pvFree = pThis->pInputStack;
pThis->pInputStack = pThis->pInputStack->pUp;
RTMemFree(pvFree);
}
ScmStreamDelete(&pThis->StrmOutput);
RTStrSpaceDestroy(&pThis->StrSpace, vbcppMacroFree, NULL);
pThis->StrSpace = NULL;
uint32_t i = pThis->cIncludes;
while (i-- > 0)
RTStrFree(pThis->papszIncludes[i]);
RTMemFree(pThis->papszIncludes);
pThis->papszIncludes = NULL;
return pThis->rcExit;
}
/**
* Initializes the C preprocessor instance data.
*
* @param pThis The C preprocessor instance data.
*/
static void vbcppInit(PVBCPP pThis)
{
vbcppSetMode(pThis, kVBCppMode_Selective);
pThis->cIncludes = 0;
pThis->papszIncludes = NULL;
pThis->pszInput = NULL;
pThis->pszOutput = NULL;
pThis->StrSpace = NULL;
pThis->UndefStrSpace = NULL;
pThis->cCondStackDepth = 0;
pThis->pCondStack = NULL;
pThis->fIf0Mode = false;
pThis->fJustDroppedLine = false;
pThis->fMaybePreprocessorLine = true;
VBCPP_BITMAP_EMPTY(pThis->bmDefined);
pThis->cCondStackDepth = 0;
pThis->pInputStack = NULL;
RT_ZERO(pThis->StrmOutput);
pThis->rcExit = RTEXITCODE_SUCCESS;
pThis->fStrmOutputValid = false;
}
int main(int argc, char **argv)
{
int rc = RTR3InitExe(argc, &argv, 0);
if (RT_FAILURE(rc))
return RTMsgInitFailure(rc);
/*
* Do the job. The code says it all.
*/
VBCPP This;
vbcppInit(&This);
bool fExit;
RTEXITCODE rcExit = vbcppParseOptions(&This, argc, argv, &fExit);
if (!fExit && rcExit == RTEXITCODE_SUCCESS)
{
rcExit = vbcppOpenStreams(&This);
if (rcExit == RTEXITCODE_SUCCESS)
rcExit = vbcppPreprocess(&This);
}
if (rcExit == RTEXITCODE_SUCCESS)
rcExit = vbcppTerm(&This);
else
vbcppTerm(&This);
return rcExit;
}