1N/A/***********************************************************************
1N/A* *
1N/A* This software is part of the ast package *
1N/A* Copyright (c) 1986-2010 AT&T Intellectual Property *
1N/A* and is licensed under the *
1N/A* Common Public License, Version 1.0 *
1N/A* by AT&T Intellectual Property *
1N/A* *
1N/A* A copy of the License is available at *
1N/A* http://www.opensource.org/licenses/cpl1.0.txt *
1N/A* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
1N/A* *
1N/A* Information and Software Systems Research *
1N/A* AT&T Research *
1N/A* Florham Park NJ *
1N/A* *
1N/A* Glenn Fowler <gsf@research.att.com> *
1N/A* *
1N/A***********************************************************************/
1N/A#pragma prototyped
1N/A/*
1N/A * Glenn Fowler
1N/A * AT&T Research
1N/A *
1N/A * preprocessor expression evaluation support
1N/A */
1N/A
1N/A#include "pplib.h"
1N/A
1N/A#include <regex.h>
1N/A
1N/A#define lex(c) ((((c)=peektoken)>=0?(peektoken=(-1)):((c)=pplex())),(c))
1N/A#define unlex(c) (peektoken=(c))
1N/A
1N/Astatic int peektoken; /* expression lookahead token */
1N/Astatic char* errmsg; /* subexpr() error message */
1N/A
1N/A/*
1N/A * exists predicate evaluation
1N/A */
1N/A
1N/Astatic int
1N/Aexists(int op, char* pred, register char* args)
1N/A{
1N/A register int c;
1N/A register int type;
1N/A char* pptoken;
1N/A long state;
1N/A char file[MAXTOKEN + 1];
1N/A
1N/A state = (pp.state & ~DISABLE);
1N/A PUSH_STRING(args);
1N/A pptoken = pp.token;
1N/A pp.token = file;
1N/A pp.state |= HEADER|PASSEOF;
1N/A type = pplex();
1N/A pp.state &= ~HEADER;
1N/A pp.token = pptoken;
1N/A switch (type)
1N/A {
1N/A case T_STRING:
1N/A case T_HEADER:
1N/A break;
1N/A default:
1N/A error(1, "%s: \"...\" or <...> argument expected", pred);
1N/A c = 0;
1N/A goto done;
1N/A }
1N/A if (op == X_EXISTS)
1N/A {
1N/A if ((c = pplex()) == ',')
1N/A {
1N/A while ((c = pplex()) == T_STRING)
1N/A {
1N/A if (pathaccess(pp.token, file, NiL, 0, pp.path, MAXTOKEN + 1))
1N/A {
1N/A pathcanon(pp.path, 0, 0);
1N/A message((-2, "%s: %s found", pred, pp.path));
1N/A c = 1;
1N/A goto done;
1N/A }
1N/A if ((c = pplex()) != ',') break;
1N/A }
1N/A if (c) error(1, "%s: \"...\" arguments expected", pred);
1N/A strcpy(pp.path, file);
1N/A message((-2, "%s: %s not found", pred, file));
1N/A c = 0;
1N/A }
1N/A else c = ppsearch(file, type, SEARCH_EXISTS) >= 0;
1N/A }
1N/A else
1N/A {
1N/A register struct ppfile* fp;
1N/A
1N/A fp = ppsetfile(file);
1N/A c = fp->flags || fp->guard == INC_IGNORE;
1N/A }
1N/A done:
1N/A while (pplex());
1N/A pp.state = state;
1N/A return c;
1N/A}
1N/A
1N/A/*
1N/A * strcmp/match predicate evaluation
1N/A */
1N/A
1N/Astatic int
1N/Acompare(char* pred, char* args, int match)
1N/A{
1N/A register int c;
1N/A char* pptoken;
1N/A long state;
1N/A regex_t re;
1N/A char tmp[MAXTOKEN + 1];
1N/A
1N/A state = (pp.state & ~DISABLE);
1N/A PUSH_STRING(args);
1N/A pp.state |= PASSEOF;
1N/A pptoken = pp.token;
1N/A pp.token = tmp;
1N/A if (!pplex())
1N/A goto bad;
1N/A pp.token = pptoken;
1N/A if (pplex() != ',' || !pplex())
1N/A goto bad;
1N/A if (!match)
1N/A c = strcmp(tmp, pp.token);
1N/A else if ((c = regcomp(&re, pp.token, REG_AUGMENTED|REG_LENIENT|REG_NULL)) || (c = regexec(&re, tmp, NiL, 0, 0)) && c != REG_NOMATCH)
1N/A regfatal(&re, 4, c);
1N/A else
1N/A {
1N/A c = !c;
1N/A regfree(&re);
1N/A }
1N/A if ((pp.state & PASSEOF) && pplex())
1N/A goto bad;
1N/A pp.state = state;
1N/A return c;
1N/A bad:
1N/A pp.token = pptoken;
1N/A error(2, "%s: 2 arguments expected", pred);
1N/A while (pplex());
1N/A pp.state = state;
1N/A return 0;
1N/A}
1N/A
1N/A/*
1N/A * #if predicate parse and evaluation
1N/A */
1N/A
1N/Astatic int
1N/Apredicate(int warn)
1N/A{
1N/A register char* args;
1N/A register struct pplist* p;
1N/A register struct ppsymbol* sym;
1N/A register int type;
1N/A int index;
1N/A
1N/A static char pred[MAXID + 1];
1N/A
1N/A /*
1N/A * first gather the args
1N/A */
1N/A
1N/A index = (int)hashref(pp.strtab, pp.token);
1N/A if (warn && peekchr() != '(') switch (index)
1N/A {
1N/A case X_DEFINED:
1N/A case X_EXISTS:
1N/A case X_INCLUDED:
1N/A case X_MATCH:
1N/A case X_NOTICED:
1N/A case X_OPTION:
1N/A case X_SIZEOF:
1N/A case X_STRCMP:
1N/A break;
1N/A default:
1N/A if (pp.macref) pprefmac(pp.token, REF_IF);
1N/A return 0;
1N/A }
1N/A strcpy(pred, pp.token);
1N/A pp.state |= DISABLE;
1N/A type = pppredargs();
1N/A pp.state &= ~DISABLE;
1N/A switch (type)
1N/A {
1N/A case T_ID:
1N/A case T_STRING:
1N/A break;
1N/A default:
1N/A unlex(type);
1N/A /*FALLTHROUGH*/
1N/A case 0:
1N/A if (index && !(pp.state & STRICT))
1N/A error(1, "%s: predicate argument expected", pred);
1N/A if (pp.macref) pprefmac(pred, REF_IF);
1N/A return 0;
1N/A }
1N/A args = pp.args;
1N/A
1N/A /*
1N/A * now evaluate
1N/A */
1N/A
1N/A debug((-6, "pred=%s args=%s", pred, args));
1N/A if ((pp.state & STRICT) && !(pp.mode & HOSTED)) switch (index)
1N/A {
1N/A case X_DEFINED:
1N/A case X_SIZEOF:
1N/A break;
1N/A default:
1N/A error(1, "%s(%s): non-standard predicate test", pred, args);
1N/A return 0;
1N/A }
1N/A switch (index)
1N/A {
1N/A case X_DEFINED:
1N/A if (type != T_ID) error(1, "%s: identifier argument expected", pred);
1N/A else if ((sym = pprefmac(args, REF_IF)) && sym->macro) return 1;
1N/A else if (args[0] == '_' && args[1] == '_' && !strncmp(args, "__STDPP__", 9))
1N/A {
1N/A if (pp.hosted == 1 && pp.in->prev->type == IN_FILE)
1N/A {
1N/A pp.mode |= HOSTED;
1N/A pp.flags |= PP_hosted;
1N/A }
1N/A return *(args + 9) ? (int)hashref(pp.strtab, args + 9) : 1;
1N/A }
1N/A break;
1N/A case X_EXISTS:
1N/A case X_INCLUDED:
1N/A return exists(index, pred, args);
1N/A case X_MATCH:
1N/A case X_STRCMP:
1N/A return compare(pred, args, index == X_MATCH);
1N/A case X_NOTICED:
1N/A if (type != T_ID) error(1, "%s: identifier argument expected", pred);
1N/A else if (((sym = pprefmac(args, REF_IF)) || (sym = ppsymref(pp.symtab, args))) && (sym->flags & SYM_NOTICED)) return 1;
1N/A break;
1N/A case X_OPTION:
1N/A return ppoption(args);
1N/A case X_SIZEOF:
1N/A error(2, "%s invalid in #%s expressions", pred, dirname(IF));
1N/A break;
1N/A default:
1N/A if (warn && !(pp.mode & HOSTED) && (sym = ppsymref(pp.symtab, pred)) && (sym->flags & SYM_PREDICATE))
1N/A error(1, "use #%s(%s) to disambiguate", pred, args);
1N/A if (p = (struct pplist*)hashget(pp.prdtab, pred))
1N/A {
1N/A if (!*args) return 1;
1N/A while (p)
1N/A {
1N/A if (streq(p->value, args)) return 1;
1N/A p = p->next;
1N/A }
1N/A }
1N/A break;
1N/A }
1N/A return 0;
1N/A}
1N/A
1N/A/*
1N/A * evaluate a long integer subexpression with precedence
1N/A * taken from the library routine streval()
1N/A * may be called recursively
1N/A *
1N/A * NOTE: all operands are evaluated as both the parse
1N/A * and evaluation are done on the fly
1N/A */
1N/A
1N/Astatic long
1N/Asubexpr(register int precedence, int* pun)
1N/A{
1N/A register int c;
1N/A register long n;
1N/A register long x;
1N/A register int operand = 1;
1N/A int un = 0;
1N/A int xn;
1N/A
1N/A switch (lex(c))
1N/A {
1N/A case 0:
1N/A case '\n':
1N/A unlex(c);
1N/A if (!errmsg && !(pp.mode & INACTIVE)) errmsg = "more tokens expected";
1N/A return 0;
1N/A case '-':
1N/A n = -subexpr(13, &un);
1N/A break;
1N/A case '+':
1N/A n = subexpr(13, &un);
1N/A break;
1N/A case '!':
1N/A n = !subexpr(13, &un);
1N/A break;
1N/A case '~':
1N/A n = ~subexpr(13, &un);
1N/A break;
1N/A default:
1N/A unlex(c);
1N/A n = 0;
1N/A operand = 0;
1N/A break;
1N/A }
1N/A un <<= 1;
1N/A for (;;)
1N/A {
1N/A switch (lex(c))
1N/A {
1N/A case 0:
1N/A case '\n':
1N/A goto done;
1N/A case ')':
1N/A if (!precedence)
1N/A {
1N/A if (!errmsg && !(pp.mode & INACTIVE)) errmsg = "too many )'s";
1N/A return 0;
1N/A }
1N/A goto done;
1N/A case '(':
1N/A n = subexpr(1, &un);
1N/A if (lex(c) != ')')
1N/A {
1N/A unlex(c);
1N/A if (!errmsg && !(pp.mode & INACTIVE)) errmsg = "closing ) expected";
1N/A return 0;
1N/A }
1N/A gotoperand:
1N/A if (operand)
1N/A {
1N/A if (!errmsg && !(pp.mode & INACTIVE)) errmsg = "operator expected";
1N/A return 0;
1N/A }
1N/A operand = 1;
1N/A un <<= 1;
1N/A continue;
1N/A case '?':
1N/A if (precedence > 1) goto done;
1N/A un = 0;
1N/A if (lex(c) == ':')
1N/A {
1N/A if (!n) n = subexpr(2, &un);
1N/A else
1N/A {
1N/A x = pp.mode;
1N/A pp.mode |= INACTIVE;
1N/A subexpr(2, &xn);
1N/A pp.mode = x;
1N/A }
1N/A }
1N/A else
1N/A {
1N/A unlex(c);
1N/A x = subexpr(2, &xn);
1N/A if (lex(c) != ':')
1N/A {
1N/A unlex(c);
1N/A if (!errmsg && !(pp.mode & INACTIVE)) errmsg = ": expected for ? operator";
1N/A return 0;
1N/A }
1N/A if (n)
1N/A {
1N/A n = x;
1N/A un = xn;
1N/A subexpr(2, &xn);
1N/A }
1N/A else n = subexpr(2, &un);
1N/A }
1N/A break;
1N/A case ':':
1N/A goto done;
1N/A case T_ANDAND:
1N/A case T_OROR:
1N/A xn = (c == T_ANDAND) ? 4 : 3;
1N/A if (precedence >= xn) goto done;
1N/A if ((n != 0) == (c == T_ANDAND)) n = subexpr(xn, &un) != 0;
1N/A else
1N/A {
1N/A x = pp.mode;
1N/A pp.mode |= INACTIVE;
1N/A subexpr(xn, &un);
1N/A pp.mode = x;
1N/A }
1N/A un = 0;
1N/A break;
1N/A case '|':
1N/A if (precedence > 4) goto done;
1N/A n |= subexpr(5, &un);
1N/A break;
1N/A case '^':
1N/A if (precedence > 5) goto done;
1N/A n ^= subexpr(6, &un);
1N/A break;
1N/A case '&':
1N/A if (precedence > 6) goto done;
1N/A n &= subexpr(7, &un);
1N/A break;
1N/A case T_EQ:
1N/A case T_NE:
1N/A if (precedence > 7) goto done;
1N/A n = (n == subexpr(8, &un)) == (c == T_EQ);
1N/A un = 0;
1N/A break;
1N/A case '<':
1N/A case T_LE:
1N/A case T_GE:
1N/A case '>':
1N/A if (precedence > 8) goto done;
1N/A x = subexpr(9, &un);
1N/A switch (c)
1N/A {
1N/A case '<':
1N/A switch (un)
1N/A {
1N/A case 01:
1N/A n = n < (unsigned long)x;
1N/A break;
1N/A case 02:
1N/A n = (unsigned long)n < x;
1N/A break;
1N/A case 03:
1N/A n = (unsigned long)n < (unsigned long)x;
1N/A break;
1N/A default:
1N/A n = n < x;
1N/A break;
1N/A }
1N/A break;
1N/A case T_LE:
1N/A switch (un)
1N/A {
1N/A case 01:
1N/A n = n <= (unsigned long)x;
1N/A break;
1N/A case 02:
1N/A n = (unsigned long)n <= x;
1N/A break;
1N/A case 03:
1N/A n = (unsigned long)n <= (unsigned long)x;
1N/A break;
1N/A default:
1N/A n = n <= x;
1N/A break;
1N/A }
1N/A break;
1N/A case T_GE:
1N/A switch (un)
1N/A {
1N/A case 01:
1N/A n = n >= (unsigned long)x;
1N/A break;
1N/A case 02:
1N/A n = (unsigned long)n >= x;
1N/A break;
1N/A case 03:
1N/A n = (unsigned long)n >= (unsigned long)x;
1N/A break;
1N/A default:
1N/A n = n >= x;
1N/A break;
1N/A }
1N/A break;
1N/A case '>':
1N/A switch (un)
1N/A {
1N/A case 01:
1N/A n = n > (unsigned long)x;
1N/A break;
1N/A case 02:
1N/A n = (unsigned long)n > x;
1N/A break;
1N/A case 03:
1N/A n = (unsigned long)n > (unsigned long)x;
1N/A break;
1N/A default:
1N/A n = n > x;
1N/A break;
1N/A }
1N/A break;
1N/A }
1N/A un = 0;
1N/A break;
1N/A case T_LSHIFT:
1N/A case T_RSHIFT:
1N/A if (precedence > 9) goto done;
1N/A x = subexpr(10, &un);
1N/A if (c == T_LSHIFT) n <<= x;
1N/A else n >>= x;
1N/A un >>= 1;
1N/A break;
1N/A case '+':
1N/A case '-':
1N/A if (precedence > 10) goto done;
1N/A x = subexpr(11, &un);
1N/A if (c == '+') n += x;
1N/A else n -= x;
1N/A break;
1N/A case '*':
1N/A case '/':
1N/A case '%':
1N/A if (precedence > 11) goto done;
1N/A x = subexpr(12, &un);
1N/A if (c == '*') n *= x;
1N/A else if (x == 0)
1N/A {
1N/A if (!errmsg && !(pp.mode & INACTIVE)) errmsg = "divide by zero";
1N/A return 0;
1N/A }
1N/A else if (c == '/') n /= x;
1N/A else n %= x;
1N/A break;
1N/A case '#':
1N/A pp.state |= DISABLE;
1N/A c = pplex();
1N/A pp.state &= ~DISABLE;
1N/A if (c != T_ID)
1N/A {
1N/A if (!errmsg && !(pp.mode & INACTIVE)) errmsg = "# must precede a predicate identifier";
1N/A return 0;
1N/A }
1N/A n = predicate(0);
1N/A goto gotoperand;
1N/A case T_ID:
1N/A n = predicate(1);
1N/A goto gotoperand;
1N/A case T_CHARCONST:
1N/A c = *(pp.toknxt - 1);
1N/A *(pp.toknxt - 1) = 0;
1N/A n = chrtoi(pp.token + 1);
1N/A *(pp.toknxt - 1) = c;
1N/A if (n & ~((1<<CHAR_BIT)-1))
1N/A {
1N/A if (!(pp.mode & HOSTED))
1N/A error(1, "'%s': multi-character character constants are not portable", pp.token);
1N/A }
1N/A#if CHAR_MIN < 0
1N/A else n = (char)n;
1N/A#endif
1N/A goto gotoperand;
1N/A case T_DECIMAL_U:
1N/A case T_DECIMAL_UL:
1N/A case T_OCTAL_U:
1N/A case T_OCTAL_UL:
1N/A case T_HEXADECIMAL_U:
1N/A case T_HEXADECIMAL_UL:
1N/A un |= 01;
1N/A /*FALLTHROUGH*/
1N/A case T_DECIMAL:
1N/A case T_DECIMAL_L:
1N/A case T_OCTAL:
1N/A case T_OCTAL_L:
1N/A case T_HEXADECIMAL:
1N/A case T_HEXADECIMAL_L:
1N/A n = strtoul(pp.token, NiL, 0);
1N/A if ((unsigned long)n > LONG_MAX) un |= 01;
1N/A goto gotoperand;
1N/A case T_WCHARCONST:
1N/A n = chrtoi(pp.token);
1N/A goto gotoperand;
1N/A default:
1N/A if (!errmsg && !(pp.mode & INACTIVE)) errmsg = "invalid token";
1N/A return 0;
1N/A }
1N/A if (errmsg) return 0;
1N/A if (!operand) goto nooperand;
1N/A }
1N/A done:
1N/A unlex(c);
1N/A if (!operand)
1N/A {
1N/A nooperand:
1N/A if (!errmsg && !(pp.mode & INACTIVE)) errmsg = "operand expected";
1N/A return 0;
1N/A }
1N/A if (un) *pun |= 01;
1N/A return n;
1N/A}
1N/A
1N/A/*
1N/A * preprocessor expression evaluator using modified streval(3)
1N/A * *pun!=0 if result is unsigned
1N/A */
1N/A
1N/Along
1N/Appexpr(int* pun)
1N/A{
1N/A long n;
1N/A int opeektoken;
1N/A long ppstate;
1N/A
1N/A ppstate = (pp.state & (CONDITIONAL|DISABLE|NOSPACE|STRIP));
1N/A pp.state &= ~(DISABLE|STRIP);
1N/A pp.state |= CONDITIONAL|NOSPACE;
1N/A opeektoken = peektoken;
1N/A peektoken = -1;
1N/A *pun = 0;
1N/A n = subexpr(0, pun);
1N/A if (peektoken == ':' && !errmsg && !(pp.mode & INACTIVE)) errmsg = "invalid use of :";
1N/A if (errmsg)
1N/A {
1N/A error(2, "%s in expression", errmsg);
1N/A errmsg = 0;
1N/A n = 0;
1N/A }
1N/A peektoken = opeektoken;
1N/A pp.state &= ~(CONDITIONAL|NOSPACE);
1N/A pp.state |= ppstate;
1N/A if (*pun) debug((-4, "ppexpr() = %luU", n));
1N/A else debug((-4, "ppexpr() = %ld", n));
1N/A return n;
1N/A}
1N/A
1N/A/*
1N/A * return non-zero if option s is set
1N/A */
1N/A
1N/Aint
1N/Appoption(char* s)
1N/A{
1N/A switch ((int)hashget(pp.strtab, s))
1N/A {
1N/A case X_ALLMULTIPLE:
1N/A return pp.mode & ALLMULTIPLE;
1N/A case X_BUILTIN:
1N/A return pp.mode & BUILTIN;
1N/A case X_CATLITERAL:
1N/A return pp.mode & CATLITERAL;
1N/A case X_COMPATIBILITY:
1N/A return pp.state & COMPATIBILITY;
1N/A case X_DEBUG:
1N/A return -error_info.trace;
1N/A case X_ELSEIF:
1N/A return pp.option & ELSEIF;
1N/A case X_FINAL:
1N/A return pp.option & FINAL;
1N/A case X_HOSTDIR:
1N/A return pp.mode & HOSTED;
1N/A case X_HOSTED:
1N/A return pp.flags & PP_hosted;
1N/A case X_INITIAL:
1N/A return pp.option & INITIAL;
1N/A case X_KEYARGS:
1N/A return pp.option & KEYARGS;
1N/A case X_LINEBASE:
1N/A return pp.flags & PP_linebase;
1N/A case X_LINEFILE:
1N/A return pp.flags & PP_linefile;
1N/A case X_LINETYPE:
1N/A return pp.flags & PP_linetype;
1N/A case X_PLUSCOMMENT:
1N/A return pp.option & PLUSCOMMENT;
1N/A case X_PLUSPLUS:
1N/A return pp.option & PLUSPLUS;
1N/A case X_PLUSSPLICE:
1N/A return pp.option & PLUSSPLICE;
1N/A case X_PRAGMAEXPAND:
1N/A return pp.option & PRAGMAEXPAND;
1N/A case X_PREDEFINED:
1N/A return pp.option & PREDEFINED;
1N/A case X_PREFIX:
1N/A return pp.option & PREFIX;
1N/A case X_PROTOTYPED:
1N/A return pp.option & PROTOTYPED;
1N/A case X_READONLY:
1N/A return pp.mode & READONLY;
1N/A case X_REGUARD:
1N/A return pp.option & REGUARD;
1N/A case X_SPACEOUT:
1N/A return pp.state & SPACEOUT;
1N/A case X_SPLICECAT:
1N/A return pp.option & SPLICECAT;
1N/A case X_SPLICESPACE:
1N/A return pp.option & SPLICESPACE;
1N/A case X_STRICT:
1N/A return pp.state & STRICT;
1N/A case X_STRINGSPAN:
1N/A return pp.option & STRINGSPAN;
1N/A case X_STRINGSPLIT:
1N/A return pp.option & STRINGSPLIT;
1N/A case X_TEST:
1N/A return pp.test;
1N/A case X_TEXT:
1N/A return !(pp.state & NOTEXT);
1N/A case X_TRANSITION:
1N/A return pp.state & TRANSITION;
1N/A case X_TRUNCATE:
1N/A return pp.truncate;
1N/A case X_WARN:
1N/A return pp.state & WARN;
1N/A default:
1N/A if (pp.state & WARN) error(1, "%s: unknown option name", s);
1N/A return 0;
1N/A }
1N/A}