/*
 * $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

#ident	"$Header: expression.c 1.1 89/10/25 $"

#include <stdio.h>
#include "config.h"
#include "expression.h"

/*
 *  Evaluate an expression.
 */
expr_type
evaluate(expr, forpar)
register struct expr *expr;		/* this expression is being evaluated */
register struct symbol *forpar;		/* for this configuration parameter */
{
	static void print_expr();		/* print an expression */
	extern expr_type parameter_value();	/* return parameter value */
	register expr_type val;

	switch (ARITH_OP(expr->ex_op)) {
	case OPadd:
		val = evaluate(expr->ex_u.expr.lexpr, forpar)
		    + evaluate(expr->ex_u.expr.rexpr, forpar);
		break;
	case OPsub:
		val = evaluate(expr->ex_u.expr.lexpr, forpar)
		    - evaluate(expr->ex_u.expr.rexpr, forpar);
		break;
	case OPmul:
		val = evaluate(expr->ex_u.expr.lexpr, forpar)
		    * evaluate(expr->ex_u.expr.rexpr, forpar);
		break;
	case OPdiv:
		val = evaluate(expr->ex_u.expr.rexpr, forpar);
		if (val != 0)
			val = evaluate(expr->ex_u.expr.lexpr, forpar)/val;
		else {
			fprintf(stderr, "Division by zero occurred.\n");
			fprintf(stderr, "expression was:\n");
			print_expr(stderr, expr->ex_u.expr.rexpr);
			val = evaluate(expr->ex_u.expr.lexpr, forpar);
		}
		break;
	case OPneg:
		val = -evaluate(expr->ex_u.expr.lexpr, forpar);
		break;
	case OPsym:
		val = parameter_value(expr->ex_u.sym, forpar);
		break;
	case OPconst:
		val = expr->ex_u.constant;
		break;
	default:
		fatal("evaluate(): internal error.\n");
		exit(1);
	}
	return(val);
}

/*
 *  Function used to print an expression (left to right).
 */
void
print_expr(fp, expr)
FILE *fp;				/* output file stream */
register struct expr *expr;		/* expression tree to be printed */
{
	static void  iprint_expr();

	iprint_expr(fp, expr, EXOPadd);
}

/*
 *  Internal function to print an expression.
 */
static void
iprint_expr(fp, expr, prevop)
register FILE *fp;			/* output file stream */
register struct expr *expr;		/* expression tree to be printed */
int prevop;				/* previous node's operation */
{
	extern char *parameter_name();	/* return name of parameter */
	register int currop;		/* current operation */

	currop = expr->ex_op;
	if (ARITH_PREC(prevop) > ARITH_PREC(currop))
		putc('(', fp);
	switch (currop) {
	case EXOPadd:
		iprint_expr(fp, expr->ex_u.expr.lexpr, currop);
		fprintf(fp, " + ");
		iprint_expr(fp, expr->ex_u.expr.rexpr, currop);
		break;
	case EXOPsub:
		iprint_expr(fp, expr->ex_u.expr.lexpr, currop);
		fprintf(fp, " - ");
		iprint_expr(fp, expr->ex_u.expr.rexpr, currop);
		break;
	case EXOPmul:
		iprint_expr(fp, expr->ex_u.expr.lexpr, currop);
		fprintf(fp, "*");
		iprint_expr(fp, expr->ex_u.expr.rexpr, currop);
		break;
	case EXOPdiv:
		iprint_expr(fp, expr->ex_u.expr.lexpr, currop);
		fprintf(fp, "/");
		iprint_expr(fp, expr->ex_u.expr.rexpr, currop);
		break;
	case EXOPneg:
		fprintf(fp, "-");
		iprint_expr(fp, expr->ex_u.expr.lexpr, currop);
		break;
	case EXOPsym:
		fprintf(fp, "%s", parameter_name(expr->ex_u.sym));
		break;
	case EXOPconst:
		fprintf(fp, "%ld", (long)expr->ex_u.constant);
		break;
	default:
		fatal("iprint_expr(): internal ex_op error\n");
		exit(1);
	}
	if (ARITH_PREC(prevop) > ARITH_PREC(currop))
		putc(')', fp);
}
