mirror of
https://github.com/NishiOwO/JWasm.git
synced 2025-04-21 16:54:39 +00:00
672 lines
24 KiB
C
672 lines
24 KiB
C
/****************************************************************************
|
|
*
|
|
* 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: JWasm conditional processing routines. Handles directives
|
|
* [ELSE]IF[E], ELSE, ENDIF
|
|
* [ELSE]IFB, [ELSE]IFNB
|
|
* [ELSE]IFDEF, [ELSE]IFNDEF
|
|
* [ELSE]IFDIF[I], [ELSE]IFIDN[I]
|
|
* [ELSE]IF1, [ELSE]IF2
|
|
* .ERR, .ERRNZ, .ERRE
|
|
* .ERRB, .ERRNB
|
|
* .ERRDEF, .ERRNDEF
|
|
* .ERRDIF[I], .ERRIDN[I]
|
|
* .ERR1, .ERR2
|
|
****************************************************************************/
|
|
|
|
|
|
#include <ctype.h>
|
|
|
|
#include "globals.h"
|
|
#include "parser.h"
|
|
#include "condasm.h"
|
|
#include "reswords.h"
|
|
#include "expreval.h"
|
|
#include "listing.h"
|
|
#include "input.h"
|
|
#include "macro.h"
|
|
#include "types.h"
|
|
#include "fastpass.h"
|
|
|
|
/*
|
|
the current if-block can be in one of 3 states:
|
|
state assembly possible state change to
|
|
---------------------------------------------------------
|
|
inactive off active
|
|
active on done
|
|
done off -
|
|
--------------------------------------------------------
|
|
up to v2.04, there was a fourth state:
|
|
condition check on active, inactive
|
|
it was necessary because lines may have been tokenized multiple times.
|
|
*/
|
|
|
|
enum if_state CurrIfState;
|
|
static int blocknestlevel;
|
|
static int falseblocknestlevel;
|
|
static uint_32 elseoccured; /* 2.06: bit field, magnitude must be >= MAX_IF_NESTING */
|
|
|
|
#ifdef DEBUG_OUT
|
|
static const char *GetCurrIfStatString( void )
|
|
/********************************************/
|
|
{
|
|
switch ( CurrIfState ) {
|
|
case BLOCK_ACTIVE: return( "BLOCK_ACTIVE" );
|
|
case BLOCK_INACTIVE: return( "BLOCK_INACTIVE" );
|
|
default: return( "BLOCK_DONE" );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* this code runs after the first token has been scanned,
|
|
* if it is a IFx, ELSEx or ENDIF.
|
|
* updates variables <blocknestlevel> and <falseblocknestlevel>.
|
|
*/
|
|
|
|
void conditional_assembly_prepare( int directive )
|
|
/************************************************/
|
|
{
|
|
DebugMsg1(("condasm_prepare(%s), old status: %s, lvl=%u, falselvl=%u\n",
|
|
GetResWName( directive, NULL), GetCurrIfStatString(), blocknestlevel, falseblocknestlevel));
|
|
switch( directive ) {
|
|
case T_IF:
|
|
case T_IF1:
|
|
case T_IF2:
|
|
case T_IFB:
|
|
case T_IFDEF:
|
|
case T_IFDIF:
|
|
case T_IFDIFI:
|
|
case T_IFE:
|
|
case T_IFIDN:
|
|
case T_IFIDNI:
|
|
case T_IFNB:
|
|
case T_IFNDEF:
|
|
if( CurrIfState != BLOCK_ACTIVE ) {
|
|
falseblocknestlevel++;
|
|
break;
|
|
}
|
|
if( blocknestlevel == MAX_IF_NESTING ) {
|
|
EmitError( NESTING_LEVEL_TOO_DEEP );
|
|
break;
|
|
}
|
|
elseoccured &= ~( 1 << blocknestlevel ); /* v2.06: added */
|
|
blocknestlevel++;
|
|
break;
|
|
case T_ELSE:
|
|
case T_ELSEIF:
|
|
case T_ELSEIF1:
|
|
case T_ELSEIF2:
|
|
case T_ELSEIFB:
|
|
case T_ELSEIFDEF:
|
|
case T_ELSEIFDIF:
|
|
case T_ELSEIFDIFI:
|
|
case T_ELSEIFE:
|
|
case T_ELSEIFIDN:
|
|
case T_ELSEIFIDNI:
|
|
case T_ELSEIFNB:
|
|
case T_ELSEIFNDEF:
|
|
if ( blocknestlevel ) { /* v2.04: do nothing if there was no IFx */
|
|
if ( falseblocknestlevel > 0 ) {
|
|
break;
|
|
}
|
|
/* v2.06: check added to detect multiple ELSE branches */
|
|
if ( elseoccured & ( 1 << ( blocknestlevel - 1 ) ) ) {
|
|
EmitError( ELSE_CLAUSE_ALREADY_OCCURED_IN_THIS_IF_BLOCK );
|
|
break;
|
|
}
|
|
/* status may change:
|
|
* inactive -> active
|
|
* active -> done
|
|
*/
|
|
CurrIfState = (( CurrIfState == BLOCK_INACTIVE ) ? BLOCK_ACTIVE : BLOCK_DONE );
|
|
/* v2.06: no further ELSEx once ELSE was detected */
|
|
if ( directive == T_ELSE )
|
|
elseoccured |= ( 1 << ( blocknestlevel - 1 ) );
|
|
} else {
|
|
EmitErr( BLOCK_NESTING_ERROR, GetResWName( directive, NULL ) );
|
|
}
|
|
break;
|
|
case T_ENDIF:
|
|
if ( blocknestlevel ) {
|
|
if ( falseblocknestlevel > 0 ) {
|
|
falseblocknestlevel--;
|
|
break;
|
|
}
|
|
blocknestlevel--;
|
|
CurrIfState = BLOCK_ACTIVE; /* v2.04: added */
|
|
} else {
|
|
EmitErr( BLOCK_NESTING_ERROR, GetResWName( directive, NULL ) );
|
|
}
|
|
break;
|
|
}
|
|
DebugMsg1(("condasm_prepare(%s), new status: %s, lvl=%u, falselvl=%u\n",
|
|
GetResWName( directive, NULL ), GetCurrIfStatString(), blocknestlevel, falseblocknestlevel));
|
|
return;
|
|
}
|
|
|
|
/* handle [ELSE]IF[N]DEF
|
|
* <string> is
|
|
* - the value of a T_ID item!
|
|
* - "" (item is T_FINAL)
|
|
*/
|
|
|
|
static bool check_defd( const char *name )
|
|
/****************************************/
|
|
{
|
|
struct asym *sym;
|
|
if ( *name ) {
|
|
sym = SymSearch( name );
|
|
if( sym ) {
|
|
DebugMsg1(("check_defd(%s): state=%u defined=%u\n", name, sym->state, sym->isdefined ));
|
|
/* v2.04: changed. the "defined" flag is active for ALL symbols */
|
|
//if ( sym->state == SYM_INTERNAL || sym->state == SYM_MACRO || sym->state == SYM_TMACRO || sym->state == SYM_UNDEFINED ) {
|
|
return( sym->isdefined );
|
|
}
|
|
DebugMsg1(("check_defd(%s): sym=NULL\n", name ));
|
|
}
|
|
return( FALSE );
|
|
}
|
|
|
|
/* handle [ELSE]IF[N]B
|
|
*/
|
|
static bool check_blank( const char *string )
|
|
/*******************************************/
|
|
{
|
|
for ( ;*string; string++ )
|
|
if ( isspace(*string) == FALSE )
|
|
return FALSE;
|
|
return( TRUE );
|
|
}
|
|
|
|
/* Are two strings different?
|
|
* Used by [ELSE]IFDIF[I] and [ELSE]IFIDN[I]
|
|
*/
|
|
|
|
static bool check_dif( const char *string1, const char *string2, bool sensitive )
|
|
/*******************************************************************************/
|
|
{
|
|
if( sensitive ) {
|
|
return( strcmp( string1, string2 ) != 0 );
|
|
} else {
|
|
return( _stricmp( string1, string2 ) != 0 );
|
|
}
|
|
}
|
|
|
|
ret_code CondAsmDirective( int i, struct asm_tok tokenarray[] )
|
|
/*************************************************************/
|
|
{
|
|
int directive = tokenarray[i].tokval;
|
|
const char *string1;
|
|
const char *string2;
|
|
enum if_state NextIfState;
|
|
struct expr opndx;
|
|
|
|
if ( CurrIfState != BLOCK_ACTIVE ) {
|
|
DebugMsg1(("CondAsmDirective(%s), CurrIfState=%u(%s), lvl=%u, falselvl=%u\n",
|
|
GetResWName(directive, NULL), CurrIfState, GetCurrIfStatString(), blocknestlevel, falseblocknestlevel));
|
|
if ( i || ModuleInfo.listif ) {
|
|
LstWriteSrcLine();
|
|
}
|
|
return( NOT_ERROR );
|
|
}
|
|
|
|
if ( ModuleInfo.list == TRUE ) {
|
|
if ( MacroLevel == 0 ||
|
|
ModuleInfo.list_macro == LM_LISTMACROALL ||
|
|
ModuleInfo.listif )
|
|
LstWriteSrcLine();
|
|
}
|
|
|
|
DebugMsg1(("CondAsmDirective(%s), BLOCK_ACTIVE, lvl=%u, falselvl=%u [%s]\n", GetResWName(directive, NULL), blocknestlevel, falseblocknestlevel, tokenarray[i].tokpos ));
|
|
|
|
i++; /* go past IFx, ELSEx, ENDIF */
|
|
|
|
/* check params and call appropriate test routine */
|
|
|
|
switch( GetSflagsSp(directive) ) {
|
|
case CC_NUMARG: /* [ELSE]IF[E] */
|
|
/* no forward reference allowed, symbol must be defined */
|
|
if ( ( ERROR == EvalOperand( &i, tokenarray, Token_Count, &opndx, EXPF_NOUNDEF ) ) ) {
|
|
DebugMsg(("CondAsmDirective(%s), EvalOperand returned with ERROR\n", GetResWName(directive, NULL) ));
|
|
/* v2.11: don't exit, assume 0 */
|
|
//return( ERROR );
|
|
opndx.kind = EXPR_CONST;
|
|
opndx.value = 0;
|
|
i = Token_Count;
|
|
}
|
|
#if 0 /* v2.05: obsolete */
|
|
if ( opndx.sym && opndx.sym->state == SYM_UNDEFINED ) {
|
|
EmitErr( SYMBOL_NOT_DEFINED, opndx.sym->name );
|
|
} else
|
|
#endif
|
|
if ( opndx.kind == EXPR_CONST )
|
|
;
|
|
else if ( opndx.kind == EXPR_ADDR && opndx.indirect == FALSE ) {
|
|
opndx.value += opndx.sym->offset;
|
|
/* v2.07: Masm doesn't accept a relocatable item,
|
|
* so emit at least a warning!
|
|
*/
|
|
EmitWarn( 2, CONSTANT_EXPECTED );
|
|
} else {
|
|
return( EmitError( CONSTANT_EXPECTED ) );
|
|
}
|
|
if ( directive == T_IF || directive == T_ELSEIF )
|
|
NextIfState = ( opndx.value ) ? BLOCK_ACTIVE : BLOCK_INACTIVE;
|
|
else
|
|
NextIfState = ( !opndx.value ) ? BLOCK_ACTIVE : BLOCK_INACTIVE;
|
|
break;
|
|
case CC_LITARG: /* [ELSE]IFDIF[I], [ELSE]IFIDN[I] */
|
|
string1 = tokenarray[i].string_ptr;
|
|
if ( tokenarray[i].token != T_STRING || tokenarray[i].string_delim != '<' ) {
|
|
if ( tokenarray[i].token == T_ID && SymSearch( string1 ) == NULL )
|
|
EmitErr( SYMBOL_NOT_DEFINED, string1 );
|
|
else
|
|
EmitError( TEXT_ITEM_REQUIRED );
|
|
return( ERROR );
|
|
}
|
|
i++;
|
|
if ( tokenarray[i].token != T_COMMA ) {
|
|
return( EmitErr( EXPECTING_COMMA, tokenarray[i].tokpos ) );
|
|
}
|
|
i++;
|
|
string2 = tokenarray[i].string_ptr;
|
|
if ( tokenarray[i].token != T_STRING || tokenarray[i].string_delim != '<' ) {
|
|
if ( tokenarray[i].token == T_ID && SymSearch( string2 ) == NULL )
|
|
EmitErr( SYMBOL_NOT_DEFINED, string2 );
|
|
else
|
|
EmitError( TEXT_ITEM_REQUIRED );
|
|
return( ERROR );
|
|
}
|
|
i++;
|
|
DebugMsg1(("CondAsmDirective(%s), cmp >%s< and >%s<\n", GetResWName(directive, NULL), string1, string2 ));
|
|
switch ( directive ) {
|
|
case T_IFDIF:
|
|
case T_ELSEIFDIF:
|
|
NextIfState = check_dif( string1, string2, TRUE ) ? BLOCK_ACTIVE : BLOCK_INACTIVE;
|
|
break;
|
|
case T_IFDIFI:
|
|
case T_ELSEIFDIFI:
|
|
NextIfState = check_dif( string1, string2, FALSE ) ? BLOCK_ACTIVE : BLOCK_INACTIVE;
|
|
break;
|
|
case T_IFIDN:
|
|
case T_ELSEIFIDN:
|
|
NextIfState = !check_dif( string1, string2, TRUE ) ? BLOCK_ACTIVE : BLOCK_INACTIVE;
|
|
break;
|
|
default:
|
|
NextIfState = !check_dif( string1, string2, FALSE ) ? BLOCK_ACTIVE : BLOCK_INACTIVE;
|
|
}
|
|
break;
|
|
case CC_BLKARG: /* [ELSE]IF[N]B */
|
|
string1 = tokenarray[i].string_ptr;
|
|
|
|
if ( tokenarray[i].token != T_STRING || tokenarray[i].string_delim != '<' ) {
|
|
if ( tokenarray[i].token == T_ID && SymSearch( string1 ) == NULL )
|
|
EmitErr( SYMBOL_NOT_DEFINED, string1 );
|
|
else
|
|
EmitError( TEXT_ITEM_REQUIRED );
|
|
return( ERROR );
|
|
}
|
|
i++;
|
|
if ( directive == T_IFB || directive == T_ELSEIFB ) {
|
|
NextIfState = check_blank( string1 ) ? BLOCK_ACTIVE : BLOCK_INACTIVE;
|
|
} else {
|
|
NextIfState = !check_blank( string1 ) ? BLOCK_ACTIVE : BLOCK_INACTIVE;
|
|
}
|
|
break;
|
|
case CC_PASS1: /* [ELSE]IF1 */
|
|
/* v2.04: changed */
|
|
//NextIfState = ((Parse_Pass == PASS_1) ? BLOCK_ACTIVE : BLOCK_INACTIVE);
|
|
NextIfState = BLOCK_ACTIVE;
|
|
break;
|
|
case CC_PASS2: /* [ELSE]IF2 */
|
|
if ( ModuleInfo.setif2 == FALSE ) {
|
|
EmitError( IF2_NOT_ALLOWED );
|
|
break;
|
|
}
|
|
/* v2.04: changed */
|
|
//NextIfState = ((Parse_Pass == PASS_1) ? BLOCK_INACTIVE : BLOCK_ACTIVE);
|
|
NextIfState = BLOCK_ACTIVE;
|
|
break;
|
|
case CC_SYMARG: /* [ELSE]IF[N]DEF */
|
|
NextIfState = BLOCK_INACTIVE;
|
|
/* Masm's implementation works with IDs as arguments only. The rest
|
|
* will return FALSE. However, it's nice to be able to check whether
|
|
* a reserved word is defined or not.
|
|
*/
|
|
/* v2.0: [ELSE]IF[N]DEF is valid *without* an argument! */
|
|
//if ( tokenarray[i].token == T_ID && tokenarray[i+1].token == T_FINAL) {
|
|
if ( tokenarray[i].token == T_FINAL ) {
|
|
} else if ( tokenarray[i].token == T_ID ) {
|
|
/* v2.07: handle structs + members (if -Zne is NOT set) */
|
|
struct asym *sym;
|
|
if ( Options.strict_masm_compat == FALSE &&
|
|
tokenarray[i+1].token == T_DOT &&
|
|
( sym = SymSearch( tokenarray[i].string_ptr ) ) &&
|
|
( ( sym->state == SYM_TYPE ) || sym->type ) ) {
|
|
uint_32 value;
|
|
value = 0;
|
|
do {
|
|
i += 2;
|
|
/* if it's a structured variable, use its type! */
|
|
if ( sym->state != SYM_TYPE )
|
|
sym = sym->type;
|
|
sym = SearchNameInStruct( sym, tokenarray[i].string_ptr, &value, 0 );
|
|
} while ( sym && tokenarray[i+1].token == T_DOT );
|
|
NextIfState = ( sym ? BLOCK_ACTIVE : BLOCK_INACTIVE );
|
|
} else {
|
|
NextIfState = ( check_defd( tokenarray[i].string_ptr ) ? BLOCK_ACTIVE : BLOCK_INACTIVE );
|
|
}
|
|
i++;
|
|
} else if ( tokenarray[i].token == T_RES_ID && tokenarray[i].tokval == T_FLAT ) {
|
|
/* v2.09: special treatment of FLAT added */
|
|
NextIfState = (( ModuleInfo.flat_grp && ModuleInfo.flat_grp->sym.isdefined ) ? BLOCK_ACTIVE : BLOCK_INACTIVE );
|
|
i++;
|
|
} else if ( Options.strict_masm_compat == FALSE && (
|
|
tokenarray[i].token == T_RES_ID ||
|
|
tokenarray[i].token == T_STYPE ||
|
|
tokenarray[i].token == T_INSTRUCTION ||
|
|
tokenarray[i].token == T_DIRECTIVE ||
|
|
//tokenarray[i].token == T_UNARY_OP ||
|
|
//tokenarray[i].token == T_BINARY_OP ||
|
|
tokenarray[i].token == T_REG ) &&
|
|
tokenarray[i+1].token == T_FINAL ) {
|
|
NextIfState = BLOCK_ACTIVE;
|
|
i++;
|
|
}
|
|
if ( tokenarray[i].token != T_FINAL ) {
|
|
EmitWarn( 2, IFDEF_EXPECTS_SYMBOL_ARGUMENT, tokenarray[i-1].tokpos );
|
|
while ( tokenarray[i].token != T_FINAL ) i++;
|
|
}
|
|
if ( directive == T_IFNDEF || directive == T_ELSEIFNDEF )
|
|
NextIfState = ( ( NextIfState == BLOCK_ACTIVE ) ? BLOCK_INACTIVE : BLOCK_ACTIVE );
|
|
break;
|
|
default: /* ELSE and ENDIF */
|
|
NextIfState = BLOCK_ACTIVE;
|
|
break;
|
|
}
|
|
|
|
if ( tokenarray[i].token != T_FINAL ) {
|
|
return( EmitErr( SYNTAX_ERROR_EX, tokenarray[i].string_ptr ) );
|
|
}
|
|
|
|
CurrIfState = NextIfState;
|
|
|
|
DebugMsg1(("CondAsmDirective(%s) exit, state=%s, lvl=%u, falselvl=%u\n",
|
|
GetResWName(directive, NULL), GetCurrIfStatString(), blocknestlevel, falseblocknestlevel));
|
|
return( NOT_ERROR );
|
|
}
|
|
|
|
static char * GetErrText( int i, struct asm_tok tokenarray[] )
|
|
/************************************************************/
|
|
{
|
|
*StringBufferEnd = NULLC;
|
|
if ( i ) {
|
|
if ( tokenarray[i].token != T_STRING || tokenarray[i].string_delim != '<' ) {
|
|
TextItemError( &tokenarray[i] );
|
|
} else {
|
|
*(StringBufferEnd+0) = ':';
|
|
*(StringBufferEnd+1) = ' ';
|
|
//GetLiteralValue( buffer+2, text->string_ptr );
|
|
strcpy( StringBufferEnd+2, tokenarray[i].string_ptr );
|
|
}
|
|
}
|
|
return( StringBufferEnd );
|
|
|
|
}
|
|
|
|
/* v2.05: the error directives are no longer handled in the
|
|
* preprocessor, because the errors are displayed in pass 2 only
|
|
* - .err [<text>]
|
|
* - .err<1|2> [<text>]
|
|
* - .err<e|nz> expression [, <text>]
|
|
* - .errdif[i] literal1, literal2 [, <text>]
|
|
* - .erridn[i] literal1, literal2 [, <text>]
|
|
* - .err[n]b text_literal [, <text>]
|
|
* - .err[n]def symbol [, <text>]
|
|
*/
|
|
ret_code ErrorDirective( int i, struct asm_tok tokenarray[] )
|
|
/***********************************************************/
|
|
{
|
|
struct expr opndx;
|
|
unsigned direct;
|
|
const char *string1;
|
|
const char *string2;
|
|
int errmsg = EMPTY;
|
|
int erridx = 0;
|
|
struct asym *sym;
|
|
int idloc;
|
|
|
|
direct = tokenarray[i].tokval;
|
|
|
|
i++; /* go past directive */
|
|
|
|
/* get an expression if necessary */
|
|
switch( GetSflagsSp( direct) ) {
|
|
case CC_NUMARG: /* .ERR[E|NZ] */
|
|
|
|
if (( ERROR == EvalOperand( &i, tokenarray, Token_Count, &opndx, 0 ) ))
|
|
return( ERROR );
|
|
if ( opndx.kind == EXPR_CONST )
|
|
;
|
|
else if ( opndx.kind == EXPR_ADDR && opndx.indirect == FALSE && opndx.sym && opndx.sym->state == SYM_UNDEFINED )
|
|
;//opndx.value += opndx.sym->offset;
|
|
else {
|
|
return( EmitError( CONSTANT_EXPECTED ) );
|
|
}
|
|
if ( tokenarray[i].token == T_COMMA && tokenarray[i+1].token != T_FINAL ) {
|
|
i++;
|
|
erridx = i++;
|
|
}
|
|
if ( Parse_Pass == PASS_1 )
|
|
break;
|
|
if ( direct == T_DOT_ERRNZ && opndx.value ) {
|
|
errmsg = FORCED_NOT_ZERO;
|
|
} else if ( direct == T_DOT_ERRE && !opndx.value ) {
|
|
errmsg = FORCED_EQUAL;
|
|
}
|
|
|
|
if ( errmsg != EMPTY )
|
|
EmitErr( errmsg, opndx.value, GetErrText( erridx, tokenarray ) );
|
|
break;
|
|
case CC_SYMARG: /* .ERR[N]DEF */
|
|
/* there's a special handling of these directives in ExpandLine()! */
|
|
if ( tokenarray[i].token != T_ID ) {
|
|
return( EmitErr( SYNTAX_ERROR_EX, tokenarray[i].string_ptr ) );
|
|
}
|
|
idloc = i;
|
|
/* skip the next param */
|
|
do {
|
|
i++;
|
|
} while ( tokenarray[i].token == T_DOT || tokenarray[i].token == T_ID );
|
|
if ( tokenarray[i].token == T_COMMA && tokenarray[i+1].token != T_FINAL ) {
|
|
/* v2.05: added */
|
|
/* v2.08: obsolete, the expansion occurs in the preprocessor.
|
|
* See ExpandLine() in expans.c
|
|
*/
|
|
//ExpandLineItems( tokenarray[i].tokpos, i, tokenarray, TRUE, FALSE );
|
|
i++;
|
|
erridx = i++; /* Masm seems to accept anything as text */
|
|
}
|
|
|
|
//if ( tokenarray[i].token != T_FINAL )
|
|
// break;
|
|
|
|
/* should run on pass 2 only! */
|
|
if ( Parse_Pass == PASS_1 )
|
|
break;
|
|
|
|
/* don't use check_defd()! */
|
|
/* v2.07: check for structured variables */
|
|
if ( Options.strict_masm_compat == FALSE &&
|
|
tokenarray[idloc+1].token == T_DOT &&
|
|
( sym = SymSearch( tokenarray[idloc].string_ptr ) ) &&
|
|
( ( sym->state == SYM_TYPE ) || sym->type ) ) {
|
|
uint_32 value;
|
|
int j = idloc;
|
|
int size;
|
|
value = 0;
|
|
do {
|
|
j += 2;
|
|
/* if it's a structured variable, use its type! */
|
|
if ( sym->state != SYM_TYPE )
|
|
sym = sym->type;
|
|
sym = SearchNameInStruct( sym, tokenarray[j].string_ptr, &value, 0 );
|
|
} while ( sym && tokenarray[j+1].token == T_DOT );
|
|
if ( tokenarray[j].token == T_ID )
|
|
j++;
|
|
else if ( tokenarray[j].token != T_FINAL && tokenarray[j].token != T_COMMA ) {
|
|
return( EmitErr( SYNTAX_ERROR_EX, tokenarray[j].string_ptr ) );
|
|
}
|
|
size = tokenarray[j].tokpos - tokenarray[idloc].tokpos;
|
|
memcpy( StringBufferEnd, tokenarray[idloc].tokpos, size );
|
|
*(StringBufferEnd+size) = NULLC;
|
|
} else {
|
|
sym = SymSearch( tokenarray[idloc].string_ptr );
|
|
strcpy( StringBufferEnd, tokenarray[idloc].string_ptr );
|
|
}
|
|
if ( sym && sym->state == SYM_UNDEFINED )
|
|
sym = NULL;
|
|
|
|
/* Masm "usually" ignores the optional errtxt! */
|
|
if( direct == T_DOT_ERRDEF && sym != NULL )
|
|
EmitErr( FORCED_DEF, StringBufferEnd );
|
|
else if( direct == T_DOT_ERRNDEF && sym == NULL )
|
|
EmitErr( FORCED_NOT_DEF, StringBufferEnd );
|
|
break;
|
|
case CC_BLKARG: /* .ERR[N]B */
|
|
string1 = tokenarray[i].string_ptr;
|
|
if ( tokenarray[i].token != T_STRING || tokenarray[i].string_delim != '<' ) {
|
|
return( TextItemError( &tokenarray[i] ) );
|
|
}
|
|
i++;
|
|
if ( tokenarray[i].token == T_COMMA && tokenarray[i+1].token != T_FINAL ) {
|
|
i++;
|
|
erridx = i++;
|
|
}
|
|
if ( Parse_Pass == PASS_1 )
|
|
break;
|
|
if ( direct == T_DOT_ERRB && check_blank( string1 ) )
|
|
errmsg = FORCED_BLANK;
|
|
else if ( direct == T_DOT_ERRNB && !check_blank( string1 ) )
|
|
errmsg = FORCED_NOT_BLANK;
|
|
if ( errmsg != EMPTY )
|
|
EmitErr( errmsg, string1, GetErrText( erridx, tokenarray ) );
|
|
break;
|
|
case CC_LITARG: /* .ERRDIF[I], .ERRIDN[I] */
|
|
string1 = tokenarray[i].string_ptr;
|
|
if ( tokenarray[i].token != T_STRING || tokenarray[i].string_delim != '<' ) {
|
|
return( TextItemError( &tokenarray[i] ) );
|
|
}
|
|
i++;
|
|
if ( tokenarray[i].token != T_COMMA ) {
|
|
return( EmitErr( EXPECTING_COMMA, tokenarray[i].tokpos ) );
|
|
}
|
|
i++;
|
|
string2 = tokenarray[i].string_ptr;
|
|
if ( tokenarray[i].token != T_STRING || tokenarray[i].string_delim != '<' ) {
|
|
return( TextItemError( &tokenarray[i] ) );
|
|
}
|
|
i++;
|
|
if ( tokenarray[i].token == T_COMMA && tokenarray[i+1].token != T_FINAL ) {
|
|
i++;
|
|
erridx = i++;
|
|
}
|
|
if ( Parse_Pass == PASS_1 )
|
|
break;
|
|
|
|
switch ( direct ) {
|
|
case T_DOT_ERRDIF:
|
|
if ( check_dif( string1, string2, TRUE ) )
|
|
errmsg = FORCED_DIF;
|
|
break;
|
|
case T_DOT_ERRDIFI:
|
|
if ( check_dif( string1, string2, FALSE ) )
|
|
errmsg = FORCED_DIF;
|
|
break;
|
|
case T_DOT_ERRIDN:
|
|
if ( !check_dif( string1, string2, TRUE ) )
|
|
errmsg = FORCED_IDN;
|
|
break;
|
|
default:
|
|
if ( !check_dif( string1, string2, FALSE ) )
|
|
errmsg = FORCED_IDN;
|
|
}
|
|
if ( errmsg != EMPTY )
|
|
EmitErr( errmsg, string1, string2, GetErrText( erridx, tokenarray ) );
|
|
break;
|
|
case CC_PASS2: /* .ERR2 */
|
|
if ( ModuleInfo.setif2 == FALSE ) {
|
|
return( EmitError( IF2_NOT_ALLOWED ) );
|
|
}
|
|
case CC_PASS1: /* .ERR1 */
|
|
default: /* .ERR */
|
|
if ( tokenarray[i].token != T_FINAL ) {
|
|
erridx = i++;
|
|
}
|
|
if ( Parse_Pass == PASS_1 )
|
|
break;
|
|
EmitErr( FORCED_ERR, GetErrText( erridx, tokenarray ) );
|
|
break;
|
|
}
|
|
if ( tokenarray[i].token != T_FINAL ) {
|
|
return( EmitErr( SYNTAX_ERROR_EX, tokenarray[i].tokpos ) );
|
|
}
|
|
return( NOT_ERROR );
|
|
}
|
|
|
|
void CondCheckOpen( void )
|
|
/************************/
|
|
{
|
|
if( blocknestlevel > 0 ) {
|
|
EmitErr( BLOCK_NESTING_ERROR, "if-else" );
|
|
}
|
|
return;
|
|
}
|
|
|
|
int GetIfNestLevel( void )
|
|
/************************/
|
|
{
|
|
return( blocknestlevel );
|
|
}
|
|
|
|
void SetIfNestLevel( int newlevel )
|
|
/*********************************/
|
|
{
|
|
blocknestlevel = newlevel;
|
|
return;
|
|
}
|
|
|
|
/* init (called once per module) */
|
|
|
|
void CondInit( void )
|
|
/*******************/
|
|
{
|
|
CurrIfState = BLOCK_ACTIVE;
|
|
blocknestlevel = 0;
|
|
falseblocknestlevel = 0;
|
|
}
|