/* "p2c", a Pascal to C translator.
   Copyright (C) 1989 David Gillespie.
   Author's address: daveg@csvax.caltech.edu; 256-80 Caltech/Pasadena CA 91125.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation (any version).

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; see the file COPYING.  If not, write to
the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */

#define PROTO_EXPR3_C
#include "trans.h"

#define ISCONST(kind) ((kind)==EK_CONST || (kind)==EK_LONGCONST)
#define MOVCONST(ex) (ISCONST((ex)->kind) && (ex)->val.type->kind != TK_STRING)
#define COMMUTATIVE (kind != EK_COMMA && type->kind != TK_REAL)

int ischartype(ex)
Expr *ex;
{
    if (ord_type(ex->val.type)->kind == TK_CHAR)
	return 1;
    if (true_type(ex)->kind == TK_CHAR)
	return 1;
    if (ISCONST(ex->kind) && ex->nargs > 0 &&
	ex->args[0]->kind == EK_NAME &&
	ex->args[0]->val.s[0] == '\'')
	return 1;
    return 0;
}

Expr *commute(a, b, kind)
Expr *a, *b;
enum exprkind kind;
{
    int i, di;
    Type *type;

    if (debug>2) { fprintf(outf,"commute("); dumpexpr(a); fprintf(outf,", "); dumpexpr(b); fprintf(outf,")\n"); }
#if 1
    type = promote_type_bin(a->val.type, b->val.type);
#else
    type = a->val.type;
    if (b->val.type->kind == TK_REAL)
        type = b->val.type;
#endif
    if (MOVCONST(a) && !MOVCONST(b) && COMMUTATIVE)
        swapexprs(a, b);                /* put constant last */
    if (a->kind == kind) {
        di = (MOVCONST(a->args[a->nargs-1]) && COMMUTATIVE) ? -1 : 0;
        if (b->kind == kind) {
            for (i = 0; i < b->nargs; i++)
                insertarg(&a, a->nargs + di, b->args[i]);
            FREE(b);
        } else
            insertarg(&a, a->nargs + di, b);
        a->val.type = type;
    } else if (b->kind == kind) {
        if (MOVCONST(a) && COMMUTATIVE)
            insertarg(&b, b->nargs, a);
        else
            insertarg(&b, 0, a);
        a = b;
        a->val.type = type;
    } else {
        a = makeexpr_bin(kind, type, a, b);
    }
    if (debug>2) { fprintf(outf,"commute returns "); dumpexpr(a); fprintf(outf,"\n"); }
    return a;
}


Expr *makeexpr_plus(a, b)
Expr *a, *b;
{
    int i, j, k;
    Type *type;

    if (debug>2) { fprintf(outf,"makeexpr_plus("); dumpexpr(a); fprintf(outf,", "); dumpexpr(b); fprintf(outf,")\n"); }
    if (!a)
        return b;
    if (!b)
        return a;
    if (a->kind == EK_NEG && a->args[0]->kind == EK_PLUS)
        a = neg_inside_sum(grabarg(a, 0));
    if (b->kind == EK_NEG && b->args[0]->kind == EK_PLUS)
        b = neg_inside_sum(grabarg(b, 0));
    a = commute(enum_to_int(a), enum_to_int(b), EK_PLUS);
    type = NULL;
    for (i = 0; i < a->nargs; i++) {
        if (ord_type(a->args[i]->val.type)->kind == TK_CHAR ||
            a->args[i]->val.type->kind == TK_POINTER ||
            a->args[i]->val.type->kind == TK_STRING) {   /* for string literals */
            if (type == ord_type(a->args[i]->val.type))
                type = tp_integer;   /* 'z'-'a' and p1-p2 are integers */
            else
                type = ord_type(a->args[i]->val.type);
        }
    }
    if (type)
        a->val.type = type;
    for (i = 0; i < a->nargs && !ISCONST(a->args[i]->kind); i++) ;
    if (i < a->nargs-1) {
        for (j = i+1; j < a->nargs; j++) {
            if (ISCONST(a->args[j]->kind)) {
                if ((ord_type(a->args[i]->val.type) == ord_type(a->args[j]->val.type) ||
		     ord_type(a->args[i]->val.type)->kind == TK_INTEGER ||
		     ord_type(a->args[j]->val.type)->kind == TK_INTEGER) &&
		    (!ischartype(a->args[i]) || !ischartype(a->args[j])) &&
                    (a->args[i]->val.type->kind != TK_REAL &&
                     a->args[i]->val.type->kind != TK_STRING &&
                     a->args[j]->val.type->kind != TK_REAL &&
                     a->args[j]->val.type->kind != TK_STRING)) {
                    a->args[i]->val.i += a->args[j]->val.i;
                    delfreearg(&a, j);
                    j--;
                } else if (a->args[i]->val.type->kind == TK_STRING &&
                           ord_type(a->args[j]->val.type)->kind == TK_INTEGER &&
                           a->args[j]->val.i < 0 &&
                           a->args[j]->val.i >= -stringleaders) {
                    /* strictly speaking, the following is illegal pointer arithmetic */
                    a->args[i] = makeexpr_lstring(a->args[i]->val.s + a->args[j]->val.i,
                                                  a->args[i]->val.i - a->args[j]->val.i);
                    for (k = 0; k < - a->args[j]->val.i; k++)
                        a->args[i]->val.s[k] = '>';
                    delfreearg(&a, j);
                    j--;
                }
            }
        }
    }
    if (checkconst(a->args[a->nargs-1], 0))
        delfreearg(&a, a->nargs-1);
    for (i = 0; i < a->nargs; i++) {
        if (a->args[i]->kind == EK_NEG && nosideeffects(a->args[i], 1)) {
            for (j = 0; j < a->nargs; j++) {
                if (exprsame(a->args[j], a->args[i]->args[0], 1)) {
                    delfreearg(&a, i);
                    if (i < j) j--; else i--;
                    delfreearg(&a, j);
                    i--;
                    break;
                }
            }
        }
    }
    if (a->nargs < 2) {
        if (a->nargs < 1) {
	    type = a->val.type;
            FREE(a);
            a = gentle_cast(makeexpr_long(0), type);
	    a->val.type = type;
	    return a;
        } else {
            b = a->args[0];
            FREE(a);
            return b;
        }
    }
    if (a->nargs == 2 && ISCONST(a->args[1]->kind) &&
	a->args[1]->val.i <= -127 &&
	true_type(a->args[0]) == tp_char && signedchars != 0) {
	a->args[0] = force_unsigned(a->args[0]);
    }
    if (a->nargs > 2 &&
	ISCONST(a->args[a->nargs-1]->kind) &&
	ISCONST(a->args[a->nargs-2]->kind) &&
	ischartype(a->args[a->nargs-1]) &&
	ischartype(a->args[a->nargs-2])) {
	i = a->args[a->nargs-1]->val.i;
	j = a->args[a->nargs-2]->val.i;
	if ((i == 'a' || i == 'A' || i == -'a' || i == -'A') &&
	    (j == 'a' || j == 'A' || j == -'a' || j == -'A')) {
	    if (abs(i+j) == 32) {
		delfreearg(&a, a->nargs-1);
		delsimpfreearg(&a, a->nargs-1);
		a = makeexpr_bicall_1((i+j > 0) ? "_tolower" : "_toupper",
				      tp_char, a);
	    }
	}
    }
    return a;
}


