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

extern short candeclare;

#define BR_NEVER        0x1     /* never use braces */
#define BR_FUNCTION     0x2     /* function body */
#define BR_THENPART     0x4     /* before an "else" */
#define BR_ALWAYS       0x8     /* always use braces */
#define BR_REPEAT       0x10    /* "do-while" loop */
#define BR_TRY          0x20    /* in a recover block */
#define BR_ELSEPART     0x40    /* after an "else" */
#define BR_CASE         0x80    /* case of a switch stmt */

Static int usebraces(sp, opts)
Stmt *sp;
int opts;
{
    if (opts & (BR_FUNCTION|BR_ALWAYS))
        return 1;
    if (opts & BR_NEVER)
        return 0;
    switch (bracesalways) {
        case 0:
            if (sp) {
                if (sp->next ||
                    sp->kind == SK_TRY ||
                    (sp->kind == SK_IF && !sp->stm2) ||
                    (opts & BR_REPEAT))
                    return 1;
            }
            break;

        case 1:
            return 1;
            break;

        default:
            if (sp) {
                if (sp->next ||
                    sp->kind == SK_IF ||
                    sp->kind == SK_WHILE ||
                    sp->kind == SK_REPEAT ||
                    sp->kind == SK_TRY ||
		    sp->kind == SK_CASE ||
                    sp->kind == SK_FOR)
                    return 1;
            }
            break;
    }
    if (sp != NULL &&
	findcomment(curcomments, CMT_NOT | CMT_TRAIL, sp->serial) != NULL)
	return 1;
    return 0;
}



#define outspnl(spflag) output((spflag) ? " " : "\n")

#define openbrace()                 \
    wbraces = (!candeclare);        \
    if (wbraces) {                  \
        output("{");                \
        outspnl(braceline <= 0);    \
        candeclare = 1;             \
    }

#define closebrace()                \
    if (wbraces) {                  \
        if (sp->next || braces)     \
            output("}\n");          \
        else                        \
            braces = 1;             \
    }

Meaning *outcontext;

Static void outnl(serial)
int serial;
{
    outtrailcomment(curcomments, serial, commentindent);
}


