/*-
 * Copyright (c) 1998, 2002-2008 Kiyoshi Matsui <kmatsui@t3.rim.or.jp>
 * All rights reserved.
 *
 * Some parts of this code are derived from the public domain software
 * DECUS cpp (1984,1985) written by Martin Minow.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 *                              E V A L . C
 *                  E x p r e s s i o n   E v a l u a t i o n
 *
 * The routines to evaluate #if expression are placed here.
 * Some routines are used also to evaluate the value of numerical tokens.
 */

#if PREPROCESSED
#include    "mcpp.H"
#else
#include    "system.H"
#include    "internal.H"
#endif

typedef struct optab {
    char    op;                     /* Operator                     */
    char    prec;                   /* Its precedence               */
    char    skip;                   /* Short-circuit: non-0 to skip */
} OPTAB;

static int      eval_lex( void);
                /* Get type and value of token  */
static int      chk_ops( void);
                /* Check identifier-like ops    */
static VAL_SIGN *   eval_char( char * const token);
                /* Evaluate character constant  */
static expr_t   eval_one( char ** seq_pp, int wide, int mbits, int * ucn8);
                /* Evaluate a character         */
static VAL_SIGN *   eval_eval( VAL_SIGN * valp, int op);
                /* Entry to #if arithmetic      */
static expr_t   eval_signed( VAL_SIGN ** valpp, expr_t v1, expr_t v2, int op);
                /* Do signed arithmetic of expr.*/
static expr_t   eval_unsigned( VAL_SIGN ** valpp, uexpr_t v1u, uexpr_t v2u
        , int op);
                /* Do unsigned arithmetic       */
static void     overflow( const char * op_name, VAL_SIGN ** valpp
        , int ll_overflow);
                /* Diagnose overflow of expr.   */
static int      do_sizeof( void);
                /* Evaluate sizeof (type)       */
static int      look_type( int typecode);
                /* Look for type of the name    */
static void     dump_val( const char * msg, const VAL_SIGN * valp);
                /* Print value of an operand    */
static void     dump_stack( const OPTAB * opstack, const OPTAB * opp
        , const VAL_SIGN * value, const VAL_SIGN * valp);
                /* Print stacked operators      */

/* For debug and error messages.    */
static const char * const   opname[ OP_END + 1] = {
    "end of expression",    "val",  "(",
    "unary +",      "unary -",      "~",    "!",
    "*",    "/",    "%",
    "+",    "-",    "<<",   ">>",
    "<",    "<=",   ">",    ">=",   "==",   "!=",
    "&",    "^",    "|",    "&&",   "||",
    "?",    ":",
    ")",    "(none)"
};

/*
 * opdope[] has the operator (and operand) precedence:
 *     Bits
 *        7     Unused (so the value is always positive)
 *      6-2     Precedence (0000 .. 0174)
 *      1-0     Binary op. flags:
 *          10  The next binop flag (binop should/not follow).
 *          01  The binop flag (should be set/cleared when this op is seen).
 * Note:   next binop
 * value    1   0   Value doesn't follow value.
 *                  Binop, ), END should follow value, value or unop doesn't.
 *  (       0   0   ( doesn't follow value.  Value follows.
 * unary    0   0   Unop doesn't follow value.  Value follows.
 * binary   0   1   Binary op follows value.  Value follows.
 *  )       1   1   ) follows value.  Binop, ), END follows.
 * END      0   1   END follows value, doesn't follow ops.
 */

static const char   opdope[ OP_END + 1] = {
    0001,                               /* End of expression        */
    0002, 0170,                         /* VAL (constant), LPA      */
/* Unary op's   */
    0160, 0160, 0160, 0160,             /* PLU, NEG, COM, NOT       */
/* Binary op's  */
    0151, 0151, 0151,                   /* MUL, DIV, MOD,           */
    0141, 0141, 0131, 0131,             /* ADD, SUB, SL, SR         */
    0121, 0121, 0121, 0121, 0111, 0111, /* LT, LE, GT, GE, EQ, NE   */
    0101, 0071, 0061, 0051, 0041,       /* AND, XOR, OR, ANA, ORO   */
    0031, 0031,                         /* QUE, COL                 */
/* Parens       */
    0013, 0023                          /* RPA, END                 */
};
/*
 * OP_QUE, OP_RPA and unary operators have alternate precedences:
 */
#define OP_RPA_PREC     0013
#define OP_QUE_PREC     0024    /* From right to left grouping      */
#define OP_UNOP_PREC    0154            /*      ditto               */

/*
 * S_ANDOR and S_QUEST signal "short-circuit" boolean evaluation, so that
 *      #if FOO != 0 && 10 / FOO ...
 * doesn't generate an error message.  They are stored in optab.skip.
 */
#define S_ANDOR         2
#define S_QUEST         1

static VAL_SIGN     ev;     /* Current value and signedness     */
static int          skip = 0;   /* 3-way signal of skipping expr*/
static const char * const   non_eval
        = " (in non-evaluated sub-expression)";             /* _W8_ */

#if HAVE_LONG_LONG && COMPILER == INDEPENDENT
    static int  w_level = 1;    /* warn_level at overflow of long   */
#else
    static int  w_level = 2;
#endif

/*
 * In KR and OLD_PREP modes.
 * Define bits for the basic types and their adjectives.
 */
#define T_CHAR          1
#define T_INT           2
#define T_FLOAT         4
#define T_DOUBLE        8
#define T_LONGDOUBLE    16
#define T_SHORT         32
#define T_LONG          64
#define T_LONGLONG      128
#define T_SIGNED        256
#define T_UNSIGNED      512
#define T_PTR           1024        /* Pointer to data objects      */
#define T_FPTR          2048        /* Pointer to functions         */

/*
 * The SIZES structure is used to store the values for #if sizeof.
 */
typedef struct sizes {
        int             bits;       /* If this bit is set,          */
        int             size;       /* this is the datum size value */
        int             psize;      /* this is the pointer size     */
} SIZES;

/*
 * S_CHAR, etc.  define the sizeof the basic TARGET machine word types.
 *      By default, sizes are set to the values for the HOST computer.  If
 *      this is inappropriate, see those tables for details on what to change.
 *      Also, if you have a machine where sizeof (signed int) differs from
 *      sizeof (unsigned int), you will have to edit those tables and code in
 *      eval.c.
 * Note: sizeof in #if expression is disallowed by Standard.
 */

#define S_CHAR      (sizeof (char))
#define S_SINT      (sizeof (short int))
#define S_INT       (sizeof (int))
#define S_LINT      (sizeof (long int))
#define S_FLOAT     (sizeof (float))
#define S_DOUBLE    (sizeof (double))
#define S_PCHAR     (sizeof (char *))
#define S_PSINT     (sizeof (short int *))
#define S_PINT      (sizeof (int *))
#define S_PLINT     (sizeof (long int *))
#define S_PFLOAT    (sizeof (float *))
#define S_PDOUBLE   (sizeof (double *))
#define S_PFPTR     (sizeof (int (*)()))
#if HAVE_LONG_LONG
#if (HOST_COMPILER == BORLANDC) \
        || (HOST_COMPILER == MSC && defined(_MSC_VER) && (_MSC_VER < 1300))
#define S_LLINT     (sizeof (__int64))
#define S_PLLINT    (sizeof (__int64 *))
#else
#define S_LLINT     (sizeof (long long int))
#define S_PLLINT    (sizeof (long long int *))
#endif
#endif
#define S_LDOUBLE   (sizeof (long double))
#define S_PLDOUBLE  (sizeof (long double *))

