/************************************************************************/
/*                                                                      */
/* 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 "rc.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, sizeof(Msg_Text), Msg_Temp, "(");
            fatal(1012);                /* missing left paren */
        }
    } else if( Currtok != L_NOTOKEN ) {
        Msg_Temp = GET_MSG(4067);
        SET_MSG (Msg_Text, sizeof(Msg_Text), Msg_Temp, PPifel_str);
        warning(4067);
    }

    if( Parencnt > 0 ) {
        Msg_Temp = GET_MSG(4012);
        SET_MSG (Msg_Text, sizeof(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;
    long PrimVal;

    val = prim();
    for(;;) {
        if( nextis(L_MULT) )
            val *= prim();
        else if( nextis(L_DIV) ) {
            PrimVal = prim();
            if (PrimVal)
                val /= PrimVal;
            else
                val = PrimVal;
        }
        else if( nextis(L_MOD) ) {
            PrimVal = prim();
            if (PrimVal)
                val %= PrimVal;
            else
                val = 0;
        }
        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, sizeof(Msg_Text), Msg_Temp, ")");
            fatal (1012);
        }
    } else if( ! nextis(L_CINTEGER) ) {
        Msg_Temp = GET_MSG(1017);
        SET_MSG (Msg_Text, sizeof(Msg_Text), Msg_Temp);
        fatal(1017);    /* invalid integer constant expression */
    }
    return(Currval);
}