/* -*- 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 const 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 */ }; static int evalop(struct pri); static struct value tokval(Token *); static struct value vals[NSTAK], *vp; static 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, ""); 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: np = lookup(tp, 0); if (np != NULL && np->flag & (ISDEFINED | ISMAC)) v.val = 1; break; case NAME2: np = lookup(tp, 0); if (np != 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; i = digit(*p); if (i >= 0 && i <= 7) { n = i; p += 1; i = digit(*p); if (i >= 0 && i <= 7) { p += 1; n <<= 3; n += i; i = digit(*p); if (i >= 0 && i <= 7) { p += 1; n <<= 3; n += i; } } } else if (*p == 'x') { p += 1; while (1) { i = digit(*p); if (i < 0 || i > 16) break; 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: */