/*
 *	Constant expression evaluator -- performs a standard recursive
 *	descent parse to evaluate any legal C constant expression.  All
 *	operators without side-effects are implemented (thus ++, --, and all
 *	assignment operators are omitted).  
 *
 *	The macro expansion routine is called to process the expression
 *	before it is evaluated.  All macros used in the expression must
 *	ultimately evaluate to legal C constants.  This code was originally
 *	written by Mike Lutz and was modified by Bob Harper to fit into this
 *	program and to conform to C language standards.
 */

/*
 * Include global constant defintions.
 */

#include "mpdefs.h"

/*
 * Variables global to expr
 */

static char *nxtch = EOS;	/* Parser scan pointer			*/
static char expbuf[EXPBUFSZ];	/* Buffer for expansion routine		*/
/*
 * For longjmp
 */
#include	<setjmp.h>
static jmp_buf	exp_jump;

expr(exptr)
char *exptr;
{
	char		*dst;

	setjmp(exp_jump);
	dst = expbuf;
	if (setjmp(exp_jump) != 0)
		return (FALSE);
	if (expand(exptr, &dst, expbuf+EXPBUFSZ-2, 0) == ERROR)
		return(FALSE);
	*dst = EOS;
	nxtch = expbuf;
	if (setjmp(exp_jump) != 0)
		return (FALSE);
	return(eval());
}

/*	<eval> ::= <query> <end-of-string>	*/

eval()
{
	register int rval;

	rval = query() ;
	if ( skipws() == EOS )
		return( rval ) ;
	experr( "Ill-formed expression" ) ;
}

/*	<query> ::= <lor> | <lor> '?' <query> ':' <query>	*/

query()
{
	register int bool, true_val, false_val ;

	bool = lor() ;
	if ( skipws() != '?' ) {
		ungetch() ;
		return( bool ) ;
	}

	true_val = query() ;
	if ( skipws() != ':' )
		experr( "Bad query" ) ;

	false_val = query() ;
	return( bool ? true_val : false_val ) ;
}

/*	<lor> ::= <land> { '||' <land> }	*/

lor()
{
	register int c, vl, vr ;

	vl = land() ;
	while ( (c = skipws()) == '|' && getch() == '|' ) {
		vr = land() ;
		vl = vl || vr ;
	}

	if ( c == '|' )
		ungetch() ;
	ungetch() ;
	return( vl ) ;
}

/*	<land> ::= <bor> { '&&' <bor> }		*/

land()
{
	register int c, vl, vr ;

	vl = bor() ;
	while ( (c = skipws()) == '&' && getch() == '&' ) {
		vr = bor() ;
		vl = vl && vr ;
	}

	if ( c == '&' )
		ungetch() ;
	ungetch() ;
	return( vl ) ;
}

/*	<bor> ::= <bxor> { '|' <bxor> }		*/

bor()
{
	register int vl, vr, c ;

	vl = bxor() ;
	while ( (c = skipws()) == '|' && getch() != '|' ) {
		ungetch() ;
		vr = bxor() ;
		vl |= vr ;
	}

	if ( c == '|' )
		ungetch() ;
	ungetch() ;
	return( vl ) ;
}

/*	<bxor> ::= <band> { '^' <band> }	*/

bxor()
{
	register int vl, vr ;

	vl = band() ;
	while ( skipws() == '^' ) {
		vr = band() ;
		vl ^= vr ;
	}

	ungetch() ;
	return( vl ) ;
}

/*	<band> ::= <eql> { '&' <eql> }	*/

band()
{
	register int vl, vr, c ;

	vl = eql() ;
	while ( (c = skipws()) == '&' && getch() != '&' ) {
		ungetch() ;
		vr = eql() ;
		vl &= vr ;
	}

	if ( c == '&' )
		ungetch() ;
	ungetch() ;
	return( vl ) ;
}

/*	<eql> ::= <relat> { <eqrel> <relat> }	*/

eql()
{
	register int vl, vr, rel ;

	vl = relat() ;
	while ( (rel = geteql()) != -1 ) {
		vr = relat() ;

		switch ( rel ) {

		case EQL:
			vl = (vl == vr) ;
			break ;
		case NEQ:
			vl = (vl != vr) ;
			break ;
		}
	}
	return( vl ) ;
}

/*	<relat> ::= <shift> { <rel> <shift> }	*/

relat()
{
	register int vl, vr, rel ;

	vl = shift() ;
	while ( (rel = getrel()) != -1 ) {

		vr = shift() ;
		switch ( rel ) {

		case LEQ:
			vl = (vl <= vr) ;
			break ;
		case LSS:
			vl = (vl < vr) ;
			break ;
		case GTR:
			vl = (vl > vr) ;
			break ;
		case GEQ:
			vl = (vl >= vr) ;
			break ;
		}
	}
	return( vl ) ;
}

/*	<shift> ::= <primary> { <shop> <primary> }	*/

shift()
{
	register int vl, vr, c ;

	vl = primary() ;
	while ( ( (c = skipws()) == '<' || c == '>' ) && c == getch() ) {
		vr = primary() ;

		if ( c == '<' )
			vl <<= vr ;
		else
			vl >>= vr ;
	}

	if ( c == '<' || c == '>' )
		ungetch() ;
	ungetch() ;
	return( vl ) ;
}

