/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */
 
#include "cpp.h"
 
#define NSTAK   32
#define SGN 0
#define UNS 1
#define UND 2
 
#define UNSMARK 0x1000
 
struct value
{
    int val;
    int type;
};
 
/* conversion types */
#define RELAT   1
#define ARITH   2
#define LOGIC   3
#define SPCL    4
#define SHIFT   5
#define UNARY   6
 
/* operator priority, arity, and conversion type, indexed by tokentype */
struct pri
{
    char pri;
    char arity;
    char ctype;
};
 
static struct pri priority[] =
{
    {
        0, 0, 0
    },                                  /* END */
    {
        0, 0, 0
    },                                  /* UNCLASS */
    {
        0, 0, 0
    },                                  /* NAME */
    {
        0, 0, 0
    },                                  /* NUMBER */
    {
        0, 0, 0
    },                                  /* STRING */
    {
        0, 0, 0
    },                                  /* CCON */
    {
        0, 0, 0
    },                                  /* NL */
    {
        0, 0, 0
    },                                  /* WS */
    {
        0, 0, 0
    },                                  /* DSHARP */
    {
        11, 2, RELAT
    },                                  /* EQ */
    {
        11, 2, RELAT
    },                                  /* NEQ */
    {
        12, 2, RELAT
    },                                  /* LEQ */
    {
        12, 2, RELAT
    },                                  /* GEQ */
    {
        13, 2, SHIFT
    },                                  /* LSH */
    {
        13, 2, SHIFT
    },                                  /* RSH */
    {
        7, 2, LOGIC
    },                                  /* LAND */
    {
        6, 2, LOGIC
    },                                  /* LOR */
    {
        0, 0, 0
    },                                  /* PPLUS */
    {
        0, 0, 0
    },                                  /* MMINUS */
    {
        0, 0, 0
    },                                  /* ARROW */
    {
        0, 0, 0
    },                                  /* SBRA */
    {
        0, 0, 0
    },                                  /* SKET */
    {
        3, 0, 0
    },                                  /* LP */
    {
        3, 0, 0
    },                                  /* RP */
    {
        0, 0, 0
    },                                  /* DOT */
    {
        10, 2, ARITH
    },                                  /* AND */
    {
        15, 2, ARITH
    },                                  /* STAR */
    {
        14, 2, ARITH
    },                                  /* PLUS */
    {
        14, 2, ARITH
    },                                  /* MINUS */
    {
        16, 1, UNARY
    },                                  /* TILDE */
    {
        16, 1, UNARY
    },                                  /* NOT */
    {
        15, 2, ARITH
    },                                  /* SLASH */
    {
        15, 2, ARITH
    },                                  /* PCT */
    {
        12, 2, RELAT
    },                                  /* LT */
    {
        12, 2, RELAT
    },                                  /* GT */
    {
        9, 2, ARITH
    },                                  /* CIRC */
    {
        8, 2, ARITH
    },                                  /* OR */
    {
        5, 2, SPCL
    },                                  /* QUEST */
    {
        5, 2, SPCL
    },                                  /* COLON */
    {
        0, 0, 0
    },                                  /* ASGN */
    {
        4, 2, 0
    },                                  /* COMMA */
    {
        0, 0, 0
    },                                  /* SHARP */
    {
        0, 0, 0
    },                                  /* SEMIC */
    {
        0, 0, 0
    },                                  /* CBRA */
    {
        0, 0, 0
    },                                  /* CKET */
    {
        0, 0, 0
    },                                  /* ASPLUS */
    {
        0, 0, 0
    },                                  /* ASMINUS */
    {
        0, 0, 0
    },                                  /* ASSTAR */
    {
        0, 0, 0
    },                                  /* ASSLASH */
    {
        0, 0, 0
    },                                  /* ASPCT */
    {
        0, 0, 0
    },                                  /* ASCIRC */
    {
        0, 0, 0
    },                                  /* ASLSH */
    {
        0, 0, 0
    },                                  /* ASRSH */
    {
        0, 0, 0
    },                                  /* ASOR */
    {
        0, 0, 0
    },                                  /* ASAND */
    {
        0, 0, 0
    },                                  /* ELLIPS */
    {
        0, 0, 0
    },                                  /* DSHARP1 */
    {
        0, 0, 0
    },                                  /* NAME1 */
    {
        0, 0, 0
    },                                  /* NAME2 */
    {
        16, 1, UNARY
    },                                  /* DEFINED */
    {
        16, 0, UNARY
    },                                  /* UMINUS */
    {
        16, 1, UNARY
    },                                  /* ARCHITECTURE */
};
 