Expr *makeexpr_minus(a, b)
Expr *a, *b;
{
    int okneg;

    if (debug>2) { fprintf(outf,"makeexpr_minus("); dumpexpr(a); fprintf(outf,", "); dumpexpr(b); fprintf(outf,")\n"); }
    if (ISCONST(b->kind) && b->val.i == 0 &&       /* kludge for array indexing */
        ord_type(b->val.type)->kind == TK_ENUM) {
        b->val.type = tp_integer;
    }
    okneg = (a->kind != EK_PLUS && b->kind != EK_PLUS);
    a = makeexpr_plus(a, makeexpr_neg(b));
    if (okneg && a->kind == EK_PLUS)
        a->val.i = 1;   /* this flag says to write as "a-b" if possible */
    return a;
}


Expr *makeexpr_inc(a, b)
Expr *a, *b;
{
    Type *type;

    type = a->val.type;
    a = makeexpr_plus(makeexpr_charcast(a), b);
    if (ord_type(type)->kind != TK_INTEGER &&
	ord_type(type)->kind != TK_CHAR)
	a = makeexpr_cast(a, type);
    return a;
}



/* Apply the distributive law for a sum of products */
Expr *distribute_plus(ex)
Expr *ex;
{
    int i, j, icom;
    Expr *common, *outer, *ex2, **exp;

    if (debug>2) { fprintf(outf,"distribute_plus("); dumpexpr(ex); fprintf(outf,")\n"); }
    if (ex->kind != EK_PLUS)
        return ex;
    for (i = 0; i < ex->nargs; i++)
        if (ex->args[i]->kind == EK_TIMES)
            break;
    if (i == ex->nargs)
        return ex;
    outer = NULL;
    icom = 0;
    for (;;) {
	ex2 = ex->args[0];
	if (ex2->kind == EK_NEG)
	    ex2 = ex2->args[0];
        if (ex2->kind == EK_TIMES) {
	    if (icom >= ex2->nargs)
		break;
            common = ex2->args[icom];
	    if (common->kind == EK_NEG)
		common = common->args[0];
        } else {
	    if (icom > 0)
		break;
            common = ex2;
	    icom++;
	}
        for (i = 1; i < ex->nargs; i++) {
	    ex2 = ex->args[i];
	    if (ex2->kind == EK_NEG)
		ex2 = ex2->args[i];
            if (ex2->kind == EK_TIMES) {
                for (j = ex2->nargs; --j >= 0; ) {
                    if (exprsame(ex2->args[j], common, 1) ||
			(ex2->args[j]->kind == EK_NEG &&
			 exprsame(ex2->args[j]->args[0], common, 1)))
                        break;
                }
                if (j < 0)
                    break;
            } else {
                if (!exprsame(ex2, common, 1))
                    break;
            }
        }
        if (i == ex->nargs) {
            if (debug>2) { fprintf(outf,"distribute_plus does "); dumpexpr(common); fprintf(outf,"\n"); }
	    common = copyexpr(common);
            for (i = 0; i < ex->nargs; i++) {
		if (ex->args[i]->kind == EK_NEG)
		    ex2 = *(exp = &ex->args[i]->args[0]);
		else
		    ex2 = *(exp = &ex->args[i]);
		if (ex2->kind == EK_TIMES) {
                    for (j = ex2->nargs; --j >= 0; ) {
                        if (exprsame(ex2->args[j], common, 1)) {
                            delsimpfreearg(exp, j);
                            break;
                        } else if (ex2->args[j]->kind == EK_NEG &&
				   exprsame(ex2->args[j]->args[0], common,1)) {
			    freeexpr(ex2->args[j]);
			    ex2->args[j] = makeexpr_long(-1);
			    break;
			}
                    }
		} else {
		    freeexpr(ex2);
		    *exp = makeexpr_long(1);
                }
		ex->args[i] = resimplify(ex->args[i]);
            }
            outer = makeexpr_times(common, outer);
        } else
	    icom++;
    }
    return makeexpr_times(resimplify(ex), outer);
}