typedef struct types {
    int         type;               /* This is the bits for types   */
    char *      token_name;         /* this is the token word       */
    int         excluded;           /* but these aren't legal here. */
} TYPES;

#define ANYSIGN     (T_SIGNED | T_UNSIGNED)
#define ANYFLOAT    (T_FLOAT | T_DOUBLE | T_LONGDOUBLE)
#if HAVE_LONG_LONG
#define ANYINT      (T_CHAR | T_SHORT | T_INT | T_LONG | T_LONGLONG)
#else
#define ANYINT      (T_CHAR | T_SHORT | T_INT | T_LONG)
#endif

static const TYPES  basic_types[] = {
    { T_CHAR,       "char",         ANYFLOAT | ANYINT },
    { T_SHORT,      "short",        ANYFLOAT | ANYINT },
    { T_INT,        "int",          ANYFLOAT | T_CHAR | T_INT },
    { T_LONG,       "long",         ANYFLOAT | ANYINT },
#if HAVE_LONG_LONG
#if HOST_COMPILER == BORLANDC
    { T_LONGLONG,   "__int64",      ANYFLOAT | ANYINT },
#else
    { T_LONGLONG,   "long long",    ANYFLOAT | ANYINT },
#endif
#endif
    { T_FLOAT,      "float",        ANYFLOAT | ANYINT | ANYSIGN },
    { T_DOUBLE,     "double",       ANYFLOAT | ANYINT | ANYSIGN },
    { T_LONGDOUBLE, "long double",  ANYFLOAT | ANYINT | ANYSIGN },
    { T_SIGNED,     "signed",       ANYFLOAT | ANYINT | ANYSIGN },
    { T_UNSIGNED,   "unsigned",     ANYFLOAT | ANYINT | ANYSIGN },
    { 0,            NULL,           0 }     /* Signal end           */
};

/*
 * In this table, T_FPTR (pointer to function) should be placed last.
 */
static const SIZES  size_table[] = {
    { T_CHAR,   S_CHAR,     S_PCHAR     },          /* char         */
    { T_SHORT,  S_SINT,     S_PSINT     },          /* short int    */
    { T_INT,    S_INT,      S_PINT      },          /* int          */
    { T_LONG,   S_LINT,     S_PLINT     },          /* long         */
#if HAVE_LONG_LONG
    { T_LONGLONG, S_LLINT,  S_PLLINT    },          /* long long    */
#endif
    { T_FLOAT,  S_FLOAT,    S_PFLOAT    },          /* float        */
    { T_DOUBLE, S_DOUBLE,   S_PDOUBLE   },          /* double       */
    { T_LONGDOUBLE, S_LDOUBLE, S_PLDOUBLE },        /* long double  */
    { T_FPTR,   0,          S_PFPTR     },          /* int (*())    */
    { 0,        0,          0           }           /* End of table */
};

#define is_binary(op)   (FIRST_BINOP <= op && op <= LAST_BINOP)
#define is_unary(op)    (FIRST_UNOP  <= op && op <= LAST_UNOP)


#if MCPP_LIB
void    init_eval( void)
{
    skip = 0;
}
#endif

expr_t  eval_if( void)
/*
 * Evaluate a #if expression.  Straight-forward operator precedence.
 * This is called from directive() on encountering an #if directive.
 * It calls the following routines:
 * eval_lex()   Lexical analyser -- returns the type and value of
 *              the next input token.
 * eval_eval()  Evaluates the current operator, given the values on the
 *              value stack.  Returns a pointer to the (new) value stack.
 */
{
    VAL_SIGN        value[ NEXP * 2 + 1];   /* Value stack          */
    OPTAB           opstack[ NEXP * 3 + 1]; /* Operator stack       */
    int             parens = 0;     /* Nesting levels of (, )       */
    int             prec;           /* Operator precedence          */
    int             binop = 0;      /* Set if binary op. needed     */
    int             op1;            /* Operator from stack          */
    int             skip_cur;       /* For short-circuit testing    */
    VAL_SIGN *      valp = value;   /* -> Value and signedness      */
    OPTAB *         opp = opstack;  /* -> Operator stack            */
    int             op;             /* Current operator             */

    opp->op = OP_END;               /* Mark bottom of stack         */
    opp->prec = opdope[ OP_END];    /* And its precedence           */
    skip = skip_cur = opp->skip = 0;        /* Not skipping now     */

    while (1) {
        if (mcpp_debug & EXPRESSION)
            mcpp_fprintf( DBG
                    , "In eval loop skip = %d, binop = %d, line is: %s\n"
                    , opp->skip, binop, infile->bptr);
        skip = opp->skip;
        op = eval_lex();
        skip = 0;                   /* Reset to be ready to return  */
        switch (op) {
        case OP_SUB :
            if (binop == 0)
                op = OP_NEG;                /* Unary minus          */
            break;
        case OP_ADD :
            if (binop == 0)
                op = OP_PLU;                /* Unary plus           */
            break;
        case OP_FAIL:
            return  0L;                     /* Token error          */
        }
        if (mcpp_debug & EXPRESSION)
            mcpp_fprintf( DBG
                    , "op = %s, opdope = %04o, binop = %d, skip = %d\n"
                    , opname[ op], opdope[ op], binop, opp->skip);
        if (op == VAL) {                    /* Value?               */
            if (binop != 0) {               /* Binop is needed      */
                cerror( "Misplaced constant \"%s\""         /* _E_  */
                        , work_buf, 0L, NULL);
                return  0L;
            } else if (& value[ NEXP * 2] <= valp) {
                cerror( "More than %.0s%ld constants stacked at %s" /* _E_  */
                        , NULL, (long) (NEXP * 2 - 1), work_buf);
                return  0L;
            } else {
                if (mcpp_debug & EXPRESSION) {
                    dump_val( "pushing ", &ev);
                    mcpp_fprintf( DBG, " onto value stack[%d]\n"
                            , (int)(valp - value));
                }
                valp->val = ev.val;
                (valp++)->sign = ev.sign;
                binop = 1;  /* Binary operator or so should follow  */
            }
            continue;
        }                                   /* Else operators       */
        prec = opdope[ op];
        if (binop != (prec & 1)) {
            if (op == OP_EOE)
                cerror( "Unterminated expression"           /* _E_  */
                        , NULL, 0L, NULL);
            else
                cerror( "Operator \"%s\" in incorrect context"      /* _E_  */
                        , opname[ op], 0L, NULL);
            return  0L;
        }
        binop = (prec & 2) >> 1;            /* Binop should follow? */

        while (1) {
            if (mcpp_debug & EXPRESSION)
                mcpp_fprintf( DBG
                        , "op %s, prec %d, stacked op %s, prec %d, skip %d\n"
                , opname[ op], prec, opname[ opp->op], opp->prec, opp->skip);

            /* Stack coming sub-expression of higher precedence.    */
            if (opp->prec < prec) {
                if (op == OP_LPA) {
                    prec = OP_RPA_PREC;
                    if (standard && (warn_level & 4)
                            && ++parens == std_limits.exp_nest + 1)
                        cwarn(
                    "More than %.0s%ld nesting of parens"   /* _W4_ */
                            , NULL, (long) std_limits.exp_nest, NULL);
                } else if (op == OP_QUE) {
                    prec = OP_QUE_PREC;
                } else if (is_unary( op)) {
                    prec = OP_UNOP_PREC;
                }
                op1 = opp->skip;            /* Save skip for test   */
                /*
                 * Push operator onto operator stack.
                 */
                opp++;
                if (& opstack[ NEXP * 3] <= opp) {
                    cerror(
            "More than %.0s%ld operators and parens stacked at %s"  /* _E_  */
                            , NULL, (long) (NEXP * 3 - 1), opname[ op]);
                    return  0L;
                }
                opp->op = op;
                opp->prec = prec;
                if (&value[0] < valp)
                    skip_cur = (valp[-1].val != 0L);
                                            /* Short-circuit tester */
                /*
                 * Do the short-circuit stuff here.  Short-circuiting
                 * stops automagically when operators are evaluated.
                 */
                if ((op == OP_ANA && ! skip_cur)
                        || (op == OP_ORO && skip_cur)) {
                    opp->skip = S_ANDOR;    /* And/or skip starts   */
                    if (skip_cur)           /* Evaluate non-zero    */
                        valp[-1].val = 1L;  /*   value to 1         */
                } else if (op == OP_QUE) {  /* Start of ?: operator */
                    opp->skip = (op1 & S_ANDOR) | (!skip_cur ? S_QUEST : 0);
                } else if (op == OP_COL) {  /* : inverts S_QUEST    */
                    opp->skip = (op1 & S_ANDOR)
                              | (((op1 & S_QUEST) != 0) ? 0 : S_QUEST);
                } else {                    /* Other operators leave*/
                    opp->skip = op1;        /*  skipping unchanged. */
                }
                if (mcpp_debug & EXPRESSION) {
                    mcpp_fprintf( DBG, "stacking %s, ", opname[ op]);
                    if (&value[0] < valp)
                        dump_val( "valp[-1].val == ", valp - 1);
                    mcpp_fprintf( DBG, " at %s\n", infile->bptr);
                    dump_stack( opstack, opp, value, valp);
                }
                break;
            }

            /*
             * Coming sub-expression is of lower precedence.
             * Evaluate stacked sub-expression.
             * Pop operator from operator stack and evaluate it.
             * End of stack and '(', ')' are specials.
             */
            skip_cur = opp->skip;           /* Remember skip value  */
            switch ((op1 = opp->op)) {      /* Look at stacked op   */
            case OP_END:                    /* Stack end marker     */
                if (op == OP_RPA) {         /* No corresponding (   */
                    cerror( "Excessive \")\"", NULL, 0L, NULL);     /* _E_  */
                    return  0L;
                }
                if (op == OP_EOE)
                    return  valp[-1].val;   /* Finished ok.         */
                break;
            case OP_LPA:                    /* ( on stack           */
                if (op != OP_RPA) {         /* Matches ) on input?  */
                    cerror( "Missing \")\"", NULL, 0L, NULL);       /* _E_  */
                    return  0L;
                }
                opp--;                      /* Unstack it           */
                parens--;                   /* Count down nest level*/
                break;
            case OP_QUE:                    /* Evaluate true expr.  */
                break;
            case OP_COL:                    /* : on stack           */
                opp--;                      /* Unstack :            */
                if (opp->op != OP_QUE) {    /* Matches ? on stack?  */
                    cerror(
                    "Misplaced \":\", previous operator is \"%s\""  /* _E_  */
                            , opname[opp->op], 0L, NULL);
                    return  0L;
                }
                /* Evaluate op1.            Fall through            */
            default:                        /* Others:              */
                opp--;                      /* Unstack the operator */
                if (mcpp_debug & EXPRESSION) {
                    mcpp_fprintf( DBG, "Stack before evaluation of %s\n"
                            , opname[ op1]);
                    dump_stack( opstack, opp, value, valp);
                }
                if (op1 == OP_COL)
                    skip = 0;
                else
                    skip = skip_cur;
                valp = eval_eval( valp, op1);
                if (valp->sign == VAL_ERROR)
                    return  0L;     /* Out of range or divide by 0  */
                valp++;
                skip = 0;
                if (mcpp_debug & EXPRESSION) {
                    mcpp_fprintf( DBG, "Stack after evaluation\n");
                    dump_stack( opstack, opp, value, valp);
                }
            }                               /* op1 switch end       */

            if (op1 == OP_END || op1 == OP_LPA || op1 == OP_QUE)
                break;                      /* Read another op.     */
        }                                   /* Stack unwind loop    */

    }

    return  0L;                             /* Never reach here     */
}