int evalop(struct pri);
struct value tokval(Token *);
struct value vals[NSTAK], *vp;
enum toktype ops[NSTAK], *op;
 
/*
 * Evaluate an #if #elif #ifdef #ifndef line.  trp->tp points to the keyword.
 */
long
    eval(Tokenrow * trp, int kw)
{
    Token *tp;
    Nlist *np;
    size_t  ntok;
    int rnd;
 
    trp->tp++;
    if (kw == KIFDEF || kw == KIFNDEF)
    {
        if (trp->lp - trp->bp != 4 || trp->tp->type != NAME)
        {
            error(ERROR, "Syntax error in #ifdef/#ifndef");
            return 0;
        }
        np = lookup(trp->tp, 0);
        return (kw == KIFDEF) == (np && np->flag & (ISDEFINED | ISMAC));
    }
    ntok = trp->tp - trp->bp;
    kwdefined->val = KDEFINED;          /* activate special meaning of
                                         * defined */
    expandrow(trp, "<if>");
    kwdefined->val = NAME;
    vp = vals;
    op = ops;
    *op++ = END;
    for (rnd = 0, tp = trp->bp + ntok; tp < trp->lp; tp++)
    {
        switch (tp->type)
        {
            case WS:
            case NL:
                continue;
 
                /* nilary */
            case NAME:
            case NAME1:
            case NAME2:
            case NUMBER:
            case CCON:
            case STRING:
                if (rnd)
                    goto syntax;
                *vp++ = tokval(tp);
                rnd = 1;
                continue;
 
                /* unary */
            case DEFINED:
            case TILDE:
            case NOT:
                if (rnd)
                    goto syntax;
                *op++ = tp->type;
                continue;
 
                /* unary-binary */
            case PLUS:
            case MINUS:
            case STAR:
            case AND:
                if (rnd == 0)
                {
                    if (tp->type == MINUS)
                        *op++ = UMINUS;
                    if (tp->type == STAR || tp->type == AND)
                    {
                        error(ERROR, "Illegal operator * or & in #if/#elif");
                        return 0;
                    }
                    continue;
                }
                /* fall through */
 
                /* plain binary */
            case EQ:
            case NEQ:
            case LEQ:
            case GEQ:
            case LSH:
            case RSH:
            case LAND:
            case LOR:
            case SLASH:
            case PCT:
            case LT:
            case GT:
            case CIRC:
            case OR:
            case QUEST:
            case COLON:
            case COMMA:
                if (rnd == 0)
                    goto syntax;
                if (evalop(priority[tp->type]) != 0)
                    return 0;
                *op++ = tp->type;
                rnd = 0;
                continue;
 
            case LP:
                if (rnd)
                    goto syntax;
                *op++ = LP;
                continue;
 
            case RP:
                if (!rnd)
                    goto syntax;
                if (evalop(priority[RP]) != 0)
                    return 0;
                if (op <= ops || op[-1] != LP)
                {
                    goto syntax;
                }
                op--;
                continue;
 
            case SHARP:
                if ((tp + 1) < trp->lp)
                {
                    np = lookup(tp + 1, 0);
                    if (np && (np->val == KMACHINE))
                    {
                        tp++;
                        if (rnd)
                            goto syntax;
                        *op++ = ARCHITECTURE;
                        continue;
                    }
                }
                /* fall through */
 
            default:
                error(ERROR, "Bad operator (%t) in #if/#elif", tp);
                return 0;
        }
    }
    if (rnd == 0)
        goto syntax;
    if (evalop(priority[END]) != 0)
        return 0;
    if (op != &ops[1] || vp != &vals[1])
    {
        error(ERROR, "Botch in #if/#elif");
        return 0;
    }
    if (vals[0].type == UND)
        error(ERROR, "Undefined expression value");
    return vals[0].val;
syntax:
    error(ERROR, "Syntax error in #if/#elif");
    return 0;
}
 