Expr *makeexpr_times(a, b)
Expr *a, *b;
{
    int i, n;
    Type *type;

    if (debug>2) { fprintf(outf,"makeexpr_times("); dumpexpr(a); fprintf(outf,", "); dumpexpr(b); fprintf(outf,")\n"); }
    if (!a)
        return b;
    if (!b)
        return a;
    a = commute(a, b, EK_TIMES);
    if (a->val.type->kind == TK_INTEGER) {
        i = a->nargs-1;
        if (i > 0 && ISCONST(a->args[i-1]->kind)) {
            a->args[i-1]->val.i *= a->args[i]->val.i;
            delfreearg(&a, i);
        }
    }
    for (i = n = 0; i < a->nargs; i++) {
        if (expr_neg_cost(a->args[i]) < 0)
            n++;
    }
    if (n & 1) {
        for (i = 0; i < a->nargs; i++) {
            if (ISCONST(a->args[i]->kind) &&
                expr_neg_cost(a->args[i]) >= 0) {
                a->args[i] = makeexpr_neg(a->args[i]);
                n++;
                break;
            }
        }
    } else
        n++;
    for (i = 0; i < a->nargs && n >= 2; i++) {
        if (expr_neg_cost(a->args[i]) < 0) {
            a->args[i] = makeexpr_neg(a->args[i]);
            n--;
        }
    }
    if (checkconst(a->args[a->nargs-1], 1))
        delfreearg(&a, a->nargs-1);
    if (checkconst(a->args[a->nargs-1], -1)) {
        delfreearg(&a, a->nargs-1);
	a->args[0] = makeexpr_neg(a->args[0]);
    }
    if (checkconst(a->args[a->nargs-1], 0) && nosideeffects(a, 1)) {
        type = a->val.type;
        return makeexpr_cast(grabarg(a, a->nargs-1), type);
    }
    if (a->nargs < 2) {
        if (a->nargs < 1) {
            FREE(a);
            a = makeexpr_long(1);
        } else {
            b = a->args[0];
            FREE(a);
            a = b;
        }
    }
    return a;
}



Expr *makeexpr_sqr(ex, cube)
Expr *ex;
int cube;
{
    Expr *ex2;
    Meaning *tvar;
    Type *type;

    if (exprspeed(ex) <= 2 && nosideeffects(ex, 0)) {
	ex2 = NULL;
    } else {
	type = (ex->val.type->kind == TK_REAL) ? tp_longreal : tp_integer;
	tvar = makestmttempvar(type, name_TEMP);
	ex2 = makeexpr_assign(makeexpr_var(tvar), ex);
	ex = makeexpr_var(tvar);
    }
    if (cube)
	ex = makeexpr_times(ex, makeexpr_times(copyexpr(ex), copyexpr(ex)));
    else
	ex = makeexpr_times(ex, copyexpr(ex));
    return makeexpr_comma(ex2, ex);
}



Expr *makeexpr_divide(a, b)
Expr *a, *b;
{
    Expr *ex;
    int p;

    if (debug>2) {
        fprintf(outf,"makeexpr_divide(");
        dumpexpr(a);
        fprintf(outf,", ");
        dumpexpr(b);
        fprintf(outf,")\n");
    }
    if (a->val.type->kind != TK_REAL &&
	b->val.type->kind != TK_REAL) {     /* must do a real division */
        ex = docast(a, tp_longreal);
        if (ex)
            a = ex;
        else {
            ex = docast(b, tp_longreal);
            if (ex)
                b = ex;
            else
                a = makeexpr_cast(a, tp_longreal);
        }
    }
    if (a->kind == EK_TIMES) {
	for (p = 0; p < a->nargs; p++)
	    if (exprsame(a->args[p], b, 1))
		break;
	if (p < a->nargs) {
	    delfreearg(&a, p);
	    freeexpr(b);
	    if (a->nargs == 1)
		return grabarg(a, 0);
	    else
		return a;
	}
    }
    if (expr_neg_cost(a) < 0 && expr_neg_cost(b) < 0) {
        a = makeexpr_neg(a);
        b = makeexpr_neg(b);
    }
    if (checkconst(b, 0))
        warning("Division by zero [163]");
    return makeexpr_bin(EK_DIVIDE, tp_longreal, a, b);
}




