/*
 * CDE - Common Desktop Environment
 *
 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
 *
 * These libraries and programs are free software; you can
 * redistribute them and/or modify them under the terms of the GNU
 * Lesser General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * These libraries and programs are distributed in the hope that
 * they will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with these libraries and programs; if not, write
 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301 USA
 */
/* $XConsortium: streval.c /main/3 1995/11/01 16:51:05 rswiston $ */
/***************************************************************
*                                                              *
*                      AT&T - PROPRIETARY                      *
*                                                              *
*        THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF        *
*                    AT&T BELL LABORATORIES                    *
*         AND IS NOT TO BE DISCLOSED OR USED EXCEPT IN         *
*            ACCORDANCE WITH APPLICABLE AGREEMENTS             *
*                                                              *
*                Copyright (c) 1995 AT&T Corp.                 *
*              Unpublished & Not for Publication               *
*                     All Rights Reserved                      *
*                                                              *
*       The copyright notice above does not evidence any       *
*      actual or intended publication of such source code      *
*                                                              *
*               This software was created by the               *
*           Advanced Software Technology Department            *
*                    AT&T Bell Laboratories                    *
*                                                              *
*               For further information contact                *
*                    {research,attmail}!dgk                    *
*                                                              *
***************************************************************/

/* : : generated by proto : : */

#if !defined(__PROTO__)
#if defined(__STDC__) || defined(__cplusplus) || defined(_proto) || defined(c_plusplus)
#if defined(__cplusplus)
#define __MANGLE__	"C"
#else
#define __MANGLE__
#endif
#define __STDARG__
#define __PROTO__(x)	x
#define __OTORP__(x)
#define __PARAM__(n,o)	n
#if !defined(__STDC__) && !defined(__cplusplus)
#if !defined(c_plusplus)
#define const
#endif
#define signed
#define void		int
#define volatile
#define __V_		char
#else
#define __V_		void
#endif
#else
#define __PROTO__(x)	()
#define __OTORP__(x)	x
#define __PARAM__(n,o)	o
#define __MANGLE__
#define __V_		char
#define const
#define signed
#define void		int
#define volatile
#endif
#if defined(__cplusplus) || defined(c_plusplus)
#define __VARARG__	...
#else
#define __VARARG__
#endif
#if defined(__STDARG__)
#define __VA_START__(p,a)	va_start(p,a)
#else
#define __VA_START__(p,a)	va_start(p)
#endif
#endif
#include	"streval.h"
#include	<ctype.h>
#include	<error.h>
#include	"FEATURE/externs"

#define MAXLEVEL	9

struct vars			 /* vars stacked per invocation		*/
{
	const char*	nextchr; /* next char in current expression	*/
	const char*	errchr;	 /* next char after error		*/
	struct lval	errmsg;	 /* error message text			*/
	const char*	errstr;  /* error string			*/
	unsigned char	paren;	 /* parenthesis level			*/
	char		isfloat; /* set when floating number		*/
};


#define getchr()	(*cur.nextchr++)
#define peekchr()	(*cur.nextchr)
#define ungetchr()	(cur.nextchr--)

#define pushchr(s)	{struct vars old;old=cur;cur.nextchr=((char*)(s));cur.errmsg.value=0;cur.errstr=0;cur.paren=0;
#define popchr()	cur=old;}
#define ERROR(msg)	return(seterror(msg))

static struct vars	cur;
static char		noassign;	/* set to skip assignment	*/
static int		level;
static double		(*convert) __PROTO__((const char**,struct lval*,int,double));
				/* external conversion routine		*/
static double		expr __PROTO__((int));	/* subexpression evaluator	*/
static double		seterror __PROTO__((const char[]));	/* set error message string	*/


/*
 * evaluate an integer arithmetic expression in s
 *
 * (double)(*convert)(char** end, struct lval* string, int type, double value)
 *     is a user supplied conversion routine that is called when unknown 
 *     chars are encountered.
 * *end points to the part to be converted and must be adjusted by convert to
 * point to the next non-converted character; if typ is ERRMSG then string
 * points to an error message string
 *
 * NOTE: (*convert)() may call strval()
 */

double strval __PARAM__((const char *s, char** end, double(*conv)(const char**,struct lval*,int,double)), (s, end, conv)) __OTORP__(const char *s; char** end; double(*conv)();){
	register double	n;

	pushchr(s);
	cur.isfloat = 0;
	convert = conv;
	if(level++ >= MAXLEVEL)
		(void)seterror(e_recursive);
	else
		n = expr(0);
	if (cur.errmsg.value)
	{
		if(cur.errstr) s = cur.errstr;
		(void)(*convert)( &s , &cur.errmsg, ERRMSG, n);
		cur.nextchr = cur.errchr;
		n = 0;
	}
	if (end) *end = (char*)cur.nextchr;
	if(level>0) level--;
	popchr();
	return(n);
}

