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

Expr *pc_expr()
{
    return pc_expr2(0);
}

Expr *pc_expr_str(buf)
char *buf;
{
    Strlist *defsl, *sl;
    Expr *ex;

    defsl = NULL;
    sl = strlist_append(&defsl, buf);
    C_lex++;
    push_input_strlist(defsl, buf);
    ex = pc_expr();
    if (curtok != TOK_EOF)
        warning(format_s("Junk (%s) at end of C expression [306]",
			 tok_name(curtok)));
    pop_input();
    C_lex--;
    strlist_empty(&defsl);
    return ex;
}

/* Simplify an expression */

Expr *fixexpr(ex, env)
Expr *ex;
int env;
{
    Expr *ex2, *ex3, **ep;
    Type *type, *type2;
    Meaning *mp;
    char *cp;
    char sbuf[5];
    int i;
    Value val;

    if (!ex)
        return NULL;
    switch (ex->kind) {

        case EK_BICALL:
            ex2 = fix_bicall(ex, env);
            if (ex2) {
                ex = ex2;
                break;
            }
            cp = ex->val.s;
            if (!strcmp(cp, "strlen")) {
                if (ex->args[0]->kind == EK_BICALL &&
                    !strcmp(ex->args[0]->val.s, "sprintf") &&
                    sprintf_value == 0) {     /* does sprintf return char count? */
                    ex = grabarg(ex, 0);
                    strchange(&ex->val.s, "*sprintf");
                    ex = fixexpr(ex, env);
                } else {
                    ex->args[0] = fixexpr(ex->args[0], ENV_EXPR);
                }
            } else if (!strcmp(cp, name_SETIO)) {
                ex->args[0] = fixexpr(ex->args[0], ENV_BOOL);
            } else if (!strcmp(cp, "~~SETIO")) {
                ex->args[0] = fixexpr(ex->args[0], ENV_BOOL);
                ex = makeexpr_cond(ex->args[0],
                                   makeexpr_long(0),
                                   makeexpr_bicall_1(name_ESCIO, tp_int, ex->args[1]));
            } else if (!strcmp(cp, name_CHKIO)) {
                ex->args[0] = fixexpr(ex->args[0], ENV_BOOL);
                ex->args[2] = fixexpr(ex->args[2], env);
                ex->args[3] = fixexpr(ex->args[3], env);
            } else if (!strcmp(cp, "~~CHKIO")) {
                ex->args[0] = fixexpr(ex->args[0], ENV_BOOL);
                ex->args[2] = fixexpr(ex->args[2], env);
                ex->args[3] = fixexpr(ex->args[3], env);
                ex2 = makeexpr_bicall_1(name_ESCIO, tp_int, ex->args[1]);
                if (ord_type(ex->args[3]->val.type)->kind != TK_INTEGER)
                    ex2 = makeexpr_cast(ex2, ex->args[3]->val.type);
                ex = makeexpr_cond(ex->args[0], ex->args[2], ex2);
            } else if (!strcmp(cp, "assert")) {
                ex->args[0] = fixexpr(ex->args[0], ENV_BOOL);
            } else {
                for (i = 0; i < ex->nargs; i++)
                    ex->args[i] = fixexpr(ex->args[i], ENV_EXPR);
		ex = cleansprintf(ex);
                if (!strcmp(cp, "sprintf")) {
                    if (checkstring(ex->args[1], "%s")) {
                        delfreearg(&ex, 1);
                        strchange(&ex->val.s, "strcpy");
                        ex = fixexpr(ex, env);
                    } else if (sprintf_value != 1 && env != ENV_STMT) {
                        if (*sprintfname) {
                            strchange(&ex->val.s, format_s("*%s", sprintfname));
                        } else {
                            strchange(&ex->val.s, "*sprintf");
                            ex = makeexpr_comma(ex, copyexpr(ex->args[0]));
                        }
                    }
                } else if (!strcmp(cp, "strcpy")) {
                    if (env == ENV_STMT &&
                         ex->args[1]->kind == EK_BICALL &&
                         !strcmp(ex->args[1]->val.s, "strcpy") &&
                         nosideeffects(ex->args[1]->args[0], 1)) {
                        ex2 = ex->args[1];
                        ex->args[1] = copyexpr(ex2->args[0]);
                        ex = makeexpr_comma(ex2, ex);
                    }
                } else if (!strcmp(cp, "memcpy")) {
                    strchange(&ex->val.s, format_s("*%s", memcpyname));
                    if (!strcmp(memcpyname, "*bcopy")) {
                        swapexprs(ex->args[0], ex->args[1]);
                        if (env != ENV_STMT)
                            ex = makeexpr_comma(ex, copyexpr(ex->args[1]));
                    }
		} else if (!strcmp(cp, setunionname) &&
			   (ex3 = singlevar(ex->args[0])) != NULL &&
			   ((i=1, exprsame(ex->args[0], ex->args[i], 0)) ||
			    (i=2, exprsame(ex->args[0], ex->args[i], 0))) &&
			   !exproccurs(ex3, ex->args[3-i])) {
		    ep = &ex->args[3-i];
		    while ((ex2 = *ep)->kind == EK_BICALL &&
			   (!strcmp(ex2->val.s, setaddname) ||
			    !strcmp(ex2->val.s, setaddrangename)))
			ep = &ex2->args[0];
		    if (ex2->kind == EK_BICALL &&
			!strcmp(ex2->val.s, setexpandname) &&
			checkconst(ex2->args[1], 0) &&
			(mp = istempvar(ex2->args[0])) != NULL) {
			if (ex2 == ex->args[3-i]) {
			    ex = grabarg(ex, i);
			} else {
			    freeexpr(ex2);
			    *ep = ex->args[i];
			    ex = ex->args[3-i];
			}
		    }
		} else if (!strcmp(cp, setdiffname) && *setremname &&
			   (ex3 = singlevar(ex->args[0])) != NULL &&
			   exprsame(ex->args[0], ex->args[1], 0) &&
			   !exproccurs(ex3, ex->args[2])) {
		    ep = &ex->args[2];
		    while ((ex2 = *ep)->kind == EK_BICALL &&
			   !strcmp(ex2->val.s, setaddname))
			ep = &ex2->args[0];
		    if (ex2->kind == EK_BICALL &&
			!strcmp(ex2->val.s, setexpandname) &&
			checkconst(ex2->args[1], 0) &&
			(mp = istempvar(ex2->args[0])) != NULL) {
			if (ex2 == ex->args[2]) {
			    ex = grabarg(ex, 1);
			} else {
			    ex2 = ex->args[2];
			    while (ex2->kind == EK_BICALL &&
				   !strcmp(ex2->val.s, setaddname)) {
				strchange(&ex2->val.s, setremname);
				ex2 = ex2->args[0];
			    }
			    freeexpr(ex2);
			    *ep = ex->args[1];
			    ex = ex->args[2];
			}
		    }
                } else if (!strcmp(cp, setexpandname) && env == ENV_STMT &&
                           checkconst(ex->args[1], 0)) {
                    ex = makeexpr_assign(makeexpr_hat(ex->args[0], 0),
                                         ex->args[1]);
                } else if (!strcmp(cp, getbitsname)) {
		    type = ex->args[0]->val.type;
		    if (type->kind == TK_POINTER)
			type = type->basetype;
                    sbuf[0] = (type->issigned) ? 'S' : 'U';
                    sbuf[1] = (type->kind == TK_ARRAY) ? 'B' : 'S';
                    sbuf[2] = 0;
                    if (sbuf[1] == 'S' &&
                        type->smax->val.type == tp_boolean) {
                        ex = makeexpr_rel(EK_NE,
                                          makeexpr_bin(EK_BAND, tp_integer,
                                                       ex->args[0],
                                                       makeexpr_bin(EK_LSH, tp_integer,
                                                                    makeexpr_longcast(makeexpr_long(1),
                                                                                      type->basetype
                                                                                            == tp_unsigned),
                                                                    ex->args[1])),
                                          makeexpr_long(0));
                        ex = fixexpr(ex, env);
                    } else
                        strchange(&ex->val.s, format_s(cp, sbuf));
                } else if (!strcmp(cp, putbitsname)) {
		    type = ex->args[0]->val.type;
		    if (type->kind == TK_POINTER)
			type = type->basetype;
                    sbuf[0] = (type->issigned) ? 'S' : 'U';
                    sbuf[1] = (type->kind == TK_ARRAY) ? 'B' : 'S';
                    sbuf[2] = 0;
                    if (sbuf[1] == 'S' &&
                        type->smax->val.type == tp_boolean) {
                        ex = makeexpr_assign(ex->args[0],
                                             makeexpr_bin(EK_BOR, tp_integer,
                                                          copyexpr(ex->args[0]),
                                                          makeexpr_bin(EK_LSH, tp_integer,
                                                                       makeexpr_longcast(ex->args[2],
                                                                                         type->basetype
                                                                                               == tp_unsigned),
                                                                       ex->args[1])));
                    } else
                        strchange(&ex->val.s, format_s(cp, sbuf));
                } else if (!strcmp(cp, storebitsname)) {
		    type = ex->args[0]->val.type;
		    if (type->kind == TK_POINTER)
			type = type->basetype;
                    sbuf[0] = (type->issigned) ? 'S' : 'U';
                    sbuf[1] = (type->kind == TK_ARRAY) ? 'B' : 'S';
                    sbuf[2] = 0;
                    strchange(&ex->val.s, format_s(cp, sbuf));
                } else if (!strcmp(cp, clrbitsname)) {
		    type = ex->args[0]->val.type;
		    if (type->kind == TK_POINTER)
			type = type->basetype;
                    sbuf[0] = (type->kind == TK_ARRAY) ? 'B' : 'S';
                    sbuf[1] = 0;
                    if (sbuf[0] == 'S' &&
                        type->smax->val.type == tp_boolean) {
                        ex = makeexpr_assign(ex->args[0],
                                             makeexpr_bin(EK_BAND, tp_integer,
                                                   copyexpr(ex->args[0]),
                                                   makeexpr_un(EK_BNOT, tp_integer,
                                                          makeexpr_bin(EK_LSH, tp_integer,
                                                                       makeexpr_longcast(makeexpr_long(1),
                                                                                         type->basetype
                                                                                               == tp_unsigned),
                                                                       ex->args[1]))));
                    } else
                        strchange(&ex->val.s, format_s(cp, sbuf));
                } else if (!strcmp(cp, "fopen")) {
		    if (which_lang == LANG_HP &&
			ex->args[0]->kind == EK_CONST &&
			ex->args[0]->val.type->kind == TK_STRING &&
			ex->args[0]->val.i >= 1 &&
			ex->args[0]->val.i <= 2 &&
			isdigit(ex->args[0]->val.s[0]) &&
			(ex->args[0]->val.i == 1 ||
			 isdigit(ex->args[0]->val.s[1]))) {
			strchange(&ex->val.s, "fdopen");
			ex->args[0] = makeexpr_long(atoi(ex->args[0]->val.s));
		    }
		}
            }
            break;

        case EK_NOT:
            ex = makeexpr_not(fixexpr(grabarg(ex, 0), ENV_BOOL));
            break;

        case EK_AND:
        case EK_OR:
            for (i = 0; i < ex->nargs; i++)
                ex->args[i] = fixexpr(ex->args[i], ENV_BOOL);
            break;

        case EK_EQ:
        case EK_NE:
            ex->args[0] = fixexpr(ex->args[0], ENV_EXPR);
            ex->args[1] = fixexpr(ex->args[1], ENV_EXPR);
            if (checkconst(ex->args[1], 0) && env == ENV_BOOL &&
                ord_type(ex->args[1]->val.type)->kind != TK_ENUM &&
                (implicitzero > 0 ||
                 (implicitzero < 0 && ex->args[0]->kind == EK_BICALL &&
                                      boolean_bicall(ex->args[0]->val.s)))) {
                if (ex->kind == EK_EQ)
                    ex = makeexpr_not(grabarg(ex, 0));
                else {
                    ex = grabarg(ex, 0);
                    ex->val.type = tp_boolean;
                }
            }
            break;

        case EK_COND:
            ex->args[0] = fixexpr(ex->args[0], ENV_BOOL);
#if 0
            val = eval_expr(ex->args[0]);
#else
	    val = ex->args[0]->val;
	    if (ex->args[0]->kind != EK_CONST)
		val.type = NULL;
#endif
            if (val.type == tp_boolean) {
                ex = grabarg(ex, (val.i) ? 1 : 2);
                ex = fixexpr(ex, env);
            } else {
                ex->args[1] = fixexpr(ex->args[1], env);
                ex->args[2] = fixexpr(ex->args[2], env);
            }
            break;

        case EK_COMMA:
            for (i = 0; i < ex->nargs-1; ) {
                ex->args[i] = fixexpr(ex->args[i], ENV_STMT);
                if (nosideeffects(ex->args[i], 1))
                    delfreearg(&ex, i);
                else
                    i++;
            }
            ex->args[ex->nargs-1] = fixexpr(ex->args[ex->nargs-1], env);
            if (ex->nargs == 1)
                ex = grabarg(ex, 0);
            break;

        case EK_CHECKNIL:
            ex->args[0] = fixexpr(ex->args[0], ENV_EXPR);
            if (ex->nargs == 2) {
                ex->args[1] = fixexpr(ex->args[1], ENV_EXPR);
                ex2 = makeexpr_assign(copyexpr(ex->args[1]), ex->args[0]);
                ex3 = ex->args[1];
            } else {
                ex2 = copyexpr(ex->args[0]);
                ex3 = ex->args[0];
            }
            type = ex->args[0]->val.type;
            type2 = ex->val.type;
            ex = makeexpr_cond(makeexpr_rel(EK_NE, ex2, makeexpr_nil()),
                               ex3,
                               makeexpr_cast(makeexpr_bicall_0(name_NILCHECK,
                                                               tp_int),
                                             type));
            ex->val.type = type2;
            ex = fixexpr(ex, env);
            break;

        case EK_CAST:
        case EK_ACTCAST:
            if (env == ENV_STMT) {
                ex = fixexpr(grabarg(ex, 0), ENV_STMT);
            } else {
                ex->args[0] = fixexpr(ex->args[0], ENV_EXPR);
            }
            break;

        default:
            for (i = 0; i < ex->nargs; i++)
                ex->args[i] = fixexpr(ex->args[i], ENV_EXPR);
            break;
    }
    return fix_expression(ex, env);
}








