#include "tokenize.h"
#include "fastpass.h"

enum hll_flags {
    HLLF_ELSEOCCURED = 0x01,
#ifdef __EXPANDHLL
    HLLF_ELSEIF      = 0x02,
    HLLF_WHILE       = 0x04,
    HLLF_EXPRESSION  = 0x08,
#endif
};

#ifdef __EXPANDHLL
 #define HllPushLines() HllPushTestLines( hll, tokenarray )
 extern ret_code ExpandToken(char *, int *, struct asm_tok tokenarray[], int, int, int);
#else
 #define HllPushLines() HllPushTestLines( hll )
#endif


static ret_code GetSimpleExpression( struct hll_item *hll, int *i, struct asm_tok tokenarray[], int ilabel, bool is_true, char *buffer, struct hll_opnd *hllop, struct expr *opndx )
/**********************************************************************************************************************************************************************/
{
...
    buffer[0] = NULLC;
    switch ( op ) {
    case COP_EQ:
    case COP_NE:
    case COP_GT:
    case COP_LT:
    case COP_GE:
    case COP_LE:
	/* optimisation: generate 'or EAX,EAX' instead of 'cmp EAX,0' */
	if ((op == COP_EQ || op == COP_NE) &&
	    opndx->kind == EXPR_REG &&
	    opndx->indirect == FALSE &&
	    op2.kind == EXPR_CONST &&
	    op2.value == 0
#ifdef __TESTREGREG
	    ) {
	    if (Options.masm_compat_gencode)
		p = RenderInstr(buffer, "or", opndx, op1_pos, op1_end, op1_pos, op1_end, tokenarray);
	    else
		p = RenderInstr(buffer, "test", opndx, op1_pos, op1_end, op1_pos, op1_end, tokenarray);
#else
	    && Options.masm_compat_gencode) {
	    p = RenderInstr( buffer, "or", opndx, op1_pos, op1_end, op1_pos, op1_end, tokenarray );
#endif
	} else {
	    p = RenderInstr( buffer, "cmp", opndx, op1_pos, op1_end, op2_pos, op2_end, tokenarray );
	}

	hllop->lastjmp = p;

	if ( IS_SIGNED( opndx->mem_type ) || IS_SIGNED( op2.mem_type ) )
	    issigned = TRUE;
	else
	    issigned = FALSE;

	switch ( op ) {
	case COP_EQ:
	    instr = 'z';
	    neg = !is_true;
	    break;
	case COP_NE:
	    instr = 'z';
	    neg = is_true;
	    break;
	case COP_GT:
	    instr = ( issigned ? 'g' : 'a' );
	    neg = !is_true;
	    break;
	case COP_LT:
	    instr = ( issigned ? 'l' : 'b' );
	    neg = !is_true;
	    break;
	case COP_GE:
	    instr = ( issigned ? 'l' : 'b' );
	    neg = is_true;
	    break;
	case COP_LE:
	    instr = ( issigned ? 'g' : 'a' );
	    neg = is_true;
	    break;
	}
	RenderJcc( p, instr, neg, label );
	break;
    case COP_ANDB:
	p = RenderInstr( buffer, "test", opndx, op1_pos, op1_end, op2_pos, op2_end, tokenarray );
	hllop->lastjmp = p;
	RenderJcc( p, 'e', is_true, label );
	break;
    case COP_NONE:
	switch ( opndx->kind ) {
	case EXPR_REG:
	    if (opndx->indirect == FALSE) {
#ifdef __TESTREGREG
	    	if (Options.masm_compat_gencode) {
		    p = RenderInstr( buffer, "or", opndx, op1_pos, op1_end, op1_pos, op1_end, tokenarray);
	    	} else {
		    p = RenderInstr( buffer, "test", opndx, op1_pos, op1_end, op1_pos, op1_end, tokenarray);
		}
#else
		p = RenderInstr( buffer, "and", opndx, op1_pos, op1_end, op1_pos, op1_end, tokenarray );
#endif
		hllop->lastjmp = p;
		RenderJcc( p, 'z', is_true, label );
		break;
	    }
	    /* no break */
	case EXPR_ADDR:
	    p = RenderInstr( buffer, "cmp", opndx, op1_pos, op1_end, EMPTY, 0, tokenarray );
	    hllop->lastjmp = p;
	    RenderJcc( p, 'z', is_true, label );
	    break;
	case EXPR_CONST:
#if 0
	    /* v2.05: string constant is allowed! */
	    if ( opndx->string != NULL ) {
		EmitError( SYNTAX_ERROR_IN_CONTROL_FLOW_DIRECTIVE );
		return ( ERROR );
	    }
#endif
	    hllop->lastjmp = buffer;
	    if ( ( is_true == TRUE && opndx->value ) ||
		( is_true == FALSE && opndx->value == 0 ) )
		sprintf( buffer, "jmp %s" EOLSTR, label );
	    else
		strcpy( buffer, " " EOLSTR ); /* make sure there is a char */
	    break;
	}
    }
    return( NOT_ERROR );
}

