/* "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_EXPR4_C
#include "trans.h"

Expr *makeexpr_and(a, b)
Expr *a, *b;
{
    Expr *ex, **exp, *low;

    if (!a)
        return b;
    if (!b)
        return a;
    for (exp = &a; (ex = *exp)->kind == EK_AND; exp = &ex->args[1]) ;
    if ((b->kind == EK_LT || b->kind == EK_LE) &&
        ((ex->kind == EK_LE && exprsame(ex->args[1], b->args[0], 1)) ||
         (ex->kind == EK_GE && exprsame(ex->args[0], b->args[0], 1)))) {
        low = (ex->kind == EK_LE) ? ex->args[0] : ex->args[1];
        if (unsignedtrick && checkconst(low, 0)) {
            freeexpr(ex);
            b->args[0] = force_unsigned(b->args[0]);
            *exp = b;
            return a;
        }
        if (b->args[1]->val.type->kind == TK_CHAR && useisalpha) {
            if (checkconst(low, 'A') && checkconst(b->args[1], 'Z')) {
                freeexpr(ex);
                *exp = makeexpr_bicall_1("isupper", tp_boolean, grabarg(b, 0));
                return a;
            }
            if (checkconst(low, 'a') && checkconst(b->args[1], 'z')) {
                freeexpr(ex);
                *exp = makeexpr_bicall_1("islower", tp_boolean, grabarg(b, 0));
                return a;
            }
            if (checkconst(low, '0') && checkconst(b->args[1], '9')) {
                freeexpr(ex);
                *exp = makeexpr_bicall_1("isdigit", tp_boolean, grabarg(b, 0));
                return a;
            }
        }
    }
    return makeexpr_bin(EK_AND, tp_boolean, a, b);
}



Expr *makeexpr_or(a, b)
Expr *a, *b;
{
    Expr *ex, **exp, *low;

    if (!a)
        return b;
    if (!b)
        return a;
    for (exp = &a; (ex = *exp)->kind == EK_OR; exp = &ex->args[1]) ;
    if (((b->kind == EK_BICALL && !strcmp(b->val.s, "isdigit") &&
          ex->kind == EK_BICALL && !strcmp(ex->val.s, "isalpha")) ||
         (b->kind == EK_BICALL && !strcmp(b->val.s, "isalpha") &&
          ex->kind == EK_BICALL && !strcmp(ex->val.s, "isdigit"))) &&
        exprsame(ex->args[0], b->args[0], 1)) {
        strchange(&ex->val.s, "isalnum");
        freeexpr(b);
        return a;
    }
    if (((b->kind == EK_BICALL && !strcmp(b->val.s, "islower") &&
          ex->kind == EK_BICALL && !strcmp(ex->val.s, "isupper")) ||
         (b->kind == EK_BICALL && !strcmp(b->val.s, "isupper") &&
          ex->kind == EK_BICALL && !strcmp(ex->val.s, "islower"))) &&
        exprsame(ex->args[0], b->args[0], 1)) {
        strchange(&ex->val.s, "isalpha");
        freeexpr(b);
        return a;
    }
    if ((b->kind == EK_GT || b->kind == EK_GE) &&
        ((ex->kind == EK_GT && exprsame(ex->args[1], b->args[0], 1)) ||
         (ex->kind == EK_LT && exprsame(ex->args[0], b->args[0], 1)))) {
        low = (ex->kind == EK_GT) ? ex->args[0] : ex->args[1];
        if (unsignedtrick && checkconst(low, 0)) {
            freeexpr(ex);
            b->args[0] = force_unsigned(b->args[0]);
            *exp = b;
            return a;
        }
    }
    return makeexpr_bin(EK_OR, tp_boolean, a, b);
}



Expr *makeexpr_range(ex, exlow, exhigh, higheq)
Expr *ex, *exlow, *exhigh;
int higheq;
{
    Expr *ex2;
    enum exprkind rel = (higheq) ? EK_LE : EK_LT;

    if (exprsame(exlow, exhigh, 1) && higheq)
        return makeexpr_rel(EK_EQ, ex, exlow);
    ex2 = makeexpr_rel(rel, copyexpr(ex), exhigh);
    if (lelerange)
        return makeexpr_and(makeexpr_rel(EK_LE, exlow, ex), ex2);
    else
        return makeexpr_and(makeexpr_rel(EK_GE, ex, exlow), ex2);
}




Expr *makeexpr_cond(c, a, b)
Expr *c, *a, *b;
{
    Expr *ex;

    ex = makeexpr(EK_COND, 3);
    ex->val.type = a->val.type;
    ex->args[0] = c;
    ex->args[1] = a;
    ex->args[2] = b;
    if (debug>2) { fprintf(outf,"makeexpr_cond returns "); dumpexpr(ex); fprintf(outf,"\n"); }
    return ex;
}




int expr_is_lvalue(ex)
Expr *ex;
{
    Meaning *mp;

    switch (ex->kind) {

        case EK_VAR:
            mp = (Meaning *)ex->val.i;
            return ((mp->kind == MK_VAR || mp->kind == MK_PARAM) ||
                    (mp->kind == MK_CONST &&
                     (mp->type->kind == TK_ARRAY ||
                      mp->type->kind == TK_RECORD ||
                      mp->type->kind == TK_SET)));

        case EK_HAT:
            return 1;

        case EK_INDEX:
            return expr_is_lvalue(ex->args[0]);

	case EK_DOT:
	    return expr_is_lvalue(ex->args[0]);

        default:
            return 0;
    }
}


int expr_has_address(ex)
Expr *ex;
{
    if (ex->kind == EK_DOT &&
	((Meaning *)ex->val.i)->val.i)
	return 0;    /* bit fields do not have an address */
    return expr_is_lvalue(ex);
}