static int  eval_lex( void)
/*
 * Return next operator or constant to evaluate.  Called from eval_if().  It 
 * calls a special-purpose routines for character constants and numeric values:
 *      eval_char()     called to evaluate 'x'
 *      eval_num()      called to evaluate numbers
 * C++98 treats 11 identifier-like tokens as operators.
 * POST_STD forbids character constants in #if expression.
 */
{
    int     c1;
    VAL_SIGN *  valp;
    int     warn = ! skip || (warn_level & 8);
    int     token_type;
    int     c;

    ev.sign = SIGNED;                       /* Default signedness   */
    ev.val = 0L;            /* Default value (on error or 0 value)  */
    in_if = ! skip; /* Inform to expand_macro() that the macro is   */
                    /*      in #if line and not skipped expression. */
    c = skip_ws();
    if (c == '\n') {
        unget_ch();
        return  OP_EOE;                     /* End of expression    */
    }
    token_type = get_unexpandable( c, warn);
    if (standard && macro_line == MACRO_ERROR)
        return  OP_FAIL;                /* Unterminated macro call  */
    if (token_type == NO_TOKEN)
        return  OP_EOE;     /* Only macro(s) expanding to 0-token   */

    switch (token_type) {
    case NAM:
        if (standard && str_eq( identifier, "defined")) {   /* defined name */
            c1 = c = skip_ws();
            if (c == '(')                   /* Allow defined (name) */
                c = skip_ws();
            if (scan_token( c, (workp = work_buf, &workp), work_end) == NAM) {
                DEFBUF *    defp = look_id( identifier);
                if (warn) {
                    ev.val = (defp != NULL);
                    if ((mcpp_debug & MACRO_CALL) && ! skip && defp)
                        /* Annotate if the macro is in non-skipped expr.    */
                        mcpp_fprintf( OUT, "/*%s*/", defp->name);
                }
                if (c1 != '(' || skip_ws() == ')')  /* Balanced ?   */
                    return  VAL;            /* Parsed ok            */
            }
            cerror( "Bad defined syntax: %s"                /* _E_  */
                    , infile->fp ? "" : infile->buffer, 0L, NULL);
            break;
        } else if (cplus_val) {
            if (str_eq( identifier, "true")) {
                ev.val = 1L;
                return  VAL;
            } else if (str_eq( identifier, "false")) {
                ev.val = 0L;
                return  VAL;
            } else if (mcpp_mode != POST_STD
                    && (openum = id_operator( identifier)) != 0) {
                /* Identifier-like operator in C++98    */
                strcpy( work_buf, identifier);
                return  chk_ops();
            }
        } else if (! standard && str_eq( identifier, "sizeof")) {
            /* sizeof hackery       */
            return  do_sizeof();            /* Gets own routine     */
        }
        /*
         * The ANSI C Standard says that an undefined symbol
         * in an #if has the value zero.  We are a bit pickier,
         * warning except where the programmer was careful to write
         *          #if defined(foo) ? foo : 0
         */
        if ((! skip && (warn_level & 4)) || (skip && (warn_level & 8)))
            cwarn( "Undefined symbol \"%s\"%.0ld%s" /* _W4_ _W8_    */
                    , identifier, 0L, skip ? non_eval : ", evaluated to 0");
        return  VAL;
    case CHR:                               /* Character constant   */
    case WCHR:                              /* Wide char constant   */
        if (mcpp_mode == POST_STD) {
            cerror( "Can't use a character constant %s"     /* _E_  */
                    , work_buf, 0L, NULL);
            break;
        }
        valp = eval_char( work_buf);        /* 'valp' points 'ev'   */
        if (valp->sign == VAL_ERROR)
            break;
        if (mcpp_debug & EXPRESSION) {
            dump_val( "eval_char returns ", &ev);
            mcpp_fputc( '\n', DBG);
        }
        return  VAL;                        /* Return a value       */
    case STR:                               /* String literal       */
    case WSTR:                              /* Wide string literal  */
        cerror(
    "Can't use a string literal %s", work_buf, 0L, NULL);   /* _E_  */
        break;
    case NUM:                               /* Numbers are harder   */
        valp = eval_num( work_buf);         /* 'valp' points 'ev'   */
        if (valp->sign == VAL_ERROR)
            break;
        if (mcpp_debug & EXPRESSION) {
            dump_val( "eval_num returns ", &ev);
            mcpp_fputc( '\n', DBG);
        }
        return  VAL;
    case OPE:                           /* Operator or punctuator   */
        return  chk_ops();

    default:                                /* Total nonsense       */
        cerror( "Can't use the character %.0s0x%02lx"       /* _E_  */
                , NULL, (long) c, NULL);
        break;
    }

    return  OP_FAIL;                        /* Any errors           */
}