/* Output an expression */


#define bitOp(k)  ((k)==EK_BAND || (k)==EK_BOR || (k)==EK_BXOR)

#define shfOp(k)  ((k)==EK_LSH || (k)==EK_RSH)

#define logOp(k)  ((k)==EK_AND || (k)==EK_OR)

#define relOp(k)  ((k)==EK_EQ || (k)==EK_LT || (k)==EK_GT ||    \
		   (k)==EK_NE || (k)==EK_GE || (k)==EK_LE)

#define mathOp(k) ((k)==EK_PLUS || (k)==EK_TIMES || (k)==EK_NEG ||   \
		   (k)==EK_DIV || (k)==EK_DIVIDE || (k)==EK_MOD)

#define divOp(k)  ((k)==EK_DIV || (k)==EK_DIVIDE)


Static int incompat(ex, num, prec)
Expr *ex;
int num, prec;
{
    Expr *subex = ex->args[num];

    if (extraparens == 0)
	return prec;
    if (ex->kind == subex->kind) {
	if (logOp(ex->kind) || bitOp(ex->kind) ||
	    (divOp(ex->kind) && num == 0))
	    return -99;   /* not even invisible parens */
	else if (extraparens != 2)
	    return prec;
    }
    if (extraparens == 2)
	return 15;
    if (divOp(ex->kind) && num == 0 &&
	(subex->kind == EK_TIMES || divOp(subex->kind)))
	return -99;
    if (bitOp(ex->kind) || shfOp(ex->kind))
	return 15;
    if (relOp(ex->kind) && relOp(subex->kind))
	return 15;
    if ((relOp(ex->kind) || logOp(ex->kind)) && bitOp(subex->kind))
	return 15;
    if (ex->kind == EK_COMMA)
	return 15;
    if (ex->kind == EK_ASSIGN && relOp(subex->kind))
	return 15;
    if (extraparens != 1)
	return prec;
    if (ex->kind == EK_ASSIGN)
	return prec;
    if (relOp(ex->kind) && mathOp(subex->kind))
	return prec;
    return 15;
}

