mpreproc.c

来自「开放源码的编译器open watcom 1.6.0版的源代码」· C语言 代码 · 共 2,112 行 · 第 1/4 页

C
2,112
字号
/****************************************************************************
*
*                            Open Watcom Project
*
*    Portions Copyright (c) 1983-2002 Sybase, Inc. All Rights Reserved.
*
*  ========================================================================
*
*    This file contains Original Code and/or Modifications of Original
*    Code as defined in and that are subject to the Sybase Open Watcom
*    Public License version 1.0 (the 'License'). You may not use this file
*    except in compliance with the License. BY USING THIS FILE YOU AGREE TO
*    ALL TERMS AND CONDITIONS OF THE LICENSE. A copy of the License is
*    provided with the Original Code and Modifications, and is also
*    available at www.sybase.com/developer/opensource.
*
*    The Original Code and all software distributed under the License are
*    distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
*    EXPRESS OR IMPLIED, AND SYBASE AND ALL CONTRIBUTORS HEREBY DISCLAIM
*    ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF
*    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR
*    NON-INFRINGEMENT. Please see the License for the specific language
*    governing rights and limitations under the License.
*
*  ========================================================================
*
* Description:  Macro preprocessor for wmake (handles !ifdef and the like).
*
****************************************************************************/


#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#include "massert.h"
#include "mtypes.h"
#include "mstream.h"
#include "mlex.h"
#include "macros.h"
#include "make.h"
#include "mexec.h"
#include "mmemory.h"
#include "mmisc.h"
#include "mparse.h"
#include "mpreproc.h"
#include "mrcmsg.h"
#include "msg.h"

/*
 * This module presents a stream of characters to mlex.c.  Stripped from the
 * stream are comments, and preprocessing directives.  Preprocessing directives
 * are handled invisibly to mlex.c.  Also handles line continuation.
 *
 */

#define MAX_PRE_TOK     8       // chars needed for maximum keyword
#define INCLUDE      "INCLUDE"  // include directory for include file search


enum directiveTok {             // must be kept in sync with directives
    D_BLANK = -1,               // a blank line
    D_DEFINE,
    D_ELSE,
    D_ENDIF,
    D_ERROR,
    D_IF,   // MS Compatibility Directive.  NMAKE binary and string operations.
    D_IFDEF,
    D_IFEQ,
    D_IFEQI,
    D_IFNDEF,
    D_IFNEQ,
    D_IFNEQI,
    D_INCLUDE,
    D_INJECT,
    D_LOADDLL,
    D_MESSAGE,
    D_UNDEF,
    D_MAX
};

STATIC const char * const directives[] = {   // table must be lexically sorted.
    "define",
    "else",
    "endif",
    "error",
    "if",
    "ifdef",
    "ifeq",
    "ifeqi",
    "ifndef",
    "ifneq",
    "ifneqi",
    "include",
    "inject",
    "loaddll",
    "message",
    "undef"
};
#define NUM_DIRECT      (sizeof( directives ) / sizeof( char * ))

#define MAX_DIR_LEN     8       // num chars incl null-terminator

STATIC char     atStartOfLine;  /* EOL at the start of a line...
 * This is a slight optimization for the critical code in PreGetCH().  DJG
 */
STATIC STRM_T   lastChar;
STATIC BOOLEAN  doingPreProc;   // are we doing some preprocessing?


/*
 * MS Compatability extension to add the if (expression) functionality
 */

STATIC void doElIf( BOOLEAN (*logical)(void), enum directiveTok tok );

// local functions
STATIC void parseExpr ( DATAVALUE *leftVal, char *inString );
STATIC void logorExpr ( DATAVALUE *leftVal );
STATIC void logandExpr( DATAVALUE *leftVal );
STATIC void bitorExpr ( DATAVALUE *leftVal );
STATIC void bitxorExpr( DATAVALUE *leftVal );
STATIC void bitandExpr( DATAVALUE *leftVal );
STATIC void equalExpr ( DATAVALUE *leftVal );
STATIC void relateExpr( DATAVALUE *leftVal );
STATIC void shiftExpr ( DATAVALUE *leftVal );
STATIC void addExpr   ( DATAVALUE *leftVal );
STATIC void multExpr  ( DATAVALUE *leftVal );
STATIC void unaryExpr ( DATAVALUE *leftVal );

STATIC char         *currentPtr;    // Pointer to current start in string
STATIC TOKEN_TYPE   currentToken;   // Contains information for current token


