/*
 * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
 * See the copyright notice in the ACK home directory, in the file "Copyright".
 */
/* $Id$ */
/* EXPRESSION TREE HANDLING */

#include	<assert.h>
#include    <stdlib.h>
#include	"parameters.h"
#include	<alloc.h>
#include	<flt_arith.h>
#include    "expr.h"
#include    "idf.h"
#include	"arith.h"
#include	"def.h"
#include	"type.h"
#include	"label.h"
#include	"expr.h"
#include	"LLlex.h"
#include	"Lpars.h"
#include	"decspecs.h"
#include	"declar.h"
#include	"sizes.h"
#include	"level.h"
#include    "cstoper.h"
#include    "error.h"

extern char *symbol2str();
extern char options[];
extern int InSizeof;

int rank_of(int oper)
{
	/*	The rank of the operator oper is returned.
	 */
	switch (oper)
	{
	default:
		return 0; /* INT2INT etc. */
	case '[':
	case '(':
	case '.':
	case ARROW:
	case PARCOMMA:
		return 1;
	case '!':
	case PLUSPLUS:
	case MINMIN:
	case CAST:
	case SIZEOF:
	case ADDRESSOF:
		return 2; /* monadic */
	case '*':
	case '/':
	case '%':
		return 3;
	case '+':
	case '-':
		return 4;
	case LEFT:
	case RIGHT:
		return 5;
	case '<':
	case '>':
	case LESSEQ:
	case GREATEREQ:
		return 6;
	case EQUAL:
	case NOTEQUAL:
		return 7;
	case '&':
		return 8;
	case '^':
		return 9;
	case '|':
		return 10;
	case AND:
		return 11;
	case OR:
		return 12;
	case '?':
	case ':':
		return 13;
	case '=':
	case PLUSAB:
	case MINAB:
	case TIMESAB:
	case DIVAB:
	case MODAB:
	case RIGHTAB:
	case LEFTAB:
	case ANDAB:
	case XORAB:
	case ORAB:
		return 14;
	case ',':
		return 15;
	}
	/*NOTREACHED*/
}

void dot2expr(struct expr **expp)
{
	/*	The token in dot is converted into an expression, a
	 pointer to which is stored in *expp.
	 */
	register struct expr *ex = new_expr();

	*expp = ex;
	ex->ex_file = dot.tk_file;
	ex->ex_line = dot.tk_line;
	switch (DOT)
	{
	case IDENTIFIER:
		idf2expr(ex);
		break;
	case INTEGER:
		int2expr(ex);
		break;
	case FLOATING:
		float2expr(ex);
		break;
	default:
		crash("bad conversion to expression");
		/*NOTREACHED*/
	}
}

void idf2expr(register struct expr *expr)
{
	/*	Dot contains an identifier which is turned into an
	 expression.
	 Note that this constitutes an applied occurrence of
	 the identifier.
	 */
	register struct idf *idf = dot.tk_idf; /* != 0*/
	register struct def *def = idf->id_def;

	if (def == 0)
	{
		if (AHEAD == '(')
		{
			/* function call, declare name implicitly (3.3.2.2) */
			if (!options['o'])
				warning("implicit declaration of function %s", idf->id_text);
			add_def(idf, EXTERN, funint_type, level);
		}
		else
		{
			if (!is_anon_idf(idf))
				error("%s undefined", idf->id_text);
			/* declare idf anyway */
			add_def(idf, 0, error_type, level);
		}
		def = idf->id_def;
	}
	/* now def != 0 */
#ifndef	LINT
	if (!InSizeof)
	{
		if (!def->df_used)
		{
#ifndef PREPEND_SCOPES
			code_scope(idf->id_text, def);
#endif /* PREPEND_SCOPES */
			def->df_used = 1;
		}
	}
#endif	/* LINT */
	expr->ex_type = def->df_type;
	if (expr->ex_type == error_type)
	{
		expr->ex_flags |= EX_ERROR;
	}
	expr->ex_lvalue =
			(def->df_type->tp_fund == FUNCTION || def->df_type->tp_fund == ARRAY
					|| def->df_sc == ENUM) ? 0 : 1;
	if (def->df_type->tp_typequal & TQ_CONST)
		expr->ex_flags |= EX_READONLY;
	if (def->df_type->tp_typequal & TQ_VOLATILE)
		expr->ex_flags |= EX_VOLATILE;
	expr->ex_class = Value;
	if (def->df_sc == ENUM)
	{
		expr->VL_CLASS = Const;
		expr->VL_VALUE = (writh)def->df_address;
	}
#ifndef	LINT
	else if (def->df_sc == STATIC && def->df_level >= L_LOCAL)
	{
		expr->VL_CLASS = Label;
		expr->VL_LBL = def->df_address;
		expr->VL_VALUE = 0;
	}
#endif	/* LINT */
	else
	{
		expr->VL_CLASS = Name;
		expr->VL_IDF = idf;
		expr->VL_VALUE = 0;
	}
}