Static void out_block(spbase, opts, serial)
Stmt *spbase;
int opts, serial;
{
    int i, j, braces, always, trynum, istrail, hascmt;
    int gotcomments = 0;
    int saveindent, saveindent2, delta;
    Stmt *sp = spbase;
    Stmt *sp2, *sp3;
    Meaning *ctx, *mp;
    Strlist *curcmt, *cmt, *savecurcmt = curcomments;
    Strlist *trailcmt, *begincmt, *endcmt;

    if (debug>1) { fprintf(outf, "out_block of:\n"); dumpstmt(spbase,5); }
    if (opts & BR_FUNCTION) {
	if (outcontext && outcontext->comments) {
	    gotcomments = 1;
	    curcomments = outcontext->comments;
	}
	attach_comments(spbase);
    }
    braces = usebraces(sp, opts);
    trailcmt = findcomment(curcomments, CMT_TRAIL, serial);
    begincmt = findcomment(curcomments, CMT_ONBEGIN, serial);
    istrail = 1;
    if (!trailcmt) {
	trailcmt = begincmt;
	begincmt = NULL;
	istrail = 0;
    }
    endcmt = findcomment(curcomments, CMT_ONEND, serial);
    if ((begincmt || endcmt) && !(opts & BR_NEVER))
	braces = 1;
    if (opts & BR_ELSEPART) {
	cmt = findcomment(curcomments, CMT_ONELSE, serial);
	if (cmt) {
	    if (trailcmt) {
		out_spaces(bracecommentindent, commentoverindent,
			   commentlen(cmt), 0);
		output("\001");
		outcomment(cmt);
	    } else
		trailcmt = cmt;
	}
    }
    if (braces) {
	j = (opts & BR_FUNCTION) ? funcopenindent : openbraceindent;
        if (!line_start()) {
	    if (trailcmt &&
		cur_column() + commentlen(trailcmt) + 2 > linewidth &&
		outindent + commentlen(trailcmt) + 2 < linewidth)  /*close enough*/
		i = 0;
	    else if (opts & BR_ELSEPART)
		i = ((braceelseline & 2) == 0);
	    else if (braceline >= 0)
		i = (braceline == 0);
	    else
                i = ((opts & BR_FUNCTION) == 0);
	    if (trailcmt && begincmt) {
		out_spaces(commentindent, commentoverindent,
			   commentlen(trailcmt), j);
		outcomment(trailcmt);
		trailcmt = begincmt;
		begincmt = NULL;
		istrail = 0;
	    } else
		outspnl(i);
        }
	if (line_start())
	    singleindent(j);
        output("{");
        candeclare = 1;
    } else if (!sp) {
        if (!line_start())
            outspnl(!nullstmtline && !(opts & BR_TRY));
	if (line_start())
	    singleindent(tabsize);
        output(";");
    }
    if (opts & BR_CASE)
	delta = 0;
    else {
	delta = tabsize;
	if (opts & BR_FUNCTION)
	    delta = adddeltas(delta, bodyindent);
	else if (braces)
	    delta = adddeltas(delta, blockindent);
    }
    futureindent(delta);
    if (bracecombine && braces)
	i = applydelta(outindent, delta) - cur_column();
    else
	i = -1;
    if (commentvisible(trailcmt)) {
	if (line_start()) {
	    singleindent(delta);
	    out_spaces(commentoverindent, 1000, commentlen(trailcmt), 0);
	    outcomment(trailcmt);
	} else /*if (commentlen(trailcmt) + cur_column() + 1 <= linewidth)*/ {
	    out_spaces(istrail ? commentindent : bracecommentindent,
		       commentoverindent, commentlen(trailcmt), delta);
	    outcomment(trailcmt);
	} /*else {
	    output("\n");
	    singleindent(delta);
	    out_spaces(commentoverindent, 1000, commentlen(trailcmt), 0);
	    outcomment(trailcmt);
	}*/
	i = -9999;
    }
    if (i > 0)
	out_spaces(i, 0, 0, 0);
    else if (i != -9999)
	output("\n");
    saveindent = outindent;
    moreindent(delta);
    outcomment(begincmt);
    while (sp) {
	flushcomments(NULL, CMT_PRE, sp->serial);
	if (cmtdebug)
	    output(format_d("[%d] ", sp->serial));
        switch (sp->kind) {

            case SK_HEADER:
                ctx = (Meaning *)sp->exp1->val.i;
		eatblanklines();
                if (declarevars(ctx, 0))
                    outsection(minorspace);
		flushcomments(NULL, CMT_NOT | CMT_ONEND, serial);
                if (ctx->kind == MK_MODULE) {
                    if (ctx->anyvarflag) {
                        output(format_s(name_MAIN, ""));
                        output("(argc, argv);\n");
                    } else {
                        output("static int _was_initialized = 0;\n");
                        output("if (_was_initialized++)\n");
			singleindent(tabsize);
                        output("return;\n");
                    }
		    while (initialcalls) {
			output(initialcalls->s);
			output(";\n");
			strlist_remove(&initialcalls, initialcalls->s);
		    }
                } else {
                    if (ctx->varstructflag && ctx->ctx->kind == MK_FUNCTION &&
                                              ctx->ctx->varstructflag) {
                        output(format_s(name_VARS, ctx->name));
                        output(".");
                        output(format_s(name_LINK, ctx->ctx->name));
                        output(" = ");
                        output(format_s(name_LINK, ctx->ctx->name));
                        output(";\n");
                    }
                    for (mp = ctx->cbase; mp; mp = mp->cnext) {
                        if ((mp->kind == MK_VAR ||    /* these are variables with */
			     mp->kind == MK_VARREF) &&
			    mp->varstructflag &&      /* initializers which were moved */
			    mp->cnext &&              /* into a varstruct, so they */
			    mp->cnext->snext == mp && /* must be initialized now */
			    mp->cnext->constdefn) {
                            if (mp->type->kind == TK_ARRAY) {
                                output("memcpy(");
                                out_var(mp, 2);
                                output(", ");
                                out_var(mp->cnext, 2);
                                output(", sizeof(");
                                out_type(mp->type, 1);
                                output("))");
                            } else {
                                out_var(mp, 2);
                                output(" = ");
                                out_var(mp->cnext, 2);
                            }
                            output(";\n");
                        }
                    }
                }
                break;

            case SK_RETURN:
                output("return");
		if (sp->exp1) {
		    switch (returnparens) {
			
		      case 0:
			output(" ");
			out_expr(sp->exp1);
			break;
			
		      case 1:
			if (spaceexprs != 0)
			    output(" ");
			out_expr_parens(sp->exp1);
			break;
			
		      default:
			if (sp->exp1->kind == EK_VAR ||
			    sp->exp1->kind == EK_CONST ||
			    sp->exp1->kind == EK_LONGCONST ||
			    sp->exp1->kind == EK_BICALL) {
			    output(" ");
			    out_expr(sp->exp1);
			} else {
			    if (spaceexprs != 0)
				output(" ");
			    out_expr_parens(sp->exp1);
			}
			break;
		    }
		}
		output(";");
		outnl(sp->serial);
                break;

            case SK_ASSIGN:
                out_expr_stmt(sp->exp1);
                output(";");
		outnl(sp->serial);
                break;

            case SK_CASE:
                output("switch (");
                out_expr(sp->exp1);
                output(")");
                outspnl(braceline <= 0);
                output("{");
		outnl(sp->serial);
		saveindent2 = outindent;
		moreindent(tabsize);
		moreindent(switchindent);
                sp2 = sp->stm1;
                while (sp2 && sp2->kind == SK_CASELABEL) {
                    outsection(casespacing);
                    sp3 = sp2;
		    i = 0;
		    hascmt = (findcomment(curcomments, -1, sp2->serial) != NULL);
		    singleindent(caseindent);
		    flushcomments(NULL, CMT_PRE, sp2->serial);
                    for (;;) {
			if (i)
			    singleindent(caseindent);
			i = 0;
                        output("case ");
                        out_expr(sp3->exp1);
                        output(":\001");
                        sp3 = sp3->stm1;
                        if (!sp3 || sp3->kind != SK_CASELABEL)
                            break;
                        if (casetabs != 1000)
                            out_spaces(casetabs, 0, 0, 0);
                        else {
                            output("\n");
			    i = 1;
			}
                    }
                    if (sp3)
                        out_block(sp3, BR_NEVER|BR_CASE, sp2->serial);
                    else {
			outnl(sp2->serial);
			if (!hascmt)
			    output("/* blank case */\n");
		    }
                    output("break;\n");
		    flushcomments(NULL, -1, sp2->serial);
                    sp2 = sp2->next;
                }
                if (sp2) {
                    outsection(casespacing);
		    singleindent(caseindent);
		    flushcomments(NULL, CMT_PRE, sp2->serial);
                    output("default:");
                    out_block(sp2, BR_NEVER|BR_CASE, sp2->serial);
                    output("break;\n");
		    flushcomments(NULL, -1, sp2->serial);
                }
                outindent = saveindent2;
                output("}");
		curcmt = findcomment(curcomments, CMT_ONEND, sp->serial);
		if (curcmt)
		    outcomment(curcmt);
		else
		    output("\n");
                break;

            case SK_CASECHECK:
		output(name_CASECHECK);
                output("();   /* CASE value range error */\n");
                break;

            case SK_FOR:
                output("for (");
		if (for_allornone)
		    output("\007");
                if (sp->exp1 || sp->exp2 || sp->exp3 || spaceexprs > 0) {
                    if (sp->exp1)
                        out_expr_top(sp->exp1);
                    else if (spaceexprs > 0)
                        output(" ");
                    output(";\002 ");
                    if (sp->exp2)
                        out_expr(sp->exp2);
                    output(";\002 ");
                    if (sp->exp3)
                        out_expr_top(sp->exp3);
                } else {
                    output(";;");
                }
                output(")");
                out_block(sp->stm1, 0, sp->serial);
                break;

            case SK_LABEL:
                if (!line_start())
                    output("\n");
		singleindent(labelindent);
                out_expr(sp->exp1);
                output(":");
                if (!sp->next)
                    output(" ;");
                outnl(sp->serial);
                break;

            case SK_GOTO:
                /* what about non-local goto's? */
                output("goto ");
                out_expr(sp->exp1);
                output(";");
		outnl(sp->serial);
                break;

            case SK_IF:
                sp2 = sp;
                for (;;) {
                    output("if (");
                    out_expr_bool(sp2->exp1);
                    output(")");
                    if (sp2->stm2) {
			cmt = findcomment(curcomments, CMT_ONELSE, sp->serial+1);
                        i = (!cmt && sp2->stm2->kind == SK_IF &&
			     !sp2->stm2->next &&
			     ((sp2->stm2->exp2)
			      ? checkconst(sp2->stm2->exp2, 1)
			      : (elseif > 0)));
			if (braceelse &&
                            (usebraces(sp2->stm1, 0) ||
                             usebraces(sp2->stm2, 0) || i))
                            always = BR_ALWAYS;
                        else
                            always = 0;
                        out_block(sp2->stm1, BR_THENPART|always, sp->serial);
                        output("else");
                        sp2 = sp2->stm2;
                        if (i) {
                            output(" ");
                        } else {
                            out_block(sp2, BR_ELSEPART|always, sp->serial+1);
                            break;
                        }
                    } else {
                        out_block(sp2->stm1, 0, sp->serial);
                        break;
                    }
                }
                break;

            case SK_REPEAT:
                output("do");
                out_block(sp->stm1, BR_ALWAYS|BR_REPEAT, sp->serial);
                output("while (");
                out_expr_bool(sp->exp1);
                output(");");
		cmt = findcomment(curcomments, CMT_ONEND, sp->serial);
		if (commentvisible(cmt)) {
		    out_spaces(commentindent, commentoverindent,
			       commentlen(cmt), 0);
		    output("\001");
		    outcomment(cmt);
		} else
		    output("\n");
                break;

            case SK_TRY:
                trynum = sp->exp1->val.i;
                output(format_d("TRY(try%d);", trynum));
                out_block(sp->stm1, BR_NEVER|BR_TRY, sp->serial);
                if (sp->exp2)
                    output(format_ds("RECOVER2(try%d,%s);", trynum,
                                     format_s(name_LABEL, format_d("try%d", trynum))));
                else
                    output(format_d("RECOVER(try%d);", trynum));
                out_block(sp->stm2, BR_NEVER|BR_TRY, sp->serial);
                output(format_d("ENDTRY(try%d);\n", trynum));
                break;

            case SK_WHILE:
                output("while (");
                out_expr_bool(sp->exp1);
                output(")");
                out_block(sp->stm1, 0, sp->serial);
                break;

            case SK_BREAK:
                output("break;");
		outnl(sp->serial);
                break;

            case SK_CONTINUE:
                output("continue;");
		outnl(sp->serial);
                break;

	    default:
	        intwarning("out_block",
			   format_s("Misplaced statement kind %s [265]",
				    stmtkindname(sp->kind)));
		break;
        }
	flushcomments(NULL, -1, sp->serial);
        candeclare = 0;
        if (debug>1) { fprintf(outf, "in out_block:\n"); dumpstmt(spbase,5); }
        sp = sp->next;
    }
    if (opts & BR_FUNCTION) {
	cmt = extractcomment(&curcomments, CMT_ONEND, serial);
	if (findcomment(curcomments, -1, -1) != NULL)  /* check for non-DONE */
	    output("\n");
	flushcomments(NULL, -1, -1);
	curcomments = cmt;
    }
    outindent = saveindent;
    if (braces) {
	if (line_start()) {
	    if (opts & BR_FUNCTION)
		singleindent(funccloseindent);
	    else
		singleindent(closebraceindent);
	}
        output("}");
	i = 1;
	cmt = findcomment(curcomments, CMT_ONEND, serial);
	if (!(opts & BR_REPEAT) && commentvisible(cmt)) {
	    out_spaces(bracecommentindent, commentoverindent,
		       commentlen(cmt), 0);
	    output("\001");
	    outcomment(cmt);
	    i = 0;
	}
	if (i) {
	    outspnl((opts & BR_REPEAT) ||
		    ((opts & BR_THENPART) && (braceelseline & 1) == 0));
	}
        candeclare = 0;
    }
    if (gotcomments) {
	outcontext->comments = curcomments;
	curcomments = savecurcmt;
    }
}





