/************************************************************************/
/*									*/
/* RCPP - Resource Compiler Pre-Processor for NT system			*/
/*									*/
/* P0EXPR.C - Expression routines for Pre-Processor			*/
/*									*/
/* AUTHOR - Ralph Ryan, Sept. 16, 1982					*/
/* 06-Dec-90 w-BrianM  Update for NT from PM SDK RCPP			*/
/*									*/
/************************************************************************/
/*
 * DESCRIPTION  
 *	Evaluate the constant expression.  Since these routines are
 *	all recursively coupled, it is clearer NOT to document them
 *	with the standard header.  Instead, BML (British Meta Language, 
 *	a BNF like meta language) will be given for each "production" 
 *	of this recursive descent parser.
 *
 * Note - Sure, yeah, right. Frankly, I'm frightened! (w-BrianM)
 ************************************************************************/

#include <stdio.h>
#include "rcpptype.h"
#include "rcppdecl.h"
#include "rcppext.h"
#include "grammar.h"

/************************************************************************/
/* Local Function Prototypes						*/
/************************************************************************/
long and(void);
long andif(void);
long constant(void);
long constexpr(void);
long eqset(void);
long mult(void);
long or(void);
long orelse(void);
long plus(void);
long prim(void);
long relation(void);
long shift(void);
long xor(void);


/************************************************************************/
/* File Global Variables						*/
/************************************************************************/
long	Currval = 0;
static	int		Parencnt = 0;


/************************************************************************/
/* do_constexpr()							*/
/************************************************************************/
long do_constexpr(void)
{
    REG long	val;

    Parencnt = 0;
    Currtok = L_NOTOKEN;
    val = constexpr();
    if( Currtok == L_RPAREN ) {
	if( Parencnt-- == 0 ) {
	    Msg_Temp = GET_MSG(1012);
	    SET_MSG (Msg_Text, Msg_Temp, "(");
	    fatal(1012);		/* missing left paren */
	}
    }
    else if( Currtok != L_NOTOKEN ) {
	Msg_Temp = GET_MSG(4067);
	SET_MSG (Msg_Text, Msg_Temp, PPifel_str);
	warning(4067);
    }
    if( Parencnt > 0 ) {
	Msg_Temp = GET_MSG(4012);
	SET_MSG (Msg_Text, Msg_Temp, ")");
	fatal(4012);	/* missing right paren */
    }
    return(val);
}

/************************************************************************/
/* constexpr ::= orelse [ '?' orelse ':' orelse ];			*/
/************************************************************************/
long constexpr(void)
{
    REG long		val;
    REG long		val1;
    long		val2;

    val = orelse();
    if( nextis(L_QUEST) ) {
	val1 = orelse();
	if( nextis(L_COLON) )
	    val2 = orelse();
	return(val ? val1 : val2);
    }
    return(val);
}


/************************************************************************/
/* orelse ::= andif [ '||' andif ]* ;					*/
/************************************************************************/
long orelse(void)
{
    REG long val;

    val = andif();
    while(nextis(L_OROR))
	val = andif() || val;
    return(val);
}


/************************************************************************/
/* andif ::= or [ '&&' or ]* ;						*/
/************************************************************************/
long	  andif(void)
{
    REG	long val;

    val = or();
    while(nextis(L_ANDAND))
	val = or() && val;
    return(val);
}


/************************************************************************/
/* or ::= xor [ '|' xor]* ;						*/
/************************************************************************/
long or(void)
{
    REG	long val;

    val = xor();
    while( nextis(L_OR) )
	val |= xor();
    return(val);
}


/************************************************************************/
/* xor ::= and [ '^' and]* ;						*/
/************************************************************************/
long	  xor(void)
{
    REG	long val;

    val = and();
    while( nextis(L_XOR) )
	val ^= and();
    return(val);
}


/************************************************************************/
/*  and ::= eqset [ '&' eqset]* ;					*/
/************************************************************************/
long and(void)
{
    REG	long val;

    val = eqset();
    while( nextis(L_AND) )
	val &= eqset();
    return(val);
}


/************************************************************************/
/* eqset ::= relation [ ('==' | '!=') eqset] ;				*/
/************************************************************************/
long eqset(void)
{
    REG	long val;

    val = relation();
    if( nextis(L_EQUALS) )
	return(val == relation());
    if( nextis(L_NOTEQ) )
	return(val != relation());
    return(val);
}

/************************************************************************/
/* relation ::= shift [ ('<' | '>' | '<=' | '>=' ) shift] ;		*/
/************************************************************************/
long relation(void)
{
    REG	long val;

    val = shift();
    if( nextis(L_LT) )
	return(val < shift());
    if( nextis(L_GT) )
	return(val > shift());
    if( nextis(L_LTEQ) )
	return(val <= shift());
    if( nextis(L_GTEQ) )
	return(val >= shift());
    return(val);
}


/************************************************************************/
/* shift ::= plus [ ('<< | '>>') plus] ;				*/
/************************************************************************/
long shift(void)
{
    REG	long val;

    val = plus();
    if( nextis(L_RSHIFT) )
	return(val >> plus());
    if( nextis(L_LSHIFT) )
	return(val << plus());
    return(val);
}


/************************************************************************/
/* plus ::= mult [ ('+' | '-') mult ]* ;				*/
/************************************************************************/
long	  plus(void)
{
    REG	long val;

    val = mult();
    for(;;) {
	if( nextis(L_PLUS) )
	    val += mult();
	else if( nextis(L_MINUS) )
	    val -= mult();
	else
	    break;
    }
    return(val);
}


/************************************************************************/
/* mult ::= prim [ ('*' | '/' | '%' ) prim ]* ;				*/
/************************************************************************/
long mult(void)
{
    REG	long val;

    val = prim();
    for(;;) {
	if( nextis(L_MULT) )
	    val *= prim();
	else if( nextis(L_DIV) )
	    val /= prim();
	else if( nextis(L_MOD) )
	    val %= prim();
	else
	    break;
    }
    return(val);
}


/************************************************************************/
/* prim ::= constant | ( '!' | '~' | '-' ) constant 			*/
/************************************************************************/
long prim(void)
{
    if( nextis(L_EXCLAIM) )
	return( ! constant());
    else if( nextis(L_TILDE) )
	return( ~ constant() );
    else if( nextis(L_MINUS) )
	return(-constant());
    else
	return(constant());
}


/************************************************************************/
/* constant - at last, a terminal symbol  | '(' constexpr ')'		*/
/************************************************************************/
long constant(void)
{
    REG	long val;

    if( nextis(L_LPAREN) ) {
	Parencnt++;
	val = constexpr();
	if( nextis(L_RPAREN) ) {
	    Parencnt--;
	    return(val);
	}
	else {
	    Msg_Temp = GET_MSG(1012);
	    SET_MSG (Msg_Text, Msg_Temp, ")");
	    fatal (1012);
	}
    }
    else if( ! nextis(L_CINTEGER) ) {
	Msg_Temp = GET_MSG(1017);
	SET_MSG (Msg_Text, Msg_Temp);
	fatal(1017);	/* invalid integer constant expression */
    }
    return(Currval);
}