Expr *checknil(ex)
Expr *ex;
{
    if (nilcheck == 1) {
        if (singlevar(ex)) {
            ex = makeexpr_un(EK_CHECKNIL, ex->val.type, ex);
        } else {
            ex = makeexpr_bin(EK_CHECKNIL, ex->val.type, ex,
                              makeexpr_var(makestmttempvar(ex->val.type,
                                                           name_PTR)));
        }
    }
    return ex;
}


int checkvarinlists(yes, no, def, mp)
Strlist *yes, *no;
int def;
Meaning *mp;
{
    char *cp;
    Meaning *ctx;

    if (mp->kind == MK_FIELD)
	ctx = mp->rectype->meaning;
    else
	ctx = mp->ctx;
    if (ctx && ctx->name)
	cp = format_ss("%s.%s", ctx->name, mp->name);
    else
	cp = NULL;
    if (strlist_cifind(yes, cp))
	return 1;
    if (strlist_cifind(no, cp))
	return 0;
    if (strlist_cifind(yes, mp->name))
	return 1;
    if (strlist_cifind(no, mp->name))
	return 0;
    if (strlist_cifind(yes, "1"))
	return 1;
    if (strlist_cifind(no, "1"))
	return 0;
    return def;
}


void requirefilebuffer(ex)
Expr *ex;
{
    Meaning *mp;

    mp = isfilevar(ex);
    if (!mp) {
	if (ex->kind == EK_HAT)
	    ex = ex->args[0];
	if (ex->kind == EK_VAR) {
	    mp = (Meaning *)ex->val.i;
	    if (mp->kind == MK_PARAM || mp->kind == MK_VARPARAM)
		note(format_s("File parameter %s needs its associated buffers [318]",
			      mp->name));
	}
    } else if (!mp->bufferedfile &&
	       checkvarinlists(bufferedfiles, unbufferedfiles, 1, mp)) {
	if (mp->wasdeclared)
	    note(format_s("Discovered too late that %s should be buffered [143]",
			  mp->name));
	mp->bufferedfile = 1;
    }
}