/* Should have a way to convert GOTO's to the end of the function to RETURN's */


/* Convert "_RETV = foo;" at end of function to "return foo" */

Static int checkreturns(spp, nearret)
Stmt **spp;
int nearret;
{
    Stmt *sp;
    Expr *rvar, *ex;
    Meaning *mp;
    int spnearret, spnextreturn;
    int result = 0;

    if (debug>2) { fprintf(outf, "checkreturns on:\n"); dumpstmt(*spp, 5); }
    while ((sp = *spp)) {
        spnextreturn = (sp->next &&
                        sp->next->kind == SK_RETURN && sp->next->exp1 &&
                        isretvar(sp->next->exp1) == curctx->cbase);
        spnearret = (nearret && !sp->next) || spnextreturn;
        result = 0;
        switch (sp->kind) {

            case SK_ASSIGN:
                ex = sp->exp1;
                if (ex->kind == EK_ASSIGN || structuredfunc(ex)) {
                    rvar = ex->args[0];
                    mp = isretvar(rvar);
                    if (mp == curctx->cbase && spnearret) {
                        if (ex->kind == EK_ASSIGN) {
                            if (mp->kind == MK_VARPARAM) {
                                ex = makeexpr_comma(ex, makeexpr_var(mp));
                            } else {
                                ex = grabarg(ex, 1);
                                mp->refcount--;
                            }
                        }
                        sp->exp1 = ex;
                        sp->kind = SK_RETURN;
                        if (spnextreturn) {
                            mp->refcount--;
                            sp->next = sp->next->next;
                        }
                        result = 1;
                    }
                }
                break;

            case SK_RETURN:
            case SK_GOTO:
                result = 1;
                break;

            case SK_IF:
                result = checkreturns(&sp->stm1, spnearret) &    /* NOT && */
                         checkreturns(&sp->stm2, spnearret);
                break;

            case SK_TRY:
                (void) checkreturns(&sp->stm1, 0);
                (void) checkreturns(&sp->stm2, spnearret);
                break;

            /* should handle CASE statements as well */

            default:
                (void) checkreturns(&sp->stm1, 0);
                (void) checkreturns(&sp->stm2, 0);
                break;
        }
        spp = &sp->next;
    }
    return result;
}







