#include "config.h"
#include "system.h"
#include "cpplib.h"
#include "cpphash.h"
#define possible_sum_sign(a, b, sum) ((((a) ^ (b)) | ~ ((a) ^ (sum))) < 0)
static void integer_overflow PARAMS ((cpp_reader *));
static HOST_WIDEST_INT left_shift PARAMS ((cpp_reader *, HOST_WIDEST_INT,
unsigned int,
unsigned HOST_WIDEST_INT));
static HOST_WIDEST_INT right_shift PARAMS ((cpp_reader *, HOST_WIDEST_INT,
unsigned int,
unsigned HOST_WIDEST_INT));
static struct op parse_number PARAMS ((cpp_reader *, const cpp_token *));
static struct op parse_defined PARAMS ((cpp_reader *));
static struct op lex PARAMS ((cpp_reader *, int));
static const unsigned char *op_as_text PARAMS ((cpp_reader *, enum cpp_ttype));
struct op
{
enum cpp_ttype op;
U_CHAR prio;
U_CHAR flags;
U_CHAR unsignedp;
HOST_WIDEST_INT value;
};
#define CPP_ERROR CPP_COMMENT
#define CPP_ICE(msgid) \
do { cpp_ice (pfile, msgid); goto syntax_error; } while(0)
#define SYNTAX_ERROR(msgid) \
do { cpp_error (pfile, msgid); goto syntax_error; } while(0)
#define SYNTAX_ERROR2(msgid, arg) \
do { cpp_error (pfile, msgid, arg); goto syntax_error; } while(0)
struct suffix
{
const unsigned char s[4];
const unsigned char u;
const unsigned char l;
};
static const struct suffix vsuf_1[] = {
{ "u", 1, 0 }, { "U", 1, 0 },
{ "l", 0, 1 }, { "L", 0, 1 }
};
static const struct suffix vsuf_2[] = {
{ "ul", 1, 1 }, { "UL", 1, 1 }, { "uL", 1, 1 }, { "Ul", 1, 1 },
{ "lu", 1, 1 }, { "LU", 1, 1 }, { "Lu", 1, 1 }, { "lU", 1, 1 },
{ "ll", 0, 2 }, { "LL", 0, 2 }
};
static const struct suffix vsuf_3[] = {
{ "ull", 1, 2 }, { "ULL", 1, 2 }, { "uLL", 1, 2 }, { "Ull", 1, 2 },
{ "llu", 1, 2 }, { "LLU", 1, 2 }, { "LLu", 1, 2 }, { "llU", 1, 2 }
};
#define Nsuff(tab) (sizeof tab / sizeof (struct suffix))
static struct op
parse_number (pfile, tok)
cpp_reader *pfile;
const cpp_token *tok;
{
struct op op;
const U_CHAR *start = tok->val.str.text;
const U_CHAR *end = start + tok->val.str.len;
const U_CHAR *p = start;
int c = 0, i, nsuff;
unsigned HOST_WIDEST_INT n = 0, nd, MAX_over_base;
int base = 10;
int overflow = 0;
int digit, largest_digit = 0;
const struct suffix *sufftab;
op.unsignedp = 0;
if (p[0] == '0')
{
if (end - start >= 3 && (p[1] == 'x' || p[1] == 'X'))
{
p += 2;
base = 16;
}
else
{
p += 1;
base = 8;
}
}
MAX_over_base = (((unsigned HOST_WIDEST_INT) -1)
/ ((unsigned HOST_WIDEST_INT) base));
for(; p < end; p++)
{
c = *p;
if (ISDIGIT (c)
|| (base == 16 && ISXDIGIT (c)))
digit = hex_value (c);
else
break;
if (largest_digit < digit)
largest_digit = digit;
nd = n * base + digit;
overflow |= MAX_over_base < n || nd < n;
n = nd;
}
if (p < end)
{
if ((c == '.' || c == 'F' || c == 'f')
|| (base == 10 && (c == 'E' || c == 'e')
&& p+1 < end && (p[1] == '+' || p[1] == '-'))
|| (base == 16 && (c == 'P' || c == 'p')
&& p+1 < end && (p[1] == '+' || p[1] == '-')))
SYNTAX_ERROR ("floating point numbers are not valid in #if");
switch (end - p)
{
case 1: sufftab = vsuf_1; nsuff = Nsuff(vsuf_1); break;
case 2: sufftab = vsuf_2; nsuff = Nsuff(vsuf_2); break;
case 3: sufftab = vsuf_3; nsuff = Nsuff(vsuf_3); break;
default: goto invalid_suffix;
}
for (i = 0; i < nsuff; i++)
if (memcmp (p, sufftab[i].s, end - p) == 0)
break;
if (i == nsuff)
goto invalid_suffix;
op.unsignedp = sufftab[i].u;
if (CPP_WTRADITIONAL (pfile)
&& sufftab[i].u
&& ! cpp_sys_macro_p (pfile))
cpp_warning (pfile, "traditional C rejects the `U' suffix");
if (sufftab[i].l == 2 && CPP_OPTION (pfile, pedantic)
&& ! CPP_OPTION (pfile, c99))
cpp_pedwarn (pfile, "too many 'l' suffixes in integer constant");
}
if (base <= largest_digit)
cpp_pedwarn (pfile, "integer constant contains digits beyond the radix");
if (overflow)
cpp_pedwarn (pfile, "integer constant out of range");
else if ((HOST_WIDEST_INT) n < 0 && ! op.unsignedp)
{
if (base == 10)
cpp_warning (pfile, "integer constant is so large that it is unsigned");
op.unsignedp = 1;
}
op.value = n;
op.op = CPP_NUMBER;
return op;
invalid_suffix:
cpp_error (pfile, "invalid suffix '%.*s' on integer constant",
(int) (end - p), p);
syntax_error:
op.op = CPP_ERROR;
return op;
}
static struct op
parse_defined (pfile)
cpp_reader *pfile;
{
int paren = 0;
cpp_hashnode *node = 0;
const cpp_token *token;
struct op op;
cpp_context *initial_context = pfile->context;
pfile->state.prevent_expansion++;
token = cpp_get_token (pfile);
if (token->type == CPP_OPEN_PAREN)
{
paren = 1;
token = cpp_get_token (pfile);
}
if (token->type == CPP_NAME)
{
node = token->val.node;
if (paren && cpp_get_token (pfile)->type != CPP_CLOSE_PAREN)
{
cpp_error (pfile, "missing ')' after \"defined\"");
node = 0;
}
}
else
{
cpp_error (pfile, "operator \"defined\" requires an identifier");
if (token->flags & NAMED_OP)
{
cpp_token op;
op.flags = 0;
op.type = token->type;
cpp_error (pfile,
"(\"%s\" is an alternative token for \"%s\" in C++)",
cpp_token_as_text (pfile, token),
cpp_token_as_text (pfile, &op));
}
}
if (!node)
op.op = CPP_ERROR;
else
{
if (pfile->context != initial_context)
cpp_warning (pfile, "this use of \"defined\" may not be portable");
op.value = node->type == NT_MACRO;
op.unsignedp = 0;
op.op = CPP_NUMBER;
pfile->mi_ind_cmacro = node;
}
pfile->state.prevent_expansion--;
return op;
}
static struct op
lex (pfile, skip_evaluation)
cpp_reader *pfile;
int skip_evaluation;
{
struct op op;
const cpp_token *token = cpp_get_token (pfile);
switch (token->type)
{
case CPP_NUMBER:
return parse_number (pfile, token);
case CPP_CHAR:
case CPP_WCHAR:
{
unsigned int chars_seen;
if (token->type == CPP_CHAR)
op.unsignedp = 0;
else
op.unsignedp = WCHAR_UNSIGNED;
op.op = CPP_NUMBER;
op.value = cpp_interpret_charconst (pfile, token, 1, 1, 0, 0, &chars_seen);
return op;
}
case CPP_STRING:
case CPP_WSTRING:
SYNTAX_ERROR ("string constants are not valid in #if");
case CPP_OTHER:
if (ISGRAPH (token->val.c))
SYNTAX_ERROR2 ("invalid character '%c' in #if", token->val.c);
else
SYNTAX_ERROR2 ("invalid character '\\%03o' in #if", token->val.c);
case CPP_NAME:
if (token->val.node == pfile->spec_nodes.n_defined)
return parse_defined (pfile);
else if (CPP_OPTION (pfile, cplusplus)
&& (token->val.node == pfile->spec_nodes.n_true
|| token->val.node == pfile->spec_nodes.n_false))
{
op.op = CPP_NUMBER;
op.unsignedp = 0;
op.value = (token->val.node == pfile->spec_nodes.n_true);
if (CPP_PEDANTIC (pfile)
&& ! cpp_defined (pfile, DSC("__bool_true_false_are_defined")))
cpp_pedwarn (pfile, "ISO C++ does not permit \"%s\" in #if",
NODE_NAME (token->val.node));
return op;
}
else
{
op.op = CPP_NUMBER;
op.unsignedp = 0;
op.value = 0;
if (CPP_OPTION (pfile, warn_undef) && !skip_evaluation)
cpp_warning (pfile, "\"%s\" is not defined",
NODE_NAME (token->val.node));
return op;
}
case CPP_HASH:
{
int temp;
op.op = CPP_NUMBER;
if (_cpp_test_assertion (pfile, &temp))
op.op = CPP_ERROR;
op.unsignedp = 0;
op.value = temp;
return op;
}
default:
if (((int) token->type > (int) CPP_EQ
&& (int) token->type < (int) CPP_PLUS_EQ)
|| token->type == CPP_EOF)
{
op.op = token->type;
return op;
}
SYNTAX_ERROR2 ("\"%s\" is not valid in #if expressions",
cpp_token_as_text (pfile, token));
}
syntax_error:
op.op = CPP_ERROR;
return op;
}
static void
integer_overflow (pfile)
cpp_reader *pfile;
{
if (CPP_PEDANTIC (pfile))
cpp_pedwarn (pfile, "integer overflow in preprocessor expression");
}
static HOST_WIDEST_INT
left_shift (pfile, a, unsignedp, b)
cpp_reader *pfile;
HOST_WIDEST_INT a;
unsigned int unsignedp;
unsigned HOST_WIDEST_INT b;
{
if (b >= HOST_BITS_PER_WIDEST_INT)
{
if (! unsignedp && a != 0)
integer_overflow (pfile);
return 0;
}
else if (unsignedp)
return (unsigned HOST_WIDEST_INT) a << b;
else
{
HOST_WIDEST_INT l = a << b;
if (l >> b != a)
integer_overflow (pfile);
return l;
}
}
static HOST_WIDEST_INT
right_shift (pfile, a, unsignedp, b)
cpp_reader *pfile ATTRIBUTE_UNUSED;
HOST_WIDEST_INT a;
unsigned int unsignedp;
unsigned HOST_WIDEST_INT b;
{
if (b >= HOST_BITS_PER_WIDEST_INT)
return unsignedp ? 0 : a >> (HOST_BITS_PER_WIDEST_INT - 1);
else if (unsignedp)
return (unsigned HOST_WIDEST_INT) a >> b;
else
return a >> b;
}
#define FLAG_BITS 8
#define FLAG_MASK ((1 << FLAG_BITS) - 1)
#define PRIO_SHIFT (FLAG_BITS + 1)
#define EXTRACT_PRIO(CNST) ((CNST) >> FLAG_BITS)
#define EXTRACT_FLAGS(CNST) ((CNST) & FLAG_MASK)
#define HAVE_VALUE (1 << 0)
#define NO_L_OPERAND (1 << 1)
#define NO_R_OPERAND (1 << 2)
#define SHORT_CIRCUIT (1 << 3)
#define RIGHT_ASSOC (1 << FLAG_BITS)
#define FORCE_REDUCE_PRIO (0 << PRIO_SHIFT)
#define CLOSE_PAREN_PRIO (1 << PRIO_SHIFT)
#define OPEN_PAREN_PRIO ((2 << PRIO_SHIFT) | NO_L_OPERAND)
#define COMMA_PRIO (3 << PRIO_SHIFT)
#define COND_PRIO ((4 << PRIO_SHIFT) | RIGHT_ASSOC | SHORT_CIRCUIT)
#define COLON_PRIO ((5 << PRIO_SHIFT) | SHORT_CIRCUIT)
#define OROR_PRIO ((6 << PRIO_SHIFT) | SHORT_CIRCUIT)
#define ANDAND_PRIO ((7 << PRIO_SHIFT) | SHORT_CIRCUIT)
#define OR_PRIO (8 << PRIO_SHIFT)
#define XOR_PRIO (9 << PRIO_SHIFT)
#define AND_PRIO (10 << PRIO_SHIFT)
#define MINMAX_PRIO (11 << PRIO_SHIFT)
#define EQUAL_PRIO (12 << PRIO_SHIFT)
#define LESS_PRIO (13 << PRIO_SHIFT)
#define SHIFT_PRIO (14 << PRIO_SHIFT)
#define PLUS_PRIO (15 << PRIO_SHIFT)
#define MUL_PRIO (16 << PRIO_SHIFT)
#define UNARY_PRIO ((17 << PRIO_SHIFT) | RIGHT_ASSOC | NO_L_OPERAND)
static const short
op_to_prio[] =
{
0,
UNARY_PRIO,
LESS_PRIO,
LESS_PRIO,
UNARY_PRIO,
UNARY_PRIO,
MUL_PRIO,
MUL_PRIO,
MUL_PRIO,
AND_PRIO,
OR_PRIO,
XOR_PRIO,
SHIFT_PRIO,
SHIFT_PRIO,
MINMAX_PRIO,
MINMAX_PRIO,
UNARY_PRIO,
ANDAND_PRIO,
OROR_PRIO,
COND_PRIO,
COLON_PRIO,
COMMA_PRIO,
OPEN_PAREN_PRIO,
CLOSE_PAREN_PRIO,
EQUAL_PRIO,
EQUAL_PRIO,
LESS_PRIO,
LESS_PRIO
};
#define COMPARE(OP) \
top->unsignedp = 0; \
top->value = (unsigned1 | unsigned2) \
? (unsigned HOST_WIDEST_INT) v1 OP (unsigned HOST_WIDEST_INT) v2 \
: (v1 OP v2)
#define EQUALITY(OP) \
top->value = v1 OP v2; \
top->unsignedp = 0;
#define BITWISE(OP) \
top->value = v1 OP v2; \
top->unsignedp = unsigned1 | unsigned2;
#define MINMAX(OP) \
top->value = (v1 OP v2) ? v1 : v2; \
top->unsignedp = unsigned1 | unsigned2;
#define UNARY(OP) \
top->value = OP v2; \
top->unsignedp = unsigned2; \
top->flags |= HAVE_VALUE;
#define SHIFT(PSH, MSH) \
if (skip_evaluation) \
break; \
top->unsignedp = unsigned1; \
if (v2 < 0 && ! unsigned2) \
top->value = MSH (pfile, v1, unsigned1, -v2); \
else \
top->value = PSH (pfile, v1, unsigned1, v2);
int
_cpp_parse_expr (pfile)
cpp_reader *pfile;
{
#define INIT_STACK_SIZE 20
struct op init_stack[INIT_STACK_SIZE];
struct op *stack = init_stack;
struct op *limit = stack + INIT_STACK_SIZE;
struct op *top = stack + 1;
int skip_evaluation = 0;
int result;
unsigned int lex_count, saw_leading_not;
pfile->mi_ind_cmacro = 0;
saw_leading_not = 0;
lex_count = 0;
top->op = CPP_EOF;
top->prio = EXTRACT_PRIO(CLOSE_PAREN_PRIO);
top->flags = NO_R_OPERAND;
for (;;)
{
unsigned int prio;
unsigned int flags;
struct op op;
op = lex (pfile, skip_evaluation);
lex_count++;
switch (op.op)
{
case CPP_ERROR:
goto syntax_error;
push_immediate:
case CPP_NUMBER:
if (top->flags & HAVE_VALUE)
SYNTAX_ERROR ("missing binary operator");
top->value = op.value;
top->unsignedp = op.unsignedp;
top->flags |= HAVE_VALUE;
continue;
case CPP_EOF: prio = FORCE_REDUCE_PRIO; break;
case CPP_NOT:
saw_leading_not = lex_count == 1;
prio = op_to_prio[op.op];
break;
case CPP_PLUS:
case CPP_MINUS: prio = PLUS_PRIO; if (top->flags & HAVE_VALUE) break;
default: prio = op_to_prio[op.op]; break;
}
flags = EXTRACT_FLAGS(prio);
prio = EXTRACT_PRIO(prio);
if (prio == EXTRACT_PRIO(OPEN_PAREN_PRIO))
goto skip_reduction;
while (prio <= top->prio)
{
HOST_WIDEST_INT v1, v2;
unsigned int unsigned1, unsigned2;
if ((top->flags & (HAVE_VALUE | NO_R_OPERAND)) == 0)
{
if (top->op == CPP_OPEN_PAREN)
SYNTAX_ERROR ("void expression between '(' and ')'");
else
SYNTAX_ERROR2 ("operator '%s' has no right operand",
op_as_text (pfile, top->op));
}
unsigned2 = top->unsignedp, v2 = top->value;
top--;
unsigned1 = top->unsignedp, v1 = top->value;
switch (top[1].op)
{
default:
cpp_ice (pfile, "impossible operator '%s'",
op_as_text (pfile, top[1].op));
goto syntax_error;
case CPP_NOT: UNARY(!); break;
case CPP_COMPL: UNARY(~); break;
case CPP_LESS: COMPARE(<); break;
case CPP_GREATER: COMPARE(>); break;
case CPP_LESS_EQ: COMPARE(<=); break;
case CPP_GREATER_EQ: COMPARE(>=); break;
case CPP_EQ_EQ: EQUALITY(==); break;
case CPP_NOT_EQ: EQUALITY(!=); break;
case CPP_AND: BITWISE(&); break;
case CPP_XOR: BITWISE(^); break;
case CPP_OR: BITWISE(|); break;
case CPP_LSHIFT: SHIFT(left_shift, right_shift); break;
case CPP_RSHIFT: SHIFT(right_shift, left_shift); break;
case CPP_MIN: MINMAX(<); break;
case CPP_MAX: MINMAX(>); break;
case CPP_PLUS:
if (!(top->flags & HAVE_VALUE))
{
top->value = v2;
top->unsignedp = unsigned2;
top->flags |= HAVE_VALUE;
if (CPP_WTRADITIONAL (pfile))
cpp_warning (pfile,
"traditional C rejects the unary plus operator");
}
else
{
top->value = v1 + v2;
top->unsignedp = unsigned1 | unsigned2;
if (! top->unsignedp && ! skip_evaluation
&& ! possible_sum_sign (v1, v2, top->value))
integer_overflow (pfile);
}
break;
case CPP_MINUS:
if (!(top->flags & HAVE_VALUE))
{
UNARY(-);
if (!skip_evaluation && (top->value & v2) < 0 && !unsigned2)
integer_overflow (pfile);
}
else
{
top->value = v1 - v2;
top->unsignedp = unsigned1 | unsigned2;
if (! top->unsignedp && ! skip_evaluation
&& ! possible_sum_sign (top->value, v2, v1))
integer_overflow (pfile);
}
break;
case CPP_MULT:
top->unsignedp = unsigned1 | unsigned2;
if (top->unsignedp)
top->value = (unsigned HOST_WIDEST_INT) v1 * v2;
else if (!skip_evaluation)
{
top->value = v1 * v2;
if (v1 && (top->value / v1 != v2
|| (top->value & v1 & v2) < 0))
integer_overflow (pfile);
}
break;
case CPP_DIV:
case CPP_MOD:
if (skip_evaluation)
break;
if (v2 == 0)
SYNTAX_ERROR ("division by zero in #if");
top->unsignedp = unsigned1 | unsigned2;
if (top[1].op == CPP_DIV)
{
if (top->unsignedp)
top->value = (unsigned HOST_WIDEST_INT) v1 / v2;
else
{
top->value = v1 / v2;
if ((top->value & v1 & v2) < 0)
integer_overflow (pfile);
}
}
else
{
if (top->unsignedp)
top->value = (unsigned HOST_WIDEST_INT) v1 % v2;
else
top->value = v1 % v2;
}
break;
case CPP_OR_OR:
top->value = v1 || v2;
top->unsignedp = 0;
if (v1) skip_evaluation--;
break;
case CPP_AND_AND:
top->value = v1 && v2;
top->unsignedp = 0;
if (!v1) skip_evaluation--;
break;
case CPP_COMMA:
if (CPP_PEDANTIC (pfile))
cpp_pedwarn (pfile, "comma operator in operand of #if");
top->value = v2;
top->unsignedp = unsigned2;
break;
case CPP_QUERY:
SYNTAX_ERROR ("syntax error '?' without following ':'");
case CPP_COLON:
if (top[0].op != CPP_QUERY)
SYNTAX_ERROR ("syntax error ':' without preceding '?'");
top--;
if (top->value) skip_evaluation--;
top->value = top->value ? v1 : v2;
top->unsignedp = unsigned1 | unsigned2;
break;
case CPP_OPEN_PAREN:
if (op.op != CPP_CLOSE_PAREN)
SYNTAX_ERROR ("missing ')' in expression");
op.value = v2;
op.unsignedp = unsigned2;
goto push_immediate;
case CPP_EOF:
if (op.op == CPP_CLOSE_PAREN)
SYNTAX_ERROR ("missing '(' in expression");
goto done;
}
}
if (flags & SHORT_CIRCUIT)
switch (op.op)
{
case CPP_OR_OR: if (top->value) skip_evaluation++; break;
case CPP_AND_AND:
case CPP_QUERY: if (!top->value) skip_evaluation++; break;
case CPP_COLON:
if (top[-1].value)
skip_evaluation++;
else
skip_evaluation--;
default:
break;
}
skip_reduction:
if (flags & NO_L_OPERAND)
{
if (top->flags & HAVE_VALUE)
SYNTAX_ERROR2 ("missing binary operator before '%s'",
op_as_text (pfile, op.op));
}
else
{
if (!(top->flags & HAVE_VALUE))
SYNTAX_ERROR2 ("operator '%s' has no left operand",
op_as_text (pfile, op.op));
}
top++;
if (top == limit)
{
struct op *new_stack;
int old_size = (char *) limit - (char *) stack;
int new_size = 2 * old_size;
if (stack != init_stack)
new_stack = (struct op *) xrealloc (stack, new_size);
else
{
new_stack = (struct op *) xmalloc (new_size);
memcpy (new_stack, stack, old_size);
}
stack = new_stack;
top = (struct op *) ((char *) new_stack + old_size);
limit = (struct op *) ((char *) new_stack + new_size);
}
top->flags = flags;
top->prio = prio & ~EXTRACT_PRIO(RIGHT_ASSOC);
top->op = op.op;
}
done:
if (pfile->mi_ind_cmacro && !(saw_leading_not && lex_count == 3))
pfile->mi_ind_cmacro = 0;
result = (top[1].value != 0);
if (top != stack)
CPP_ICE ("unbalanced stack in #if");
else if (!(top[1].flags & HAVE_VALUE))
{
SYNTAX_ERROR ("#if with no expression");
syntax_error:
result = 0;
}
if (stack != init_stack)
free (stack);
return result;
}
static const unsigned char *
op_as_text (pfile, op)
cpp_reader *pfile;
enum cpp_ttype op;
{
cpp_token token;
token.type = op;
token.flags = 0;
return cpp_token_as_text (pfile, &token);
}