int gcd(a, b)
int a, b;
{
    if (a < 0) a = -a;
    if (b < 0) b = -b;
    while (a != 0) {
	b %= a;
	if (b != 0)
	    a %= b;
	else
	    return a;
    }
    return b;
}



/* possible signs of ex: 1=may be neg, 2=may be zero, 4=may be pos */

int negsigns(mask)
int mask;
{
    return (mask & 2) |
	   ((mask & 1) << 2) |
	   ((mask & 4) >> 2);
}


int possiblesigns(ex)
Expr *ex;
{
    Value val;
    Type *tp;
    char *cp;
    int i, mask, mask2;

    if (isliteralconst(ex, &val) && val.type) {
	if (val.type == tp_real || val.type == tp_longreal) {
	    if (realzero(val.s))
		return 2;
	    if (*val.s == '-')
		return 1;
	    return 4;
	} else
	    return (val.i < 0) ? 1 : (val.i == 0) ? 2 : 4;
    }
    if (ex->kind == EK_CAST &&
	similartypes(ex->val.type, ex->args[0]->val.type))
	return possiblesigns(ex->args[0]);
    if (ex->kind == EK_NEG)
	return negsigns(possiblesigns(ex->args[0]));
    if (ex->kind == EK_TIMES || ex->kind == EK_DIVIDE) {
	mask = possiblesigns(ex->args[0]);
	for (i = 1; i < ex->nargs; i++) {
	    mask2 = possiblesigns(ex->args[i]);
	    if (mask2 & 2)
		mask |= 2;
	    if ((mask2 & (1|4)) == 1)
		mask = negsigns(mask);
	    else if ((mask2 & (1|4)) != 4)
		mask = 1|2|4;
	}
	return mask;
    }
    if (ex->kind == EK_DIV || ex->kind == EK_MOD) {
	mask = possiblesigns(ex->args[0]);
	mask2 = possiblesigns(ex->args[1]);
	if (!((mask | mask2) & 1))
	    return 2|4;
    }
    if (ex->kind == EK_PLUS) {
	mask = 0;
	for (i = 0; i < ex->nargs; i++) {
	    mask2 = possiblesigns(ex->args[i]);
	    if ((mask & negsigns(mask2)) & (1|4))
		mask |= (1|2|4);
	    else
		mask |= mask2;
	}
	return mask;
    }
    if (ex->kind == EK_COND) {
	return possiblesigns(ex->args[1]) | possiblesigns(ex->args[2]);
    }
    if (ex->kind == EK_EQ || ex->kind == EK_LT || ex->kind == EK_GT ||
	ex->kind == EK_NE || ex->kind == EK_LE || ex->kind == EK_GE ||
	ex->kind == EK_AND || ex->kind == EK_OR || ex->kind == EK_NOT)
	return 2|4;
    if (ex->kind == EK_BICALL) {
	cp = ex->val.s;
	if (!strcmp(cp, "strlen") ||
	    !strcmp(cp, "abs") ||
	    !strcmp(cp, "labs") ||
	    !strcmp(cp, "fabs"))
	    return 2|4;
    }
    tp = (ex->kind == EK_VAR) ? ((Meaning *)ex->val.i)->type : ex->val.type;
    if (ord_range(ex->val.type, &val.i, NULL)) {
	if (val.i > 0)
	    return 4;
	if (val.i >= 0)
	    return 2|4;
    }
    if (ord_range(ex->val.type, NULL, &val.i)) {
	if (val.i < 0)
	    return 1;
	if (val.i <= 0)
	    return 1|2;
    }
    return 1|2|4;
}





Expr *dodivmod(funcname, ekind, a, b)
char *funcname;
enum exprkind ekind;
Expr *a, *b;
{
    Meaning *tvar;
    Type *type;
    Expr *asn;
    int sa, sb;

    type = promote_type_bin(a->val.type, b->val.type);
    tvar = NULL;
    sa = possiblesigns(a);
    sb = possiblesigns(b);
    if ((sa & 1) || (sb & 1)) {
	if (*funcname) {
	    asn = NULL;
	    if (*funcname == '*') {
		if (exprspeed(a) >= 5 || !nosideeffects(a, 0)) {
		    tvar = makestmttempvar(a->val.type, name_TEMP);
		    asn = makeexpr_assign(makeexpr_var(tvar), a);
		    a = makeexpr_var(tvar);
		}
		if (exprspeed(b) >= 5 || !nosideeffects(b, 0)) {
		    tvar = makestmttempvar(b->val.type, name_TEMP);
		    asn = makeexpr_comma(asn,
					 makeexpr_assign(makeexpr_var(tvar),
							 b));
		    b = makeexpr_var(tvar);
		}
	    }
	    return makeexpr_comma(asn,
				  makeexpr_bicall_2(funcname, type, a, b));
	} else {
	    if ((sa & 1) && (ekind == EK_MOD))
		note("Using % for possibly-negative arguments [317]");
	    return makeexpr_bin(ekind, type, a, b);
	}
    } else
	return makeexpr_bin(ekind, type, a, b);
}



