#include <u.h>
#include <libc.h>
#include "cpp.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 */
const 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 */
{ 16, 1, UNARY }, /* DEFINED */
{ 16, 0, UNARY }, /* UMINUS */
};
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, rand;
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>", Notinmacro);
kwdefined->val = NAME;
vp = vals;
op = ops;
*op++ = END;
for (rand=0, tp = trp->bp+ntok; tp < trp->lp; tp++) {
switch(tp->type) {
case WS:
case NL:
continue;
/* nilary */
case NAME:
case NAME1:
case NUMBER:
case CCON:
case STRING:
if (rand)
goto syntax;
*vp++ = tokval(tp);
rand = 1;
continue;
/* unary */
case DEFINED:
case TILDE:
case NOT:
if (rand)
goto syntax;
*op++ = tp->type;
continue;
/* unary-binary */
case PLUS: case MINUS: case STAR: case AND:
if (rand==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;
}
/* 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 (rand==0)
goto syntax;
if (evalop(priority[tp->type])!=0)
return 0;
*op++ = tp->type;
rand = 0;
continue;
case LP:
if (rand)
goto syntax;
*op++ = LP;
continue;
case RP:
if (!rand)
goto syntax;
if (evalop(priority[RP])!=0)
return 0;
if (op<=ops || op[-1]!=LP) {
goto syntax;
}
op--;
continue;
default:
error(ERROR,"Bad operator (%t) in #if/#elif", tp);
return 0;
}
}
if (rand==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, v2;
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;
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<=rv2; break;
case GEQ|UNSMARK:
rv1 = (unsigned long)rv1>=rv2; break;
case LT|UNSMARK:
rv1 = (unsigned long)rv1<rv2; break;
case GT|UNSMARK:
rv1 = (unsigned long)rv1>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:
break;
default:
error(ERROR, "Eval botch (unknown operator)");
return 1;
}
v1.val = rv1;
v1.type = rtype;
*vp++ = v1;
}
return 0;
}
struct value
tokval(Token *tp)
{
struct value v;
Nlist *np;
int i, base, c, longcc;
unsigned long n;
Rune r;
uchar *p;
v.type = SGN;
v.val = 0;
switch (tp->type) {
case NAME:
v.val = 0;
break;
case NAME1:
if ((np = lookup(tp, 0)) && np->flag&(ISDEFINED|ISMAC))
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;
longcc = 0;
if (*p=='L') {
p += 1;
longcc = 1;
}
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[]
= "a\ab\bf\fn\nr\rt\tv\v''\"\"??\\\\";
for (i=0; i<sizeof(cvcon); i+=2) {
if (*p == cvcon[i]) {
n = cvcon[i+1];
break;
}
}
p += 1;
if (i>=sizeof(cvcon))
error(WARNING,
"Undefined escape in character constant");
}
} else if (*p=='\'')
error(ERROR, "Empty character constant");
else {
i = chartorune(&r, (char*)p);
n = r;
p += i;
if (i>1 && longcc==0)
error(WARNING, "Undefined character constant");
}
if (*p!='\'')
error(WARNING, "Multibyte character constant undefined");
else if (n>127 && longcc==0)
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;
}
|