void string2expr(register struct expr **expp, char *str, int len)

{
	/*	The string in the argument is converted into an expression,
	 a pointer to which is stored in *expp.
	 */
	register struct expr *ex = new_expr();

	*expp = ex;
	ex->ex_file = dot.tk_file;
	ex->ex_line = dot.tk_line;
	ex->ex_type = string_type;
	/* ex->ex_type = qualifier_type(ex->ex_type, TQ_CONST); */
	ex->ex_flags |= EX_READONLY;
	/* ex->ex_lvalue = 0; */
	ex->ex_class = String;
	ex->SG_VALUE = str;
	ex->SG_LEN = len;
}

void int2expr(struct expr *expr)
{
	/*	Dot contains an integer constant which is turned
	 into an expression.
	 */
	fill_int_expr(expr, dot.tk_ival, dot.tk_fund);
}

void float2expr(register struct expr *expr)
{
	/*	Dot contains a floating point constant which is turned
	 into an expression.
	 */
	register int fund;

	fund = dot.tk_fund;
	switch (fund)
	{
	case FLOAT:
		expr->ex_type = float_type;
		break;
	case DOUBLE:
		expr->ex_type = double_type;
		break;
	case LNGDBL:
		expr->ex_type = lngdbl_type;
		break;
	default:
		crash("(float2expr) bad fund %s\n", symbol2str(fund));
	}
	expr->ex_class = Float;
	flt_str2flt(dot.tk_fval, &(expr->FL_ARITH));
	free(dot.tk_fval);
	assert(flt_status != FLT_NOFLT);
	if (flt_status == FLT_OVFL)
		expr_warning(expr, "internal floating point overflow");
}

struct expr*intexpr(
arith ivalue, int fund)
{
	/*	The value ivalue is turned into an integer expression of
	 the size indicated by fund.
	 */
	register struct expr *expr = new_expr();

	expr->ex_file = dot.tk_file;
	expr->ex_line = dot.tk_line;
	fill_int_expr(expr, (writh)ivalue, fund);
	return expr;
}

void fill_int_expr(register struct expr *ex,
writh ivalue, int fund)
{
	/*	Details derived from ivalue and fund are put into the
	 constant integer expression ex.
	 */
	switch (fund)
	{
	case INT:
		ex->ex_type = int_type;
		break;
	case UNSIGNED:
		ex->ex_type = uint_type;
		break;
	case LONG:
		ex->ex_type = long_type;
		break;
	case ULONG:
		ex->ex_type = ulong_type;
		break;
	case LNGLNG:
		ex->ex_type = lnglng_type;
		break;
	case ULNGLNG:
		ex->ex_type = ulnglng_type;
		break;
	case ERRONEOUS:		/* 123LL when no_long_long() */
		ex->ex_type = error_type;
		ex->ex_flags |= EX_ERROR;
		break;
	default:
		crash("(fill_int_expr) bad fund %s\n", symbol2str(fund));
		/*NOTREACHED*/
	}
	ex->ex_class = Value;
	ex->VL_CLASS = Const;
	ex->VL_VALUE = ivalue;
	cut_size(ex);
}