int
    evalop(struct pri pri)
{
    struct value v1;
    struct value v2 = { 0, UND };
    int rv1, rv2;
    int rtype, oper;
 
    rv2 = 0;
    rtype = 0;
    while (pri.pri < priority[op[-1]].pri)
    {
        oper = *--op;
        if (priority[oper].arity == 2)
        {
            v2 = *--vp;
            rv2 = v2.val;
        }
        v1 = *--vp;
        rv1 = v1.val;
/*lint -e574 -e644 */
        switch (priority[oper].ctype)
        {
            case 0:
            default:
                error(WARNING, "Syntax error in #if/#endif");
                return 1;
            case ARITH:
            case RELAT:
                if (v1.type == UNS || v2.type == UNS)
                    rtype = UNS;
                else
                    rtype = SGN;
                if (v1.type == UND || v2.type == UND)
                    rtype = UND;
                if (priority[oper].ctype == RELAT && rtype == UNS)
                {
                    oper |= UNSMARK;
                    rtype = SGN;
                }
                break;
            case SHIFT:
                if (v1.type == UND || v2.type == UND)
                    rtype = UND;
                else
                    rtype = v1.type;
                if (rtype == UNS)
                    oper |= UNSMARK;
                break;
            case UNARY:
                rtype = v1.type;
                break;
            case LOGIC:
            case SPCL:
                break;
        }
        switch (oper)
        {
            case EQ:
            case EQ | UNSMARK:
                rv1 = rv1 == rv2;
                break;
            case NEQ:
            case NEQ | UNSMARK:
                rv1 = rv1 != rv2;
                break;
            case LEQ:
                rv1 = rv1 <= rv2;
                break;
            case GEQ:
                rv1 = rv1 >= rv2;
                break;
            case LT:
                rv1 = rv1 < rv2;
                break;
            case GT:
                rv1 = rv1 > rv2;
                break;
            case LEQ | UNSMARK:
                rv1 = (unsigned long)rv1 <= (unsigned long)rv2;
                break;
            case GEQ | UNSMARK:
                rv1 = (unsigned long)rv1 >= (unsigned long)rv2;
                break;
            case LT | UNSMARK:
                rv1 = (unsigned long)rv1 < (unsigned long)rv2;
                break;
            case GT | UNSMARK:
                rv1 = (unsigned long)rv1 > (unsigned long)rv2;
                break;
            case LSH:
                rv1 <<= rv2;
                break;
            case LSH | UNSMARK:
                rv1 = (unsigned long) rv1 << rv2;
                break;
            case RSH:
                rv1 >>= rv2;
                break;
            case RSH | UNSMARK:
                rv1 = (unsigned long) rv1 >> rv2;
                break;
            case LAND:
                rtype = UND;
                if (v1.type == UND)
                    break;
                if (rv1 != 0)
                {
                    if (v2.type == UND)
                        break;
                    rv1 = rv2 != 0;
                }
                else
                    rv1 = 0;
                rtype = SGN;
                break;
            case LOR:
                rtype = UND;
                if (v1.type == UND)
                    break;
                if (rv1 == 0)
                {
                    if (v2.type == UND)
                        break;
                    rv1 = rv2 != 0;
                }
                else
                    rv1 = 1;
                rtype = SGN;
                break;
            case AND:
                rv1 &= rv2;
                break;
            case STAR:
                rv1 *= rv2;
                break;
            case PLUS:
                rv1 += rv2;
                break;
            case MINUS:
                rv1 -= rv2;
                break;
            case UMINUS:
                if (v1.type == UND)
                    rtype = UND;
                rv1 = -rv1;
                break;
            case OR:
                rv1 |= rv2;
                break;
            case CIRC:
                rv1 ^= rv2;
                break;
            case TILDE:
                rv1 = ~rv1;
                break;
            case NOT:
                rv1 = !rv1;
                if (rtype != UND)
                    rtype = SGN;
                break;
            case SLASH:
                if (rv2 == 0)
                {
                    rtype = UND;
                    break;
                }
                if (rtype == UNS)
                    rv1 /= (unsigned long) rv2;
                else
                    rv1 /= rv2;
                break;
            case PCT:
                if (rv2 == 0)
                {
                    rtype = UND;
                    break;
                }
                if (rtype == UNS)
                    rv1 %= (unsigned long) rv2;
                else
                    rv1 %= rv2;
                break;
            case COLON:
                if (op[-1] != QUEST)
                    error(ERROR, "Bad ?: in #if/endif");
                else
                {
                    op--;
                    if ((--vp)->val == 0)
                        v1 = v2;
                    rtype = v1.type;
                    rv1 = v1.val;
                }
                break;
 
            case DEFINED:
            case ARCHITECTURE:
                break;
 
            default:
                error(ERROR, "Eval botch (unknown operator)");
                return 1;
        }
/*lint +e574 +e644 */
        v1.val = rv1;
        v1.type = rtype;
        *vp++ = v1;
    }
    return 0;
}
 