Expr *makeexpr_hat(a, check)
Expr *a;
int check;
{
    Expr *ex;

    if (debug>2) { fprintf(outf,"makeexpr_hat("); dumpexpr(a); fprintf(outf,")\n"); }
    if (isfiletype(a->val.type)) {
	requirefilebuffer(a);
	if (*chargetfbufname &&
	    a->val.type->basetype->basetype->kind == TK_CHAR)
	    return makeexpr_bicall_1(chargetfbufname,
				     a->val.type->basetype->basetype, a);
	else if (*arraygetfbufname &&
		 a->val.type->basetype->basetype->kind == TK_ARRAY)
	    return makeexpr_bicall_2(arraygetfbufname,
				     a->val.type->basetype->basetype, a,
				     makeexpr_type(a->val.type->basetype->basetype));
	else
	    return makeexpr_bicall_2(getfbufname,
				     a->val.type->basetype->basetype, a,
				     makeexpr_type(a->val.type->basetype->basetype));
    }
    if (a->kind == EK_PLUS && 
               (ex = a->args[0])->val.type->kind == TK_POINTER &&
               (ex->val.type->basetype->kind == TK_ARRAY ||
                ex->val.type->basetype->kind == TK_STRING ||
                ex->val.type->basetype->kind == TK_SET)) {
        ex->val.type = ex->val.type->basetype;   /* convert *(a+n) to a[n] */
        deletearg(&a, 0);
        if (a->nargs == 1)
            a = grabarg(a, 0);
        return makeexpr_bin(EK_INDEX, ex->val.type->basetype, ex, a);
    }
    if (a->val.type->kind == TK_STRING || 
        a->val.type->kind == TK_ARRAY ||
        a->val.type->kind == TK_SET) {
        if (starindex == 0)
            return makeexpr_bin(EK_INDEX, a->val.type->basetype, a, makeexpr_long(0));
        else
            return makeexpr_un(EK_HAT, a->val.type->basetype, a);
    }
    if (a->val.type->kind != TK_POINTER || !a->val.type->basetype) {
        warning("bad pointer dereference [165]");
        return a;
    }
    if (a->kind == EK_CAST &&
	a->val.type->basetype->kind == TK_POINTER &&
	a->args[0]->val.type->kind == TK_POINTER &&
	a->args[0]->val.type->basetype->kind == TK_POINTER) {
	return makeexpr_cast(makeexpr_hat(a->args[0], 0),
			     a->val.type->basetype);
    }
    switch (a->val.type->basetype->kind) {

      case TK_ARRAY:
      case TK_STRING:
      case TK_SET:
	if (a->kind != EK_HAT || 1 ||
	    a->val.type == a->args[0]->val.type->basetype) {
	    a->val.type = a->val.type->basetype;
	    return a;
	}
	
      default:
	if (a->kind == EK_ADDR) {
	    ex = a->args[0];
	    FREE(a);
	    return ex;
	} else {
	    if (check)
		ex = checknil(a);
	    else
		ex = a;
	    return makeexpr_un(EK_HAT, a->val.type->basetype, ex);
        }
    }
}



Expr *un_sign_extend(a)
Expr *a;
{
    if (a->kind == EK_BICALL &&
        !strcmp(a->val.s, signextname) && *signextname) {
        return grabarg(a, 0);
    }
    return a;
}



Expr *makeexpr_addr(a)
Expr *a;
{
    Expr *ex;
    Type *type;

    a = un_sign_extend(a);
    type = makepointertype(a->val.type);
    if (debug>2) { fprintf(outf,"makeexpr_addr("); dumpexpr(a); fprintf(outf,", "); dumptypename(type, 1); fprintf(outf,")\n"); }
    if (a->kind == EK_CONST && a->val.type->kind == TK_STRING) {
        return a;     /* kludge to help assignments */
    } else if (a->kind == EK_INDEX &&
	       (a->val.type->kind != TK_ARRAY &&
		a->val.type->kind != TK_SET &&
		a->val.type->kind != TK_STRING) &&
	       (addindex == 1 ||
		(addindex != 0 && checkconst(a->args[1], 0)))) {
        ex = makeexpr_plus(makeexpr_addr(a->args[0]), a->args[1]);
        FREE(a);
        ex->val.type = type;
        return ex;
    } else {
        switch (a->val.type->kind) {
	    
	  case TK_ARRAY:
	  case TK_STRING:
	  case TK_SET:
	    if (a->val.type->smin) {
		return makeexpr_un(EK_ADDR, type, 
				   makeexpr_index(a, 
						  copyexpr(a->val.type->smin),
						  NULL));
	    }
	    a->val.type = type;
	    return a;
	    
	  default:
	    if (a->kind == EK_HAT) {
		ex = a->args[0];
		FREE(a);
		return ex;
	    } else if (a->kind == EK_ACTCAST)
		return makeexpr_actcast(makeexpr_addr(grabarg(a, 0)), type);
	    else if (a->kind == EK_CAST)
		return makeexpr_cast(makeexpr_addr(grabarg(a, 0)), type);
	    else
		return makeexpr_un(EK_ADDR, type, a);
	}
    }
}