#ifdef __HLLPROCS
static ret_code ExpandProc(struct asm_tok tokenarray[], int i, int store)
{
    int  q,e;
    struct asym *sym;
    char b[MAX_LINE_LEN];

    sym = SymSearch(tokenarray[i].string_ptr);
    if (!sym || !sym->isproc)
    	return 0;

    e = i + 1;
    strcpy(b, "invoke ");
    strcat(b, tokenarray[i].string_ptr);
    if (tokenarray[e].token == T_OP_BRACKET) {
	e++;
	strcat(b, ",");
	while (tokenarray[e].token != T_CL_BRACKET) {
	    if (tokenarray[e].token == T_FINAL)
		break;
	    if (tokenarray[e].token != T_COMMA)
		strcat(b, " ");
	    strcat(b, tokenarray[e].string_ptr);
	    e++;
	}
	if (tokenarray[e].token != T_CL_BRACKET)
	    return ERROR;
	e++;
    }
    if (store) {
    	StoreLine(b, list_pos, 0);
    } else {
    	AddLineQueue(b);
    }
    b[0] = 0;
    for (q = 0; q < i; q++) {
	if (q && tokenarray[q].token != T_COMMA)
	    strcat(b, " ");
	strcat(b, tokenarray[q].string_ptr);
    }
    if (ModuleInfo.Ofssize == USE16)
    	strcat(b, " ax");
    else if (ModuleInfo.Ofssize == USE32)
    	strcat(b, " eax");
    else
    	strcat(b, " rax");
    while (tokenarray[e].token != T_FINAL) {
    	strcat(b, " ");
	strcat(b, tokenarray[e++].string_ptr);
    }
    strcpy(CurrSource, b);
    Token_Count = Tokenize(CurrSource, 0, tokenarray, TOK_DEFAULT);
    return STRING_EXPANDED;
}
#endif

static ret_code EvaluateHllExpression( struct hll_item *hll, int *i, struct asm_tok tokenarray[], int ilabel, bool is_true )
/**************************************************************************************************************************/
{
    struct hll_opnd hllop = {NULL,0};
    struct expr opndx;
    char buffer[MAX_LINE_LEN*2];

    DebugMsg1(("EvaluateHllExpression enter\n"));

#ifdef __EXPANDHLL
    if (hll->flags & HLLF_ELSEIF || hll->flags & HLLF_WHILE) {
	if (!(hll->flags & HLLF_EXPRESSION)) {
		hll->condlines = LineStoreCurr->line;
		hll->flags |= HLLF_EXPRESSION;
		return NOT_ERROR;
	}
	return ERROR;
    } else {
#ifdef __HLLPROCS
	int q;
	for (q = *i; q < Token_Count; q++) {
	    if (tokenarray[q].token == T_ID) {
		if (ExpandProc(tokenarray, q, 0) == STRING_EXPANDED)
		    q = *i;
	    }
	}
#endif
    	buffer[0] = NULLC;
    	if (ERROR == GetExpression(hll, i, tokenarray, ilabel, is_true, buffer, &hllop, &opndx))
	    return( ERROR );
    	if (buffer[0])
	    WriteExprSrc(hll, buffer);
    }
#else
    buffer[0] = NULLC;
    if ( ERROR == GetExpression( hll, i, tokenarray, ilabel, is_true, buffer, &hllop, &opndx ) )
	return( ERROR );
    if ( buffer[0] ) {
	WriteExprSrc( hll, buffer );
    }
#endif
    if ( hll->condlines != NULL && *hll->condlines == EOLCHAR ) {
	EmitError( SYNTAX_ERROR_IN_CONTROL_FLOW_DIRECTIVE );
	return( ERROR );
    }
    return( NOT_ERROR );
}

