/* 
   Copyright 1994-2003 Free Software Foundation, Inc.

   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; either version 2 of the License, or
   (at your option) any later 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; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
   02111-1307, USA.  

   You may contact the author at:

   mailto::camille@bluegrass.net

   or by snail mail at:

   David Lindauer
   850 Washburn Ave Apt 99
   Louisville, KY 40222
*/
/*
 * Evaluate an expression which should be known at compile time.
 * This uses recursive descent.  It is roughly analogous to the
 * primary expression handler except it returns a value rather than
 * an enode list
 */
#include        <stdio.h>
#include				<string.h>
#include        "lists.h"
#include        "expr.h"
#include        "c.h"
#include				"errors.h"

extern enum e_sym lastst;
extern char lastid[];
extern LLONG_TYPE ival;
extern TABLE defsyms;
extern TYP stdint, stdchar,stduns, stdunsigned, stdlong, stdunsignedlong, *head;
extern int prm_cmangle;
extern SYM *typequal;
extern TABLE lsyms;
extern TABLE           tagtable ;
extern IFSTRUCT *ifs ;

static int intexprunsigned(TYP *tp1, TYP *tp2)
{
	if (tp1 == &stduns || tp1 == &stdunsigned)
		return TRUE ;
	return (tp2 == &stduns || tp2 ==&stdunsigned) ;
}
static LLONG_TYPE ieprimary(TYP **tp)   
/*
 * PRimary integer
 *    id
 *    iconst
 *    (cast )intexpr
 *    (intexpr)
 */
{       LLONG_TYPE     temp=0;
        SYM     *sp;
				int needclose ;
				if (tp)
					*tp = &stdint;
        if(lastst == id) {
					char *lid = lastid;
					if (prm_cmangle)
						lid++;
                	sp = gsearch(lastid);
                	if(sp == NULL) {
												if (!ifs)
													gensymerror(ERR_UNDEFINED,lastid);
                        getsym();
                        return 0;
                        }
                	if(sp->storage_class != sc_const && sp->tp->type != bt_enum) {
                        generror(ERR_NEEDCONST,0,0);
                        getsym();
                        return 0;
                        }
                	getsym();
                	return sp->value.i;
        }
        else if(lastst == iconst) {
                temp = ival;
                getsym();
                return temp;
                }
        else if(lastst == lconst) {
								if (tp)
									*tp = &stdlong;
                temp = ival;
                getsym();
                return temp;
                }
        else if(lastst == llconst) {
								if (tp)
                           *tp = &stdlong;
                temp = ival;
                getsym();
                return temp;
                }
        else if(lastst == iuconst) {
								if (tp)
									*tp = &stduns;
                temp = ival;
                getsym();
                return temp;
                }
        else if(lastst == luconst) {
								if (tp)
									*tp = &stdunsigned;
                temp = ival;
                getsym();
                return temp;
                }
        else if(lastst == lluconst) {
								if (tp)
                           *tp = &stdunsignedlong;
                temp = ival;
                getsym();
                return temp;
                }

        else if(lastst == cconst) {
								if (tp)
									*tp = &stdchar;
                temp = ival;
                getsym();
                return temp;
                }
				else if (lastst == openpa) {
					getsym();
					if (castbegin(lastst)) {
						typequal = 0;
						decl(0,0);
						decl1(sc_type,0);
						needpunc(closepa,0);
						if (tp)
						  *tp = head;
						return intexpr(0);
					}
					else {
				  	temp = intexpr(tp);
						needpunc(closepa,0);
						return(temp);
					}
				} else if (lastst == kw_sizeof) {
          getsym();
					needclose = FALSE;
					if (lastst == openpa) {
						needclose = TRUE;
						getsym();
					}
					head = 0;
					if (castbegin(lastst)) {
						typequal = 0;
            decl(0,0);
						decl1(sc_type,0);
					}
					else if (lastst == id) {
						SYM *sp = gsearch(lastid);
						if (!sp)
							sp = search(lastid,&lsyms);
						if (!sp)
							 sp = search(lastid,&tagtable) ;
						if (sp) {
							head = sp->tp ;
						} else {
							generror(ERR_SIZE,0,0);
							head = 0;
						}
						getsym() ;
					}
					else if (lastst == kw_enum) {
						getsym();
						if (lastst == id) {
							SYM *sp;
							if ((sp = search(lastid,&tagtable)) != 0) {
								head = sp->tp;
								getsym();
							}
						}
					}
          if( head != 0 ) {
					 	if (head->size == 0)
					 		generror(ERR_SIZE,0,0);
						temp = head->size ;
					}
          else {
            generror(ERR_IDEXPECT,0,0);	
						temp = 0;
          }
					if (needclose)
           	needpunc(closepa,0);
					return temp ;					
				}
        getsym();
        generror(ERR_NEEDCONST,0,0);
        return 0;
}
/*
 * Integer unary
 *   - unary
 *   ! unary
 *   ~unary
 *   primary
 */