Expr *makeexpr_addrstr(a)
Expr *a;
{
    if (debug>2) { fprintf(outf,"makeexpr_addrstr("); dumpexpr(a); fprintf(outf,")\n"); }
    if (a->val.type->kind == TK_POINTER)
	return a;
    return makeexpr_addr(a);
}



Expr *makeexpr_addrf(a)
Expr *a;
{
    Meaning *mp, *tvar;

    mp = (Meaning *)a->val.i;
    if ((a->kind == EK_VAR &&
         (mp == mp_input || mp == mp_output)) ||
        (a->kind == EK_NAME &&
         !strcmp(a->val.s, "stderr"))) {
        if (addrstdfiles == 0) {
            note(format_s("Taking address of %s; consider setting VarFiles = 0 [144]",
                          (a->kind == EK_VAR) ? ((Meaning *)a->val.i)->name
                                              : a->val.s));
            tvar = makestmttempvar(tp_text, name_TEMP);
            return makeexpr_comma(makeexpr_assign(makeexpr_var(tvar), a),
                                  makeexpr_addr(makeexpr_var(tvar)));
        }
    }
    if ((a->kind == EK_VAR &&
         mp->kind == MK_FIELD && mp->val.i) ||
        (a->kind == EK_BICALL &&
         !strcmp(a->val.s, getbitsname))) {
        warning("Can't take the address of a bit-field [166]");
    }
    return makeexpr_addr(a);
}



Expr *makeexpr_index(a, b, offset)
Expr *a, *b, *offset;
{
    Type *indextype, *btype;

    if (debug>2) { fprintf(outf,"makeexpr_index("); dumpexpr(a); fprintf(outf,", "); dumpexpr(b);
                                                                 fprintf(outf,", "); dumpexpr(offset); fprintf(outf,")\n"); }
    indextype = (a->val.type->kind == TK_ARRAY) ? a->val.type->indextype
                                                : tp_integer;
    b = gentle_cast(b, indextype);
    if (!offset)
        offset = makeexpr_long(0);
    b = makeexpr_minus(b, gentle_cast(offset, indextype));
    btype = a->val.type;
    if (btype->basetype)
	btype = btype->basetype;
    if (checkconst(b, 0) && starindex == 1)
        return makeexpr_un(EK_HAT, btype, a);
    else
        return makeexpr_bin(EK_INDEX, btype, a,
                            gentle_cast(b, indextype));
}



Expr *makeexpr_type(type)
Type *type;
{
    Expr *ex;

    ex = makeexpr(EK_TYPENAME, 0);
    ex->val.type = type;
    return ex;
}