/* write ASM test lines */

#ifdef __EXPANDHLL

/* item of a line queue */

struct line_list {
    struct line_list *next;
#ifdef DEBUG_OUT
    char lineno;
#endif
    char line[1];
};

struct input_queue {
    struct line_list    *head;
    struct line_list    *tail;
};

static ret_code HllPushTestLines( struct hll_item *hll, struct asm_tok tokenarray[])
#else
static ret_code HllPushTestLines( struct hll_item *hll )
#endif
/******************************************************/
{
    char *p = hll->condlines;
    char *p2;
    char buffer[MAX_LINE_LEN];
#ifdef __EXPANDHLL
    int result,q,i;
    bool x;
    struct line_item *lp,*cl;
    uint_32 lpos;
#endif
    DebugMsg1(("HllPushTestLines enter\n"));

#ifdef __EXPANDHLL
    if (hll->flags & HLLF_EXPRESSION) {
	if (hll->flags & HLLF_WHILE) {
	    x = TRUE;
	    i = LSTART;
	    LineStoreCurr->line[0] = ';';
	} else if (hll->flags & HLLF_ELSEIF) {
	    x = FALSE;
	    i = LTEST;
	} else {
	    return ERROR;
	}
	p = hll->condlines;
	hll->condlines = NULL;
	hll->flags &= ~(HLLF_ELSEIF | HLLF_WHILE | HLLF_EXPRESSION);
	p2 = ModuleInfo.g.line_queue->head->line;
	if (x == FALSE) {
	    strcpy(CurrSource, p2);
	    StoreLine(CurrSource, list_pos, 0);
	    p2 = ModuleInfo.g.line_queue->tail->line;
	}
	strcpy(CurrSource, p2);
	p2[6] = 0;
	CreateLabel(p2, MT_NEAR, NULL, 1); /* @@ error */
	StoreLine(CurrSource, list_pos, 0);
	DeleteLineQueue();
	strcpy(CurrSource, p);
	*p = ';';
	Token_Count = Tokenize(CurrSource, 0, tokenarray, TOK_DEFAULT);
	q = 1;
	result = ExpandToken(CurrSource, &q, tokenarray, Token_Count, 0, 0);
	if (result == STRING_EXPANDED) {
	    Token_Count = Tokenize(CurrSource, 0, tokenarray, TOK_DEFAULT);
	} else if (result == ERROR) {
	    return ERROR;
	}
#ifdef __HLLPROCS
	for (q = 1; q < Token_Count; q++) {
	    if (tokenarray[q].token == T_ID) {
		if (ExpandProc(tokenarray, q, 1) == STRING_EXPANDED)
		    q = 1;
	    }
	}
#endif
	if (x) {
	    StoreLine(".endw", list_pos, 0);
	    strcpy(p, CurrSource); /* @@ !!memory!! 512? */
	} else {
	    StoreLine(CurrSource, list_pos, 0);
	}
	q = 1;
	if (ERROR == EvaluateHllExpression(hll, &q, tokenarray, i, x))
	    return(ERROR);
	p = hll->condlines;
    }
#endif
    if ( !p )
	return( ERROR );
    while ( *p ) {
	if (*p == ' ') p++; /* there might be lines with 1 ' ' only! */
	for ( p2=buffer; *p && ( *p != EOLCHAR );)
	    *p2++ = *p++;
	*p2 = NULLC;
	if ( *p == EOLCHAR )
	    p++;
	if ( *buffer )
	    AddLineQueue( buffer );
    }
    LclFree( hll->condlines );
    hll->condlines = NULL;
    return( NOT_ERROR );
}