/* Replace all occurrences of one expression with another expression */

Expr *replaceexprexpr(ex, oldex, newex)
Expr *ex, *oldex, *newex;
{
    int i;
    Type *type;

    for (i = 0; i < ex->nargs; i++)
        ex->args[i] = replaceexprexpr(ex->args[i], oldex, newex);
    if (exprsame(ex, oldex, 2)) {
        if (ex->val.type->kind == TK_POINTER &&
            ex->val.type->basetype == oldex->val.type) {
            freeexpr(ex);
            return makeexpr_addr(copyexpr(newex));
        } else if (oldex->val.type->kind == TK_POINTER &&
                   oldex->val.type->basetype == ex->val.type) {
            freeexpr(ex);
            return makeexpr_hat(copyexpr(newex), 0);
        } else {
	    type = ex->val.type;
            freeexpr(ex);
            ex = copyexpr(newex);
	    ex->val.type = type;
	    return ex;
        }
    }
    return resimplify(ex);
}


void replaceexpr(sp, oldex, newex)
Stmt *sp;
Expr *oldex, *newex;
{
    while (sp) {
        replaceexpr(sp->stm1, oldex, newex);
        replaceexpr(sp->stm2, oldex, newex);
        if (sp->exp1)
            sp->exp1 = replaceexprexpr(sp->exp1, oldex, newex);
        if (sp->exp2)
            sp->exp2 = replaceexprexpr(sp->exp2, oldex, newex);
        if (sp->exp3)
            sp->exp3 = replaceexprexpr(sp->exp3, oldex, newex);
        sp = sp->next;
    }
}