static LLONG_TYPE ieunary(TYP **tp)
{
   LLONG_TYPE temp;
	switch (lastst) {
		case minus:
				getsym();
				temp = -ieunary(tp);
				break;
		case not:
				getsym();
				temp = !ieunary(tp);
				break;
		case compl:
				getsym();
				temp = ~ieunary(tp);
				break;
      case plus:
            getsym() ;
            temp = ieunary(tp) ;
            break ;
		default:
				temp = ieprimary(tp);
				break;
	}
	return(temp);
}
static LLONG_TYPE iemultops(TYP **tp)
/* Multiply ops */
{
   LLONG_TYPE val1 = ieunary(tp),val2;
	while (lastst == star || lastst == divide || lastst == modop) {
		TYP *tp1;
		long oper = lastst;
		getsym();
		val2 = ieunary(&tp1);
		switch(oper) {
			case star:
					if (intexprunsigned(*tp,tp1))
                  val1 = (unsigned LLONG_TYPE)val1 * (unsigned LLONG_TYPE) val2 ;
					else
						val1 = val1 * val2;
					break;
			case divide:
					if (intexprunsigned(*tp,tp1))
                  val1 = (unsigned LLONG_TYPE)val1 / (unsigned LLONG_TYPE) val2 ;
					else
						val1 = val1 / val2;
					break;
			case modop:
					if (intexprunsigned(*tp,tp1))
                  val1 = (unsigned LLONG_TYPE)val1 / (unsigned LLONG_TYPE) val2 ;
					else
						val1 = val1 % val2;
					break;
		}
		if (tp)
			*tp = maxsize(*tp,tp1);
	}
	return(val1);
}
static LLONG_TYPE ieaddops(TYP **tp)
/* Add ops */
{
   LLONG_TYPE val1 = iemultops(tp),val2;
	while (lastst == plus || lastst == minus)	{
		long oper = lastst;
		TYP *tp1;
		getsym();
		val2 = iemultops(&tp1);
		if (oper == plus) 
			val1 = val1 + val2;
		else
			val1 = val1 - val2;
		if (tp)
			*tp = maxsize(*tp,tp1);
	}
	return(val1);
}
static LLONG_TYPE ieshiftops(TYP **tp)
/* Shift ops */
{
   LLONG_TYPE val1 = ieaddops(tp), val2;
	while (lastst == lshift || lastst == rshift) {
		long oper = lastst;
		TYP *tp1;
		getsym();
		val2 = ieaddops(&tp1);
		if (oper == lshift)
			val1 <<= val2;
		else
			if (intexprunsigned(*tp,tp1)) {
            unsigned LLONG_TYPE xx = val1 ;
				xx >>= val2 ;
				val1 = xx ;
			} else
				val1 >>= val2;
		if (tp)
			*tp = maxsize(*tp,tp1);
	}
	return(val1);
}
static LLONG_TYPE ierelation(TYP **tp)
/* non-eq relations */
{
   LLONG_TYPE val1 = ieshiftops(tp), val2;
	while (lastst == lt || lastst == gt || lastst == leq || lastst == geq) {
		long oper = lastst;
		TYP *tp1;
		getsym();
		val2 = ieshiftops(&tp1);
		switch(oper) {
			case lt:
					if (intexprunsigned(*tp,tp1))
                  val1 = (unsigned LLONG_TYPE)val1 < (unsigned LLONG_TYPE)val2;
					else
						val1 = val1 < val2;
					break;
			case gt:
					if (intexprunsigned(*tp,tp1))
                  val1 = (unsigned LLONG_TYPE)val1 > (unsigned LLONG_TYPE)val2;
					else
						val1 = val1 > val2;
					break;
			case leq:
					if (intexprunsigned(*tp,tp1))
                  val1 = (unsigned LLONG_TYPE)val1 <= (unsigned LLONG_TYPE)val2;
					else
						val1 = val1 <= val2;
					break;
			case geq:
					if (intexprunsigned(*tp,tp1))
                  val1 = (unsigned LLONG_TYPE)val1 >= (unsigned LLONG_TYPE)val2;
					else
						val1 = val1 >= val2;
					break;
		}
		if (tp)
			*tp = maxsize(*tp,tp1);
	}
	return(val1);
}
static LLONG_TYPE ieequalops(TYP **tp)
/* eq relations */
{
   LLONG_TYPE val1 = ierelation(tp),val2;
	while (lastst == eq || lastst == neq) {
		long oper = lastst;
		TYP *tp1;
		getsym();
		val2 = ierelation(&tp1);
		if (oper == neq)
			val1 = val1 != val2;
		else
			val1 = val1 == val2;
		if (tp)
			*tp = maxsize(*tp,tp1);
	}
	return(val1);
}
static LLONG_TYPE ieandop(TYP **tp)
/* and op */
{
   LLONG_TYPE val1 = ieequalops(tp),val2;
	while (lastst == and) {
		TYP *tp1;
		getsym();
		val2 = ieequalops(&tp1);
		val1 = val1 & val2;
		if (tp)
			*tp = maxsize(*tp,tp1);
	}
	return(val1);
}
static LLONG_TYPE iexorop(TYP **tp)
/* xor op */
{
   LLONG_TYPE val1 = ieandop(tp),val2;
	while (lastst == uparrow) {
		TYP *tp1;
		getsym();
		val2 = ieandop(&tp1);
		val1 = val1 ^ val2;
		if (tp)
			*tp = maxsize(*tp,tp1);
	}         
	return(val1);
}
static LLONG_TYPE ieorop(TYP **tp)
/* or op */
{
   LLONG_TYPE val1 = iexorop(tp),val2;
	while (lastst == or) {
		TYP *tp1;
		getsym();
		val2 = iexorop(&tp1);
		val1 = val1 | val2;
		if (tp)
			*tp = maxsize(*tp,tp1);
	}
	return(val1);
}
static LLONG_TYPE ielandop(TYP **tp)
/* logical and op */
{
   LLONG_TYPE val1 = ieorop(tp),val2;
	while (lastst == land) {
		TYP *tp1;
		getsym();
		val2 = ieorop(&tp1);
		val1 = val1 && val2;
		if (tp)
			*tp = maxsize(*tp,tp1);
	}
	return(val1);
}
static LLONG_TYPE ielorop(TYP **tp)
/* logical or op */
{
   LLONG_TYPE val1 = ielandop(tp),val2;
	while (lastst == lor) {
		TYP *tp1;
		getsym();
		val2 = ielandop(&tp1);
		val1 = val1 || val2;
		if (tp)
			*tp = maxsize(*tp,tp1);
	}
	return(val1);
}
static LLONG_TYPE iecondop(TYP **tp)
/* Hook op */
{
   LLONG_TYPE val1 = ielorop(tp),val2, val3;
		if (lastst == hook) {
			TYP *tp1, *tp2;
			getsym();
			val2 = iecondop(&tp1);
			needpunc(colon,0);
			val3 = iecondop(&tp2);
			if (val1)
				val1 = val2;
			else
				val1 = val3;
		if (tp)
			*tp = maxsize(tp2,tp1);
		}
	return(val1);
}
LLONG_TYPE intexpr(TYP **tp)
/* Integer expressions */
{
	TYP *tpx ;
   LLONG_TYPE val ;
	val = iecondop(&tpx);
	if (tp)
		*tp = tpx ;
	return val ;
}