#define EXTRASPACE() if (spaceexprs == 1) output(" ")
#define NICESPACE()  if (spaceexprs != 0) output(" ")

void outop3(breakbefore, name)
int breakbefore;
char *name;
{
    if (breakbefore & BRK_LEFT) {
	output("\002");
	if (breakbefore & BRK_RPREF)
	    output("\013");
    }
    output(name);
    if (breakbefore & BRK_HANG)
	output("\015");
    if (breakbefore & BRK_RIGHT) {
	output("\002");
	if (breakbefore & BRK_LPREF)
	    output("\013");
    }
}

#define outop(name) do { \
    NICESPACE(); outop3(breakflag, name); NICESPACE(); \
} while (0)

#define outop2(name) do { \
    EXTRASPACE(); outop3(breakflag, name); EXTRASPACE(); \
} while (0)

#define checkbreak(code) do { \
    breakflag=(code); \
    if ((prec != -99) && (breakflag & BRK_ALLNONE)) output("\007"); \
} while (0)


Static void out_ctx(ctx, address)
Meaning *ctx;
int address;
{
    Meaning *ctx2;
    int breakflag = breakbeforedot;

    if (ctx->kind == MK_FUNCTION && ctx->varstructflag) {
        if (curctx != ctx) {
	    if (address && curctx->ctx && curctx->ctx != ctx) {
		output("\003");
		if (breakflag & BRK_ALLNONE)
		    output("\007");
	    }
            output(format_s(name_LINK, curctx->ctx->name));
            ctx2 = curctx->ctx;
            while (ctx2 && ctx2 != ctx) {
                outop2("->");
                output(format_s(name_LINK, ctx2->ctx->name));
                ctx2 = ctx2->ctx;
            }
            if (ctx2 != ctx)
                intwarning("out_ctx",
                           format_s("variable from %s not present in context path [307]",
                                     ctx->name));
	    if (address && curctx->ctx && curctx->ctx != ctx)
		output("\004");
            if (!address)
                outop2("->");
        } else {
            if (address) {
                output("&");
		EXTRASPACE();
	    }
            output(format_s(name_VARS, curctx->name));
            if (!address) {
                outop2(".");
	    }
        }
    } else {
        if (address)
            output("NULL");
    }
}



void out_var(mp, prec)
Meaning *mp;
int prec;
{
    switch (mp->kind) {

        case MK_CONST:
            output(mp->name);
            return;

        case MK_VAR:
        case MK_VARREF:
        case MK_VARMAC:
        case MK_PARAM:
        case MK_VARPARAM:
            if (mp->varstructflag) {
		output("\003");
                out_ctx(mp->ctx, 0);
		output(mp->name);
		output("\004");
	    } else
		output(mp->name);
            return;

	default:
	    if (mp->name)
		output(mp->name);
	    else
		intwarning("out_var", "mp->sym == NULL [308]");
	    return;
    }
}