/*
 * struct nestIf is used to keep track of the if-endif constructs
 *
 * These are what the various fields mean:
 *
 * if skip then         we are skipping until an elif, else or endif
 * elif skip2endif then we are skipping everything until matching endif
 * else                 we are not skipping
 * endif
 *
 * if elseFound then    we have already come across the else that matches
 *                      the current if (for error checking)
 *
 * invariant( if( curNest.skip2endif ) then curNest.skip )
 */
struct nestIf {
    BIT skip2endif : 1;
    BIT skip : 1;
    BIT elseFound : 1;
};

#define MAX_NEST    32                  // maximum depth of if nesting

STATIC struct nestIf nest[MAX_NEST];    // stack for nesting
STATIC size_t nestLevel;                // items on stack
STATIC struct nestIf curNest;           // current skip info


extern void PreProcInit( void )
/*****************************/
{
    StreamInit();

    atStartOfLine = EOL;
    doingPreProc = FALSE;

    curNest.skip2endif = FALSE;
    curNest.skip = FALSE;
    curNest.elseFound = FALSE;
    nestLevel = 0;
    lastChar = 0;
}


extern void PreProcFini( void )
/*****************************/
{
    StreamFini();
}


STATIC STRM_T eatWhite( void )
/*****************************
 * pre:
 * post:    0 or more ws characters removed from input
 * returns: the first non-whitespace character is returned
 */
{
    STRM_T  t;

    t = PreGetCH();
    while( isws( t ) ) {
        t = PreGetCH();
    }

    return( t );
}


STATIC STRM_T eatToEOL( void )
/*****************************
 * pre:
 * post:    atStartOfLine == EOL, 0 or more chars removed from input
 * returns: first ( EOL || STRM_END )
 */
{
    STRM_T  t;

    t = PreGetCH();
    while( t != EOL && t != STRM_END ) {
        t = PreGetCH();
    }

    return( t );
}


#ifdef __WATCOMC__
#pragma on (check_stack);
#endif
STATIC int getPreTok( void )
/***************************
 * pre:     the '!' has been eaten by caller
 * post:    first character following token is next char of input
 * returns: D_BLANK if no tokens on line, or token unrecognized,
 *          otherwise the D_ number of the token
 */
{
    TOKEN_T t;
    char    tok[MAX_PRE_TOK];
    int     pos;
    char    **key;
    char    *tmp;               /* to pass tok buf to bsearch */

    t = eatWhite();

    if( t == EOL ) {
        UnGetCH( t );
        return( D_BLANK );
    }

    pos = 0;
    while( isalpha( t ) && ( pos < MAX_PRE_TOK - 1 ) ) {
        tok[pos++] = t;
        t = PreGetCH();
        // MS Compatability ELSE IFEQ can also be defined as ELSEIFEQ
        // similar for other types of if preprocessor directives
        if( pos == 4 ) {
            tok[pos] = NULLCHAR;
            if( strcmpi( directives[D_ELSE], tok ) == 0 ) {
                break;
            }
        }
    }
    tok[pos] = NULLCHAR;

    UnGetCH( t );
    UnGetCH( eatWhite() );

    tmp = tok;
    key = bsearch( &tmp, directives, NUM_DIRECT, sizeof( char * ),
           (int (*)( const void *, const void * )) KWCompare );

    if( key == NULL ) {
        if( !curNest.skip ) {
            PrtMsg( ERR | LOC | UNK_PREPROC_DIRECTIVE, tok );
        }
        return( D_BLANK );
    }

    assert( ( key - (char **)directives >= 0 ) &&
            ( key - (char **)directives <= D_MAX ) );

    return( key - (char **)directives );
}
#ifdef __WATCOMC__
#pragma off(check_stack);
#endif


STATIC BOOLEAN ifDef( void )
/***************************
 * pre:
 * post:    atStartOfLine == EOL
 * returns: TRUE if macro is defined, FALSE otherwise
 */
{
    char    *name;
    char    *value;
    BOOLEAN ret;

    assert( !curNest.skip2endif );

    name = DeMacro( MAC_PUNC );
    (void)eatToEOL();

    if( !IsMacroName( name ) ) {
        FreeSafe( name );
        return( FALSE );
    }

    value = GetMacroValue( name );
    ret = value != NULL;
    if( value != NULL ) {
        FreeSafe( value );
    }
    FreeSafe( name );

    return( ret );
}


STATIC BOOLEAN ifNDef( void )
/***************************/
{
    return( !ifDef() );
}


STATIC void chopTrailWS( char *str )
/***********************************
 * chop trailing whitespace from str
 */
{
    char    *p;

    for( p = str + strlen( str ) - 1; p >= str && isws( *p ); --p ) {
        *p = NULLCHAR;
    }
}