struct expr *new_oper(struct type *tp, register struct expr *e1, int oper,
		register struct expr *e2)
{
	/*	A new expression is constructed which consists of the
	 operator oper which has e1 and e2 as operands; for a
	 monadic operator e1 == NILEXPR.
	 During the construction of the right recursive initialisation
	 tree it is possible for e2 to be NILEXPR.
	 */
	register struct expr *expr = new_expr();
	register struct oper *op;

	if (e2)
	{
		register struct expr *e = e2;

		while (e->ex_class == Oper && e->OP_LEFT)
			e = e->OP_LEFT;
		expr->ex_file = e->ex_file;
		expr->ex_line = e->ex_line;
	}
	else if (e1)
	{
		register struct expr *e = e1;

		while (e->ex_class == Oper && e->OP_RIGHT)
			e = e->OP_RIGHT;
		expr->ex_file = e->ex_file;
		expr->ex_line = e->ex_line;
	}
	else
	{
		expr->ex_file = dot.tk_file;
		expr->ex_line = dot.tk_line;
	}

	expr->ex_type = tp;
	expr->ex_class = Oper;
	/* combine depths and flags of both expressions */
	if (e2)
	{
		int e1_depth = e1 ? e1->ex_depth : 0;
		int e1_flags = e1 ? e1->ex_flags : 0;

		expr->ex_depth = (e1_depth > e2->ex_depth ? e1_depth : e2->ex_depth)
				+ 1;
		expr->ex_flags = (e1_flags | e2->ex_flags)
				& ~(EX_PARENS | EX_READONLY | EX_VOLATILE);
	}
	/*
	 * A function call should be evaluated first when possible.  Just say
	 * that the expression tree is very deep.
	 */
	if (oper == '(')
	{
		expr->ex_depth = 50;
	}
	op = &expr->ex_object.ex_oper;
	op->op_type = tp;
	op->op_oper = oper;
	op->op_left = e1;
	op->op_right = e2;
#ifdef	LINT
	lint_new_oper(expr);
#endif	/* LINT */
	return expr;
}

void chk_cst_expr(struct expr **expp)
{
	/*	The expression expr is checked for constancy.

	 There are 6 places where constant expressions occur in C:
	 1.	after #if
	 2.	in a global initialization
	 3.	as size in an array declaration
	 4.	as value in an enum declaration
	 5.	as width in a bit field
	 6.	as case value in a switch

	 The constant expression in a global initialization is
	 handled separately (by IVAL()).

	 There are various disparate restrictions on each of
	 the others in the various C compilers.  I have tried some
	 hypotheses to unify them, but all have failed.

	 Special problems (of which there is only one, sizeof in
	 Preprocessor #if) have to be dealt with locally
	 */
	register struct expr *expr = *expp;

#ifdef	DEBUG
	print_expr("constant_expression", expr);
#endif	/* DEBUG */
	switch (expr->ex_type->tp_fund)
	{
	case CHAR:
	case SHORT:
	case INT:
	case ENUM:
	case LONG:
	case LNGLNG:
		if (is_ld_cst(expr))
		{
			return;
		}
		expr_error(expr, "expression is not constant");
		break;
	default:
		expr_error(expr, "non-numerical constant expression");
		break;
	}
	erroneous2int(expp);
}

void init_expression(register struct expr ***eppp, struct expr *expr)
{
	/*	The expression expr is added to the tree designated
	 indirectly by **eppp.
	 The natural form of a tree representing an
	 initial_value_list is right-recursive, ie. with the
	 left-most comma as main operator. The iterative grammar in
	 expression.g, however, tends to produce a left-recursive
	 tree, ie. one with the right-most comma as its main
	 operator.
	 To produce a right-recursive tree from the iterative
	 grammar, we keep track of the address of the pointer where
	 the next expression must be hooked in.
	 */
	**eppp = new_oper(void_type, expr, INITCOMMA, NILEXPR);
	*eppp = &(**eppp)->OP_RIGHT;
}

int is_ld_cst(register struct expr *expr)
{
	/*	An expression is a `load-time constant' if it is of the form
	 <idf> +/- <integral> or <integral>.
	 */
#ifdef	LINT
	if (expr->ex_class == String)
	return 1;
#endif	/* LINT */
	return expr->ex_lvalue == 0 && expr->ex_class == Value;
}

int is_cp_cst(struct expr *expr)
{
	/*	An expression is a `compile-time constant' if it is a
	 load-time constant, and the idf is not there.
	 */
	return is_ld_cst(expr) && expr->VL_CLASS == Const;
}

int is_fp_cst(struct expr *expr)
{
	/*	An expression is a `floating-point constant' if it consists
	 of the float only.
	 */
	return expr->ex_class == Float;
}

int is_zero_cst(register struct expr *expr)
{
	flt_arith var;

	switch (expr->ex_class)
	{
	case Value:
		return expr->VL_VALUE == 0;
	case Float:
		flt_arith2flt((arith) 0, &var, 0);
		return flt_cmp(&var, &(expr->FL_ARITH)) == 0;
	}
	/*NOTREACHED*/
}

void free_expression(register struct expr *expr)
{
	/*	The expression expr is freed recursively.
	 */
	if (expr)
	{
		if (expr->ex_class == Oper)
		{
			free_expression(expr->OP_LEFT);
			free_expression(expr->OP_RIGHT);
		}
		free_expr(expr);
	}
}
