jwasm/expans.c
2014-06-18 19:16:56 +04:00

1537 lines
69 KiB
C

/****************************************************************************
*
* This code is Public Domain.
*
* ========================================================================
*
* Description: do macro expansion.
*
* functions:
* - myltoa() generic function which replaces ltoa()
* - RunMacro run a macro
* - ExpandText expand a source line when % operator is at pos 0
* - ExpandLineItems expand parts of a source line
* - ExpandLine expand a source line
* - ExpandLiterals expand <> or {} literals for struct initialization
****************************************************************************/
#include <ctype.h>
#include "globals.h"
#include "memalloc.h"
#include "parser.h"
#include "preproc.h"
#include "expreval.h"
#include "equate.h"
#include "input.h"
#include "tokenize.h"
#include "macro.h"
#include "condasm.h"
#include "listing.h"
#include "myassert.h"
/* TEVALUE_UNSIGNED
* 1 = the % operator used in an TEXTEQU expression is supposed to
* return an UNSIGNED value ( Masm-compatible ).
*/
#define TEVALUE_UNSIGNED 1
#define MAX_TEXTMACRO_NESTING 20
extern char *GetMacroLine( struct macro_instance *, char * );
int MacroLocals; /* counter for LOCAL names */
uint_8 MacroLevel; /* current macro nesting level */
static const char __digits[16] = {"0123456789ABCDEF"};
static ret_code ExpandTMacro( char * const, struct asm_tok tokenarray[], int equmode, int level );
/* C ltoa() isn't fully compatible since hex digits are lower case.
* for JWasm, it's ensured that 2 <= radix <= 16.
*/
char *myltoa( uint_32 value, char *buffer, unsigned radix, bool sign, bool addzero )
/**********************************************************************************/
{
char *p;
char *dst = buffer;
char tmpbuf[34];
#ifdef DEBUG_OUT
uint_32 saved_value = value;
#endif
if ( sign ) {
*dst++ = '-';
value = 0 - value;
} else if ( value == 0 ) {
*dst++ = '0';
*dst = NULLC;
return( buffer );
}
for ( p = &tmpbuf[33], *p = NULLC; value; value = value / radix )
*(--p) = __digits[value % radix];
if ( addzero && ( *p > '9') ) /* v2: add a leading '0' if first digit is alpha */
*dst++ = '0';
memcpy( dst, p, &tmpbuf[33] + 1 - p );
DebugMsg1(("myltoa( value=%" I32_SPEC "Xh, out=%s, radix=%u, sign=%u, %u)\n", saved_value, buffer, radix, sign, addzero ));
return( buffer );
}
/* get value of a literal, skip literal-character operators(!) */
/* returns no of characters copied into buffer (without terminating 00) */
/* v2.09: obsolete, the text is stored without '!' operators now */
#if 0
int GetLiteralValue( char *buffer, const char *p )
/************************************************/
{
char *dest = buffer;
if ( p )
while ( *p ) {
//if ( *p == '!' && *(p+1) != NULLC )
if ( *p == '!' )
p++;
*dest++ = *p++;
}
*dest = NULLC;
return( dest - buffer );
}
#endif
/* Read the current (macro) queue until it's done. */
static void SkipMacro( struct asm_tok tokenarray[] )
/**************************************************/
{
char buffer[MAX_LINE_LEN];
/* The queue isn't just thrown away, because any
* coditional assembly directives found in the source
* must be executed.
*/
while ( GetTextLine( buffer ) ) {
Tokenize( buffer, 0, tokenarray, TOK_DEFAULT );
}
}
#ifdef __I86__
#define PARMSTRINGSIZE ( MAX_LINE_LEN + 16 )
#else
#define PARMSTRINGSIZE ( MAX_LINE_LEN * 2 )
#endif
/* run a macro.
* - macro: macro item
* - out: value to return (for macro functions)
* - label: token of label ( or -1 if none )
* - is_exitm: returns TRUE if EXITM has been hit
* returns index of token not processed or -1 on errors
*/
int RunMacro( struct dsym *macro, int idx, struct asm_tok tokenarray[], char *out, int mflags, bool *is_exitm )
/*************************************************************************************************************/
{
char *currparm;
char *savedStringBuffer = StringBufferEnd;
int i;
//int start = idx-1;
int parmidx;
int skipcomma;
int varargcnt;
int bracket_level = -1;/* () level (needed for macro functions) */
int parm_end_delim; /* parameter end delimiter */
//char addprefix;
char *ptr;
char *parmstrings;
struct macro_info *info;
struct srcline *lnode;
struct asym *sym;
struct expr opndx;
struct macro_instance mi;
DebugMsg1(("RunMacro(%s, idx=%u src=>%s< ) enter, lvl=%u, locals=%04u\n", macro->sym.name, idx, tokenarray[idx].tokpos, MacroLevel, MacroLocals ));
if ( MacroLevel == MAX_MACRO_NESTING ) {
EmitError( NESTING_LEVEL_TOO_DEEP );
return( -1 );
}
mi.parm_array = NULL;
info = macro->e.macroinfo;
#ifdef DEBUG_OUT
info->count++;
#endif
/* invokation of macro functions requires params enclosed in "()" */
parm_end_delim = T_FINAL;
if ( macro->sym.isfunc ) {
if ( tokenarray[idx].token == T_OP_BRACKET ) { /* should be always true */
idx++;
parm_end_delim = T_CL_BRACKET;
bracket_level = 1;
}
*out = NULLC; /* v2.08: init return value buffer */
}
/* v2.08: if macro is purged, return "void" */
if ( macro->sym.purged ) {
if ( bracket_level > 0 ) {
for( ; bracket_level && tokenarray[idx].token != T_FINAL; idx++ )
if ( tokenarray[idx].token == T_OP_BRACKET )
bracket_level++;
else if ( tokenarray[idx].token == T_CL_BRACKET )
bracket_level--;
} else
idx = Token_Count;
DebugMsg1(("RunMacro(%s) exit, macro is purged\n", macro->sym.name ));
return( idx );
}
DebugMsg1(( "RunMacro(%s): params=>%s< parmcnt=%u vararg=%u\n", macro->sym.name, tokenarray[idx].tokpos, info->parmcnt, macro->sym.mac_vararg ));
if ( info->parmcnt ) {
mi.parm_array = (char **)myalloca( info->parmcnt * sizeof( char * ) + PARMSTRINGSIZE );
parmstrings = (char *)(mi.parm_array + info->parmcnt);
/* init the macro arguments pointer */
currparm = parmstrings;
}
/* now get all the parameters from the original src line.
* macro parameters are expanded if
* - it is a macro function call or
* - the expansion operator (%) is found
*/
parmidx = 0;
#if MACROLABEL
if ( macro->sym.label ) {
if ( mflags & MF_LABEL ) {
i = strlen( tokenarray[0].string_ptr );
mi.parm_array[parmidx] = currparm;
memcpy( currparm, tokenarray[0].string_ptr, i+1 );
currparm = GetAlignedPointer( currparm, i );
} else
mi.parm_array[parmidx] = "";
parmidx++;
}
#endif
*is_exitm = FALSE;
/* v2.08: allow T_FINAL to be chained, lastidx==0 is true final */
tokenarray[Token_Count].lastidx = 0;
for( varargcnt = 0, skipcomma = 0; parmidx < info->parmcnt; parmidx++ ) {
/* v2.09: don't skip comma if it was the last argument.
* this will a) make a trailing comma trigger warning 'too many arguments...'
* and b), argument handling of FOR loop is significantly simplified.
*/
if ( tokenarray[idx].token == T_COMMA && skipcomma )
idx++;
skipcomma = 1;
if ( tokenarray[idx].token == T_FINAL ||
tokenarray[idx].token == parm_end_delim ||
( tokenarray[idx].token == T_COMMA &&
( macro->sym.mac_vararg == FALSE || parmidx != info->parmcnt - 1 ) ) ) {
/* it's a blank parm */
if( info->parmlist[parmidx].required ) {
DebugMsg1(( "RunMacro(%s.%u), parameter %u required >%s<\n", macro->sym.name, parmidx, parmidx, tokenarray[idx].tokpos ));
if ( *macro->sym.name == NULLC )
EmitErr( MISSING_MACRO_ARGUMENT_2, macro->sym.value + 1 );
else
EmitErr( MISSING_MACRO_ARGUMENT, macro->sym.name, parmidx + 1 );
return( -1 );
}
if ( varargcnt == 0 ) {
mi.parm_array[parmidx] = info->parmlist[parmidx].deflt;
DebugMsg1(("RunMacro(%s.%u): curr (=def) parameter value=>%s<\n", macro->sym.name, parmidx, mi.parm_array[parmidx] ? parmidx, mi.parm_array[parmidx] : "NULL" ));
}
} else {
int inside_literal = 0;
int inside_angle_brackets = 0;
int old_tokencount = Token_Count;
*currparm = NULLC;
DebugMsg1(( "RunMacro(%s.%u), >%s<\n", macro->sym.name, parmidx, tokenarray[idx].tokpos ));
for( ptr = currparm; ( tokenarray[idx].token != T_FINAL && tokenarray[idx].token != T_COMMA ) || inside_literal; idx++ ) {
/* if were're inside a literal, go up one level and continue scanning the argument */
if ( tokenarray[idx].token == T_FINAL ) {
idx = tokenarray[idx].lastidx; /* restore token index */
inside_literal--;
if ( tokenarray[idx].string_delim == '<' )
inside_angle_brackets = 0;
else {
*ptr++ = '}';
}
continue;
}
if ( tokenarray[idx].token == T_PERCENT ) {
int max;
int cnt;
int cnt_opnum;
/* expansion of macro parameters.
* if the token behind % is not a text macro or macro function
* the expression will be always expanded and evaluated.
* Else it is expanded, but only evaluated if
*/
idx++;
while ( tokenarray[idx].token == T_PERCENT ) idx++;
i = idx;
cnt_opnum = 1;
if ( tokenarray[i].token == T_ID ) {
sym = SymSearch( tokenarray[i].string_ptr );
if ( sym && sym->isdefined &&
( sym->state == SYM_TMACRO ||
( sym->state == SYM_MACRO && sym->isfunc == TRUE && tokenarray[i+1].token == T_OP_BRACKET ) ) )
cnt_opnum = 0;
}
for( cnt = 0; tokenarray[i].token != T_FINAL && tokenarray[i].token != T_COMMA; i++ ) {
if ( is_valid_id_first_char( *tokenarray[i].string_ptr )) {
if ( tokenarray[i+1].token == T_OP_BRACKET ) {
int cnt2;
i += 2;
for ( cnt2 = 1;cnt2 && tokenarray[i].token != T_FINAL; i++ ) {
if ( tokenarray[i].token == T_OP_BRACKET )
cnt2++;
else if ( tokenarray[i].token == T_CL_BRACKET )
cnt2--;
}
i--;
}
continue;
}
/* count brackets */
if ( parm_end_delim == T_CL_BRACKET )
if ( tokenarray[i].token == T_OP_BRACKET )
cnt++;
else if ( tokenarray[i].token == T_CL_BRACKET ) {
if ( cnt == 0 )
break;
cnt--;
}
/* stop if undelimited string occurs (need to scan for '!') */
if ( tokenarray[i].token == T_STRING && tokenarray[i].string_delim == NULLC )
break;
/* names dot and amp are ok */
if ( tokenarray[i].token == T_DOT || tokenarray[i].token == '&' || tokenarray[i].token == '%' )
;
else
cnt_opnum++; /* anything else will trigger numeric evaluation */
}
if ( i == idx ) { /* no items except %? */
idx--;
continue;
}
cnt = tokenarray[i].tokpos - tokenarray[idx].tokpos;
while ( isspace( *(tokenarray[idx].tokpos+cnt-1) ) ) cnt--;
memcpy( ptr, tokenarray[idx].tokpos, cnt );
*(ptr+cnt) = NULLC;
if ( ExpandText( ptr, tokenarray, FALSE ) == ERROR ) {
StringBufferEnd = savedStringBuffer;
return(-1);
}
idx = i - 1;
if ( cnt_opnum ) {
/* convert numeric expression into a string */
max = Tokenize( ptr, Token_Count+1, tokenarray, TOK_RESCAN );
i = Token_Count + 1;
DebugMsg1(( "RunMacro(%s.%u), num expansion: >%s<\n", macro->sym.name, parmidx, ptr ));
/* the % operator won't accept forward references.
* v2.09: flag EXPF_NOUNDEF set.
*/
if ( EvalOperand( &i, tokenarray, max, &opndx, EXPF_NOUNDEF ) == ERROR )
opndx.llvalue = 0;
else if ( opndx.kind != EXPR_CONST ) {
EmitError( CONSTANT_EXPECTED );
opndx.llvalue = 0;
}
DebugMsg1(( "RunMacro(%s.%u): num expansion, opndx.type=%d, value=%d\n", macro->sym.name, parmidx, opndx.type, opndx.value ));
/* v2.08: accept constant and copy any stuff that's following */
myltoa( opndx.uvalue, StringBufferEnd, ModuleInfo.radix, opndx.hvalue < 0, FALSE );
//ptr += strlen( ptr );
if ( i != max ) {
/* the evaluator was unable to evaluate the full expression. the rest
* has to be "copied" */
DebugMsg1(( "RunMacro(%s.%u): num expansion, additional token=%s\n", macro->sym.name, parmidx, tokenarray[i].tokpos ));
/* just copy won't work, since <>-literals aren't handled correctly then */
//EmitErr( SYNTAX_ERROR_EX, tokenarray[i].tokpos );
strcat( StringBufferEnd, tokenarray[i].tokpos );
}
strcpy( ptr, StringBufferEnd );
}
ptr += strlen( ptr );
/**/myassert( ptr < parmstrings + PARMSTRINGSIZE );
continue;
}
if ( tokenarray[idx].token == T_STRING && tokenarray[idx].string_delim == '{' ) {
char *p = tokenarray[idx].string_ptr;
int tmp = idx;
/* copy the '{' */
*ptr++ = '{';
/* the string must be tokenized */
inside_literal++;
idx = Token_Count;
Token_Count = Tokenize( p, idx + 1, tokenarray, TOK_RESCAN | TOK_NOCURLBRACES );
tokenarray[Token_Count].lastidx = tmp;
continue;
}
if ( inside_angle_brackets == 0 ) {
/* track brackets for macro functions; exit if one more ')' than '(' is found */
if ( bracket_level > 0 ) {
if ( tokenarray[idx].token == T_OP_BRACKET ) {
bracket_level++;
} else if ( tokenarray[idx].token == T_CL_BRACKET ) {
bracket_level--;
if ( bracket_level == 0 )
break;
}
}
/* if there's a literal enclosed in <>, remove the delimiters and
* tokenize the item (Token_Count must be adjusted, since RunMacro()
* might be called later!)
*/
if ( tokenarray[idx].token == T_STRING && tokenarray[idx].string_delim == '<' && inside_angle_brackets == 0 ) {
char *p;
int tmp;
int size;
#if 1
if ( !strchr( tokenarray[idx].string_ptr, '%' ) ) {
memcpy( ptr, tokenarray[idx].string_ptr, tokenarray[idx].stringlen );
ptr += tokenarray[idx].stringlen;
continue;
}
#endif
tmp = idx;
size = tokenarray[idx+1].tokpos - (tokenarray[idx].tokpos+1);
p = StringBufferEnd;
memcpy( p, tokenarray[idx].tokpos+1, size );
while ( *(p+size-1) != '>' ) size--;
*(p+size-1) = NULLC;
StringBufferEnd = GetAlignedPointer( p, size );
//strcpy( tmpline, tokenarray[idx].string_ptr );
/* the string must be tokenized */
inside_literal++;
inside_angle_brackets = 1;
idx = Token_Count;
Token_Count = Tokenize( p, idx + 1, tokenarray, TOK_RESCAN );
tokenarray[Token_Count].lastidx = tmp;
/* copy spaces located before the first token */
memcpy( ptr, p, tokenarray[idx+1].tokpos - p );
ptr += tokenarray[idx+1].tokpos - p;
continue;
}
/* macros functions must be expanded always.
* text macros are expanded only selectively
*/
if ( tokenarray[idx].token == T_ID ) {
if ( sym = SymSearch( tokenarray[idx].string_ptr ) ) {
if ( sym->state == SYM_MACRO && sym->isdefined == TRUE &&
sym->isfunc == TRUE && tokenarray[idx+1].token == T_OP_BRACKET ) {
bool is_exitm2;
//int oldidx = idx;
idx = RunMacro( (struct dsym *)sym, idx+1, tokenarray, ptr, 0, &is_exitm2 );
if ( idx < 0 ) {
StringBufferEnd = savedStringBuffer;
return( idx );
}
ptr += strlen( ptr );
/* copy spaces behind macro function call */
if ( tokenarray[idx].token != T_FINAL && tokenarray[idx].token != T_COMMA ) {
i = tokenarray[idx].tokpos - ( tokenarray[idx-1].tokpos + 1 );
memcpy( ptr, tokenarray[idx-1].tokpos + 1, i );
ptr += i;
}
idx--; /* adjust token index */
/**/myassert( ptr < parmstrings + PARMSTRINGSIZE );
continue;
} else if ( sym->state == SYM_TMACRO && sym->isdefined == TRUE &&
( macro->sym.predefined && ( info->autoexp & ( 1 << parmidx ) ) ) ) {
//GetLiteralValue( ptr, sym->string_ptr );
strcpy( ptr, sym->string_ptr );
ExpandTMacro( ptr, tokenarray, FALSE, 0 );
ptr += strlen( ptr );
/* copy spaces behind text macro */
if ( tokenarray[idx+1].token != T_FINAL && tokenarray[idx+1].token != T_COMMA ) {
i = tokenarray[idx+1].tokpos - ( tokenarray[idx].tokpos + sym->name_size );
memcpy( ptr, tokenarray[idx].tokpos + sym->name_size, i );
ptr += i;
}
/**/myassert( ptr < parmstrings + PARMSTRINGSIZE );
continue;
}
}
}
}
/* get length of item */
i = tokenarray[idx+1].tokpos - tokenarray[idx].tokpos;
if ( !inside_literal && ( tokenarray[idx+1].token == T_COMMA ||
tokenarray[idx+1].token == parm_end_delim ) ) {
while ( isspace( *(tokenarray[idx].tokpos+i-1 ) ) ) i--;
}
/* the literal character operator ! is valid for macro arguments */
if ( tokenarray[idx].token == T_STRING && tokenarray[idx].string_delim == NULLC ) {
char *p;
char *end;
DebugMsg1(("RunMacro(%s.%u): undelimited string >%s<, watching '!'\n", macro->sym.name, parmidx, tokenarray[idx].string_ptr ));
p = tokenarray[idx].tokpos;
end = p + i;
/**/myassert( ( ptr + i ) < parmstrings + PARMSTRINGSIZE );
for ( ; p < end; p++ ) {
if ( *p == '!' )
p++;
*ptr++ = *p;
}
continue;
}
/**/myassert( ( ptr + i ) < parmstrings + PARMSTRINGSIZE );
memcpy( ptr, tokenarray[idx].tokpos, i );
ptr += i;
} /* end for */
*ptr = NULLC;
/* restore input status values */
Token_Count = old_tokencount;
StringBufferEnd = savedStringBuffer;
/* store the macro argument in the parameter array */
if ( macro->sym.mac_vararg && ( parmidx == info->parmcnt - 1 ) ) {
if ( varargcnt == 0 )
mi.parm_array[parmidx] = currparm;
DebugMsg1(("RunMacro(%s.%u[%u]): curr parameter value=>%s<\n", macro->sym.name, parmidx, varargcnt, currparm ));
currparm = ( macro->sym.predefined ? GetAlignedPointer( currparm, ptr - currparm ) : ptr );
/* v2.08: Masm swallows the last trailing comma */
//if ( tokenarray[idx].token == T_COMMA ) {
if ( tokenarray[idx].token == T_COMMA ) {
idx++;
if ( macro->sym.isfunc == FALSE || tokenarray[idx].token != parm_end_delim ) {
parmidx--;
if ( !macro->sym.predefined ) {
*currparm++ = ',';
}
*currparm = NULLC;
}
skipcomma = 0;
}
varargcnt++;
} else if ( *currparm ) {
mi.parm_array[parmidx] = currparm;
DebugMsg1(("RunMacro(%s.%u): curr parameter value=>%s<\n", macro->sym.name, parmidx, currparm ));
currparm = GetAlignedPointer( currparm, ptr - currparm );
} else {
mi.parm_array[parmidx] = "";
DebugMsg1(("RunMacro(%s.%u): curr parameter value=><\n", macro->sym.name, parmidx ));
}
} /*end if */
} /* end for */
/* for macro functions, check for the terminating ')' */
if ( bracket_level >= 0 ) {
if ( tokenarray[idx].token != T_CL_BRACKET ) {
for ( i = idx; idx < Token_Count && tokenarray[idx].token != T_CL_BRACKET; idx++ );
if ( idx == Token_Count ) {
DebugMsg1(("RunMacro(%s): missing ')'\n", macro->sym.name));
EmitError( MISSING_RIGHT_PARENTHESIS );
return( -1 );
} else {
DebugMsg1(("RunMacro(%s): expected ')', found >%s<\n", macro->sym.name, tokenarray[i].tokpos ));
/* v2.09: changed to a warning only (Masm-compatible) */
EmitWarn( 1, TOO_MANY_ARGUMENTS_IN_MACRO_CALL, macro->sym.name, tokenarray[i].tokpos );
}
}
idx++;
//} else if ( tokenarray[idx].token != T_FINAL && *macro->sym.name != NULLC ) { /* v2.08: changed */
} else if ( tokenarray[idx].token != T_FINAL ) {
DebugMsg1(("RunMacro(%s): expected T_FINAL, found >%s<, parmidx=%u\n", macro->sym.name, tokenarray[idx].tokpos, parmidx ));
/* v2.05: changed to a warning. That's what Masm does */
/* v2.09: don't emit a warning if it's a FOR directive
* (in this case, the caller knows better what to do ).
*/
if ( !(mflags & MF_IGNARGS) )
EmitWarn( 1, TOO_MANY_ARGUMENTS_IN_MACRO_CALL, macro->sym.name, tokenarray[idx].tokpos );
//return( -1 );
}
/* a predefined macro func with a function address? */
if ( macro->sym.predefined == TRUE && macro->sym.func_ptr != NULL ) {
mi.parmcnt = varargcnt;
macro->sym.func_ptr( &mi, out, tokenarray );
*is_exitm = TRUE;
return( idx );
}
#if 0
/* check if a (code) label before the macro is to be written
* v2.08: this is the wrong place, because the label is written
* AFTER possible macro functions in the arguments are evaluated.
* Hence this functionality has been moved to ExpandToken().
*/
addprefix = FALSE;
if ( macro->sym.isfunc == FALSE &&
#if MACROLABEL
macro->sym.label == FALSE &&
#endif
label >= 0 && start > label )
addprefix = TRUE;
#endif
mi.localstart = MacroLocals;
MacroLocals += info->localcnt; /* adjust global variable MacroLocals */
/* avoid to use values stored in struct macro_info directly. A macro
* may be redefined within the macro! Hence copy all values that are
* needed later in the while loop to macro_instance!
*/
mi.startline = info->data;
mi.currline = NULL;
mi.parmcnt = info->parmcnt;
/* v2.03: no processing if macro has no lines */
/* v2.08: addprefix is obsolete */
//if ( mi.currline || addprefix ) {
if ( mi.startline ) {
struct input_status oldstat;
int oldifnesting;
int cntgoto;
DebugMsg1(("RunMacro(%s): enter assembly loop, macro level=%u\n", macro->sym.name, MacroLevel+1 ));
/* v2.04: this listing is too excessive */
//if ( ModuleInfo.list && ( ModuleInfo.list_macro == LM_LISTMACROALL || MacroLevel == 0 ) )
//if ( MacroLevel == 0 && macro->sym.isfunc == FALSE && *macro->sym.name )
if ( macro->sym.isfunc == FALSE && *macro->sym.name )
LstWriteSrcLine();
if ( !( mflags & MF_NOSAVE ) )
tokenarray = PushInputStatus( &oldstat );
/*
* move the macro instance onto the file stack!
* Also reset the current linenumber!
*/
mi.macro = &macro->sym;
PushMacro( &mi );
MacroLevel++;
oldifnesting = GetIfNestLevel(); /* v2.10 */
cntgoto = 0; /* v2.10 */
/* Run the assembler until we hit EXITM or ENDM.
* Also handle GOTO and macro label lines!
* v2.08 no need anymore to check the queue level
* v2.11 GetPreprocessedLine() replaced by GetTextLine()
* and PreprocessLine().
*/
while ( GetTextLine( CurrSource ) ) {
if ( PreprocessLine( CurrSource, tokenarray ) == 0 )
continue;
/* skip macro label lines */
if ( tokenarray[0].token == T_COLON ) {
/* v2.05: emit the error msg here, not in StoreMacro() */
if ( tokenarray[1].token != T_ID )
EmitErr( SYNTAX_ERROR_EX, tokenarray[0].tokpos );
else if ( tokenarray[2].token != T_FINAL )
EmitErr( SYNTAX_ERROR_EX, tokenarray[2].tokpos );
continue;
}
if ( tokenarray[0].token == T_DIRECTIVE ) {
if ( tokenarray[0].tokval == T_EXITM ) {
if ( ModuleInfo.list && ModuleInfo.list_macro == LM_LISTMACROALL )
LstWriteSrcLine();
if ( tokenarray[1].token != T_FINAL ) {
/* v2.05: display error if there's more than 1 argument or
* the argument isn't a text item
*/
if ( tokenarray[1].token != T_STRING || tokenarray[1].string_delim != '<' )
TextItemError( &tokenarray[1] );
else if ( Token_Count > 2 )
EmitErr( SYNTAX_ERROR_EX, tokenarray[2].tokpos );
else if ( out ) { /* return value buffer may be NULL ( loop directives ) */
/* v2.08a: the <>-literal behind EXITM is handled specifically,
* macro operator '!' within the literal is only handled
* if it contains a placeholder (macro argument, macro local ).
*
* v2.09: handle '!' inside literal if ANY expansion occurred.
* To determine text macro or macro function expansion,
* check if there's a literal in the original line.
*/
if ( mi.currline->ph_count || *(mi.currline->line+(tokenarray[1].tokpos-CurrSource)) != '<' ) {
memcpy( out, tokenarray[1].string_ptr, tokenarray[1].stringlen + 1 );
} else {
/* since the string_ptr member has the !-operator stripped, it
* cannot be used. To get the original value of the literal,
* use tokpos.
*/
int len;
len = tokenarray[2].tokpos - (tokenarray[1].tokpos+1);
memcpy( out, tokenarray[1].tokpos+1, len );
while( *(out+len-1) != '>' ) len--;
*(out+len-1) = NULLC;
}
}
}
DebugMsg1(("RunMacro(%s): EXITM, result=>%s<\n", macro->sym.name, out ? out : "NULL" ));
/* v2.10: if a goto had occured, rescan the full macro to ensure that
* the "if"-nesting level is ok.
*/
if ( cntgoto ) {
mi.currline = NULL;
SetLineNumber( 0 );
SetIfNestLevel( oldifnesting );
}
SkipMacro( tokenarray );
*is_exitm = TRUE;
break;
#if 0 /* won't happen anymore */
} else if ( tokenarray[0].tokval == T_ENDM ) {
DebugMsg1(("RunMacro(%s): ENDM\n", macro->sym.name ));
break;
#endif
} else if ( tokenarray[0].tokval == T_GOTO ) {
if ( tokenarray[1].token != T_FINAL ) {
int len = strlen( tokenarray[1].string_ptr );
DebugMsg1(("RunMacro(%s): GOTO %s, MacroLevel=%u\n", macro->sym.name, tokenarray[1].string_ptr, MacroLevel ));
/* search for the destination line */
for( i = 1, lnode = mi.startline; lnode != NULL; lnode = lnode->next, i++ ) {
ptr = lnode->line;
//DebugMsg(("RunMacro(%s): GOTO, scan line >%s< for label >%s<\n", macro->sym.name, ptr, line));
if ( *ptr == ':' ) {
if ( lnode->ph_count ) {
fill_placeholders( StringBufferEnd, lnode->line, mi.parmcnt, mi.localstart, mi.parm_array );
ptr = StringBufferEnd;
}
ptr++;
while( isspace( *ptr )) ptr++;
DebugMsg1(("RunMacro(%s): GOTO, line=>%s<\n", macro->sym.name, ptr ));
/* macro labels are always case-insensitive! */
//if ( ( SymCmpFunc( ptr, tokenarray[1].string_ptr, len ) == 0 ) &&
if ( ( _memicmp( ptr, tokenarray[1].string_ptr, len ) == 0 ) &&
( is_valid_id_char(*(ptr+len) ) == FALSE ) ) {
/* label found! */
break;
}
}
}
if ( !lnode ) {
/* v2.05: display error msg BEFORE SkipMacro()! */
DebugMsg1(("RunMacro(%s): GOTO, label >%s< not found!\n", macro->sym.name, tokenarray[1].string_ptr ));
EmitErr( MACRO_LABEL_NOT_DEFINED, tokenarray[1].string_ptr );
} else {
DebugMsg1(("RunMacro(%s): GOTO, found label >%s<\n", macro->sym.name, ptr));
/* v2.10: rewritten, "if"-nesting-level handling added */
mi.currline = lnode;
SetLineNumber( i );
SetIfNestLevel( oldifnesting );
cntgoto++;
continue;
}
} else {
EmitErr( SYNTAX_ERROR_EX, tokenarray->tokpos );
}
SkipMacro( tokenarray );
break;
}
}
ParseLine( tokenarray );
if ( Options.preprocessor_stdout == TRUE )
WritePreprocessedLine( CurrSource );
/* the macro might contain an END directive.
* v2.08: this doesn't mean the macro is to be cancelled.
* Masm continues to run it and the assembly is stopped
* when the top source level is reached again.
*/
//if ( ModuleInfo.EndDirFound ) {
// SkipMacro( tokenarray );
// *is_exitm = TRUE; /* force loop exit */
// break;
//}
} /* end while */
MacroLevel--;
if ( !(mflags & MF_NOSAVE ) )
PopInputStatus( &oldstat );
/* don't use tokenarray from here on, it's invalid after PopInputStatus() */
#if FASTMEM==0
/* v2.06: free "old" macro line data if macro has been changed
* and isn't in use anymore */
if ( mi.startline != info->data && ( !MacroInUse( macro ) ) ) {
struct srcline *curr;
struct srcline *next;
DebugMsg1(("RunMacro(%s): macro has been changed, releasing old lines\n", macro->sym.name ));
for( curr = mi.startline ; curr; curr = next ) {
next = curr->next;
LclFree( curr );
}
}
#endif
} /* end if */
DebugMsg1(("RunMacro(%s) exit, MacroLevel=%u\n", macro->sym.name, MacroLevel ));
return( idx );
}
/* make room (or delete items) in the token buffer at position <start> */
static void AddTokens( struct asm_tok tokenarray[], int start, int count, int end )
/*********************************************************************************/
{
int i;
if ( count > 0 ) {
for( i = end; i >= start; i-- ) {
tokenarray[i+count] = tokenarray[i];
}
} else if ( count < 0 ) {
for( i = start - count; i <= end; ++i ) {
tokenarray[i+count] = tokenarray[i];
}
}
}
/*
* ExpandText() is called if
* - the evaluation operator '%' has been found as first char of the line.
* - the % operator is found in a macro argument.
* if substitute is TRUE, scanning for the substitution character '&' is active!
* Both text macros and macro functions are expanded!
*/
ret_code ExpandText( char *line, struct asm_tok tokenarray[], unsigned int substitute )
/*************************************************************************************/
{
char *pSrc;
char *pDst;
char *pIdent;
int lvl;
bool is_exitm;
int old_tokencount = Token_Count;
char *old_stringbufferend = StringBufferEnd;
char quoted_string = 0;
char macro_proc = FALSE;
//char *pStart;
ret_code rc;
struct asym *sym;
char *sp[MAX_TEXTMACRO_NESTING];
DebugMsg1(("ExpandText(line=>%s<, subst=%u ) enter\n", line, substitute ));
sp[0] = line;
pDst = StringBufferEnd;
StringBufferEnd += MAX_LINE_LEN;
rc = NOT_ERROR;
for ( lvl = 0; lvl >= 0; lvl-- ) {
pSrc = sp[lvl];
while ( *pSrc ) {
if( is_valid_id_first_char( *pSrc ) && ( substitute != 0 || quoted_string == 0 ) ) {
pIdent = pDst;
do {
*pDst++ = *pSrc++;
} while ( is_valid_id_char( *pSrc ));
*pDst = NULLC;
sym = SymSearch( pIdent );
#ifdef DEBUG_OUT
if ( sym && ( sym->state == SYM_TMACRO || sym->state == SYM_MACRO ) ) {
DebugMsg1(( "ExpandText: symbol found: %s, %s, defined=%u, *pDst-1=%c\n", sym->name, sym->state == SYM_TMACRO ? "SYM_TMACRO" : "SYM_MACRO", sym->isdefined, *(pDst-1) ));
}
#endif
if ( sym && sym->isdefined == TRUE ) {
if ( sym->state == SYM_TMACRO ) {
/* v2.08: no expansion inside quoted strings without & */
if ( quoted_string && *(pIdent-1) != '&' && *pSrc != '&' )
continue;
if ( substitute ) {
if ( *(pIdent-1) == '&' )
pIdent--;
if ( *pSrc == '&' )
pSrc++;
} else if ( pIdent > old_stringbufferend && *(pIdent-1) == '%' )
pIdent--;
sp[lvl++] = pSrc;
pSrc = StringBufferEnd;
//StringBufferEnd = GetAlignedPointer( pSrc, GetLiteralValue( pSrc, sym->string_ptr ) );
strcpy( pSrc, sym->string_ptr );
StringBufferEnd = GetAlignedPointer( pSrc, strlen( pSrc ) );
DebugMsg1(("ExpandText: %s replaced by >%s<\n", sym->name, pSrc ));
pDst = pIdent;
rc = STRING_EXPANDED;
} else if ( sym->state == SYM_MACRO && sym->isfunc == TRUE ) {
/* expand macro functions. */
char *p = pSrc;
int i;
while ( isspace(*p) ) p++;
/* no macro function invokation if the '(' is missing! */
if ( *p == '(' ) {
int j;
int cnt;
i = Token_Count + 1;
Token_Count = Tokenize( p, i, tokenarray, TOK_RESCAN );
for ( j = i, cnt = 0; j < Token_Count; j++ ) {
if ( tokenarray[j].token == T_OP_BRACKET )
cnt++;
else if ( tokenarray[j].token == T_CL_BRACKET ) {
cnt--;
if ( cnt == 0 ) {
j++;
break;
}
}
}
/* don't substitute inside quoted strings if there's no '&' */
if ( quoted_string && *(pIdent-1) != '&' && tokenarray[j].token != '&' ) {
Token_Count = old_tokencount;
continue;
}
if ( substitute ) {
if ( *(pIdent-1) == '&' )
pIdent--;
} else if ( pIdent > old_stringbufferend && *(pIdent-1) == '%' )
pIdent--;
//*StringBufferEnd = NULLC;
i = RunMacro( (struct dsym *)sym, i, tokenarray, pDst, 0, &is_exitm );
Token_Count = old_tokencount;
DebugMsg1(( "ExpandText: back from RunMacro(%s), rc=%u, text returned=>%s<, rest=>%s<\n", sym->name, i, pDst, i >= 0 ? tokenarray[i].tokpos : "" ));
if ( i == -1 ) {
return( ERROR );
}
pSrc = tokenarray[i-1].tokpos + strlen( tokenarray[i-1].string_ptr );
if ( substitute && *pSrc == '&' )
pSrc++;
sp[lvl++] = pSrc;
pSrc = StringBufferEnd;
cnt = strlen( pDst );
memcpy( pSrc, pDst, cnt + 1 );
StringBufferEnd = GetAlignedPointer( pSrc, cnt );
pDst = pIdent;
rc = STRING_EXPANDED;
}
} else if ( sym->state == SYM_MACRO ) {
macro_proc = TRUE;
}
if ( lvl == MAX_TEXTMACRO_NESTING ) {
DebugMsg(("ExpandText(line=>%s<) error exit\n", line));
EmitError( MACRO_NESTING_LEVEL_TOO_DEEP );
break;
}
}
} else {
if ( *pSrc == '"' || *pSrc == '\'' ) {
if ( quoted_string == 0 )
quoted_string = *pSrc;
else if ( *pSrc == quoted_string )
quoted_string = 0;
}
*pDst++ = *pSrc++;
}
} /* end while */
}
*pDst++ = NULLC;
StringBufferEnd = old_stringbufferend;
if ( rc == STRING_EXPANDED ) {
memcpy( line, StringBufferEnd, pDst - StringBufferEnd );
DebugMsg1(("ExpandText: expanded line=>%s<\n", line));
}
if ( substitute ) {
if ( rc == STRING_EXPANDED ) {
Token_Count = Tokenize( tokenarray[0].tokpos, 0, tokenarray, TOK_RESCAN );
}
if ( rc == STRING_EXPANDED || macro_proc ) {
return( ExpandLine( tokenarray[0].tokpos, tokenarray ) );
}
}
return( rc );
}
/* replace text macros and macro functions by their values, recursively
* outbuf in: text macro or macro function value
* outbuf out: expanded value
* equmode: if 1, don't expand macro functions
*/
static ret_code ExpandTMacro( char * const outbuf, struct asm_tok tokenarray[], int equmode, int level )
/******************************************************************************************************/
{
int old_tokencount = Token_Count;
int i;
char expanded = TRUE;
int len;
bool is_exitm;
struct asym *sym;
//char lvalue[MAX_LINE_LEN]; /* holds literal value */
char buffer[MAX_LINE_LEN];
DebugMsg1(("ExpandTMacro(text=>%s< equm=%u lvl=%u) enter\n", outbuf, equmode, level ));
if ( level >= MAX_TEXTMACRO_NESTING ) {
return( EmitError( MACRO_NESTING_LEVEL_TOO_DEEP ) );
}
while ( expanded == TRUE ) {
i = old_tokencount + 1;
Token_Count = Tokenize( outbuf, i, tokenarray, TOK_RESCAN );
expanded = FALSE;
for ( ; i < Token_Count; i++ ) {
if ( tokenarray[i].token == T_ID ) {
sym = SymSearch( tokenarray[i].string_ptr );
/* expand macro functions */
if ( sym && sym->state == SYM_MACRO &&
sym->isdefined == TRUE && sym->isfunc == TRUE &&
tokenarray[i+1].token == T_OP_BRACKET && equmode == FALSE ) {
len = tokenarray[i].tokpos - outbuf;
memcpy( buffer, outbuf, len );
i = RunMacro( (struct dsym *)sym, i+1, tokenarray, buffer+len, 0, &is_exitm );
if ( i < 0 ) {
Token_Count = old_tokencount;
return( ERROR );
}
DebugMsg1(("ExpandTMacro(%u): repl >%s()< by >%s<\n", level, sym->name, buffer+len ));
strcat( buffer+len, tokenarray[i].tokpos );
strcpy( outbuf, buffer );
expanded = TRUE;
/* is i to be decremented here? */
break;
} else if ( sym && sym->state == SYM_TMACRO && sym->isdefined == TRUE ) {
char *p;
len = tokenarray[i].tokpos - outbuf;
memcpy( buffer, outbuf, len );
//GetLiteralValue( buffer+len, sym->string_ptr );
strcpy( buffer+len, sym->string_ptr );
DebugMsg1(("ExpandTMacro(>%s<, %u): calling ExpandTMacro, value >%s<\n", sym->name, level, buffer+len ));
if ( ERROR == ExpandTMacro( buffer + len, tokenarray, equmode, level+1 ) ) {
Token_Count = old_tokencount;
return( ERROR );
}
DebugMsg1(("ExpandTMacro(%u): repl >%s< by >%s<\n", level, sym->name, buffer+len ));
//if ( level || ( tokenarray[i+1].token != T_FINAL && tokenarray[i+1].token != T_COMMA ))
p = tokenarray[i].tokpos + sym->name_size;
//else
// p = tokenarray[i+1].tokpos;
strcat( buffer+len, p );
strcpy( outbuf, buffer );
expanded = TRUE;
break;
}
}
}
}
Token_Count = old_tokencount;
//strcpy( outbuf, lvalue );
return( NOT_ERROR );
}
/* rebuild a source line
* adjust all "pos" values behind the current pos
* - newstring = new value of item i
* - i = token buffer index of item to replace
* - outbuf = start of source line to rebuild
* - oldlen = old length of item i
* - pos_line = position of item in source line
*/
static ret_code RebuildLine( const char *newstring, int i, struct asm_tok tokenarray[], unsigned oldlen, unsigned pos_line, int addbrackets )
/*******************************************************************************************************************************************/
{
char *dest;
const char *src;
unsigned newlen;
unsigned rest = strlen( tokenarray[i].tokpos + oldlen ) + 1;
char buffer[MAX_LINE_LEN];
dest = tokenarray[i].tokpos;
memcpy( buffer, dest + oldlen, rest ); /* save content of line behind item */
newlen = strlen( newstring );
if ( addbrackets ) {
newlen += 2; /* count '<' and '>' */
for ( src = newstring; *src; src++ )
if ( *src == '<' || *src == '>' || *src == '!' ) /* count '!' operator */
newlen++;
}
if ( newlen > oldlen )
if ( ( pos_line + newlen - oldlen + rest ) >= MAX_LINE_LEN ) {
return( EmitErr( EXPANDED_LINE_TOO_LONG, tokenarray[0].tokpos ) );
}
if ( addbrackets ) {
*dest++ = '<';
for ( src = newstring; *src; src++ ) {
if ( *src == '<' || *src == '>' || *src == '!' ) /* count '!' operator */
*dest++ = '!';
*dest++ = *src;
}
*dest++ = '>';
} else {
memcpy( dest, newstring, newlen );
dest += newlen;
}
memcpy( dest, buffer, rest ); /* add rest of line */
/* v2.05: changed '<' to '<=' */
for ( i++; i <= Token_Count; i++ ) {
tokenarray[i].tokpos = tokenarray[i].tokpos - oldlen + newlen;
}
return( NOT_ERROR );
}
/* expand one token
* line: full source line
* *pi: index of token in tokenarray
* equmode: if 1, dont expand macro functions
*/
static ret_code ExpandToken( char *line, int *pi, struct asm_tok tokenarray[], int max, int bracket_flags, int equmode )
/**********************************************************************************************************************/
{
int pos;
int tmp;
int i = *pi;
int size;
int addbrackets = bracket_flags;
char evaluate = FALSE;
//char *p;
bool is_exitm;
struct expr opndx;
struct asym *sym;
ret_code rc = NOT_ERROR;
char buffer[MAX_LINE_LEN];
for ( ; i < max && tokenarray[i].token != T_COMMA; i++ ) {
/* v2.05: the '%' should only be handled as an operator if addbrackets==TRUE,
* which means that the current directive is a preprocessor directive and the
* expected argument is a literal (or text macro).
*/
if ( tokenarray[i].token == T_PERCENT && addbrackets && evaluate == FALSE ) {
evaluate = TRUE;
addbrackets = FALSE;
equmode = FALSE;
pos = i;
DebugMsg1(("ExpandToken: %% found, line=%s\n", tokenarray[i].tokpos ));
continue;
}
if( tokenarray[i].token == T_ID ) {
sym = SymSearch( tokenarray[i].string_ptr );
DebugMsg1(("ExpandToken: testing id >%s< equmode=%u\n", tokenarray[i].string_ptr, equmode ));
/* don't check isdefined flag (which cannot occur in pass one, and this code usually runs
* in pass one only!
*/
//if( sym && sym->isdefined ) {
if( sym ) {
if ( sym->state == SYM_MACRO ) {
tmp = i; /* save index of macro name */
if ( sym->isfunc == TRUE ) {
/* ignore macro functions without a following '(' */
if ( tokenarray[i+1].token != T_OP_BRACKET ) {
DebugMsg1(("ExpandToken(%s): macro function without () - not expanded!\n", sym->name ));
continue;
}
i++;
if ( equmode == TRUE ) {
i++; /* skip '(' */
/* go beyond the ')' */
for ( tmp = 1; i < max; i++ ) {
if ( tokenarray[i].token == T_OP_BRACKET )
tmp++;
else if ( tokenarray[i].token == T_CL_BRACKET ) {
tmp--;
if ( tmp == 0 )
break;
}
}
i--;
continue;
}
//DebugMsg1(("ExpandToken: macro function %s to be expanded\n", sym->name ));
i = RunMacro( (struct dsym *)sym, i, tokenarray, buffer,
#if MACROLABEL
(tmp == 1 ? MF_LABEL : 0),
#else
0,
#endif
&is_exitm );
if ( i == -1 )
return( ERROR );
DebugMsg1(("ExpandToken(%s, addbr=%u): macro function expanded to >%s<\n", sym->name, addbrackets, buffer));
/* expand text, but don't if macro was at position 0 (might be a text macro definition directive */
/* v2.09: don't expand further if addbrackets is set */
if ( tmp && (!addbrackets) && ( ERROR == ExpandTMacro( buffer, tokenarray, equmode, 0 ) ) )
return( ERROR );
/* get size of string to replace ( must be done before AddTokens() */
size = ( tokenarray[i-1].tokpos + 1) - tokenarray[tmp].tokpos;
AddTokens( tokenarray, tmp+1, tmp+1 - i, Token_Count );
Token_Count += (tmp+1) - i;
if ( Token_Count < max ) /* take care not to read beyond T_FINAL */
max = Token_Count;
if ( ERROR == RebuildLine( buffer, tmp, tokenarray,
size, tokenarray[tmp].tokpos - line, addbrackets ) )
return( ERROR );
rc = STRING_EXPANDED;
i = tmp;
} else {
/* a macro proc is expanded at pos 0 or pos 2
* (or at pos 1 if sym->label is on)
*/
if ( i == 0 ||
( i == 2 && ( tokenarray[1].token == T_COLON ||
tokenarray[1].token == T_DBL_COLON ))
#if MACROLABEL
|| ( i == 1 && sym->label )
#endif
)
;
else {
DebugMsg1(("ExpandToken(%s): macro proc at pos %u NOT expanded\n", sym->name, i ));
#if 1 /* v2.03: no error, just don't expand! */
continue;
#else
return( EmitErr( SYNTAX_ERROR_EX, sym->name ) );
#endif
}
/* v2.08: write optional code label. This has been
* moved out from RunMacro().
*/
if ( i == 2 ) {
if ( ERROR == WriteCodeLabel( line, tokenarray ) )
return( ERROR );
}
//buffer[0] = NULLC; /* nothing should be returned, just to be safe */
DebugMsg1(("ExpandToken(%s): macro proc to be expanded\n", sym->name ));
i = RunMacro( (struct dsym *)sym, i+1, tokenarray, NULL,
#if MACROLABEL
MF_NOSAVE | (i == 1 ? MF_LABEL : 0),
#else
MF_NOSAVE,
#endif
&is_exitm );
DebugMsg1(("ExpandToken(%s): macro proc called\n", sym->name));
if ( i == -1 )
return( ERROR );
#if 0
/* it's possible to "hide" the EXITM directive when the
* macro lines are read. But it's not useful for macro
* procs to check if exitm has been executed, because
* Masm simply will ignore anything that's "returned".
*/
if ( is_exitm ) {
DebugMsg(("ExpandToken: EXITM in macro procedure!\n" ));
strcat( buffer, tokenarray[tmp].tokpos );
strcpy( line, buffer );
rc = STRING_EXPANDED;
} else
#endif
return( EMPTY ); /* no further processing */
break;
}
} else if( sym->state == SYM_TMACRO ) {
//GetLiteralValue( buffer, sym->string_ptr );
strcpy( buffer, sym->string_ptr );
if ( ERROR == ExpandTMacro( buffer, tokenarray, equmode, 0 ) )
return( ERROR );
DebugMsg1(("ExpandToken(%s, addbr=%u): value >%s< expanded to >%s<\n", sym->name, addbrackets, sym->string_ptr, buffer ));
if ( ERROR == RebuildLine( buffer, i, tokenarray, strlen( tokenarray[i].string_ptr ),
tokenarray[i].tokpos - line, addbrackets ) )
return( ERROR );
rc = STRING_EXPANDED;
DebugMsg1(("ExpandToken(%s): rest after expansion: %s\n", sym->name, tokenarray[i].tokpos ));
}
}
}
}
*pi = i;
if ( evaluate ) {
int old_tokencount = Token_Count;
if ( i == (pos+1) ) { /* just a single %? */
opndx.value = 0;
i = pos;
} else {
i = pos++;
tmp = tokenarray[*pi].tokpos - tokenarray[pos].tokpos;
memcpy( buffer, tokenarray[pos].tokpos, tmp );
buffer[tmp] = NULLC;
tmp = old_tokencount + 1;
Token_Count = Tokenize( buffer, tmp, tokenarray, TOK_RESCAN );
if ( EvalOperand( &tmp, tokenarray, Token_Count, &opndx, EXPF_NOUNDEF ) == ERROR )
opndx.value = 0; /* v2.09: assume value 0, don't return with ERROR */
else if ( opndx.kind != EXPR_CONST ) {
/* v2.09: with flag EXPF_NOUNDEF, EvalOperand() will have returned
* with error if there's an undefined symbol involved
*/
//if ( opndx.sym && opndx.sym->state == SYM_UNDEFINED )
// EmitErr( SYMBOL_NOT_DEFINED, opndx.sym->name );
//else {
DebugMsg(("ExpandToken: 'constant expected' error\n"));
EmitError( CONSTANT_EXPECTED );
//}
//return( ERROR );
opndx.value = 0; /* assume value 0 */
}
Token_Count = old_tokencount;
}
#if TEVALUE_UNSIGNED
/* v2.03: Masm compatible: returns an unsigned value */
myltoa( opndx.value, StringBufferEnd, ModuleInfo.radix, FALSE, FALSE );
#else
myltoa( opndx.value, StringBufferEnd, ModuleInfo.radix, opndx.hvalue < 0, FALSE );
#endif
/* v2.05: get size of string to be "replaced" */
tmp = tokenarray[*pi].tokpos - tokenarray[i].tokpos;
DebugMsg1(("ExpandToken: curr pos=%u, start expr=%u, expr size=%d\n", *pi, i, tmp ));
//tokenarray[i].token = T_STRING;
tokenarray[i].string_ptr = StringBufferEnd;
AddTokens( tokenarray, i+1, i+1 - *pi, Token_Count );
Token_Count += (i+1) - *pi;
if ( ERROR == RebuildLine( StringBufferEnd, i, tokenarray,
tmp, tokenarray[i].tokpos - line, bracket_flags ) )
return( ERROR );
rc = STRING_EXPANDED;
}
return( rc );
}
/* used by EQU ( may also be used by directives flagged with DF_NOEXPAND
* if they have to partially expand their arguments ).
* equmode: 1=don't expand macro functions
*/
int ExpandLineItems( char *line, int i, struct asm_tok tokenarray[], int addbrackets, int equmode )
/*************************************************************************************************/
{
int k;
int lvl;
int tmp;
ret_code rc;
for ( lvl = 0; ; lvl++ ) {
rc = NOT_ERROR;
for( k = i; k < Token_Count; ) {
tmp = ExpandToken( line, &k, tokenarray, Token_Count, addbrackets, equmode );
if ( tmp == ERROR )
return( lvl );
if ( tmp == STRING_EXPANDED )
rc = STRING_EXPANDED;
if ( tokenarray[k].token == T_COMMA )
k++;
}
if ( rc == NOT_ERROR )
break;
/* expansion happened, re-tokenize and continue! */
Token_Count = Tokenize( line, i, tokenarray, TOK_RESCAN );
if ( lvl == MAX_TEXTMACRO_NESTING ) {
EmitError( MACRO_NESTING_LEVEL_TOO_DEEP );
break;
}
}
return( lvl );
}
/* v2.09: added, expand literals for structured data items.
* since 2.09, initialization of structure members is no longer
* done by generated code, but more directly inside data.c.
* This improves Masm-compatibility, but OTOH requires to expand
* the literals explicitly.
*/
void ExpandLiterals( int i, struct asm_tok tokenarray[] )
/*******************************************************/
{
int cnt = 0;
int idx;
/* count non-empty literals */
for ( idx = i; idx < Token_Count; idx++ ) {
if ( tokenarray[idx].token == T_STRING &&
tokenarray[idx].stringlen &&
( tokenarray[idx].string_delim == '<' || tokenarray[idx].string_delim == '{' ) ) {
cnt++;
}
}
/* if non-empty literals are found, expand the line. if the line
* was expanded, re-tokenize it.
*/
if ( cnt ) {
if ( ExpandText( tokenarray[i].tokpos, tokenarray, FALSE ) == STRING_EXPANDED )
Tokenize( tokenarray[i].tokpos, i, tokenarray, TOK_RESCAN );
}
}
/* scan current line for (text) macros and expand them.
* this is only called when the % operator is not the first item.
*/
ret_code ExpandLine( char *string, struct asm_tok tokenarray[] )
/**************************************************************/
{
int count;
unsigned int bracket_flags; /* flags */
int flags;
int lvl;
int i;
int j;
ret_code rc;
struct asym *sym;
/* filter certain conditions.
* bracket_flags: for (preprocessor) directives that expect a literal
* parameter, the expanded argument has to be enclosed in '<>' again.
*/
DebugMsg1(( "ExpandLine(>%s<) enter\n", string ));
for ( lvl = 0; lvl < MAX_TEXTMACRO_NESTING; lvl++ ) {
bracket_flags = 0;
count = 0;
rc = NOT_ERROR;
i = ( Token_Count > 2 && ( tokenarray[1].token == T_COLON || tokenarray[1].token == T_DBL_COLON ) && tokenarray[2].token == T_DIRECTIVE ) ? 2 : 0;
if ( tokenarray[i].token == T_DIRECTIVE ) {
flags = GetValueSp( tokenarray[i].tokval );
if ( flags & DF_STRPARM ) {
bracket_flags = -1;
/* v2.08 handle .ERRDEF and .ERRNDEF here. Previously
* expansion for these directives was handled in condasm.asm,
* and the directives were flagged as DF_NOEXPAND.
*/
if ( tokenarray[i].dirtype == DRT_ERRDIR ) {
if (tokenarray[i].tokval == T_DOT_ERRDEF || tokenarray[i].tokval == T_DOT_ERRNDEF ) {
if ( i )
rc = ExpandToken( string, &count, tokenarray, 1, FALSE, FALSE );
while ( tokenarray[i].token != T_FINAL && tokenarray[i].token != T_COMMA ) i++;
count = i; /* don't expand the symbol name */
}
}
} else if ( flags & DF_NOEXPAND ) {
/* [ELSE]IF[N]DEF, ECHO, FOR[C]
* .[NO|X]CREF, INCLUDE */
/* don't expand arguments */
return( NOT_ERROR );
}
} else if ( Token_Count > 1 && tokenarray[1].token == T_DIRECTIVE ) {
switch ( tokenarray[1].dirtype ) {
case DRT_CATSTR:
bracket_flags = -1;
count = 2;
break;
case DRT_SUBSTR:
/* syntax: name SUBSTR <literal>, pos [, size] */
bracket_flags = 0x1;
count = 2;
break;
case DRT_SIZESTR:
/* syntax: label SIZESTR literal */
rc = ExpandToken( string, &count, tokenarray, 1, FALSE, FALSE );
bracket_flags = 0x1;
count = 2;
break;
case DRT_INSTR:
/* syntax: label INSTR [number,] literal, literal */
rc = ExpandToken( string, &count, tokenarray, 1, FALSE, FALSE );
/* check if the optional <number> argument is given */
for ( i = 2, count = 0, j = 0; i < Token_Count; i++ ) {
if ( tokenarray[i].token == T_OP_BRACKET )
count++;
else if ( tokenarray[i].token == T_CL_BRACKET )
count--;
else if ( tokenarray[i].token == T_COMMA && count == 0 )
j++;
}
bracket_flags = ( ( j > 1 ) ? 0x6 : 0x3 );
count = 2;
break;
case DRT_MACRO:
sym = SymSearch( tokenarray[0].string_ptr );
/* don't expand macro DEFINITIONs!
* the name is an exception, if it's not the macro itself
*/
if ( sym && sym->state != SYM_MACRO )
rc = ExpandToken( string, &count, tokenarray, 1, FALSE, FALSE );
count = Token_Count; /* stop further expansion */
break;
case DRT_EQU:
/* EQU is a special case. If the - expanded - expression is
* a number, then the value for EQU is numeric. Else the
* expression isn't expanded at all. This effectively makes it
* impossible to expand EQU lines here.
*/
#if 0 /* v2.09: EQU should NEVER be expanded here. See regression test equate20.aso */
sym = SymSearch( tokenarray[0].string_ptr );
if ( sym == NULL || sym->state == SYM_TMACRO )
#endif
return( NOT_ERROR );
}
} else {
/* v2.08: expand the very first token and then ... */
rc = ExpandToken( string, &count, tokenarray, 1, FALSE, FALSE );
if( rc == ERROR || rc == EMPTY )
return( rc );
if ( rc == STRING_EXPANDED ) {
/* ... fully retokenize - the expansion might have revealed a conditional
* assembly directive
*/
Token_Count = Tokenize( string, 0, tokenarray, TOK_DEFAULT );
continue;
}
#if 1 /* v2.10. see regression test equate27.asm */
if ( count == 1 && tokenarray[0].token == T_ID && tokenarray[1].token == T_ID ) {
rc = ExpandToken( string, &count, tokenarray, 2, FALSE, FALSE );
if( rc == ERROR || rc == EMPTY )
return( rc );
if ( rc == STRING_EXPANDED ) {
Token_Count = Tokenize( string, 0, tokenarray, TOK_DEFAULT );
continue;
}
}
#endif
}
/* scan the line from left to right for (text) macros.
* it's currently not quite correct. a macro proc should only
* be evaluated in the following cases:
* 1. it is the first token of a line
* 2. it is the second token, and the first one is an ID
* 3. it is the third token, the first one is an ID and
* the second is a ':' or '::'.
*/
while ( count < Token_Count ) {
int tmp;
int addbrackets;
addbrackets = bracket_flags & 1;
if ( bracket_flags != -1 )
bracket_flags = bracket_flags >> 1;
tmp = ExpandToken( string, &count, tokenarray, Token_Count, addbrackets, FALSE );
if( tmp < NOT_ERROR ) /* ERROR or EMPTY? */
return( tmp );
if ( tmp == STRING_EXPANDED )
rc = STRING_EXPANDED;
if ( tokenarray[count].token == T_COMMA )
count++;
}
if( rc == STRING_EXPANDED ) {
DebugMsg1(( "ExpandLine(%s): expansion occured, retokenize\n", string ));
Token_Count = Tokenize( string, 0, tokenarray, TOK_RESCAN | TOK_LINE );
} else
break;
} /* end for() */
if ( lvl == MAX_TEXTMACRO_NESTING ) {
return( EmitError( MACRO_NESTING_LEVEL_TOO_DEEP ) );
}
DebugMsg1(( "ExpandLine(>%s<) exit, rc=%u, token_count=%u\n", string, rc, Token_Count ));
return( rc );
}