Expr *makeexpr_div(a, b)
Expr *a, *b;
{
    Meaning *mp;
    Type *type;
    long i;
    int p;

    if (ISCONST(a->kind) && ISCONST(b->kind)) {
        if (a->val.i >= 0 && b->val.i > 0) {
	    a->val.i /= b->val.i;
	    freeexpr(b);
	    return a;
	}
	i = gcd(a->val.i, b->val.i);
	if (i >= 0) {
	    a->val.i /= i;
	    b->val.i /= i;
	}
    }
    if (((b->kind == EK_CONST && (i = b->val.i)) ||
         (b->kind == EK_VAR && (mp = (Meaning *)b->val.i)->kind == MK_CONST &&
                               (i = mp->val.i) && foldconsts != 0)) && i > 0) {
        if (i == 1)
            return a;
        if (div_po2 > 0) {
            p = 0;
            while (!(i&1))
                p++, i >>= 1;
            if (i == 1) {
		type = promote_type_bin(a->val.type, b->val.type);
                return makeexpr_bin(EK_RSH, type, a, makeexpr_long(p));
            }
        }
    }
    if (a->kind == EK_TIMES) {
	for (p = 0; p < a->nargs; p++) {
	    if (exprsame(a->args[p], b, 1)) {
		delfreearg(&a, p);
		freeexpr(b);
		if (a->nargs == 1)
		    return grabarg(a, 0);
		else
		    return a;
	    } else if (ISCONST(a->args[p]->kind) && ISCONST(b->kind)) {
		i = gcd(a->args[p]->val.i, b->val.i);
		if (i > 1) {
		    a->args[p]->val.i /= i;
		    b->val.i /= i;
		    i = a->args[p]->val.i;
		    delfreearg(&a, p);
		    a = makeexpr_times(a, makeexpr_long(i));   /* resimplify */
		    p = -1;   /* start the loop over */
		}
	    }
	}
    }
    if (checkconst(b, 1)) {
        freeexpr(b);
        return a;
    } else if (checkconst(b, -1)) {
        freeexpr(b);
        return makeexpr_neg(a);
    } else {
        if (checkconst(b, 0))
            warning("Division by zero [163]");
        return dodivmod(divname, EK_DIV, a, b);
    }
}



Expr *makeexpr_mod(a, b)
Expr *a, *b;
{
    Meaning *mp;
    Type *type;
    long i;

    if (a->kind == EK_CONST && b->kind == EK_CONST &&
        a->val.i >= 0 && b->val.i > 0) {
        a->val.i %= b->val.i;
        freeexpr(b);
        return a;
    }
    if (((b->kind == EK_CONST && (i = b->val.i)) ||
         (b->kind == EK_VAR && (mp = (Meaning *)b->val.i)->kind == MK_CONST &&
                               (i = mp->val.i) && foldconsts != 0)) && i > 0) {
        if (i == 1)
            return makeexpr_long(0);
        if (mod_po2 != 0) {
            while (!(i&1))
                i >>= 1;
            if (i == 1) {
		type = promote_type_bin(a->val.type, b->val.type);
                return makeexpr_bin(EK_BAND, type, a,
                                    makeexpr_minus(b, makeexpr_long(1)));
            }
        }
    }
    if (checkconst(b, 0))
        warning("Division by zero [163]");
    return dodivmod(modname, EK_MOD, a, b);
}



Expr *makeexpr_rem(a, b)
Expr *a, *b;
{
    if (!(possiblesigns(a) & 1) && !(possiblesigns(b) & 1))
	return makeexpr_mod(a, b);
    if (checkconst(b, 0))
        warning("Division by zero [163]");
    if (!*remname)
	note("Translating REM same as MOD [141]");
    return dodivmod(*remname ? remname : modname, EK_MOD, a, b);
}





int expr_not_cost(a)
Expr *a;
{
    int i, c;

    switch (a->kind) {

        case EK_CONST:
            return 0;

        case EK_NOT:
            return -1;

        case EK_EQ:
        case EK_NE:
        case EK_LT:
        case EK_GT:
        case EK_LE:
        case EK_GE:
            return 0;

        case EK_AND:
        case EK_OR:
            c = 0;
            for (i = 0; i < a->nargs; i++)
                c += expr_not_cost(a->args[i]);
            return (c > 1) ? 1 : c;

        case EK_BICALL:
            if (!strcmp(a->val.s, oddname) ||
                !strcmp(a->val.s, evenname))
                return 0;
            return 1;

        default:
            return 1;
    }
}