static int  chk_ops( void)
/*
 * Check the operator.
 * If it can't be used in #if expression return OP_FAIL
 * else return openum.
 */
{
    switch (openum) {
    case OP_STR:    case OP_CAT:    case OP_ELL:
    case OP_1:      case OP_2:      case OP_3:
        cerror( "Can't use the operator \"%s\""             /* _E_  */
                , work_buf, 0L, NULL);
        return  OP_FAIL;
    default:
        return  openum;
    }
}

static int  do_sizeof( void)
/*
 * Process the sizeof (basic type) operation in an #if string.
 * Sets ev.val to the size and returns
 *      VAL             success
 *      OP_FAIL         bad parse or something.
 * This routine is never called in STD and POST_STD mode.
 */
{
    const char * const  no_type = "sizeof: No type specified";      /* _E_  */
    int     warn = ! skip || (warn_level & 8);
    int     type_end = FALSE;
    int     typecode = 0;
    int     token_type = NO_TOKEN;
    const SIZES *   sizp = NULL;

    if (get_unexpandable( skip_ws(), warn) != OPE || openum != OP_LPA)
        goto  no_good;                      /* Not '('              */

    /*
     * Scan off the tokens.
     */

    while (! type_end) {
        token_type = get_unexpandable( skip_ws(), warn);
                                /* Get next token expanding macros  */
        switch (token_type) {
        case OPE:
            if (openum == OP_LPA) {         /* thing (*)() func ptr */
                if (get_unexpandable( skip_ws(), warn) == OPE
                        && openum == OP_MUL
                        && get_unexpandable( skip_ws(), warn) == OPE
                        && openum == OP_RPA) {      /* (*)          */
                    if (get_unexpandable( skip_ws(), warn) != OPE
                            || openum != OP_LPA
                            || get_unexpandable( skip_ws(), warn) != OPE
                            || openum != OP_RPA)    /* Not ()       */
                        goto  no_good;
                    typecode |= T_FPTR;     /* Function pointer     */
                } else {                    /* Junk is an error     */
                    goto  no_good;
                }
            } else {                        /* '*' or ')'           */
                type_end = TRUE;
            }
            break;
        case NAM:                           /* Look for type comb.  */
            if ((typecode = look_type( typecode)) == 0)
                return  OP_FAIL;            /* Illegal type or comb.*/
            break;
        default:    goto  no_good;          /* Illegal token        */
        }
    }                                       /* End of while         */

    /*
     * We are at the end of the type scan.  Chew off '*' if necessary.
     */
    if (token_type == OPE) {
        if (openum == OP_MUL) {             /* '*'                  */
            typecode |= T_PTR;
            if (get_unexpandable( skip_ws(), warn) != OPE)
                goto  no_good;
        }
        if (openum == OP_RPA) {             /* ')'                  */
            /*
             * Last syntax check
             * We assume that all function pointers are the same size:
             *          sizeof (int (*)()) == sizeof (float (*)())
             * We assume that signed and unsigned don't change the size:
             *          sizeof (signed int) == sizeof (unsigned int)
             */
            if ((typecode & T_FPTR) != 0) { /* Function pointer     */
                typecode = T_FPTR | T_PTR;
            } else {                        /* Var or var * datum   */
                typecode &= ~(T_SIGNED | T_UNSIGNED);
#if HAVE_LONG_LONG
                if ((typecode & (T_SHORT | T_LONG | T_LONGLONG)) != 0)
#else
                if ((typecode & (T_SHORT | T_LONG)) != 0)
#endif
                    typecode &= ~T_INT;
            }
            if ((typecode & ~T_PTR) == 0) {
                cerror( no_type, NULL, 0L, NULL);
                return  OP_FAIL;
            } else {
                /*
                 * Exactly one bit (and possibly T_PTR) may be set.
                 */
                for (sizp = size_table; sizp->bits != 0; sizp++) {
                    if ((typecode & ~T_PTR) == sizp->bits) {
                        ev.val = ((typecode & T_PTR) != 0)
                                ? sizp->psize : sizp->size;
                        break;
                    }
                }
            }
        } else {
            goto  no_good;
        }
    } else {
        goto  no_good;
    }

    if (mcpp_debug & EXPRESSION) {
        if (sizp)
            mcpp_fprintf( DBG,
            "sizp->bits:0x%x sizp->size:0x%x sizp->psize:0x%x ev.val:0x%lx\n"
                    , sizp->bits, sizp->size, sizp->psize
                    , (unsigned long) ev.val);
    }
    return  VAL;

no_good:
    unget_ch();
    cerror( "sizeof: Syntax error", NULL, 0L, NULL);        /* _E_  */
    return  OP_FAIL;
}

static int  look_type(
    int     typecode
)
{
    const char * const  unknown_type
            = "sizeof: Unknown type \"%s\"%.0ld%s";     /* _E_ _W8_ */
    const char * const  illeg_comb
    = "sizeof: Illegal type combination with \"%s\"%.0ld%s";    /* _E_ _W8_ */
    int     token_type;
    const TYPES *   tp;

    if (str_eq( identifier, "long")) {
        if ((token_type
                = get_unexpandable( skip_ws(), !skip || (warn_level & 8)))
                == NO_TOKEN)
            return  typecode;
        if (token_type == NAM) {
#if HAVE_LONG_LONG
            if (str_eq( identifier, "long")) {
                strcpy( work_buf, "long long");
                goto  basic;
            }
#endif
            if (str_eq( identifier, "double")) {
                strcpy( work_buf, "long double");
                goto  basic;
            }
        }
        unget_string( work_buf, NULL);      /* Not long long        */
        strcpy( work_buf, "long");          /*   nor long double    */
    }

    /*
     * Look for this unexpandable token in basic_types.
     */
basic:
    for (tp = basic_types; tp->token_name != NULL; tp++) {
        if (str_eq( work_buf, tp->token_name))
            break;
    }

    if (tp->token_name == NULL) {
        if (! skip) {
            cerror( unknown_type, work_buf, 0L, NULL);
            return  0;
        } else if (warn_level & 8) {
            cwarn( unknown_type, work_buf, 0L, non_eval);
        }
    }
    if ((typecode & tp->excluded) != 0) {
        if (! skip) {
            cerror( illeg_comb, work_buf, 0L, NULL);
            return  0;
        } else if (warn_level & 8) {
            cwarn( illeg_comb, work_buf, 0L, non_eval);
        }
    }

    if (mcpp_debug & EXPRESSION) {
        if (tp->token_name)
            mcpp_fprintf( DBG,
            "sizeof -- typecode:0x%x tp->token_name:\"%s\" tp->type:0x%x\n"
                    , typecode, tp->token_name, tp->type);
    }
    return  typecode |= tp->type;           /* Or in the type bit   */
}