Expr *makeexpr_sizeof(ex, incskipped)
Expr *ex;
int incskipped;
{
    Expr *ex2, *ex3;
    Type *btype;
    char *name;

    if (ex->val.type->meaning) {
	name = find_special_variant(ex->val.type->meaning->name,
				    "SpecialSizeOf", specialsizeofs, 1);
	if (name) {
	    freeexpr(ex);
	    return pc_expr_str(name);
	}
    }
    switch (ex->val.type->kind) {

        case TK_CHAR:
        case TK_BOOLEAN:
            freeexpr(ex);
            return makeexpr_long(1);

        case TK_SUBR:
	    btype = findbasetype(ex->val.type, 0);
	    if (btype->kind == TK_CHAR || btype == tp_abyte) {
		freeexpr(ex);
		return makeexpr_long(1);
	    }
	    break;

        case TK_STRING:
        case TK_ARRAY:
            if (!ex->val.type->meaning || ex->val.type->kind == TK_STRING) {
                ex3 = arraysize(ex->val.type, incskipped);
                return makeexpr_times(ex3,
                                      makeexpr_sizeof(makeexpr_type(
                                           ex->val.type->basetype), 1));
            }
            break;

        case TK_SET:
            ord_range_expr(ex->val.type->indextype, NULL, &ex2);
            freeexpr(ex);
            return makeexpr_times(makeexpr_plus(makeexpr_div(copyexpr(ex2),
                                                             makeexpr_setbits()),
                                                makeexpr_long(2)),
                                  makeexpr_sizeof(makeexpr_type(tp_integer), 0));
            break;

	default:
	    break;
    }
    if (ex->kind != EK_CONST &&
        (findbasetype(ex->val.type,0)->meaning || /* if type has a name... */
         ex->val.type->kind == TK_STRING ||       /* if C sizeof(expr) will give wrong answer */
         ex->val.type->kind == TK_ARRAY ||
         ex->val.type->kind == TK_SET)) {
        ex2 = makeexpr_type(ex->val.type);
        freeexpr(ex);
        ex = ex2;
    }
    return makeexpr_un(EK_SIZEOF, tp_integer, ex);
}




/* Compute a measure of how fast or slow the expression is likely to be.
   0 is a constant, 1 is a variable, extra points added per "operation". */

int exprspeed(ex)
Expr *ex;
{
    Meaning *mp, *mp2;
    int i, cost, speed;

    switch (ex->kind) {

        case EK_VAR:
            mp = (Meaning *)ex->val.i;
            if (mp->kind == MK_CONST)
                return 0;
            if (!mp->ctx || mp->ctx->kind == MK_FUNCTION)
                return 1;
            i = 1;
            for (mp2 = curctx; mp2 && mp2 != mp->ctx; mp2 = mp2->ctx)
                i++;    /* cost of following static links */
            return (i);

        case EK_CONST:
        case EK_LONGCONST:
        case EK_SIZEOF:
            return 0;

        case EK_ADDR:
            speed = exprspeed(ex->args[0]);
            return (speed > 1) ? speed : 0;

        case EK_DOT:
            return exprspeed(ex->args[0]);

        case EK_NEG:
            return exprspeed(ex->args[0]) + 1;

        case EK_CAST:
        case EK_ACTCAST:
            i = (ord_type(ex->val.type)->kind == TK_REAL) !=
                (ord_type(ex->args[0]->val.type)->kind == TK_REAL);
            return (i + exprspeed(ex->args[0]));

        case EK_COND:
            return 2 + exprspeed(ex->args[0]) +
                   MAX(exprspeed(ex->args[1]), exprspeed(ex->args[2]));

        case EK_AND:
        case EK_OR:
        case EK_COMMA:
            speed = 2;
            for (i = 0; i < ex->nargs; i++)
                speed += exprspeed(ex->args[i]);
            return speed;

        case EK_FUNCTION:
        case EK_BICALL:
        case EK_SPCALL:
            return 1000;

        case EK_ASSIGN:
        case EK_POSTINC:
        case EK_POSTDEC:
            return 100 + exprspeed(ex->args[0]) + exprspeed(ex->args[1]);

        default:
            cost = (ex->kind == EK_PLUS) ? 1 : 2;
            if (ex->val.type->kind == TK_REAL)
                cost *= 2;
            speed = -cost;
            for (i = 0; i < ex->nargs; i++) {
                if (!isliteralconst(ex->args[i], NULL) ||
                    ex->val.type->kind == TK_REAL)
                    speed += exprspeed(ex->args[i]) + cost;
            }
            return MAX(speed, 0);
    }
}




int noargdependencies(ex, vars)
Expr *ex;
int vars;
{
    int i;

    for (i = 0; i < ex->nargs; i++) {
        if (!nodependencies(ex->args[i], vars))
            return 0;
    }
    return 1;
}


