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 + -
显示快捷键?