blob: d404872827b1a74a7977b865fd323d94b7db4b04 [file] [log] [blame]
/**************************************************************
*
* 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
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
#include "cpp.h"
#include <stdlib.h>
#include <string.h>
#define NSTAK 32
#define SGN 0
#define UNS 1
#define UND 2
#define UNSMARK 0x1000
struct value
{
long 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;
} 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;
int ntok, 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/#elsif");
return 0;
}
continue;
}
/* flow 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/#elsif", 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/#elsif");
return 0;
}
if (vals[0].type == UND)
error(ERROR, "Undefined expression value");
return vals[0].val;
syntax:
error(ERROR, "Syntax error in #if/#elsif");
return 0;
}
int
evalop(struct pri pri)
{
struct value v1;
struct value v2 = { 0, UND };
long 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 long 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/#elsif", 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 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/#elsif");
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;
}