Expr *makeexpr_not(a)
Expr *a;
{
    Expr *ex;
    int i;

    if (debug>2) { fprintf(outf,"makeexpr_not("); dumpexpr(a); fprintf(outf,")\n"); }
    switch (a->kind) {

        case EK_CONST:
            if (a->val.type == tp_boolean) {
                a->val.i = !a->val.i;
                return a;
            }
            break;

        case EK_EQ:
            a->kind = EK_NE;
            return a;

        case EK_NE:
            a->kind = EK_EQ;
            return a;

        case EK_LT:
            a->kind = EK_GE;
            return a;

        case EK_GT:
            a->kind = EK_LE;
            return a;

        case EK_LE:
            a->kind = EK_GT;
            return a;

        case EK_GE:
            a->kind = EK_LT;
            return a;

        case EK_AND:
        case EK_OR:
            if (expr_not_cost(a) > 0)
                break;
            a->kind = (a->kind == EK_OR) ? EK_AND : EK_OR;
            for (i = 0; i < a->nargs; i++)
                a->args[i] = makeexpr_not(a->args[i]);
            return a;

        case EK_NOT:
            ex = a->args[0];
            FREE(a);
            ex->val.type = tp_boolean;
            return ex;

        case EK_BICALL:
            if (!strcmp(a->val.s, oddname) && *evenname) {
                strchange(&a->val.s, evenname);
                return a;
            } else if (!strcmp(a->val.s, evenname)) {
                strchange(&a->val.s, oddname);
                return a;
            }
            break;

	default:
	    break;
    }
    return makeexpr_un(EK_NOT, tp_boolean, a);
}




Type *mixsets(ep1, ep2)
Expr **ep1, **ep2;
{
    Expr *ex1 = *ep1, *ex2 = *ep2;
    Meaning *tvar;
    long min1, max1, min2, max2;
    Type *type;

    if (ex1->val.type->kind == TK_SMALLSET &&
        ex2->val.type->kind == TK_SMALLSET)
        return ex1->val.type;
    if (ex1->val.type->kind == TK_SMALLSET) {
        tvar = makestmttempvar(ex2->val.type, name_SET);
        ex1 = makeexpr_bicall_2(setexpandname, ex2->val.type,
                                makeexpr_var(tvar),
                                makeexpr_arglong(ex1, 1));
    }
    if (ex2->val.type->kind == TK_SMALLSET) {
        tvar = makestmttempvar(ex1->val.type, name_SET);
        ex2 = makeexpr_bicall_2(setexpandname, ex1->val.type,
                                makeexpr_var(tvar),
                                makeexpr_arglong(ex2, 1));
    }
    if (ord_range(ex1->val.type->indextype, &min1, &max1) &&
        ord_range(ex2->val.type->indextype, &min2, &max2)) {
        if (min1 <= min2 && max1 >= max2)
            type = ex1->val.type;
        else if (min2 <= min1 && max2 >= max1)
            type = ex2->val.type;
        else {
            if (min2 < min1) min1 = min2;
            if (max2 > max1) max1 = max2;
            type = maketype(TK_SET);
            type->basetype = tp_integer;
            type->indextype = maketype(TK_SUBR);
            type->indextype->basetype = ord_type(ex1->val.type->indextype);
            type->indextype->smin = makeexpr_long(min1);
            type->indextype->smax = makeexpr_long(max1);
        }
    } else
	type = ex1->val.type;
    *ep1 = ex1, *ep2 = ex2;
    return type;
}



Meaning *istempprocptr(ex)
Expr *ex;
{
    Meaning *mp;

    if (debug>2) { fprintf(outf,"istempprocptr("); dumpexpr(ex); fprintf(outf,")\n"); }
    if (ex->kind == EK_COMMA && ex->nargs == 3) {
        if ((mp = istempvar(ex->args[2])) != NULL &&
	    mp->type->kind == TK_PROCPTR &&
	    ex->args[0]->kind == EK_ASSIGN &&
	    ex->args[0]->args[0]->kind == EK_DOT &&
	    exprsame(ex->args[0]->args[0]->args[0], ex->args[2], 1) &&
	    ex->args[1]->kind == EK_ASSIGN &&
	    ex->args[1]->args[0]->kind == EK_DOT &&
	    exprsame(ex->args[1]->args[0]->args[0], ex->args[2], 1))
	    return mp;
    }
    if (ex->kind == EK_COMMA && ex->nargs == 2) {
        if ((mp = istempvar(ex->args[1])) != NULL &&
	    mp->type->kind == TK_CPROCPTR &&
	    ex->args[0]->kind == EK_ASSIGN &&
	    exprsame(ex->args[0]->args[0], ex->args[1], 1))
	    return mp;
    }
    return NULL;
}




Expr *makeexpr_stringify(ex)
Expr *ex;
{
    ex = makeexpr_stringcast(ex);
    if (ex->val.type->kind == TK_STRING)
        return ex;
    return makeexpr_sprintfify(ex);
}