VAL_SIGN *  eval_num(
    const char *  nump                      /* Preprocessing number */
)
/*
 * Evaluate number for #if lexical analysis.  Note: eval_num recognizes
 * the unsigned suffix, but only returns a signed expr_t value, and stores
 * the signedness to ev.sign, which is set UNSIGNED (== unsigned) if the
 * value is not in the range of positive (signed) expr_t.
 */
{
    const char * const  not_integer = "Not an integer \"%s\"";      /* _E_  */
    const char * const  out_of_range
            = "Constant \"%s\"%.0ld%s is out of range"; /* _E_ _W1_ _W8_    */
    expr_t          value;
    uexpr_t         v, v1;  /* unsigned long long or unsigned long  */
    int             uflag = FALSE;
    int             lflag = FALSE;
    int             erange = FALSE;
    int             base;
    int             c, c1;
    const char *    cp = nump;
#if HAVE_LONG_LONG
    const char * const  out_of_range_long =
        "Constant \"%s\"%.0ld%s is out of range "   /* _E_ _W1_ _W2_ _W8_   */
            "of (unsigned) long";
    const char * const  ll_suffix =
"LL suffix is used in other than C99 mode \"%s\"%.0ld%s"; /* _W1_ _W2_ _W8_ */
#if COMPILER == MSC || COMPILER == BORLANDC
    const char * const  i64_suffix =
"I64 suffix is used in other than C99 mode \"%s\"%.0ld%s";  /* _W2_ _W8_    */
#endif
    int             llflag = FALSE;
    int             erange_long = FALSE;
#endif

    ev.sign = SIGNED;                       /* Default signedness   */
    ev.val = 0L;                            /* Default value        */
    if ((char_type[ c = *cp++ & UCHARMAX] & DIG) == 0)   /* Dot     */
        goto  num_err;
    if (c != '0') {                         /* Decimal              */
        base = 10;
    } else if ((c = *cp++ & UCHARMAX) == 'x' || c == 'X') {
        base = 16;                          /* Hexadecimal          */
        c = *cp++ & UCHARMAX;
    } else if (c == EOS) {                  /* 0                    */
        return  & ev;
    } else {                                /* Octal or illegal     */
        base = 8;
    }

    v = v1 = 0L;
    for (;;) {
        c1 = c;
        if (isupper( c1))
            c1 = tolower( c1);
        if (c1 >= 'a')
            c1 -= ('a' - 10);
        else
            c1 -= '0';
        if (c1 < 0 || base <= c1)
            break;
        v1 *= base;
        v1 += c1;
        if (v1 / base < v) {                /* Overflow             */
            if (! skip)
                goto  range_err;
            else
                erange = TRUE;
        }
#if HAVE_LONG_LONG
        if (! stdc3 && v1 > ULONGMAX)
            /* Overflow of long or unsigned long    */
            erange_long = TRUE;
#endif
        v = v1;
        c = *cp++ & UCHARMAX;
    }

    value = v;
    while (c == 'u' || c == 'U' || c == 'l' || c == 'L') {
        if (c == 'u' || c == 'U') {
            if (uflag)
                goto  num_err;
            uflag = TRUE;
        } else if (c == 'l' || c == 'L') {
#if HAVE_LONG_LONG
            if (llflag) {
                goto  num_err;
            } else if (lflag) {
                llflag = TRUE;
                if (! stdc3 && ((! skip && (warn_level & w_level))
                        || (skip && (warn_level & 8))))
                    cwarn( ll_suffix, nump, 0L, skip ? non_eval : NULL);
            } else {
                lflag = TRUE;
            }
#else
            if (lflag)
                goto  num_err;
            else
                lflag = TRUE;
#endif
        }
        c = *cp++;
    }
#if HAVE_LONG_LONG && (COMPILER == MSC || COMPILER == BORLANDC)
    if (tolower( c) == 'i') {
        c1 = atoi( cp);
        if (c1 == 64) {
            if (! stdc3 && ((! skip && (warn_level & w_level))
                    || (skip && (warn_level & 8))))
                cwarn( i64_suffix, nump, 0L, skip ? non_eval : NULL);
            cp += 2;
        } else if (c1 == 32 || c1 == 16) {
            cp += 2;
        } else if (c1 == 8) {
            cp++;
        }
        c = *cp++;
    }
#endif

    if (c != EOS)
        goto  num_err;

    if (standard) {
        if (uflag)      /* If 'U' suffixed, uexpr_t is treated as unsigned  */
            ev.sign = UNSIGNED;
        else
            ev.sign = (value >= 0L);
#if HAVE_LONG_LONG
    } else {
        if (value > LONGMAX)
            erange_long = TRUE;
#endif
    }

    ev.val = value;
    if (erange && (warn_level & 8))
        cwarn( out_of_range, nump, 0L, non_eval);
#if HAVE_LONG_LONG
    else if (erange_long && ((skip && (warn_level & 8))
            || (! stdc3 && ! skip && (warn_level & w_level))))
        cwarn( out_of_range_long, nump, 0L, skip ? non_eval : NULL);
#endif
    return  & ev;

range_err:
    cerror( out_of_range, nump, 0L, NULL);
    ev.sign = VAL_ERROR;
    return  & ev;
num_err:
    cerror( not_integer, nump, 0L, NULL);
    ev.sign = VAL_ERROR;
    return  & ev;
}