STATIC BOOLEAN ifOp( void )
/**************************
 * MS Compatability  -
 * Allows for NMAKE compatability in binary and string operators
 * process the operands found in !if
 */
{
    char        *test;
    DATAVALUE   temp;

    assert( !curNest.skip2endif );

    test = DeMacro( EOL );
    (void)eatToEOL();

    parseExpr( &temp, test );

    FreeSafe( test );
    return( (BOOLEAN)temp.data.number );
}


STATIC void ifEqProcess( char const **v1, char **v2 )
/****************************************************
 * pre:
 * post:    atStartOfLine == EOL
 * returns: v1 & v2 set to freeable strings
 */
{
    char        *name;
    char        *test;
    char        *value;
    char const  *beg;

    assert( !curNest.skip2endif );

    *v1 = NULL;
    *v2 = NULL;

    name = DeMacro( MAC_PUNC );
    test = DeMacro( EOL );
    (void)eatToEOL();

    if( !IsMacroName( name ) ) {
        FreeSafe( name );
        FreeSafe( test );
        return;
    }

    value = WrnGetMacroValue( name );

    FreeSafe( name );               /* don't need name any more */

    if( value == NULL ) {
        FreeSafe( test );
        return;
    }

    UnGetCH( EOL );
    InsString( value, TRUE );
    value = DeMacro( EOL );
    (void)eatToEOL();

    chopTrailWS( test );            /* chop trailing ws */

    beg = SkipWS( test );           /* find first non-ws */

    *v1 = value;
    *v2 = StrDupSafe( beg );        /* 18-nov-91 */
    FreeSafe( test );
}


STATIC BOOLEAN ifEq( void )
/**************************
 * pre:
 * post:    atStartOfLine == EOL
 * returns: TRUE if macro equals text, FALSE otherwise
 */
{
    BOOLEAN     ret;
    char const  *v1;
    char        *v2;

    ifEqProcess( &v1, &v2 );
    if( v1 == NULL ) {
        return( 0 );
    }
    ret = strcmp( v1, v2 ) == 0;

    FreeSafe( (void *)v1 );
    FreeSafe( v2 );

    return( ret );
}


STATIC BOOLEAN ifEqi( void )
/***************************
 * pre:
 * post:    atStartOfLine == EOL
 * returns: TRUE if macro equals text (case independence), FALSE otherwise
 */
{
    BOOLEAN     ret;
    char const  *v1;
    char        *v2;

    ifEqProcess( &v1, &v2 );
    if( v1 == NULL ) {
        return( 0 );
    }
    ret = stricmp( v1, v2 ) == 0;

    FreeSafe( (void *)v1 );
    FreeSafe( v2 );

    return( ret );
}


STATIC BOOLEAN ifNEq( void )
/**************************/
{
    return( !ifEq() );
}


STATIC BOOLEAN ifNEqi( void )
/***************************/
{
    return( !ifEqi() );
}


STATIC void bangIf( BOOLEAN (*logical)(void), enum directiveTok tok )
/********************************************************************
 * pre:
 * post:    nestLevel > old(nestLevel); skip if false logical, or currently
 *          skipping; !elseFound
 * aborts:  if nestLevel >= MAX_NEST
 */
{
    if( nestLevel >= MAX_NEST ) {
        PrtMsg( FTL | LOC | IF_NESTED_TOO_DEEP );
    }

    nest[nestLevel++] = curNest; // save old nesting on the stack

        // remember that curNest still contains info from previous level
    curNest.skip2endif = (curNest.skip || curNest.skip2endif);
    curNest.skip = FALSE;
    curNest.elseFound = FALSE;

    if( !curNest.skip2endif ) { // ok to interpret if arguments?
        curNest.skip = !logical();
    } else {
        // this block is to be skipped, don't interpret args to if
        curNest.skip = TRUE;
        (void)eatToEOL();
    }

    if( curNest.skip ) {
        PrtMsg( DBG | INF | LOC | SKIPPING_BLOCK, directives[tok] );
    } else {
        PrtMsg( DBG | INF | LOC | ENTERING_BLOCK, directives[tok] );
    }
}


STATIC void bangEndIf( void )
/****************************
 * pre:
 * post:    atStartOfLine == EOL; curNest < old(curNest)
 * aborts:  if not nested
 */
{
    PrtMsg( DBG | INF | LOC | AT_ENDIF );

    if( nestLevel == 0 ) {
        PrtMsg( FTL | LOC | UNMATCHED_WITH_IF, directives[D_ENDIF] );
    }
    curNest = nest[--nestLevel];

    (void)eatToEOL();
}


STATIC void doElse( void )
/*************************
 * pre:
 * post:    elseFound; skip if !old( skip ) || skip2endif

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?