/*	<primary> ::= <term> { <addop> <term> }		*/

primary()
{
	register int c, vl, vr ;

	vl = term() ;
	while ( (c = skipws()) == '+' || c == '-' ) {
		vr = term() ;
		if ( c == '+' )
			vl += vr ;
		else
			vl -= vr ;
	}

	ungetch() ;
	return( vl ) ;
}

/*	<term> := <unary> { <mulop> <unary> }	*/

term()
{
	register int c, vl, vr ;

	vl = unary() ;
	while ( (c = skipws()) == '*' || c == '/' || c == '%' ) {
		vr = unary() ;

		switch ( c ) {
			case '*':
				vl *= vr ;
				break ;
			case '/':
				vl /= vr ;
				break ;
			case '%':
				vl %= vr ;
				break ;
		}
	}
	ungetch() ;
	return( vl ) ;
}

/*	<unary> ::= <factor> | <unop> <unary>	*/

unary()
{
	register int val, c ;

	if ( (c = skipws()) == '!' || c == '~' || c == '-' ) {
		val = unary() ;

		switch ( c ) {
		case '!':	return( ! val ) ;
		case '~':	return( ~ val ) ;
		case '-':	return( - val ) ;
		}
	}

	ungetch() ;
	return( factor() ) ;
}

/*	<factor> ::= <num> | '(' <query> ')'	*/

factor()
{
	register int val ;

	if ( skipws() == '(' ) {
		val = query() ;
		if ( skipws() != ')' )
			experr( "Bad factor" ) ;
		return( val ) ;
	}

	ungetch() ;
	return( const() ) ;
}

/*	<const> ::= <num> | '<char>'	*/

const()
{
/*
 * Note: const() handles multi-byte constants
 */

	register int	i;
	register int	value;
	register char	c;
	int		v[sizeof (int)];


	if (skipws() != '\'') {
		ungetch();
		return(num(0));
	}
	for (i = 0; i < sizeof(int); i++) {
		if ((c = getch()) == '\'') {
			ungetch();
			break;
		}
		if (c == '\\') {
			switch ( c = getch() ) {
			case '0':
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
				ungetch();
				c = num(1);
				break;
			case 'n':
				c = 012;
				break;
			case 'r':
				c = 015;
				break;
			case 't':
				c = 011;
				break;
			case 'b':
				c = 010;
				break;
			case 'f':
				c = 014;
				break;
			}
		}
		v[i] = c;
	}
	if (i == 0 || getch() != '\'')
		experr("Illegal character constant");
	for (value = 0; --i >= 0;) {
		value <<= 8;
		value += v[i];
	}
	return(value);
}

/*	<num> ::= <digit> | <num> <digit>	*/

num(flag)
int	flag;	/* if flag is zero, the first byte selects conversion	*/
{
	register int rval, c, base;
	int ndig;

	c = skipws();
	base = (flag || c == '0') ? OCTAL : DECIMAL;
	rval = 0 ;
	ndig = 0 ;
	while ( c >= '0' && c <= (base == OCTAL ? '7' : '9') ) {
		rval *= base ;
		rval += (c - '0') ;
		c = getch() ;
		ndig++ ;
	}

	ungetch() ;
	if ( ndig )
		return( rval ) ;
	experr( "Bad constant" ) ;
}

/*	<eqlrel> ::= '=' | '==' | '!='	*/

geteql()
{
	register int c1, c2 ;

	c1 = skipws() ; c2 = getch() ;

	switch ( c1 ) {

	case '=':
		if ( c2 != '=' )
			ungetch() ;
		return( EQL ) ;

	case '!':
		if ( c2 == '=' )
			return( NEQ ) ;
		ungetch() ; ungetch() ;
		return( -1 ) ;

	default:
		ungetch() ; ungetch() ;
		return( -1 ) ;
	}
}


/*	<rel> ::= '<' | '>' | '<=' | '>='	*/

getrel()
{
	register int c1, c2;

	c1 = skipws();
	c2 = getch() ;

	switch ( c1 ) {

	case '<':
		if ( c2 == '=' )
			return( LEQ ) ;
		ungetch() ;
		return( LSS ) ;

	case '>':
		if ( c2 == '=' )
			return( GEQ ) ;
		ungetch() ;
		return( GTR ) ;

	default:
		ungetch() ; ungetch() ;
		return( -1 ) ;
	}
}

/*	return next character from the expression string.	*/

getch()
{
	return(*nxtch++);
}

/*	Put back the last character examined.	*/

ungetch()
{
	return( *--nxtch ) ;
}

/*	Skip over any white space and return terminating char.	*/

skipws()
{
	register char c;

	while ( (c = getch()) <= ' ' && c > EOS )
		;
	return( c ) ;
}

/* 
 * Error handler - resets environment to eval(), prints an error,
 * and returns FALSE
 */

experr(msg)
char *msg;
{
	printerr(msg);
	longjmp(exp_jump, -1);		/* Force eval() to return FALSE	*/
}