static VAL_SIGN *   eval_char(
    char * const    token
)
/*
 * Evaluate a character constant.
 * This routine is never called in POST_STD mode.
 */
{
    const char * const  w_out_of_range
        = "Wide character constant %s%.0ld%s is out of range";  /* _E_ _W8_ */
    const char * const  c_out_of_range
    = "Integer character constant %s%.0ld%s is out of range";   /* _E_ _W8_ */
    uexpr_t     value;
    uexpr_t     tmp;
    expr_t      cl;
    int         erange = FALSE;
    int         wide = (*token == 'L');
    int         ucn8;
    int         i;
    int         bits, mbits, u8bits, bits_save;
    char *      cp = token + 1;         /* Character content        */
#if HAVE_LONG_LONG
    const char * const  w_out_of_range_long =
        "Wide character constant %s%.0ld%s is "     /* _E_ _W1_ _W2_ _W8_   */
            "out of range of unsigned long";
    const char * const  c_out_of_range_long =
        "Integer character constant %s%.0ld%s is "  /* _E_ _W1_ _W2_ _W8_   */
            "out of range of unsigned long";
    int         erange_long = FALSE;
#endif

    bits = CHARBIT;
    u8bits = CHARBIT * 4;
    if (mbchar & UTF8)
        mbits = CHARBIT * 4;
    else
        mbits = CHARBIT * 2;
    if (mcpp_mode == STD && wide) {          /* Wide character constant  */
        cp++;                           /* Skip 'L'                 */
        bits = mbits;
    }
    if (char_type[ *cp & UCHARMAX] & mbchk) {
        cl = mb_eval( &cp);
        bits = mbits;
    } else if ((cl = eval_one( &cp, wide, mbits, (ucn8 = FALSE, &ucn8)))
                == -1L) {
        ev.sign = VAL_ERROR;
        return  & ev;
    }
    bits_save = bits;
    value = cl;

    for (i = 0; *cp != '\'' && *cp != EOS; i++) {
        if (char_type[ *cp & UCHARMAX] & mbchk) {
            cl = mb_eval( &cp);
            if (cl == 0)
                /* Shift-out sequence of multi-byte or wide character   */
                continue;
            bits = mbits;
        } else {
            cl = eval_one( &cp, wide, mbits, (ucn8 = FALSE, &ucn8));
            if (cl == -1L) {
                ev.sign = VAL_ERROR;
                return  & ev;
            }
#if OK_UCN
            if (ucn8 == TRUE)
                bits = u8bits;
            else
                bits = bits_save;
#endif
        }
        tmp = value;
        value = (value << bits) | cl;   /* Multi-char or multi-byte char    */
        if ((value >> bits) < tmp) {    /* Overflow                 */
            if (! skip)
                goto  range_err;
            else
                erange = TRUE;
        }
#if HAVE_LONG_LONG
        if ((mcpp_mode == STD && (! stdc3 && value > ULONGMAX))
                || (! standard && value > LONGMAX))
            erange_long = TRUE;
#endif
    }

    ev.sign = ((expr_t) value >= 0L);
    ev.val = value;

    if (erange && skip && (warn_level & 8)) {
        if (wide)
            cwarn( w_out_of_range, token, 0L, non_eval);
        else
            cwarn( c_out_of_range, token, 0L, non_eval);
#if HAVE_LONG_LONG
    } else if (erange_long && ((skip && (warn_level & 8))
            || (! stdc3 && ! skip && (warn_level & w_level)))) {
        if (wide)
            cwarn( w_out_of_range_long, token, 0L, skip ? non_eval : NULL);
        else
            cwarn( c_out_of_range_long, token, 0L, skip ? non_eval : NULL);
#endif
    }

    if (i == 0)         /* Constant of single (character or wide-character) */
        return  & ev;

    if ((! skip && (warn_level & 4)) || (skip && (warn_level & 8))) {
        if (mcpp_mode == STD && wide)
            cwarn(
"Multi-character wide character constant %s%.0ld%s isn't portable"  /* _W4_ _W8_    */
                    , token, 0L, skip ? non_eval : NULL);
        else
            cwarn(
"Multi-character or multi-byte character constant %s%.0ld%s isn't portable" /* _W4_ _W8_    */
                    , token, 0L, skip ? non_eval : NULL);
    }
    return  & ev;

range_err:
    if (wide)
        cerror( w_out_of_range, token, 0L, NULL);
    else
        cerror( c_out_of_range, token, 0L, NULL);
    ev.sign = VAL_ERROR;
    return  & ev;
}

static expr_t   eval_one(
    char **     seq_pp,         /* Address of pointer to sequence   */
                    /* eval_one() advances the pointer to sequence  */
    int     wide,                       /* Flag of wide-character   */
    int     mbits,              /* Number of bits of a wide-char    */
    int *   ucn8                            /* Flag of UCN-32 bits  */
)
/*
 * Called from eval_char() above to get a single character, single multi-
 * byte character or wide character (with or without \ escapes).
 * Returns the value of the character or -1L on error.
 */
{
#if OK_UCN
    const char * const  ucn_malval
        = "UCN cannot specify the value %.0s\"%08lx\""; /* _E_ _W8_ */
#endif
    const char * const  out_of_range
        = "%s%ld bits can't represent escape sequence '%s'";    /* _E_ _W8_ */
    uexpr_t         value;
    int             erange = FALSE;
    char *          seq = *seq_pp;  /* Initial seq_pp for diagnostic*/
    const char *    cp;
    const char *    digits;
    unsigned        uc;
    unsigned        uc1;
    int             count;
    int             bits;
    size_t          wchar_max;

    uc = *(*seq_pp)++ & UCHARMAX;

    if (uc != '\\')                 /* Other than escape sequence   */
        return  (expr_t) uc;

    /* escape sequence  */
    uc1 = uc = *(*seq_pp)++ & UCHARMAX;
    switch (uc) {
    case 'a':
        return  '\a';
    case 'b':
        return  '\b';
    case 'f':
        return  '\f';
    case 'n':
        return  '\n';
    case 'r':
        return  '\r';
    case 't':
        return  '\t';
    case 'v':
        return  '\v';
#if OK_UCN
    case 'u':   case 'U':
        if (! stdc2)
            goto  undefined;
        /* Else Universal character name    */
        /* Fall through */
#endif
    case 'x':                               /* '\xFF'               */
        if (! standard)
            goto  undefined;
        digits = "0123456789abcdef";
        bits = 4;
        uc = *(*seq_pp)++ & UCHARMAX;
        break;
    case '0': case '1': case '2': case '3':
    case '4': case '5': case '6': case '7':
        digits = "01234567";
        bits = 3;
        break;
    case '\'':  case '"':   case '?':   case '\\':
        return  (expr_t) uc;
    default:
        goto  undefined;
    }

    wchar_max = (UCHARMAX << CHARBIT) | UCHARMAX;
    if (mbits == CHARBIT * 4) {
        if (mcpp_mode == STD)
            wchar_max = (wchar_max << CHARBIT * 2) | wchar_max;
        else
            wchar_max = LONGMAX;
    }

    value = 0L;
    for (count = 0; ; ++count) {
        if (isupper( uc))
            uc = tolower( uc);
        if ((cp = strchr( digits, uc)) == NULL)
            break;
        if (count >= 3 && bits == 3)
            break;      /* Octal escape sequence at most 3 digits   */
#if OK_UCN
        if ((count >= 4 && uc1 == 'u') || (count >= 8 && uc1 == 'U'))
            break;
#endif
        value = (value << bits) | (cp - digits);
#if OK_UCN
        if (wchar_max < value && uc1 != 'u' && uc1 != 'U')
#else
        if (wchar_max < value)
#endif
        {
            if (! skip)
                goto  range_err;
            else
                erange = TRUE;
        }
        uc = *(*seq_pp)++ & UCHARMAX;
    }
    (*seq_pp)--;

    if (erange) {
        value &= wchar_max;
        goto  range_err;
    }

    if (count == 0 && bits == 4)            /* '\xnonsense'         */
        goto  undefined;
#if OK_UCN
    if (uc1 == 'u' || uc1 == 'U') {
        if ((count < 4 && uc1 == 'u') || (count < 8 && uc1 == 'U'))
            goto  undefined;
        if ((value >= 0L && value <= 0x9FL
                    && value != 0x24L && value != 0x40L && value != 0x60L)
                || (!stdc3 && value >= 0xD800L && value <= 0xDFFFL)) {
            if (!skip)
                cerror( ucn_malval, NULL, (long) value, NULL);
            else if (warn_level & 8)
                cwarn( ucn_malval, NULL, (long) value, NULL);
        }
        if (count >= 8 && uc1 == 'U')
            *ucn8 = TRUE;
        return  (expr_t) value;
    }
#endif  /* OK_UCN   */
    if (! wide && (UCHARMAX < value)) {
        value &= UCHARMAX;
        goto  range_err;
    }
    return  (expr_t) value;

undefined:
    uc1 = **seq_pp;
    **seq_pp = EOS;                         /* For diagnostic       */
    if ((! skip && (warn_level & 1)) || (skip && (warn_level & 8)))
        cwarn(
    "Undefined escape sequence%s %.0ld'%s'"         /* _W1_ _W8_    */
                , skip ? non_eval : NULL, 0L, seq);
    **seq_pp = uc1;
    *seq_pp = seq + 1;
    return  (expr_t) '\\';              /* Returns the escape char  */

range_err:
    uc1 = **seq_pp;
    **seq_pp = EOS;                         /* For diagnostic       */
    if (wide) {
        if (! skip)
            cerror( out_of_range, NULL, (long) mbits, seq);
        else if (warn_level & 8)
            cwarn( out_of_range, non_eval, (long) mbits, seq);
    } else {
        if (! skip)
            cerror( out_of_range, NULL, (long) CHARBIT, seq);
        else if (warn_level & 8)
            cwarn( out_of_range, non_eval, (long) CHARBIT, seq);
    }

    **seq_pp = uc1;
    if (! skip)
        return  -1L;
    else
        return  (expr_t) value;
}