struct value
    tokval(Token * tp)
{
    struct value v;
    Nlist *np;
    int i, base;
    unsigned int n;
    uchar *p, c;
 
    v.type = SGN;
    v.val = 0;
    switch (tp->type)
    {
 
        case NAME:
            v.val = 0;
            break;
 
        case NAME1:
            if ((np = lookup(tp, 0)) != NULL && np->flag & (ISDEFINED | ISMAC))
                v.val = 1;
            break;
 
        case NAME2:
            if ((np = lookup(tp, 0)) != NULL && np->flag & (ISARCHITECTURE))
                v.val = 1;
            break;
 
        case NUMBER:
            n = 0;
            base = 10;
            p = tp->t;
            c = p[tp->len];
            p[tp->len] = '\0';
            if (*p == '0')
            {
                base = 8;
                if (p[1] == 'x' || p[1] == 'X')
                {
                    base = 16;
                    p++;
                }
                p++;
            }
            for (;; p++)
            {
                if ((i = digit(*p)) < 0)
                    break;
                if (i >= base)
                    error(WARNING,
                          "Bad digit in number %t", tp);
                n *= base;
                n += i;
            }
            if (n >= 0x80000000 && base != 10)
                v.type = UNS;
            for (; *p; p++)
            {
                if (*p == 'u' || *p == 'U')
                    v.type = UNS;
                else
                    if (*p == 'l' || *p == 'L')
                        ;
                    else
                    {
                        error(ERROR,
                              "Bad number %t in #if/#elif", tp);
                        break;
                    }
            }
            v.val = n;
            tp->t[tp->len] = c;
            break;
 
        case CCON:
            n = 0;
            p = tp->t;
            if (*p == 'L')
            {
                p += 1;
                error(WARNING, "Wide char constant value undefined");
            }
            p += 1;
            if (*p == '\\')
            {
                p += 1;
                if ((i = digit(*p)) >= 0 && i <= 7)
                {
                    n = i;
                    p += 1;
                    if ((i = digit(*p)) >= 0 && i <= 7)
                    {
                        p += 1;
                        n <<= 3;
                        n += i;
                        if ((i = digit(*p)) >= 0 && i <= 7)
                        {
                            p += 1;
                            n <<= 3;
                            n += i;
                        }
                    }
                }
                else
                    if (*p == 'x')
                    {
                        p += 1;
                        while ((i = digit(*p)) >= 0 && i <= 15)
                        {
                            p += 1;
                            n <<= 4;
                            n += i;
                        }
                    }
                    else
                    {
                        static const char cvcon[] = "b\bf\fn\nr\rt\tv\v''\"\"??\\\\";
                        static size_t cvlen = sizeof(cvcon) - 1;
 
                        size_t j;
                        for (j = 0; j < cvlen; j += 2)
                        {
                            if (*p == cvcon[j])
                            {
                                n = cvcon[j + 1];
                                break;
                            }
                        }
                        p += 1;
                        if (j >= cvlen)
                            error(WARNING,
                               "Undefined escape in character constant");
                    }
            }
            else
                if (*p == '\'')
                    error(ERROR, "Empty character constant");
                else
                    n = *p++;
            if (*p != '\'')
                error(WARNING, "Multibyte character constant undefined");
            else
                if (n > 127)
                    error(WARNING, "Character constant taken as not signed");
            v.val = n;
            break;
 
        case STRING:
            error(ERROR, "String in #if/#elif");
            break;
    }
    return v;
}
 
int
    digit(int i)
{
    if ('0' <= i && i <= '9')
        i -= '0';
    else
        if ('a' <= i && i <= 'f')
            i -= 'a' - 10;
        else
            if ('A' <= i && i <= 'F')
                i -= 'A' - 10;
            else
                i = -1;
    return i;
}
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V707 Giving short names to global variables is considered to be bad practice. It is suggested to rename 'op' variable.

V707 Giving short names to global variables is considered to be bad practice. It is suggested to rename 'vp' variable.