/*   
 * evaluate a subexpression with precedence
 */

static double expr __PARAM__((register int precedence), (precedence)) __OTORP__(register int precedence;){
	register int	c, op;
	register double	n, x;
	int		wasop, incr=0;
	struct lval	lvalue, assignop;
	const char	*pos;
	char		invalid=0;

	while ((c=getchr()) && isspace(c));
	switch (c)
	{
	case 0:
		if(precedence>5)
			ERROR(e_moretokens);
		return(0);

	case '-':
		incr = -2;
		/* FALL THRU */
	case '+':
		incr++;
		if(c != peekchr())
		{
			/* unary plus or minus */
			n = incr*expr(2*MAXPREC-1);
			incr = 0;
		}
		else /* ++ or -- */
		{
			invalid = 1;
			getchr();
		}
		break;

	case '!':
		n = !expr(2*MAXPREC-1);
		break;
	case '~':
	{
		long nl;
		n = expr(2*MAXPREC-1);
		nl = (long)n;
		if(nl != n)
			ERROR(e_incompatible);
		else
			n = ~nl;
		break;
	}
	default:
		ungetchr();
		invalid = 1;
		break;
	}
	wasop = invalid;
	lvalue.value = 0;
	lvalue.fun = 0;
	while(1)
	{
		cur.errchr = cur.nextchr;
		if((c=getchr()) >= sizeof(strval_states))
			op = (c=='|' ? A_OR: (c=='^'?A_XOR:A_REG));
		else if((op=strval_states[c])==0)
			continue;
		switch(op)
		{
			case A_EOF:
				ungetchr();
				break;
			case A_DIG:
#ifdef future
				n = c - '0';
				while((c=getchr()), isdigit(c))
					n = (10*n) + (c-'0');
				wasop = 0;
				ungetchr();
				continue;
#endif
			case A_REG:	case A_DOT:
				op = 0;
				break;
			case A_QUEST:
				if(*cur.nextchr==':')
				{
					cur.nextchr++;
					op = A_QCOLON;
				}
				break;
			case A_LT:	case A_GT: 
				if(*cur.nextchr==c)
				{
					cur.nextchr++;
					op -= 2;
					break;
				}
				/* FALL THRU */
			case A_NOT:	case A_COLON:
				c = '=';
				/* FALL THRU */
			case A_ASSIGN:
			case A_PLUS:	case A_MINUS:
			case A_OR:	case A_AND:
				if(*cur.nextchr==c)
				{
					cur.nextchr++;
					op--;
				}
		}
		assignop.value = 0;
		if(op && wasop++ && op!=A_LPAR)
			ERROR(e_synbad);
		/* check for assignment operation */
		if(peekchr()== '=' && !(strval_precedence[op]&NOASSIGN))
		{
			if(!noassign && (!lvalue.value || precedence > 2))
				ERROR(e_notlvalue);
			assignop = lvalue;
			getchr();
			c = 3;
		}
		else
		{
			c = (strval_precedence[op]&PRECMASK);
			c *= 2;
		}
		/* from here on c is the new precedence level */
		if(lvalue.value && (op!=A_ASSIGN))
		{
			if(noassign)
				n = 1;
			else
			{
				pos = cur.nextchr;
				n = (*convert)(&cur.nextchr, &lvalue, VALUE, n);
				if (cur.nextchr!=pos)
				{
					if(cur.errmsg.value = lvalue.value)
						cur.errstr = cur.nextchr;
					ERROR(e_synbad);
				}
			}
			if(cur.nextchr==0)
				ERROR(e_badnum);
			if(!(strval_precedence[op]&SEQPOINT))
				lvalue.value = 0;
			invalid = 0;
		}
		if(invalid && op>A_ASSIGN)
			ERROR(e_synbad);
		if(precedence >= c)
			goto done;
		if(strval_precedence[op]&RASSOC)
			c--;
		if(c < 2*MAXPREC && !(strval_precedence[op]&SEQPOINT))
		{
			wasop = 0;
			x = expr(c);
		}
		if((strval_precedence[op]&NOFLOAT) && !noassign && cur.isfloat)
			ERROR(e_incompatible);
		switch(op)
		{
		case A_RPAR:
			if(!cur.paren)
				ERROR(e_paren);
			if(invalid)
				ERROR(e_synbad);
			goto done;

		case A_COMMA:
			wasop = 0;
			n = expr(c);
			lvalue.value = 0;
			break;

		case A_LPAR:
		{
			char	savefloat = cur.isfloat;
			double (*fun) __PROTO__((double));
			fun = lvalue.fun;
			lvalue.fun = 0;
			cur.isfloat = 0;
			if(!invalid)
				ERROR(e_synbad);
			cur.paren++;
			n = expr(1);
			cur.paren--;
			if(fun)
			{
				n = (*fun)(n);
				cur.isfloat = (fun!=floor);
			}
			cur.isfloat |= savefloat;
			if (getchr() != ')')
				ERROR(e_paren);
			wasop = 0;
			break;
		}

		case A_PLUSPLUS:
			incr = 1;
			goto common;
		case A_MINUSMINUS:
			incr = -1;
		common:
			x = n;
			wasop=0;
		case A_ASSIGN:
			if(!noassign && !lvalue.value)
				ERROR(e_notlvalue);
			n = x;
			assignop = lvalue;
			lvalue.value = 0;
			break;

		case A_QUEST:
			if(!n)
				noassign++;
			x = expr(1);
			if(!n)
				noassign--;
			if(getchr()!=':')
				ERROR(e_questcolon);
			if(n)
			{
				n = x;
				noassign++;
				(void)expr(c);
				noassign--;
			}
			else
				n = expr(c);
			lvalue.value = 0;
			wasop = 0;
			break;

		case A_COLON:
			(void)seterror(e_badcolon);
			break;

		case A_OR:
			n = (long)n | (long)x;
			break;

		case A_QCOLON:
		case A_OROR:
			if(n)
			{
				noassign++;
				expr(c);
				noassign--;
			}
			else
				n = expr(c);
			if(op==A_OROR)
				n = (n!=0);
			lvalue.value = 0;
			wasop=0;
			break;

		case A_XOR:
			n = (long)n ^ (long)x;
			break;

		case A_NOT:
			ERROR(e_synbad);

		case A_AND:
			n = (long)n & (long)x;
			break;

		case A_ANDAND:
			if(n==0)
			{
				noassign++;
				expr(c);
				noassign--;
			}
			else
				n = (expr(c)!=0);
			lvalue.value = 0;
			wasop=0;
			break;

		case A_EQ:
			n = n == x;
			break;

		case A_NEQ:
			n = n != x;
			break;

		case A_LT:
			n = n < x;
			break;

		case A_LSHIFT:
			n = (long)n << (long)x;
			break;

		case A_LE:
			n = n <= x;
			break;

		case A_GT:
			n = n > x;
			break;

		case A_RSHIFT:
			n = (long)n >> (long)x;
			break;

		case A_GE:
			n = n >= x;
			break;

		case A_PLUS:
			n +=  x;
			break;

		case A_MINUS:
			n -=  x;
			break;

		case A_TIMES:
			n *=  x;
			break;

		case A_DIV:
			if(x!=0)
			{
				if(cur.isfloat)
					n /=  x;
				else
					n =  (long)n / (long)x;
				break;
			}

		case A_MOD:
			if(x!=0)
				n = (long)n % (long)x;
			else if(!noassign)
				ERROR(e_divzero);
			break;

		default:
			if(!wasop)
				ERROR(e_synbad);
			wasop = 0;
			pos = --cur.nextchr;
			lvalue.isfloat = 0;
			n = (*convert)(&cur.nextchr, &lvalue, LOOKUP, n);
			if (cur.nextchr == pos)
			{
				if(cur.errmsg.value = lvalue.value)
					cur.errstr = pos;
				ERROR(e_synbad);
			}
			cur.isfloat |= lvalue.isfloat;
	
			/* check for function call */
			if(lvalue.fun)
				continue;
			/* this handles ++x and --x */
			if(!noassign && incr)
			{
				if(lvalue.value)
				{
					pos = cur.nextchr;
					n = (*convert)(&cur.nextchr, &lvalue, VALUE, n);
					if (cur.nextchr!=pos)
					{
						if(cur.errmsg.value = lvalue.value)
							cur.errstr=cur.nextchr;
						ERROR(e_synbad);
					}
				}
				n += incr;
				incr = 0;
				goto common;
			}
			break;
		}
		invalid = 0;
		if(!noassign && assignop.value)
			(void)(*convert)(&cur.nextchr,&assignop,ASSIGN,n+incr);
		incr = 0;
	}
 done:
	cur.nextchr = cur.errchr;
	return(n);
}

/*
 * set error message string
 */

static double seterror __PARAM__((const char *msg), (msg)) __OTORP__(const char *msg;){
	if(!cur.errmsg.value)
		cur.errmsg.value = (char*)msg;
	cur.errchr = cur.nextchr;
	cur.nextchr = "";
	level = 0;
	return(0);
}

#ifdef _mem_name_exception
#undef error
    int matherr __PARAM__((struct exception *ep), (ep)) __OTORP__(struct exception *ep;){
	const char *message;
	switch(ep->type)
	{
	    case DOMAIN:
		message = e_domain;
		break;
	    case SING:
		message = e_singularity;
		break;
	    case OVERFLOW:
		message = e_overflow;
		break;
	    default:
		return(1);
	}
	level=0;
	error(ERROR_exit(1),message,ep->name);
	return(0);
    }
#endif /* _mem_name_exception */