static VAL_SIGN *   eval_eval(
    VAL_SIGN *  valp,
    int         op
)
/*
 * One or two values are popped from the value stack and do arithmetic.
 * The result is pushed onto the value stack.
 * eval_eval() returns the new pointer to the top of the value stack.
 */
{
    const char * const  zero_div = "%sDivision by zero%.0ld%s"; /* _E_ _W8_ */
#if HAVE_LONG_LONG
    const char * const  neg_format =
"Negative value \"%" LL_FORM "d\" is converted to positive \"%" /* _W1_ _W8_*/
                    LL_FORM "u\"%%s";
#else
    const char * const  neg_format =
"Negative value \"%ld\" is converted to positive \"%lu\"%%s";   /* _W1_ _W8_*/
#endif
    expr_t  v1, v2;
    int     sign1, sign2;

    if (is_binary( op)) {
        v2 = (--valp)->val;
        sign2 = valp->sign;
    } else {
        v2 = 0L;                            /* Dummy    */
        sign2 = SIGNED;                     /* Dummy    */
    }
    v1 = (--valp)->val;
    sign1 = valp->sign;
    if (mcpp_debug & EXPRESSION) {
        mcpp_fprintf( DBG, "%s op %s", (is_binary( op)) ? "binary" : "unary"
                , opname[ op]);
        dump_val( ", v1 = ", valp);
        if (is_binary( op))
            dump_val( ", v2 = ", valp + 1);
        mcpp_fputc( '\n', DBG);
    }

    if (standard
            && (sign1 == UNSIGNED || sign2 == UNSIGNED) && is_binary( op)
            && op != OP_ANA && op != OP_ORO && op != OP_SR && op != OP_SL) {
        if (((sign1 == SIGNED && v1 < 0L) || (sign2 == SIGNED && v2 < 0L)
                ) && ((! skip && (warn_level & 1))
                    || (skip && (warn_level & 8)))) {
            char    negate[(((sizeof (expr_t) * 8) / 3) + 1) * 2 + 50];
            expr_t  v3;

            v3 = (sign1 == SIGNED ? v1 : v2);
            sprintf( negate, neg_format, v3, v3);
            cwarn( negate, skip ? non_eval : NULL, 0L, NULL);
        }
        valp->sign = sign1 = sign2 = UNSIGNED;
    }
    if ((op == OP_SL || op == OP_SR)
            && ((! skip && (warn_level & 1)) || (skip && (warn_level & 8)))) {
        if (v2 < 0L || v2 >= sizeof (expr_t) * CHARBIT)
            cwarn( "Illegal shift count %.0s\"%ld\"%s"      /* _W1_ _W8_    */
                , NULL, (long) v2, skip ? non_eval : NULL);
#if HAVE_LONG_LONG
        else if (! stdc3 && v2 >= sizeof (long) * CHARBIT
                && ((! skip && (warn_level & w_level))
                    || (skip && (warn_level & 8))))
            cwarn(
"Shift count %.0s\"%ld\" is larger than bit count of long%s"    /* _W1_ _W8_*/
                , NULL, (long) v2, skip ? non_eval : NULL);
#endif
    }
    if ((op == OP_DIV || op == OP_MOD) && v2 == 0L) {
        if (! skip) {
            cerror( zero_div, NULL, 0L, NULL);
            valp->sign = VAL_ERROR;
            return  valp;
        } else {
            if (warn_level & 8)
                cwarn( zero_div, NULL, 0L, non_eval);
            valp->sign = sign1;
            valp->val = (expr_t) EXPR_MAX;
            return  valp;
        }
    }

    if (! standard || sign1 == SIGNED)
        v1 = eval_signed( & valp, v1, v2, op);
    else
        v1 = eval_unsigned( & valp, (uexpr_t) v1, (uexpr_t) v2, op);

    if (valp->sign == VAL_ERROR)                /* Out of range */
        return  valp;

    switch (op) {
    case OP_NOT:    case OP_EQ:     case OP_NE:
    case OP_LT:     case OP_LE:     case OP_GT:     case OP_GE:
    case OP_ANA:    case OP_ORO:
        valp->sign = SIGNED;
        break;
    default:
        valp->sign = sign1;
        break;
    }
    valp->val = v1;
    return  valp;
}

static expr_t   eval_signed(
    VAL_SIGN ** valpp,
    expr_t      v1,
    expr_t      v2,
    int         op
)
/*
 * Apply the argument operator to the signed data.
 * OP_COL is a special case.
 */
{
    const char * const   illeg_op
        = "Bug: Illegal operator \"%s\" in eval_signed()";      /* _F_  */
    const char * const  not_portable
        = "\"%s\" of negative number isn't portable%.0ld%s";    /* _W1_ _W8_*/
    const char *    op_name = opname[ op];
    VAL_SIGN *      valp = *valpp;
    expr_t  val;
    int     chk;                /* Flag of overflow in long long    */

    switch (op) {
    case OP_EOE:
    case OP_PLU:                        break;
    case OP_NEG:
        chk = v1 && v1 == -v1;
        if (chk
#if HAVE_LONG_LONG
                || (! stdc3 && v1 && (long) v1 == (long) -v1)
#endif 
            )
            overflow( op_name, valpp, chk);
        v1 = -v1;
        break;
    case OP_COM:    v1 = ~v1;           break;
    case OP_NOT:    v1 = !v1;           break;
    case OP_MUL:
        val = v1 * v2;
        chk = v1 && v2 && (val / v1 != v2 || val / v2 != v1);
        if (chk
#if HAVE_LONG_LONG
                || (! stdc3 && v1 && v2
                    && ((long)val / (long)v1 != (long)v2
                        || (long)val / (long)v2 != (long)v1))
#endif
            )
            overflow( op_name, valpp, chk);
        v1 = val;
        break;
    case OP_DIV:
    case OP_MOD:
        /* Division by 0 has been already diagnosed by eval_eval(). */
        chk = -v1 == v1 && v2 == -1;
        if (chk             /* LONG_MIN / -1 on two's complement    */
#if HAVE_LONG_LONG
                || (! stdc3
                    && (long)-v1 == (long)v1 && (long)v2 == (long)-1)
#endif
            )
            overflow( op_name, valpp, chk);
        else if (! stdc3 && (v1 < 0L || v2 < 0L)
                && ((! skip && (warn_level & 1))
                    || (skip && (warn_level & 8))))
            cwarn( not_portable, op_name, 0L, skip ? non_eval : NULL);
        if (op == OP_DIV)
            v1 /= v2;
        else
            v1 %= v2;
        break;
    case OP_ADD:
        val = v1 + v2;
        chk = (v2 > 0L && v1 > val) || (v2 < 0L && v1 < val);
        if (chk
#if HAVE_LONG_LONG
                || (! stdc3
                    && (((long)v2 > 0L && (long)v1 > (long)val)
                        || ((long)v2 < 0L && (long)v1 < (long)val)))
#endif
            )
            overflow( op_name, valpp, chk);
        v1 = val;
        break;
    case OP_SUB:
        val = v1 - v2;
        chk = (v2 > 0L && val > v1) || (v2 < 0L && val < v1);
        if (chk
#if HAVE_LONG_LONG
                || (! stdc3
                    && (((long)v2 > 0L && (long)val > (long)v1)
                        || ((long)v2 < 0L && (long)val < (long)v1)))
#endif
            )
            overflow( op_name, valpp, chk);
        v1 = val;
        break;
    case OP_SL:     v1 <<= v2;          break;
    case OP_SR:
        if (v1 < 0L
                && ((!skip && (warn_level & 1))
                    || (skip && (warn_level & 8))))
            cwarn( not_portable, op_name, 0L, skip ? non_eval : NULL);
        v1 >>= v2;
        break;
    case OP_LT:     v1 = (v1 < v2);     break;
    case OP_LE:     v1 = (v1 <= v2);    break;
    case OP_GT:     v1 = (v1 > v2);     break;
    case OP_GE:     v1 = (v1 >= v2);    break;
    case OP_EQ:     v1 = (v1 == v2);    break;
    case OP_NE:     v1 = (v1 != v2);    break;
    case OP_AND:    v1 &= v2;           break;
    case OP_XOR:    v1 ^= v2;           break;
    case OP_OR:     v1 |= v2;           break;
    case OP_ANA:    v1 = (v1 && v2);    break;
    case OP_ORO:    v1 = (v1 || v2);    break;
    case OP_COL:
        /*
         * If v1 has the "true" value, v2 has the "false" value.
         * The top of the value stack has the test.
         */
        v1 = (--*valpp)->val ? v1 : v2;
        break;
    default:
        cfatal( illeg_op, op_name, 0L, NULL);
    }

    *valpp = valp;
    return  v1;
}