Stmt *mixassignments(sp, mp)
Stmt *sp;
Meaning *mp;
{
    if (!sp)
        return NULL;
    sp->next = mixassignments(sp->next, mp);
    if (sp->next &&
	 sp->kind == SK_ASSIGN &&
         sp->exp1->kind == EK_ASSIGN &&
         sp->exp1->args[0]->kind == EK_VAR &&
         (!mp || mp == (Meaning *)sp->exp1->args[0]->val.i) &&
         ord_type(sp->exp1->args[0]->val.type)->kind == TK_INTEGER &&
         nodependencies(sp->exp1->args[1], 0) &&
         sp->next->kind == SK_ASSIGN &&
         sp->next->exp1->kind == EK_ASSIGN &&
         (exprsame(sp->exp1->args[0], sp->next->exp1->args[0], 1) ||
          (mp && mp->istemporary)) &&
         exproccurs(sp->next->exp1->args[1], sp->exp1->args[0]) == 1) {
        sp->next->exp1->args[1] = replaceexprexpr(sp->next->exp1->args[1],
                                                  sp->exp1->args[0],
                                                  sp->exp1->args[1]);
        if (mp && mp->istemporary)
            canceltempvar(mp);
        return sp->next;
    }
    return sp;
}

/* Do various simple (sometimes necessary) massages on the statements */