int nodependencies(ex, vars)
Expr *ex;
int vars;   /* 1 if explicit dependencies on vars count as dependencies */
{           /* 2 if global but not local vars count as dependencies */
    Meaning *mp;

    if (debug>2) { fprintf(outf,"nodependencies("); dumpexpr(ex); fprintf(outf,")\n"); }
    if (!noargdependencies(ex, vars))
        return 0;
    switch (ex->kind) {

        case EK_VAR:
            mp = (Meaning *)ex->val.i;
	    if (mp->kind == MK_CONST)
		return 1;
	    if (vars == 2 &&
		mp->ctx == curctx &&
		mp->ctx->kind == MK_FUNCTION &&
		!mp->varstructflag)
		return 1;
            return (mp->kind == MK_CONST ||
		    (!vars &&
		     (mp->kind == MK_VAR || mp->kind == MK_VARREF ||
		      mp->kind == MK_PARAM || mp->kind == MK_VARPARAM)));

        case EK_BICALL:
            return nosideeffects_func(ex);

        case EK_FUNCTION:
        case EK_SPCALL:
        case EK_ASSIGN:
        case EK_POSTINC:
        case EK_POSTDEC:
        case EK_HAT:
        case EK_INDEX:
            return 0;

        default:
            return 1;
    }
}



int exprdependsvar(ex, mp)
Expr *ex;
Meaning *mp;
{
    int i;

    i = ex->nargs;
    while (--i >= 0)
	if (exprdependsvar(ex->args[i], mp))
	    return 1;
    switch (ex->kind) {

        case EK_VAR:
	    return ((Meaning *)ex->val.i == mp);

	case EK_BICALL:
	    if (nodependencies(ex, 1))
		return 0;

	/* fall through */
	case EK_FUNCTION:
	case EK_SPCALL:
	    return (mp->ctx != curctx ||
		    mp->ctx->kind != MK_FUNCTION ||
		    mp->varstructflag);

	case EK_HAT:
	    return 1;

	default:
	    return 0;
    }
}


int exprdepends(ex, ex2)
Expr *ex, *ex2;     /* Expression ex somehow depends on value of ex2 */
{
    switch (ex2->kind) {

        case EK_VAR:
	    return exprdependsvar(ex, (Meaning *)ex2->val.i);

	case EK_CONST:
	case EK_LONGCONST:
	    return 0;

	case EK_INDEX:
	case EK_DOT:
	    return exprdepends(ex, ex2->args[0]);

	default:
	    return !nodependencies(ex, 1);
    }
}


int nosideeffects_func(ex)
Expr *ex;
{
    Meaning *mp;
    Symbol *sp;

    switch (ex->kind) {

        case EK_FUNCTION:
            mp = (Meaning *)ex->val.i;
            sp = findsymbol_opt(mp->name);
            return sp && (sp->flags & (NOSIDEEFF|DETERMF));

        case EK_BICALL:
            sp = findsymbol_opt(ex->val.s);
            return sp && (sp->flags & (NOSIDEEFF|DETERMF));

        default:
            return 0;
    }
}



int deterministic_func(ex)
Expr *ex;
{
    Meaning *mp;
    Symbol *sp;

    switch (ex->kind) {

        case EK_FUNCTION:
            mp = (Meaning *)ex->val.i;
            sp = findsymbol_opt(mp->name);
            return sp && (sp->flags & DETERMF);

        case EK_BICALL:
            sp = findsymbol_opt(ex->val.s);
            return sp && (sp->flags & DETERMF);

        default:
            return 0;
    }
}




int noargsideeffects(ex, mode)
Expr *ex;
int mode;
{
    int i;

    for (i = 0; i < ex->nargs; i++) {
        if (!nosideeffects(ex->args[i], mode))
            return 0;
    }
    return 1;
}


/* mode=0: liberal about bicall's: safe unless sideeffects_bicall() */
/* mode=1: conservative about bicall's: must be explicitly NOSIDEEFF */