ret_code HllStartDir( int i, struct asm_tok tokenarray[] )
/********************************************************/
{
...
    hll->condlines = NULL;
#ifdef __EXPANDHLL
    hll->flags = 0;
#endif
...
    switch ( cmd ) {
    case T_DOT_IF:
	hll->cmd = HLL_IF;
#ifndef __EXPANDHLL
    	hll->flags = 0;
#endif
...
	break;
    case T_DOT_WHILE:
    case T_DOT_REPEAT:
	/* create the label to loop start */
	hll->labels[LSTART] = GetHllLabel();
	hll->labels[LEXIT] = GetHllLabel();
	if ( cmd == T_DOT_WHILE ) {
	    hll->cmd = HLL_WHILE;
	    if ( tokenarray[i].token != T_FINAL ) {
#ifdef __EXPANDHLL
		if (Parse_Pass == PASS_1) {
		    hll->flags |= HLLF_WHILE;
		    if (tokenarray[i+1].token == T_FINAL &&
		    	tokenarray[i].string_ptr[0] == '1' &&
		    	tokenarray[i].string_ptr[1] == 0)
		    	hll->flags = 0;
		}
#endif
...
}

ret_code HllEndDir( int i, struct asm_tok tokenarray[] )
/******************************************************/
{
...
    case T_DOT_ENDW:
	if ( hll->cmd != HLL_WHILE ) {
	    DebugMsg(("HllEndDir: no .WHILE on the hll stack\n"));
	    EmitErr( BLOCK_NESTING_ERROR, tokenarray[i].string_ptr );
	    return( ERROR );
	}
#ifdef __EXPANDHLL
	cmd = (hll->flags & HLLF_EXPRESSION);
	if (Parse_Pass == PASS_1)
#endif
	/* create test label  */
	if ( hll->labels[LTEST] > 0 ) {
	    AddLineQueueX( "%s" LABELQUAL, GetLabelStr( hll->labels[LTEST], buff ) );
	    DebugMsg(("HllEndDir: created: %s" LABELQUAL "\n", GetLabelStr( hll->labels[LTEST], buff ) ));
	}
	HllPushLines();
#ifdef __EXPANDHLL
	    if (cmd) i = Token_Count - 1;
#endif
	AddLineQueueX( "%s" LABELQUAL, GetLabelStr( hll->labels[LEXIT], buff ) );
	i++;
	break;
    case T_DOT_UNTILCXZ:
	if ( hll->cmd != HLL_REPEAT ) {
	    DebugMsg(("HllEndDir: no .REPEAT on the hll stack\n"));
	    EmitErr( BLOCK_NESTING_ERROR, tokenarray[i].string_ptr );
	    return( ERROR );
	}
	AddLineQueueX( "%s" LABELQUAL, GetLabelStr( hll->labels[LTEST], buff ) );

	i++;
	/* read in optional (simple) expression */
	if ( tokenarray[i].token != T_FINAL ) {
	    if ( ERROR == EvaluateHllExpression( hll, &i, tokenarray, LSTART, FALSE ) ) {
		rc = ERROR;
		break;
	    }
	    if ( CheckCXZLines( hll ) == ERROR ) {
		EmitError( EXPR_TOO_COMPLEX_FOR_UNTILCXZ );
		rc = ERROR;
		break;
	    }
	    /* write condition lines */
	    HllPushLines();
	} else {
#ifdef __UNTILCXZ
	    if (Options.masm_compat_gencode) {
		AddLineQueueX(" loop %s", GetLabelStr(hll->labels[LSTART], buff));
	    } else {
	    	char ecx[16];
	    	if (ModuleInfo.Ofssize == USE16)
    		    strcpy(ecx, "cx");
	    	else if (ModuleInfo.Ofssize == USE32)
    		    strcpy(ecx, "ecx");
	    	else
    		    strcpy(ecx, "rcx");
	    	AddLineQueueX( " dec %s", ecx );
	    	AddLineQueueX( " jnz %s", GetLabelStr( hll->labels[LSTART], buff ) );
	    }
#else
	    AddLineQueueX( " loop %s", GetLabelStr( hll->labels[LSTART], buff ) );
#endif
	}
...
}

ret_code HllExitDir( int i, struct asm_tok tokenarray[] )
/*******************************************************/
{
    struct hll_item     *hll;
    ret_code    	rc = NOT_ERROR;
    char		*savedlines;
#ifdef __WATCOMC__
    enum hll_flags      savedflags;  	/* v2.08: added */
#else
    char		savedflags;
#endif
    enum hll_cmd	savedcmd;
    int 		cmd = tokenarray[i].tokval;
    char buff[16];

    DebugMsg1(("HllExitDir(%s) enter\n", tokenarray[i].string_ptr ));

    hll = HllStack;

    if ( hll == NULL ) {
	DebugMsg(("HllExitDir stack error\n"));
	EmitError( DIRECTIVE_MUST_BE_IN_CONTROL_BLOCK );
	return( ERROR );
    }

    NewLineQueue();

    switch ( cmd ) {
    case T_DOT_ELSEIF:
#ifdef __EXPANDHLL
    if (Parse_Pass == PASS_1)
	hll->flags |= HLLF_ELSEIF;
#endif
    case T_DOT_ELSE:
	if ( hll->cmd != HLL_IF ) {
	    DebugMsg(("HllExitDir(%s): labels[LTEST]=%X\n", tokenarray[i].string_ptr, hll->labels[LTEST]));
	    EmitErr( BLOCK_NESTING_ERROR, tokenarray[i].string_ptr );
	    return( ERROR );
	}
	/* v2.08: check for multiple ELSE clauses */
	if ( hll->flags & HLLF_ELSEOCCURED ) {
	    EmitError( DOT_ELSE_CLAUSE_ALREADY_OCCURED_IN_THIS_DOT_IF_BLOCK );
	    return( ERROR );
	}

	/* the "labels[LEXIT]" label is only needed if an .ELSE branch exists.
	 That's why it is created delayed.
	 */
	if ( hll->labels[LEXIT] == 0 )
	    hll->labels[LEXIT] = GetHllLabel();
#ifdef __EXPANDHLL
	if (hll->flags & HLLF_ELSEIF || cmd == T_DOT_ELSE)
#endif
	AddLineQueueX( " jmp %s", GetLabelStr( hll->labels[LEXIT], buff ) );
	if ( hll->labels[LTEST] > 0 ) {
#ifdef __EXPANDHLL
	if (hll->flags & HLLF_ELSEIF || cmd == T_DOT_ELSE)
#endif
	    AddLineQueueX( "%s" LABELQUAL, GetLabelStr( hll->labels[LTEST], buff ) );
	    hll->labels[LTEST] = 0;
	}
	i++;
	if ( cmd == T_DOT_ELSEIF ) {
	    /* create new labels[LTEST] label */
	    hll->labels[LTEST] = GetHllLabel();
	    if ( ERROR == EvaluateHllExpression( hll, &i, tokenarray, LTEST, FALSE ) ) {
		rc = ERROR;
		break;
	    }
#ifdef __EXPANDHLL
	    cmd = (hll->flags & HLLF_EXPRESSION);
	    rc =
#endif
	    HllPushLines();
#ifdef __EXPANDHLL
	    if (cmd) i = Token_Count;
#endif
	} else
	    hll->flags |= HLLF_ELSEOCCURED;
	break;
...
}