Expr *makeexpr_rel(rel, a, b)
enum exprkind rel;
Expr *a, *b;
{
    int i, sign;
    Expr *ex, *ex2;
    Meaning *mp;
    char *name;

    if (debug>2) { fprintf(outf,"makeexpr_rel(%s,", exprkindname(rel)); dumpexpr(a); fprintf(outf,", "); dumpexpr(b); fprintf(outf,")\n"); }

    a = makeexpr_unlongcast(a);
    b = makeexpr_unlongcast(b);
    if ((compenums == 0 || (compenums < 0 && ansiC <= 0)) &&
	(rel != EK_EQ && rel != EK_NE)){
	a = enum_to_int(a);
	b = enum_to_int(b);
    }
    if (a->val.type != b->val.type) {
        if (a->val.type->kind == TK_STRING &&
            a->kind != EK_CONST) {
            b = makeexpr_stringify(b);
        } else if (b->val.type->kind == TK_STRING &&
                   b->kind != EK_CONST) {
            a = makeexpr_stringify(a);
        } else if (ord_type(a->val.type)->kind == TK_CHAR ||
                   a->val.type->kind == TK_ARRAY) {
            b = gentle_cast(b, ord_type(a->val.type));
        } else if (ord_type(b->val.type)->kind == TK_CHAR ||
                   b->val.type->kind == TK_ARRAY) {
            a = gentle_cast(a, ord_type(b->val.type));
        } else if (a->val.type == tp_anyptr && !voidstar) {
            a = gentle_cast(a, b->val.type);
        } else if (b->val.type == tp_anyptr && !voidstar) {
            b = gentle_cast(b, a->val.type);
        }
    }
    if (useisspace && b->val.type->kind == TK_CHAR && checkconst(b, ' ')) {
        if (rel == EK_EQ) {
            freeexpr(b);
            return makeexpr_bicall_1("isspace", tp_boolean, a);
        } else if (rel == EK_NE) {
            freeexpr(b);
            return makeexpr_not(makeexpr_bicall_1("isspace", tp_boolean, a));
        }
    }
    if (rel == EK_LT || rel == EK_GE)
        sign = 1;
    else if (rel == EK_GT || rel == EK_LE)
        sign = -1;
    else
        sign = 0;
    if (ord_type(b->val.type)->kind == TK_INTEGER ||
	ord_type(b->val.type)->kind == TK_CHAR) {
        for (;;) {
            if (a->kind == EK_PLUS && ISCONST(a->args[a->nargs-1]->kind) &&
                 a->args[a->nargs-1]->val.i &&
                 (ISCONST(b->kind) ||
                  (b->kind == EK_PLUS && ISCONST(b->args[b->nargs-1]->kind)))) {
                b = makeexpr_minus(b, copyexpr(a->args[a->nargs-1]));
                a = makeexpr_minus(a, copyexpr(a->args[a->nargs-1]));
                continue;
            }
            if (b->kind == EK_PLUS && ISCONST(b->args[b->nargs-1]->kind) &&
                 b->args[b->nargs-1]->val.i &&
                 ISCONST(a->kind)) {
                a = makeexpr_minus(a, copyexpr(b->args[b->nargs-1]));
                b = makeexpr_minus(b, copyexpr(b->args[b->nargs-1]));
                continue;
            }
            if (b->kind == EK_PLUS && sign &&
                 checkconst(b->args[b->nargs-1], sign)) {
                b = makeexpr_plus(b, makeexpr_long(-sign));
                switch (rel) {
                    case EK_LT:
                        rel = EK_LE;
                        break;
                    case EK_GT:
                        rel = EK_GE;
                        break;
                    case EK_LE:
                        rel = EK_LT;
                        break;
                    case EK_GE:
                        rel = EK_GT;
                        break;
		    default:
			break;
                }
                sign = -sign;
                continue;
            }
            if (a->kind == EK_TIMES && checkconst(b, 0) && !sign) {
                for (i = 0; i < a->nargs; i++) {
                    if (ISCONST(a->args[i]->kind) && a->args[i]->val.i)
                        break;
                    if (a->args[i]->kind == EK_SIZEOF)
                        break;
                }
                if (i < a->nargs) {
                    delfreearg(&a, i);
                    continue;
                }
            }
            break;
        }
        if (a->kind == EK_BICALL && !strcmp(a->val.s, "strlen") &&
            checkconst(b, 0)) {
            if (rel == EK_LT || rel == EK_GE) {
                note("Unusual use of STRLEN encountered [142]");
            } else {
                freeexpr(b);
                a = makeexpr_hat(grabarg(a, 0), 0);
                b = makeexpr_char(0);      /* "strlen(a) = 0" => "*a == 0" */
                if (rel == EK_EQ || rel == EK_LE)
                    return makeexpr_rel(EK_EQ, a, b);
                else
                    return makeexpr_rel(EK_NE, a, b);
            }
        }
        if (ISCONST(a->kind) && ISCONST(b->kind)) {
            if ((a->val.i == b->val.i && (rel == EK_EQ || rel == EK_GE || rel == EK_LE)) ||
                (a->val.i <  b->val.i && (rel == EK_NE || rel == EK_LE || rel == EK_LT)) ||
                (a->val.i >  b->val.i && (rel == EK_NE || rel == EK_GE || rel == EK_GT)))
                return makeexpr_val(make_ord(tp_boolean, 1));
            else
                return makeexpr_val(make_ord(tp_boolean, 0));
        }
	if ((a->val.type == tp_char || true_type(a) == tp_char) &&
	    ISCONST(b->kind) && signedchars != 0) {
	    i = (b->val.i == 128 && sign == 1) ||
		(b->val.i == 127 && sign == -1);
	    if (highcharbits && (highcharbits > 0 || signedchars < 0) && i) {
		if (highcharbits == 2)
		    b = makeexpr_long(128);
		else
		    b = makeexpr_un(EK_BNOT, tp_integer, makeexpr_long(127));
		return makeexpr_rel((rel == EK_GE || rel == EK_GT)
				    ? EK_NE : EK_EQ,
				    makeexpr_bin(EK_BAND, tp_integer,
						 eatcasts(a), b),
				    makeexpr_long(0));
	    } else if (signedchars == 1 && i) {
		return makeexpr_rel((rel == EK_GE || rel == EK_GT)
				    ? EK_LT : EK_GE,
				    eatcasts(a), makeexpr_long(0));
	    } else if (signedchars == 1 && b->val.i >= 128 && sign == 0) {
		b->val.i -= 256;
	    } else if (b->val.i >= 128 ||
		       (b->val.i == 127 && sign != 0)) {
		if (highcharbits && (highcharbits > 0 || signedchars < 0))
		    a = makeexpr_bin(EK_BAND, a->val.type, eatcasts(a),
				     makeexpr_long(255));
		else
		    a = force_unsigned(a);
	    }
	}
    } else if (a->val.type->kind == TK_STRING &&
               b->val.type->kind == TK_STRING) {
        if (b->kind == EK_CONST && b->val.i == 0 && !sign) {
            a = makeexpr_hat(a, 0);
            b = makeexpr_char(0);      /* "a = ''" => "*a == 0" */
        } else {
            a = makeexpr_bicall_2("strcmp", tp_int, a, b);
            b = makeexpr_long(0);
        }
    } else if ((a->val.type->kind == TK_ARRAY ||
		a->val.type->kind == TK_STRING ||
		a->val.type->kind == TK_RECORD) &&
	       (b->val.type->kind == TK_ARRAY ||
		b->val.type->kind == TK_STRING ||
		b->val.type->kind == TK_RECORD)) {
        if (a->val.type->kind == TK_ARRAY) {
            if (b->val.type->kind == TK_ARRAY) {
                ex = makeexpr_sizeof(copyexpr(a), 0);
                ex2 = makeexpr_sizeof(copyexpr(b), 0);
                if (!exprsame(ex, ex2, 1))
                    warning("Incompatible array sizes [164]");
                freeexpr(ex2);
            } else {
                ex = makeexpr_sizeof(copyexpr(a), 0);
            }
        } else
            ex = makeexpr_sizeof(copyexpr(b), 0);
	name = (usestrncmp &&
		a->val.type->kind == TK_ARRAY &&
		a->val.type->basetype->kind == TK_CHAR) ? "strncmp" : "memcmp";
        a = makeexpr_bicall_3(name, tp_int,
			      makeexpr_addr(a), 
			      makeexpr_addr(b), ex);
        b = makeexpr_long(0);
    } else if (a->val.type->kind == TK_SET ||
               a->val.type->kind == TK_SMALLSET) {
        if (rel == EK_GE) {
            swapexprs(a, b);
            rel = EK_LE;
        }
        if (mixsets(&a, &b)->kind == TK_SMALLSET) {
            if (rel == EK_LE) {
                a = makeexpr_bin(EK_BAND, tp_integer,
                                 a, makeexpr_un(EK_BNOT, tp_integer, b));
                b = makeexpr_long(0);
                rel = EK_EQ;
            }
        } else if (b->kind == EK_BICALL &&
                   !strcmp(b->val.s, setexpandname) &&
                   (mp = istempvar(b->args[0])) != NULL &&
                   checkconst(b->args[1], 0)) {
            canceltempvar(mp);
            a = makeexpr_hat(a, 0);
            b = grabarg(b, 1);
            if (rel == EK_LE)
                rel = EK_EQ;
        } else {
            ex = makeexpr_bicall_2((rel == EK_LE) ? subsetname : setequalname,
                                   tp_boolean, a, b);
            return (rel == EK_NE) ? makeexpr_not(ex) : ex;
        }
    } else if (a->val.type->kind == TK_PROCPTR ||
	       a->val.type->kind == TK_CPROCPTR) {
        /* we compare proc only (not link) -- same as Pascal compiler! */
	if (a->val.type->kind == TK_PROCPTR)
	    a = makeexpr_dotq(a, "proc", tp_anyptr);
        if ((mp = istempprocptr(b)) != NULL) {
            canceltempvar(mp);
	    b = grabarg(grabarg(b, 0), 1);
            if (!voidstar)
                b = makeexpr_cast(b, tp_anyptr);
        } else if (b->val.type->kind == TK_PROCPTR)
            b = makeexpr_dotq(b, "proc", tp_anyptr);
    }
    return makeexpr_bin(rel, tp_boolean, a, b);
}