Stmt bogusreturn = { SK_RETURN, NULL, NULL, NULL, NULL, NULL, NULL };

Static int isescape(ex)
Expr *ex;
{
    if (ex->kind == EK_BICALL && (!strcmp(ex->val.s, name_ESCAPE) ||
                                  !strcmp(ex->val.s, name_ESCIO) ||
				  !strcmp(ex->val.s, name_OUTMEM) ||
				  !strcmp(ex->val.s, name_CASECHECK) ||
				  !strcmp(ex->val.s, name_NILCHECK) ||
                                  !strcmp(ex->val.s, "_exit") ||
                                  !strcmp(ex->val.s, "exit")))
        return 1;
    if (ex->kind == EK_CAST)
        return isescape(ex->args[0]);
    return 0;
}


/* check if a block can never exit by falling off the end */
Static int deadendblock(sp)
Stmt *sp;
{
    if (!sp)
        return 0;
    while (sp->next)
        sp = sp->next;
    return (sp->kind == SK_GOTO ||
            sp->kind == SK_BREAK ||
            sp->kind == SK_CONTINUE ||
            sp->kind == SK_RETURN ||
            sp->kind == SK_CASECHECK ||
            (sp->kind == SK_IF && deadendblock(sp->stm1) &&
                                  deadendblock(sp->stm2)) ||
            (sp->kind == SK_ASSIGN && isescape(sp->exp1)));
}




int expr_is_bool(ex, want)
Expr *ex;
int want;
{
    long val;

    if (ex->val.type == tp_boolean && isconstexpr(ex, &val))
        return (val == want);
    return 0;
}




/* Returns 1 if c1 implies c2, 0 otherwise */
/* If not1 is true, then checks if (!c1) implies c2; similarly for not2 */

/* Identities used:
        c1 -> (c2a && c2b)      <=>     (c1 -> c2a) && (c1 -> c2b)
        c1 -> (c2a || c2b)      <=>     (c1 -> c2a) || (c1 -> c2b)
        (c1a && c1b) -> c2      <=>     (c1a -> c2) || (c1b -> c2)
        (c1a || c1b) -> c2      <=>     (c1a -> c2) && (c1b -> c2)
        (!c1) -> (!c2)          <=>     c2 -> c1
        (a == b) -> c2(b)       <=>     c2(a)
        !(c1 && c2)             <=>     (!c1) || (!c2)
        !(c1 || c2)             <=>     (!c1) && (!c2)
*/
/* This could be smarter about, e.g., (a>5) -> (a>0) */