int nosideeffects(ex, mode)
Expr *ex;
int mode;
{
    if (debug>2) { fprintf(outf,"nosideeffects("); dumpexpr(ex); fprintf(outf,")\n"); }
    if (!noargsideeffects(ex, mode))
        return 0;
    switch (ex->kind) {

        case EK_BICALL:
            if (mode == 0)
                return !sideeffects_bicall(ex->val.s);

        /* fall through */
        case EK_FUNCTION:
            return nosideeffects_func(ex);

        case EK_SPCALL:
        case EK_ASSIGN:
        case EK_POSTINC:
        case EK_POSTDEC:
            return 0;

        default:
            return 1;
    }
}



int exproccurs(ex, ex2)
Expr *ex, *ex2;
{
    int i, count = 0;

    if (debug>2) { fprintf(outf,"exproccurs("); dumpexpr(ex); fprintf(outf,", "); dumpexpr(ex2); fprintf(outf,")\n"); }
    for (i = 0; i < ex->nargs; i++)
        count += exproccurs(ex->args[i], ex2);
    if (exprsame(ex, ex2, 0))
        count++;
    return count;
}



Expr *singlevar(ex)
Expr *ex;
{
    if (debug>2) { fprintf(outf,"singlevar("); dumpexpr(ex); fprintf(outf,")\n"); }
    switch (ex->kind) {

        case EK_VAR:
        case EK_MACARG:
            return ex;

        case EK_HAT:
        case EK_ADDR:
        case EK_DOT:
            return singlevar(ex->args[0]);

        case EK_INDEX:
            if (!nodependencies(ex->args[1], 1))
                return NULL;
            return singlevar(ex->args[0]);

	default:
	    return NULL;
    }
}



/* Is "ex" a function which takes a return buffer pointer as its
   first argument, and returns a copy of that pointer? */

int structuredfunc(ex)
Expr *ex;
{
    Meaning *mp;
    Symbol *sp;

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

        case EK_FUNCTION:
            mp = (Meaning *)ex->val.i;
            if (mp->isfunction && mp->cbase && mp->cbase->kind == MK_VARPARAM)
                return 1;
            sp = findsymbol_opt(mp->name);
            return sp && (sp->flags & (STRUCTF|STRLAPF));

        case EK_BICALL:
            sp = findsymbol_opt(ex->val.s);
            return sp && (sp->flags & (STRUCTF|STRLAPF));

	default:
	    return 0;
    }
}



int strlapfunc(ex)
Expr *ex;
{
    Meaning *mp;
    Symbol *sp;

    switch (ex->kind) {

        case EK_FUNCTION:
            mp = (Meaning *)ex->val.i;
            sp = findsymbol_opt(mp->name);
            return sp && (sp->flags & STRLAPF);

        case EK_BICALL:
            sp = findsymbol_opt(ex->val.s);
            return sp && (sp->flags & STRLAPF);

        default:
            return 0;
    }
}



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

    if (debug>2) { fprintf(outf,"istempvar("); dumpexpr(ex); fprintf(outf,")\n"); }
    if (ex->kind == EK_VAR) {
        mp = (Meaning *)ex->val.i;
        if (mp->istemporary)
            return mp;
        else
            return NULL;
    }
    return NULL;
}



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

    if (debug>2) { fprintf(outf,"isretvar("); dumpexpr(ex); fprintf(outf,")\n"); }
    if (ex->kind == EK_HAT)
        ex = ex->args[0];
    if (ex->kind == EK_VAR) {
        mp = (Meaning *)ex->val.i;
        if (mp->ctx && mp->ctx->kind == MK_FUNCTION &&
            mp->ctx->isfunction && mp == mp->ctx->cbase)
            return mp;
        else
            return NULL;
    }
    return NULL;
}



Expr *bumpstring(ex, index, offset)
Expr *ex, *index;
int offset;
{
    if (checkconst(index, offset)) {
        freeexpr(index);
        return ex;
    }
    if (addindex != 0)
        ex = makeexpr_plus(makeexpr_addrstr(ex),
			   makeexpr_minus(index, makeexpr_long(offset)));
    else
        ex = makeexpr_addr(makeexpr_index(ex, index, makeexpr_long(offset)));
    ex->val.type = tp_str255;
    return ex;
}