static expr_t   eval_unsigned(
    VAL_SIGN ** valpp,
    uexpr_t     v1u,
    uexpr_t     v2u,
    int         op
)
/*
 * Apply the argument operator to the unsigned data.
 * Called from eval_eval() only in Standard mode.
 */
{
    const char * const   illeg_op
        = "Bug: Illegal operator \"%s\" in eval_unsigned()";    /* _F_  */
    const char *    op_name = opname[ op];
    VAL_SIGN *      valp = *valpp;
    uexpr_t     v1 = 0;
    int     chk;        /* Flag of overflow in unsigned long long   */
    int     minus;      /* Big integer converted from signed long   */

    minus = ! stdc3 && (v1u > ULONGMAX || v2u > ULONGMAX);

    switch (op) {
    case OP_EOE:
    case OP_PLU:    v1 = v1u;           break;
    case OP_NEG:
        v1 = -v1u;
        if (v1u)
            overflow( op_name, valpp, TRUE);
        break;
    case OP_COM:    v1 = ~v1u;          break;
    case OP_NOT:    v1 = !v1u;          break;
    case OP_MUL:
        v1 = v1u * v2u;
        chk = v1u && v2u && (v1 / v2u != v1u || v1 / v1u != v2u);
        if (chk
#if HAVE_LONG_LONG
                || (! stdc3 && ! minus && v1 > ULONGMAX)
#endif
            )
            overflow( op_name, valpp, chk);
        break;
    case OP_DIV:
        /* Division by 0 has been already diagnosed by eval_eval().  */
        v1 = v1u / v2u;
        break;
    case OP_MOD:
        v1 = v1u % v2u;
        break;
    case OP_ADD:
        v1 = v1u + v2u;
        chk = v1 < v1u;
        if (chk
#if HAVE_LONG_LONG
                || (! stdc3 && ! minus && v1 > ULONGMAX)
#endif
            )
            overflow( op_name, valpp, chk);
        break;
    case OP_SUB:
        v1 = v1u - v2u;
        chk = v1 > v1u;
        if (chk
#if HAVE_LONG_LONG
                || (! stdc3 && ! minus && v1 > ULONGMAX)
#endif
            )
            overflow( op_name, valpp, chk);
        break;
    case OP_SL:     v1 = v1u << v2u;    break;
    case OP_SR:     v1 = v1u >> v2u;    break;
    case OP_LT:     v1 = (v1u < v2u);   break;
    case OP_LE:     v1 = (v1u <= v2u);  break;
    case OP_GT:     v1 = (v1u > v2u);   break;
    case OP_GE:     v1 = (v1u >= v2u);  break;
    case OP_EQ:     v1 = (v1u == v2u);  break;
    case OP_NE:     v1 = (v1u != v2u);  break;
    case OP_AND:    v1 = v1u & v2u;     break;
    case OP_XOR:    v1 = v1u ^ v2u;     break;
    case OP_OR:     v1 = v1u | v2u;     break;
    case OP_ANA:    v1 = (v1u && v2u);  break;
    case OP_ORO:    v1 = (v1u || v2u);  break;
    case OP_COL:    valp--;
        if (valp->val)
            v1 = v1u;
        else
            v1 = v2u;
        break;
    default:
        cfatal( illeg_op, op_name, 0L, NULL);
    }

    *valpp = valp;
    return  v1;
}

static void overflow(
    const char *    op_name,
    VAL_SIGN ** valpp,
    int         ll_overflow     /* Flag of overflow in long long    */
)
{
    const char * const  out_of_range
        = "Result of \"%s\" is out of range%.0ld%s";    /* _E_ _W1_ _W8_    */

#if HAVE_LONG_LONG
    if (standard && ! ll_overflow) {
        /* Overflow of long not in C99 mode */
        if ((! skip && (warn_level & w_level)) || (skip && (warn_level & 8)))
            cwarn( out_of_range, op_name, 0L, " of (unsigned) long");
    } else
#endif
    if (skip) {
        if (warn_level & 8)
            cwarn( out_of_range, op_name, 0L, non_eval);
        /* Else don't warn  */
    } else if (standard && (*valpp)->sign == UNSIGNED) {/* Never overflow   */
        if (warn_level & 1)
            cwarn( out_of_range, op_name, 0L, NULL);
    } else {
        cerror( out_of_range, op_name, 0L, NULL);
        (*valpp)->sign = VAL_ERROR;
    }
}

static void dump_val(
    const char *        msg,
    const VAL_SIGN *    valp
)
/*
 * Dump a value by internal representation.
 */
{
#if HAVE_LONG_LONG
    const char * const  format
                = "%s(%ssigned long long) 0x%016" LL_FORM "x";
#else
    const char * const  format = "%s(%ssigned long) 0x%08lx";
#endif
    int     sign = valp->sign;

    mcpp_fprintf( DBG, format, msg, sign ? "" : "un", valp->val);
}

static void dump_stack(
    const OPTAB *       opstack,        /* Operator stack               */
    const OPTAB *       opp,            /* Pointer into operator stack  */
    const VAL_SIGN *    value,          /* Value stack                  */
    const VAL_SIGN *    valp            /* -> value vector              */
)
/*
 * Dump stacked operators and values.
 */
{
    if (opstack < opp)
        mcpp_fprintf( DBG, "Index op prec skip name -- op stack at %s"
                , infile->bptr);

    while (opstack < opp) {
        mcpp_fprintf( DBG, " [%2d] %2d %04o    %d %s\n", (int)(opp - opstack)
                , opp->op, opp->prec, opp->skip, opname[ opp->op]);
        opp--;
    }

    while (value <= --valp) {
        mcpp_fprintf( DBG, "value[%d].val = ", (int)(valp - value));
        dump_val( "", valp);
        mcpp_fputc( '\n', DBG);
    }
}