int implies(c1, c2, not1, not2)
Expr *c1, *c2;
int not1, not2;
{
    Expr *ex;
    int i;

    if (c1->kind == EK_EQ && c1->args[0]->val.type == tp_boolean) {
        if (checkconst(c1->args[0], 1)) {     /* things like "flag = true" */
            return implies(c1->args[1], c2, not1, not2);
        } else if (checkconst(c1->args[1], 1)) {
            return implies(c1->args[0], c2, not1, not2);
        } else if (checkconst(c1->args[0], 0)) {
            return implies(c1->args[1], c2, !not1, not2);
        } else if (checkconst(c1->args[1], 0)) {
            return implies(c1->args[0], c2, !not1, not2);
        }
    }
    if (c2->kind == EK_EQ && c2->args[0]->val.type == tp_boolean) {
        if (checkconst(c2->args[0], 1)) {
            return implies(c1, c2->args[1], not1, not2);
        } else if (checkconst(c2->args[1], 1)) {
            return implies(c1, c2->args[0], not1, not2);
        } else if (checkconst(c2->args[0], 0)) {
            return implies(c1, c2->args[1], not1, !not2);
        } else if (checkconst(c2->args[1], 0)) {
            return implies(c1, c2->args[0], not1, !not2);
        }
    }
    switch (c2->kind) {

        case EK_AND:
            if (not2)               /* c1 -> (!c2a || !c2b) */
                return (implies(c1, c2->args[0], not1, 1) ||
                        implies(c1, c2->args[1], not1, 1));
            else                    /* c1 -> (c2a && c2b) */
                return (implies(c1, c2->args[0], not1, 0) &&
                        implies(c1, c2->args[1], not1, 0));

        case EK_OR:
            if (not2)               /* c1 -> (!c2a && !c2b) */
                return (implies(c1, c2->args[0], not1, 1) &&
                        implies(c1, c2->args[1], not1, 1));
            else                    /* c1 -> (c2a || c2b) */
                return (implies(c1, c2->args[0], not1, 0) ||
                        implies(c1, c2->args[1], not1, 0));

        case EK_NOT:                /* c1 -> (!c2) */
            return (implies(c1, c2->args[0], not1, !not2));

        case EK_CONST:
            if ((c2->val.i != 0) != not2)  /* c1 -> true */
                return 1;
            break;

	default:
	    break;
    }
    switch (c1->kind) {

        case EK_AND:
            if (not1)               /* (!c1a || !c1b) -> c2 */
                return (implies(c1->args[0], c2, 1, not2) &&
                        implies(c1->args[1], c2, 1, not2));
            else                    /* (c1a && c1b) -> c2 */
                return (implies(c1->args[0], c2, 0, not2) ||
                        implies(c1->args[1], c2, 0, not2));

        case EK_OR:
            if (not1)               /* (!c1a && !c1b) -> c2 */
                return (implies(c1->args[0], c2, 1, not2) ||
                        implies(c1->args[1], c2, 1, not2));
            else                    /* (c1a || c1b) -> c2 */
                return (implies(c1->args[0], c2, 0, not2) &&
                        implies(c1->args[1], c2, 0, not2));

        case EK_NOT:                /* (!c1) -> c2 */
            return (implies(c1->args[0], c2, !not1, not2));

        case EK_CONST:
            if ((c1->val.i != 0) == not1)  /*  false -> c2 */
                return 1;
            break;

        case EK_EQ:                 /* (a=b) -> c2 */
        case EK_ASSIGN:             /* (a:=b) -> c2 */
        case EK_NE:                 /* (a<>b) -> c2 */
            if ((c1->kind == EK_NE) == not1) {
                if (c1->args[0]->kind == EK_VAR) {
                    ex = replaceexprexpr(copyexpr(c2), c1->args[0], c1->args[1]);
                    i = expr_is_bool(ex, !not2);
                    freeexpr(ex);
                    if (i)
                        return 1;
                }
                if (c1->args[1]->kind == EK_VAR) {
                    ex = replaceexprexpr(copyexpr(c2), c1->args[1], c1->args[0]);
                    i = expr_is_bool(ex, !not2);
                    freeexpr(ex);
                    if (i)
                        return 1;
                }
            }
            break;

	default:
	    break;
    }
    if (not1 == not2 && exprequiv(c1, c2)) {    /* c1 -> c1 */
        return 1;
    }
    return 0;
}





void infiniteloop(sp)
Stmt *sp;
{
    switch (infloopstyle) {

        case 1:      /* write "for (;;) ..." */
            sp->kind = SK_FOR;
            freeexpr(sp->exp1);
            sp->exp1 = NULL;
            break;

        case 2:      /* write "while (1) ..." */
            sp->kind = SK_WHILE;
            freeexpr(sp->exp1);
            sp->exp1 = makeexpr_val(make_ord(tp_boolean, 1));
            break;

        case 3:      /* write "do ... while (1)" */
            sp->kind = SK_REPEAT;
            freeexpr(sp->exp1);
            sp->exp1 = makeexpr_val(make_ord(tp_boolean, 1));
            break;

        default:     /* leave it alone */
            break;

    }
}





Expr *print_func(ex)
Expr *ex;
{
    if (!ex || ex->kind != EK_BICALL)
	return NULL;
    if ((!strcmp(ex->val.s, "printf") &&
	 ex->args[0]->kind == EK_CONST) ||
	!strcmp(ex->val.s, "putchar") ||
	!strcmp(ex->val.s, "puts"))
	return ex_output;
    if ((!strcmp(ex->val.s, "fprintf") ||
	 !strcmp(ex->val.s, "sprintf")) &&
	ex->args[1]->kind == EK_CONST)
	return ex->args[0];
    if (!strcmp(ex->val.s, "putc") ||
	!strcmp(ex->val.s, "fputc") ||
	!strcmp(ex->val.s, "fputs"))
	return ex->args[1];
    return NULL;
}

