/* 
   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
*/
/*
 * this evaluates an expression that may have floating point values in it
 * e.g. the initializer for a module-scoped float or double
 */
#include        <stdio.h>
#include				<string.h>
#include        "lists.h"
#include        "expr.h"
#include        "c.h"
#include				"errors.h"

extern LLONG_TYPE ival;
extern long double rval;
extern enum e_sym lastst;
extern char lastid[];
extern TABLE defsyms;
extern int prm_cmangle;
extern SYM *typequal;

long double floatexpr(void);

/* Primary for constant floats
 *   id
 *   iconst
 *   rconst
 *   defined(MACRO)
 *   (cast) floatexpr
 */
static long double feprimary(void)   
{       long double     temp=0;
        SYM     *sp;
        if(lastst == id) {
								char *lid = lastid;
								if (prm_cmangle)
									lid++;
                sp = gsearch(lastid);
                if(sp == NULL) {
											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) {
                temp = ival;
                getsym();
                return temp;
                }
        else if(lastst == iuconst) {
                temp = (unsigned)ival;
                getsym();
                return temp;
                }
        else if(lastst == luconst) {
                temp = (unsigned)ival;
                getsym();
                return temp;
                }
				else if (lastst = rconst || lastst == lrconst || lastst == fconst) {
								temp = rval;
								getsym();
								return temp;
				}
				else if (lastst == openpa) {
					getsym();
					if (castbegin(lastst)) {
						typequal = 0;
						decl(0,0);
						decl1(sc_type,0);
						needpunc(closepa,0);
						return floatexpr();
					}
					else {
				  	temp = floatexpr();
						needpunc(closepa,0);
						return(temp);
					}
				}
        getsym();
        generror(ERR_NEEDCONST,0,0);
        return 0;
}
/* Unary for floating const
 *    -unary
 *    !unary
 *    primary
 */
static long double feunary(void)
{
	long double temp;
	switch (lastst) {
		case minus:
				getsym();
				temp = -feunary();
				break;
		case  en_not:
				getsym();
				temp = !feunary();
				break;
      case plus:
            getsym() ;
            temp = feunary() ;
            break ;
		default:
				temp = feprimary();
				break;
	}
	return(temp);
}
/* Multiply ops */
static long double femultops(void)
{
	long double val1 = feunary(),val2;
	while (lastst == star || lastst == divide) {
		int oper = lastst;
		getsym();
		val2 = feunary();
		switch(oper) {
			case star:
					val1 = val1 * val2;
					break;
			case divide:
					val1 = val1 / val2;
					break;
		}
	}
	return(val1);
}
/* Add ops */
static long double feaddops(void)
{
	long double val1 = femultops(),val2;
	while (lastst == plus || lastst == minus)	{
		int oper = lastst;
		getsym();
		val2 = femultops();
		if (oper == plus) 
			val1 = val1 + val2;
		else
			val1 = val1 - val2;
	}
	return(val1);
}
/* Floating point constant expression */
long double floatexpr(void)
{
	return(feaddops());
}