OSDN Git Service

2001-05-03 David O'Brien <obrien@FreeBSD.org>
[pf3gnuchains/gcc-fork.git] / gcc / cppexp.c
index 01efa0e..1231a7e 100644 (file)
@@ -1,5 +1,7 @@
 /* Parse C expressions for cpplib.
-   Copyright (C) 1987, 92, 94, 95, 97, 98, 1999, 2000 Free Software Foundation.
+   Copyright (C) 1987, 1992, 1994, 1995, 1997, 1998, 1999, 2000, 2001
+   Free Software Foundation.
+   Contributed by Per Bothner, 1994.
 
 This program is free software; you can redistribute it and/or modify it
 under the terms of the GNU General Public License as published by the
@@ -14,13 +16,7 @@ GNU General Public License for more details.
 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA.
-
- In other words, you are welcome to use, share and improve this program.
- You are forbidden to forbid anyone else to use, share and improve
- what you give them.   Help stamp out software-hoarding!
-
-Written by Per Bothner 1994.  */
+Boston, MA 02111-1307, USA.  */
 
 /* Parse a C expression from text in a string  */
    
@@ -29,22 +25,6 @@ Written by Per Bothner 1994.  */
 #include "cpplib.h"
 #include "cpphash.h"
 
-#ifndef CHAR_TYPE_SIZE
-#define CHAR_TYPE_SIZE BITS_PER_UNIT
-#endif
-
-#ifndef INT_TYPE_SIZE
-#define INT_TYPE_SIZE BITS_PER_WORD
-#endif
-
-#ifndef LONG_TYPE_SIZE
-#define LONG_TYPE_SIZE BITS_PER_WORD
-#endif
-
-#ifndef WCHAR_TYPE_SIZE
-#define WCHAR_TYPE_SIZE INT_TYPE_SIZE
-#endif
-
 #ifndef MAX_CHAR_TYPE_SIZE
 #define MAX_CHAR_TYPE_SIZE CHAR_TYPE_SIZE
 #endif
@@ -62,8 +42,8 @@ Written by Per Bothner 1994.  */
 #endif
 
 #define MAX_CHAR_TYPE_MASK (MAX_CHAR_TYPE_SIZE < HOST_BITS_PER_WIDEST_INT \
-                           ? (~(~(HOST_WIDEST_INT) 0 << MAX_CHAR_TYPE_SIZE)) \
-                           : ~ (HOST_WIDEST_INT) 0)
+                   ? (~(~(HOST_WIDEST_INT) 0 << MAX_CHAR_TYPE_SIZE)) \
+                   : ~ (HOST_WIDEST_INT) 0)
 
 #define MAX_WCHAR_TYPE_MASK (MAX_WCHAR_TYPE_SIZE < HOST_BITS_PER_WIDEST_INT \
                             ? ~(~(HOST_WIDEST_INT) 0 << MAX_WCHAR_TYPE_SIZE) \
@@ -80,55 +60,77 @@ static HOST_WIDEST_INT left_shift PARAMS ((cpp_reader *, HOST_WIDEST_INT,
 static HOST_WIDEST_INT right_shift PARAMS ((cpp_reader *, HOST_WIDEST_INT,
                                            unsigned int,
                                            unsigned HOST_WIDEST_INT));
-static struct operation parse_number PARAMS ((cpp_reader *, U_CHAR *,
-                                             U_CHAR *));
-static struct operation parse_charconst PARAMS ((cpp_reader *, U_CHAR *,
-                                                U_CHAR *));
-static struct operation parse_defined PARAMS ((cpp_reader *));
-static HOST_WIDEST_INT parse_escape PARAMS ((cpp_reader *, U_CHAR **,
-                                            HOST_WIDEST_INT));
-static struct operation lex PARAMS ((cpp_reader *, int));
-
-
-#define ERROR 299
-#define OROR 300
-#define ANDAND 301
-#define EQUAL 302
-#define NOTEQUAL 303
-#define LEQ 304
-#define GEQ 305
-#define LSH 306
-#define RSH 307
-#define NAME 308
-#define INT 309
-#define CHAR 310
-
-struct operation
+static struct op parse_number PARAMS ((cpp_reader *, const cpp_token *));
+static struct op parse_charconst PARAMS ((cpp_reader *, const cpp_token *));
+static struct op parse_defined PARAMS ((cpp_reader *));
+static HOST_WIDEST_INT parse_escape PARAMS ((cpp_reader *, const U_CHAR **,
+                                            const U_CHAR *, HOST_WIDEST_INT));
+static struct op lex PARAMS ((cpp_reader *, int, cpp_token *));
+static const unsigned char *op_as_text PARAMS ((cpp_reader *, enum cpp_ttype));
+
+struct op
 {
-  short op;
-  U_CHAR prio; /* Priority of op (relative to it right operand).  */
+  enum cpp_ttype op;
+  U_CHAR prio;         /* Priority of op.  */
   U_CHAR flags;
-  U_CHAR unsignedp;    /* true if value should be treated as unsigned */
-  HOST_WIDEST_INT value;        /* The value logically "right" of op.  */
+  U_CHAR unsignedp;    /* True if value should be treated as unsigned.  */
+  HOST_WIDEST_INT value; /* The value logically "right" of op.  */
 };
 
+/* There is no "error" token, but we can't get comments in #if, so we can
+   abuse that token type.  */
+#define CPP_ERROR CPP_COMMENT
+
+/* With -O2, gcc appears to produce nice code, moving the error
+   message load and subsequent jump completely out of the main path.  */
+#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)
+
 /* Parse and convert an integer for #if.  Accepts decimal, hex, or octal
    with or without size suffixes.  */
+struct suffix
+{
+  unsigned char s[4];
+  unsigned char u;
+  unsigned char l;
+};
+
+const struct suffix vsuf_1[] = {
+  { "u", 1, 0 }, { "U", 1, 0 },
+  { "l", 0, 1 }, { "L", 0, 1 }
+};
+
+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 }
+};
+
+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 operation
-parse_number (pfile, start, end)
+static struct op
+parse_number (pfile, tok)
      cpp_reader *pfile;
-     U_CHAR *start;
-     U_CHAR *end;
+     const cpp_token *tok;
 {
-  struct operation op;
-  U_CHAR *p = start;
-  int c;
+  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;
-  int spec_long = 0;
+  const struct suffix *sufftab;
 
   op.unsignedp = 0;
 
@@ -150,48 +152,21 @@ parse_number (pfile, start, end)
   MAX_over_base = (((unsigned HOST_WIDEST_INT) -1)
                   / ((unsigned HOST_WIDEST_INT) base));
 
-  while (p < end)
+  for(; p < end; p++)
     {
-      c = *p++;
+      c = *p;
 
       if (c >= '0' && c <= '9')
        digit = c - '0';
-      else if (base == 16 && c >= 'a' && c <= 'f') /* FIXME: assumes ASCII */
+      /* We believe that in all live character sets, a-f are
+        consecutive, and so are A-F.  */
+      else if (base == 16 && c >= 'a' && c <= 'f')
        digit = c - 'a' + 10;
       else if (base == 16 && c >= 'A' && c <= 'F')
        digit = c - 'A' + 10;
-      else if (c == '.')
-       {
-         /* It's a float since it contains a point.  */
-         cpp_error (pfile,
-               "floating point numbers are not allowed in #if expressions");
-         goto error;
-       }
       else
-       {
-         /* `l' means long, and `u' means unsigned.  */
-         for (;;)
-           {
-             if (c == 'l' || c == 'L')
-                 spec_long++;
-             else if (c == 'u' || c == 'U')
-                 op.unsignedp++;
-             else
-               {
-                 /* Decrement p here so that the error for an invalid number
-                    will be generated below in the case where this is the
-                    last character in the buffer.  */
-                 p--;
-                 break;
-               }
-             if (p == end)
-               break;
-             c = *p++;
-           }
-         /* Don't look for any more digits after the suffixes.  */
-         break;
-       }
-      
+       break;
+
       if (largest_digit < digit)
        largest_digit = digit;
       nd = n * base + digit;
@@ -199,20 +174,42 @@ parse_number (pfile, start, end)
       n = nd;
     }
 
-  if (p != end)
-    {
-      cpp_error (pfile, "invalid number in #if expression");
-      goto error;
-    }
-  else if (spec_long > (CPP_OPTION (pfile, c89) ? 1 : 2))
+  if (p < end)
     {
-      cpp_error (pfile, "too many `l' suffixes in integer constant");
-      goto error;
-    }
-  else if (op.unsignedp > 1)
-    {
-      cpp_error (pfile, "too many `u' suffixes in integer constant");
-      goto error;
+      /* Check for a floating point constant.  Note that float constants
+        with an exponent or suffix but no decimal point are technically
+        invalid (C99 6.4.4.2) but accepted elsewhere.  */
+      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");
+  
+      /* Determine the suffix. l means long, and u means unsigned.
+        See the suffix tables, above.  */
+      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)
@@ -225,58 +222,57 @@ parse_number (pfile, start, end)
   else if ((HOST_WIDEST_INT) n < 0 && ! op.unsignedp)
     {
       if (base == 10)
-       cpp_warning (pfile,
-                    "integer constant is so large that it is unsigned");
+       cpp_warning (pfile, "integer constant is so large that it is unsigned");
       op.unsignedp = 1;
     }
 
   op.value = n;
-  op.op = INT;
+  op.op = CPP_INT;
   return op;
 
- error:
-  op.op = ERROR;
+ invalid_suffix:
+  cpp_error (pfile, "invalid suffix '%.*s' on integer constant",
+            (int) (end - p), p);
+ syntax_error:
+  op.op = CPP_ERROR;
   return op;
 }
 
 /* Parse and convert a character constant for #if.  Understands backslash
    escapes (\n, \031) and multibyte characters (if so configured).  */
-static struct operation
-parse_charconst (pfile, start, end)
+static struct op
+parse_charconst (pfile, tok)
      cpp_reader *pfile;
-     U_CHAR *start;
-     U_CHAR *end;
+     const cpp_token *tok;
 {
-  struct operation op;
+  struct op op;
   HOST_WIDEST_INT result = 0;
   int num_chars = 0;
   int num_bits;
-  unsigned int width = MAX_CHAR_TYPE_SIZE, mask = MAX_CHAR_TYPE_MASK;
+  unsigned int width = MAX_CHAR_TYPE_SIZE;
+  HOST_WIDEST_INT mask = MAX_CHAR_TYPE_MASK;
   int max_chars;
-  U_CHAR *ptr = start;
+  const U_CHAR *ptr = tok->val.str.text;
+  const U_CHAR *end = ptr + tok->val.str.len;
 
   int c = -1;
 
-  if (*ptr == 'L')
-    {
-      ++ptr;
-      width = MAX_WCHAR_TYPE_SIZE, mask = MAX_WCHAR_TYPE_MASK;
-    }
+  if (tok->type == CPP_WCHAR)
+    width = MAX_WCHAR_TYPE_SIZE, mask = MAX_WCHAR_TYPE_MASK;
   max_chars = MAX_LONG_TYPE_SIZE / width;
 
-  ++ptr;  /* skip initial quote */
-
   while (ptr < end)
     {
       c = *ptr++;
-      if (c == '\'' || c == '\0')
-       break;
+      if (c == '\'')
+       CPP_ICE ("unescaped ' in character constant");
       else if (c == '\\')
        {
-         c = parse_escape (pfile, &ptr, mask);
+         c = parse_escape (pfile, &ptr, end, mask);
          if (width < HOST_BITS_PER_INT
              && (unsigned int) c >= (unsigned int)(1 << width))
-           cpp_pedwarn (pfile, "escape sequence out of range for character");
+           cpp_pedwarn (pfile,
+                        "escape sequence out of range for character");
        }
          
       /* Merge character into result; ignore excess chars.  */
@@ -290,30 +286,16 @@ parse_charconst (pfile, start, end)
     }
 
   if (num_chars == 0)
-    {
-      cpp_error (pfile, "empty character constant");
-      goto error;
-    }
-  else if (c != '\'')
-    {
-      /* cpp_get_token has already emitted an error if !traditional. */
-      if (! CPP_TRADITIONAL (pfile))
-       cpp_error (pfile, "malformatted character constant");
-      goto error;
-    }
+    SYNTAX_ERROR ("empty character constant");
   else if (num_chars > max_chars)
-    {
-      cpp_error (pfile, "character constant too long");
-      goto error;
-    }
-  else if (num_chars != 1 && ! CPP_TRADITIONAL (pfile))
+    SYNTAX_ERROR ("character constant too long");
+  else if (num_chars != 1)
     cpp_warning (pfile, "multi-character character constant");
 
   /* If char type is signed, sign-extend the constant.  */
   num_bits = num_chars * width;
       
-  if (cpp_defined (pfile, (const U_CHAR *)"__CHAR_UNSIGNED__",
-                  sizeof ("__CHAR_UNSIGNED__")-1)
+  if (pfile->spec_nodes.n__CHAR_UNSIGNED__->type == NT_MACRO
       || ((result >> (num_bits - 1)) & 1) == 0)
     op.value = result & ((unsigned HOST_WIDEST_INT) ~0
                         >> (HOST_BITS_PER_WIDEST_INT - num_bits));
@@ -323,243 +305,263 @@ parse_charconst (pfile, start, end)
 
   /* This is always a signed type.  */
   op.unsignedp = 0;
-  op.op = CHAR;
+  op.op = CPP_INT;
   return op;
 
- error:
-  op.op = ERROR;
syntax_error:
+  op.op = CPP_ERROR;
   return op;
 }
 
-static struct operation
+static struct op
 parse_defined (pfile)
      cpp_reader *pfile;
 {
-  int paren = 0, len;
-  U_CHAR *tok;
-  enum cpp_token token;
-  struct operation op;
-  long old_written = CPP_WRITTEN (pfile);
+  int paren = 0;
+  cpp_hashnode *node = 0;
+  cpp_token token;
+  struct op op;
 
-  op.unsignedp = 0;
-  op.op = INT;
+  /* Don't expand macros.  */
+  pfile->state.prevent_expansion++;
 
-  pfile->no_macro_expand++;
-  token = _cpp_get_directive_token (pfile);
-  if (token == CPP_LPAREN)
+  cpp_get_token (pfile, &token);
+  if (token.type == CPP_OPEN_PAREN)
     {
-      paren++;
-      CPP_SET_WRITTEN (pfile, old_written);
-      token = _cpp_get_directive_token (pfile);
+      paren = 1;
+      cpp_get_token (pfile, &token);
     }
 
-  if (token != CPP_NAME)
-    goto oops;
+  if (token.type == CPP_NAME)
+    {
+      node = token.val.node;
+      if (paren)
+       {
+         cpp_get_token (pfile, &token);
+         if (token.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;
 
-  tok = pfile->token_buffer + old_written;
-  len = CPP_PWRITTEN (pfile) - tok;
-  op.value = cpp_defined (pfile, tok, len);
+         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 (paren)
+  if (!node)
+    op.op = CPP_ERROR;
+  else
     {
-      if (_cpp_get_directive_token (pfile) != CPP_RPAREN)
-       goto oops;
-    }
-  CPP_SET_WRITTEN (pfile, old_written);
-  pfile->no_macro_expand--;
-  return op;
+      op.value = node->type == NT_MACRO;
+      op.unsignedp = 0;
+      op.op = CPP_INT;
 
- oops:
-  CPP_SET_WRITTEN (pfile, old_written);
-  pfile->no_macro_expand--;
-  cpp_error (pfile, "`defined' without an identifier");
+      /* No macros?  At top of file?  */
+      if (pfile->mi_state == MI_OUTSIDE && pfile->mi_cmacro == 0
+         && pfile->mi_if_not_defined == MI_IND_NOT && pfile->mi_lexed == 1)
+       {
+         cpp_start_lookahead (pfile);
+         cpp_get_token (pfile, &token);
+         if (token.type == CPP_EOF)
+           pfile->mi_ind_cmacro = node;
+         cpp_stop_lookahead (pfile, 0);
+       }
+    }
 
-  op.op = ERROR;
+  pfile->state.prevent_expansion--;
   return op;
 }
 
-
-struct token {
-  const char *operator;
-  int token;
-};
-
-static const struct token tokentab2[] = {
-  {"&&", ANDAND},
-  {"||", OROR},
-  {"<<", LSH},
-  {">>", RSH},
-  {"==", EQUAL},
-  {"!=", NOTEQUAL},
-  {"<=", LEQ},
-  {">=", GEQ},
-  {"++", ERROR},
-  {"--", ERROR},
-  {NULL, ERROR}
-};
-
 /* Read one token.  */
 
-static struct operation
-lex (pfile, skip_evaluation)
+static struct op
+lex (pfile, skip_evaluation, token)
      cpp_reader *pfile;
      int skip_evaluation;
+     cpp_token *token;
 {
-  const struct token *toktab;
-  enum cpp_token token;
-  struct operation op;
-  U_CHAR *tok_start, *tok_end;
-  long old_written;
-
-  old_written = CPP_WRITTEN (pfile);
-  token = _cpp_get_directive_token (pfile);
-
-  tok_start = pfile->token_buffer + old_written;
-  tok_end = CPP_PWRITTEN (pfile);
-  CPP_SET_WRITTEN (pfile, old_written);
-  switch (token)
+  struct op op;
+
+  cpp_get_token (pfile, token);
+
+  switch (token->type)
     {
-    case CPP_EOF: /* Should not happen ...  */
-    case CPP_VSPACE:
-      op.op = 0;
-      return op;
+    case CPP_INT:
     case CPP_NUMBER:
-      return parse_number (pfile, tok_start, tok_end);
-    case CPP_STRING:
-    case CPP_WSTRING:
-      cpp_error (pfile, "string constants are not allowed in #if expressions");
-      op.op = ERROR;
-      return op;
+      return parse_number (pfile, token);
 
     case CPP_CHAR:
     case CPP_WCHAR:
-      return parse_charconst (pfile, tok_start, tok_end);
+      return parse_charconst (pfile, token);
 
-    case CPP_NAME:
-      if (!strcmp (tok_start, "defined"))
-       return parse_defined (pfile);
+    case CPP_STRING:
+    case CPP_WSTRING:
+      SYNTAX_ERROR ("string constants are not valid in #if");
 
-      op.op = INT;
-      op.unsignedp = 0;
-      op.value = 0;
+    case CPP_FLOAT:
+      SYNTAX_ERROR ("floating point numbers are not valid in #if");
 
-      if (CPP_OPTION (pfile, warn_undef) && !skip_evaluation)
-       cpp_warning (pfile, "`%.*s' is not defined",
-                    (int) (tok_end - tok_start), tok_start);
-      return op;
+    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_ASSERTION:
-      op.op = INT;
-      op.unsignedp = 0;
-      op.value = cpp_defined (pfile, tok_start, tok_end - tok_start);
-      return op;
+    case CPP_NAME:
+      if (token->val.node == pfile->spec_nodes.n_defined)
+       {
+         if (pfile->context->prev && CPP_PEDANTIC (pfile))
+           cpp_pedwarn (pfile, "\"defined\" operator appears during macro expansion");
 
-    case CPP_OTHER:
-      /* See if it is a special token of length 2.  */
-      if (tok_start + 2 == tok_end)
-        {
-         for (toktab = tokentab2; toktab->operator != NULL; toktab++)
-           if (tok_start[0] == toktab->operator[0]
-               && tok_start[1] == toktab->operator[1])
-               break;
-         if (toktab->token == ERROR)
-           cpp_error (pfile, "`%s' not allowed in operand of `#if'",
-                      tok_start);
-         op.op = toktab->token; 
+         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_INT;
+         op.unsignedp = 0;
+         op.value = (token->val.node == pfile->spec_nodes.n_true);
+
+         /* Warn about use of true or false in #if when pedantic
+            and stdbool.h has not been included.  */
+         if (CPP_PEDANTIC (pfile)
+             && ! cpp_defined (pfile, DSC("__bool_true_false_are_defined")))
+           cpp_pedwarn (pfile, "ISO C++ does not permit \"%s\" in #if",
+                        token->val.node->name);
          return op;
        }
-      /* fall through */
+      else
+       {
+         /* Controlling #if expressions cannot contain identifiers (they
+            could become macros in the future).  */
+         pfile->mi_state = MI_FAILED;
+
+         op.op = CPP_INT;
+         op.unsignedp = 0;
+         op.value = 0;
+
+         if (CPP_OPTION (pfile, warn_undef) && !skip_evaluation)
+           cpp_warning (pfile, "\"%s\" is not defined", token->val.node->name);
+         return op;
+       }
+
+    case CPP_HASH:
+      {
+       int temp;
+
+       op.op = CPP_INT;
+       if (_cpp_test_assertion (pfile, &temp))
+         op.op = CPP_ERROR;
+       op.unsignedp = 0;
+       op.value = temp;
+       return op;
+      }
+
+    case CPP_NOT:
+      /* We don't worry about its position here.  */
+      pfile->mi_if_not_defined = MI_IND_NOT;
+      /* Fall through.  */
+
     default:
-      op.op = *tok_start;
-      return op;
-  }
-}
+      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;
+}
 
 /* Parse a C escape sequence.  STRING_PTR points to a variable
    containing a pointer to the string to parse.  That pointer
    is updated past the characters we use.  The value of the
    escape sequence is returned.
 
-   A negative value means the sequence \ newline was seen,
-   which is supposed to be equivalent to nothing at all.
-
-   If \ is followed by a null character, we return a negative
-   value and leave the string pointer pointing at the null character.
-
    If \ is followed by 000, we return 0 and leave the string pointer
    after the zeros.  A value of 0 does not mean end of string.  */
 
 static HOST_WIDEST_INT
-parse_escape (pfile, string_ptr, result_mask)
+parse_escape (pfile, string_ptr, limit, result_mask)
      cpp_reader *pfile;
-     U_CHAR **string_ptr;
+     const U_CHAR **string_ptr;
+     const U_CHAR *limit;
      HOST_WIDEST_INT result_mask;
 {
-  register int c = *(*string_ptr)++;
+  const U_CHAR *ptr = *string_ptr;
+  /* We know we have at least one following character.  */
+  int c = *ptr++;
   switch (c)
     {
-    case 'a':
-      return TARGET_BELL;
-    case 'b':
-      return TARGET_BS;
-    case 'e':
-    case 'E':
+    case 'a': c = TARGET_BELL;   break;
+    case 'b': c = TARGET_BS;     break;
+    case 'f': c = TARGET_FF;     break;
+    case 'n': c = TARGET_NEWLINE; break;
+    case 'r': c = TARGET_CR;     break;
+    case 't': c = TARGET_TAB;    break;
+    case 'v': c = TARGET_VT;     break;
+
+    case 'e': case 'E':
       if (CPP_PEDANTIC (pfile))
-       cpp_pedwarn (pfile, "non-ANSI-standard escape sequence, `\\%c'", c);
-      return TARGET_ESC;
-    case 'f':
-      return TARGET_FF;
-    case 'n':
-      return TARGET_NEWLINE;
-    case 'r':
-      return TARGET_CR;
-    case 't':
-      return TARGET_TAB;
-    case 'v':
-      return TARGET_VT;
-    case '\n':
-      return -2;
-    case 0:
-      (*string_ptr)--;
-      return 0;
+       cpp_pedwarn (pfile, "non-ISO-standard escape sequence, '\\%c'", c);
+      c = TARGET_ESC;
+      break;
       
-    case '0':
-    case '1':
-    case '2':
-    case '3':
-    case '4':
-    case '5':
-    case '6':
-    case '7':
+    case '0': case '1': case '2': case '3':
+    case '4': case '5': case '6': case '7':
       {
-       register HOST_WIDEST_INT i = c - '0';
-       register int count = 0;
+       unsigned int i = c - '0';
+       int count = 0;
        while (++count < 3)
          {
-           c = *(*string_ptr)++;
-           if (c >= '0' && c <= '7')
-             i = (i << 3) + c - '0';
-           else
-             {
-               (*string_ptr)--;
-               break;
-             }
+           if (ptr >= limit)
+             break;
+           
+           c = *ptr;
+           if (c < '0' || c > '7')
+             break;
+           ptr++;
+           i = (i << 3) + c - '0';
          }
        if (i != (i & result_mask))
          {
            i &= result_mask;
            cpp_pedwarn (pfile, "octal escape sequence out of range");
          }
-       return i;
+       c = i;
+       break;
       }
+
     case 'x':
       {
-       register unsigned HOST_WIDEST_INT i = 0, overflow = 0;
-       register int digits_found = 0, digit;
+       unsigned int i = 0, overflow = 0;
+       int digits_found = 0, digit;
        for (;;)
          {
-           c = *(*string_ptr)++;
+           if (ptr >= limit)
+             break;
+           c = *ptr;
            if (c >= '0' && c <= '9')
              digit = c - '0';
            else if (c >= 'a' && c <= 'f')
@@ -567,10 +569,8 @@ parse_escape (pfile, string_ptr, result_mask)
            else if (c >= 'A' && c <= 'F')
              digit = c - 'A' + 10;
            else
-             {
-               (*string_ptr)--;
-               break;
-             }
+             break;
+           ptr++;
            overflow |= i ^ (i << 4 >> 4);
            i = (i << 4) + digit;
            digits_found = 1;
@@ -582,11 +582,12 @@ parse_escape (pfile, string_ptr, result_mask)
            i &= result_mask;
            cpp_pedwarn (pfile, "hex escape sequence out of range");
          }
-       return i;
+       c = i;
+       break;
       }
-    default:
-      return c;
     }
+  *string_ptr = ptr;
+  return c;
 }
 
 static void
@@ -636,60 +637,131 @@ right_shift (pfile, a, unsignedp, b)
     return a >> b;
 }
 \f
-/* Operator precedence table.
+/* Operator precedence and flags table.
 
 After an operator is returned from the lexer, if it has priority less
 than or equal to the operator on the top of the stack, we reduce the
-stack one operator and repeat the test.  As equal priorities reduce,
-this is naturally left-associative.
+stack by one operator and repeat the test.  Since equal priorities
+reduce, this is naturally left-associative.
 
 We handle right-associative operators by clearing the lower bit of all
 left-associative operators, and setting it for right-associative ones.
-After the reduction phase, when an operator is pushed onto the stack,
-its RIGHT_ASSOC bit is cleared.  This means that at reduction time, a
-right-associative operator of otherwise equal precedence to the
-operator on the top of the stack will have a greater priority by 1,
-avoiding a reduction pass and making the logic right-associative.
+After the reduction phase of a new operator, just before it is pushed
+onto the stack, its RIGHT_ASSOC bit is cleared.  The effect is that
+during the reduction phase, the current right-associative operator has
+a priority one greater than any other operator of otherwise equal
+precedence that has been pushed on the top of the stack.  This avoids
+a reduction pass, and effectively makes the logic right-associative.
 
 The remaining cases are '(' and ')'.  We handle '(' by skipping the
 reduction phase completely.  ')' is given lower priority than
 everything else, including '(', effectively forcing a reduction of the
-parenthesised expression.  If there is no matching '(', the expression
-will be reduced to the beginning, the ')' pushed, and the reduction
-pass forced by the next ')', or the end of the expression, will meet
-it and output an appropriate error message.  */
-
-#define RIGHT_ASSOC               1
-#define PREVENT_REDUCE_PRIO (0 << 1)
-#define FORCE_REDUCE_PRIO   (1 << 1)
-#define CLOSE_PAREN_PRIO    (2 << 1)
-#define OPEN_PAREN_PRIO     (3 << 1)
-#define COMMA_PRIO          (4 << 1)
-#define COND_PRIO          ((5 << 1) + RIGHT_ASSOC)
-#define COLON_PRIO          (6 << 1)
-#define OROR_PRIO           (7 << 1)
-#define ANDAND_PRIO         (8 << 1)
-#define OR_PRIO             (9 << 1)
-#define XOR_PRIO           (10 << 1)
-#define AND_PRIO           (11 << 1)
-#define EQUAL_PRIO         (12 << 1)
-#define LESS_PRIO          (13 << 1)
-#define SHIFT_PRIO         (14 << 1)
-#define PLUS_PRIO          (15 << 1)
-#define MUL_PRIO           (16 << 1)
-#define UNARY_PRIO        ((17 << 1) + RIGHT_ASSOC)
-
-#define LEFT_OPERAND_REQUIRED 1
-#define RIGHT_OPERAND_REQUIRED 2
-#define HAVE_VALUE 4
+parenthesised expression.  If there is no matching '(', the stack will
+be reduced all the way to the beginning, exiting the parser in the
+same way as the ultra-low priority end-of-expression dummy operator.
+The exit code checks to see if the operator that caused it is ')', and
+if so outputs an appropriate error message.
+
+The parser assumes all shifted operators require a right operand
+unless the flag NO_R_OPERAND is set, and similarly for NO_L_OPERAND.
+These semantics are automatically checked, any extra semantics need to
+be handled with operator-specific code.  */
+
+#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)
+
+/* Flags.  */
+#define HAVE_VALUE     (1 << 0)
+#define NO_L_OPERAND   (1 << 1)
+#define NO_R_OPERAND   (1 << 2)
+#define SHORT_CIRCUIT  (1 << 3)
+
+/* Priority and flag combinations.  */
+#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)
+
+/* Operator to priority map.  Must be in the same order as the first
+   N entries of enum cpp_ttype.  */
+static const short
+op_to_prio[] =
+{
+  /* EQ */             0,              /* dummy entry - can't happen */
+  /* NOT */            UNARY_PRIO,
+  /* GREATER */                LESS_PRIO,
+  /* LESS */           LESS_PRIO,
+  /* PLUS */           UNARY_PRIO,     /* note these two can be unary */
+  /* MINUS */          UNARY_PRIO,     /* or binary */
+  /* MULT */           MUL_PRIO,
+  /* DIV */            MUL_PRIO,
+  /* MOD */            MUL_PRIO,
+  /* AND */            AND_PRIO,
+  /* OR */             OR_PRIO,
+  /* XOR */            XOR_PRIO,
+  /* RSHIFT */         SHIFT_PRIO,
+  /* LSHIFT */         SHIFT_PRIO,
+  /* MIN */            MINMAX_PRIO,    /* C++ specific */
+  /* MAX */            MINMAX_PRIO,    /* extensions */
+
+  /* COMPL */          UNARY_PRIO,
+  /* AND_AND */                ANDAND_PRIO,
+  /* OR_OR */          OROR_PRIO,
+  /* QUERY */          COND_PRIO,
+  /* COLON */          COLON_PRIO,
+  /* COMMA */          COMMA_PRIO,
+  /* OPEN_PAREN */     OPEN_PAREN_PRIO,
+  /* CLOSE_PAREN */    CLOSE_PAREN_PRIO,
+  /* EQ_EQ */          EQUAL_PRIO,
+  /* NOT_EQ */         EQUAL_PRIO,
+  /* GREATER_EQ */     LESS_PRIO,
+  /* LESS_EQ */                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 LOGICAL(OP) \
-             top->value = v1 OP v2;\
-             top->unsignedp = unsigned1 || unsigned2;
+  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);
 
 /* Parse and evaluate a C expression, reading from PFILE.
    Returns the truth value of the expression.  */
@@ -698,8 +770,8 @@ int
 _cpp_parse_expr (pfile)
      cpp_reader *pfile;
 {
-  /* The implementation is an operator precedence parser,
-     i.e. a bottom-up parser, using a stack for not-yet-reduced tokens.
+  /* The implementation is an operator precedence parser, i.e. a
+     bottom-up parser, using a stack for not-yet-reduced tokens.
 
      The stack base is 'stack', and the current stack pointer is 'top'.
      There is a stack element for each operator (only),
@@ -709,371 +781,308 @@ _cpp_parse_expr (pfile)
      In that case the 'flags' field has the HAVE_VALUE flag set.  */
 
 #define INIT_STACK_SIZE 20
-  struct operation init_stack[INIT_STACK_SIZE];
-  struct operation *stack = init_stack;
-  struct operation *limit = stack + INIT_STACK_SIZE;
-  register struct operation *top = stack;
+  struct op init_stack[INIT_STACK_SIZE];
+  struct op *stack = init_stack;
+  struct op *limit = stack + INIT_STACK_SIZE;
+  cpp_token token;
+  register struct op *top = stack + 1;
   int skip_evaluation = 0;
-  long old_written = CPP_WRITTEN (pfile);
   int result;
 
-  pfile->parsing_if_directive++;
-  top->prio = PREVENT_REDUCE_PRIO;
-  top->flags = 0;
+  /* Set up detection of #if ! defined().  */
+  pfile->mi_lexed = 0;
+  pfile->mi_if_not_defined = MI_IND_NONE;
+
+  /* We've finished when we try to reduce this.  */
+  top->op = CPP_EOF;
+  /* Nifty way to catch missing '('.  */
+  top->prio = EXTRACT_PRIO(CLOSE_PAREN_PRIO);
+  /* Avoid missing right operand checks.  */
+  top->flags = NO_R_OPERAND;
+
   for (;;)
     {
       unsigned int prio;
-      struct operation op;
-      U_CHAR flags = 0;
+      unsigned int flags;
+      struct op op;
 
       /* Read a token */
-      op = lex (pfile, skip_evaluation);
-
-      /* See if the token is an operand, in which case go to set_value.
-        If the token is an operator, figure out its left and right
-        priorities, and then goto maybe_reduce.  */
+      op = lex (pfile, skip_evaluation, &token);
+      pfile->mi_lexed++;
 
+      /* If the token is an operand, push its value and get next
+        token.  If it is an operator, get its priority and flags, and
+        try to reduce the expression on the stack.  */
       switch (op.op)
        {
-       case NAME:
-         cpp_ice (pfile, "lex returns a NAME");
-         goto syntax_error;
-       case ERROR:
+       case CPP_ERROR:
          goto syntax_error;
-       default:
-         cpp_error (pfile, "invalid character in #if");
-         goto syntax_error;
-
-       case INT:  case CHAR:
        push_immediate:
+       case CPP_INT:
          /* Push a value onto the stack.  */
          if (top->flags & HAVE_VALUE)
-           {
-             cpp_error (pfile, "suspected missing binary operator in #if");
-             goto syntax_error;
-           }
+           SYNTAX_ERROR ("missing binary operator");
          top->value = op.value;
          top->unsignedp = op.unsignedp;
          top->flags |= HAVE_VALUE;
          continue;
 
-       case '+':  case '-':
-         prio = PLUS_PRIO;
-         if (top->flags & HAVE_VALUE)
-             break;
-         /* else fall through */
-       case '!':  case '~':
-         flags |= RIGHT_OPERAND_REQUIRED;
-         prio = UNARY_PRIO;  goto maybe_reduce;
-
-       case '*':
-       case '/':
-       case '%':    prio = MUL_PRIO;  break;
-       case '<':
-       case '>':
-       case LEQ:
-       case GEQ:    prio = LESS_PRIO;  break;
-       case NOTEQUAL:
-       case EQUAL:  prio = EQUAL_PRIO;  break;
-       case LSH:
-       case RSH:    prio = SHIFT_PRIO;  break;
-       case '&':    prio = AND_PRIO;  break;
-       case '^':    prio = XOR_PRIO;  break;
-       case '|':    prio = OR_PRIO;  break;
-       case ANDAND: prio = ANDAND_PRIO;  break;
-       case OROR:   prio = OROR_PRIO;  break;
-       case ',':    prio = COMMA_PRIO;  break;
-       case '(':    prio = OPEN_PAREN_PRIO;  goto skip_reduction;
-       case ')':
-         prio = CLOSE_PAREN_PRIO;
-         flags = HAVE_VALUE;   /* At least, we will have after reduction.  */
-         goto maybe_reduce;
-        case ':':    prio = COLON_PRIO;  goto maybe_reduce;
-        case '?':    prio = COND_PRIO;   goto maybe_reduce;
-       case 0:      prio = FORCE_REDUCE_PRIO;  goto maybe_reduce;
+       case CPP_EOF:   prio = FORCE_REDUCE_PRIO;       break;
+       case CPP_PLUS:
+       case CPP_MINUS: prio = PLUS_PRIO;  if (top->flags & HAVE_VALUE) break;
+          /* else unary; fall through */
+       default:        prio = op_to_prio[op.op];       break;
        }
 
-      /* Binary operation.  */
-      flags = LEFT_OPERAND_REQUIRED|RIGHT_OPERAND_REQUIRED;
+      /* Separate the operator's code into priority and flags.  */
+      flags = EXTRACT_FLAGS(prio);
+      prio = EXTRACT_PRIO(prio);
+      if (prio == EXTRACT_PRIO(OPEN_PAREN_PRIO))
+       goto skip_reduction;
 
-    maybe_reduce:
       /* Check for reductions.  Then push the operator.  */
       while (prio <= top->prio)
        {
-         HOST_WIDEST_INT v1 = top[-1].value, v2 = top[0].value;
-         unsigned int unsigned1 = top[-1].unsignedp;
-         unsigned int unsigned2 = top[0].unsignedp;
-         top--;
-         if ((top[1].flags & LEFT_OPERAND_REQUIRED)
-             && ! (top[0].flags & HAVE_VALUE))
-           {
-             cpp_error (pfile, "syntax error - missing left operand");
-             goto syntax_error;
-           }
-         if ((top[1].flags & RIGHT_OPERAND_REQUIRED)
-             && ! (top[1].flags & HAVE_VALUE))
+         HOST_WIDEST_INT v1, v2;
+         unsigned int unsigned1, unsigned2;
+         
+         /* Most operators that can appear on the stack require a
+            right operand.  Check this before trying to reduce.  */
+         if ((top->flags & (HAVE_VALUE | NO_R_OPERAND)) == 0)
            {
-             cpp_error (pfile, "syntax error - missing right operand");
-             goto syntax_error;
+             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));
            }
-         /* top[0].value = (top[1].op)(v1, v2);*/
+
+         unsigned2 = top->unsignedp, v2 = top->value;
+         top--;
+         unsigned1 = top->unsignedp, v1 = top->value;
+
+         /* Now set top->value = (top[1].op)(v1, v2); */
          switch (top[1].op)
            {
-           case '+':
+           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))
-               { /* Unary '+' */
+               {
+                 /* Can't use UNARY(+) because K+R C did not have unary
+                    plus.  Can't use UNARY() because some compilers object
+                    to the empty argument.  */
                  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;
+                 top->unsignedp = unsigned1 | unsigned2;
                  if (! top->unsignedp && ! skip_evaluation
                      && ! possible_sum_sign (v1, v2, top->value))
                    integer_overflow (pfile);
                }
              break;
-           case '-':
+           case CPP_MINUS:
              if (!(top->flags & HAVE_VALUE))
-               { /* Unary '-' */
-                 top->value = - v2;
+               {
+                 UNARY(-);
                  if (!skip_evaluation && (top->value & v2) < 0 && !unsigned2)
                    integer_overflow (pfile);
-                 top->unsignedp = unsigned2;
-                 top->flags |= HAVE_VALUE;
                }
              else
                { /* Binary '-' */
                  top->value = v1 - v2;
-                 top->unsignedp = unsigned1 || unsigned2;
+                 top->unsignedp = unsigned1 | unsigned2;
                  if (! top->unsignedp && ! skip_evaluation
                      && ! possible_sum_sign (top->value, v2, v1))
                    integer_overflow (pfile);
                }
              break;
-           case '*':
-             top->unsignedp = unsigned1 || unsigned2;
+           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))
+                 if (v1 && (top->value / v1 != v2
+                            || (top->value & v1 & v2) < 0))
                    integer_overflow (pfile);
                }
              break;
-           case '/':
+           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)
                {
-                 cpp_error (pfile, "division by zero in #if");
-                 v2 = 1;
+                 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);
+                   }
                }
-             top->unsignedp = unsigned1 || unsigned2;
-             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);
-               }
-             break;
-           case '%':
-             if (skip_evaluation)
-               break;
-             if (v2 == 0)
-               {
-                 cpp_error (pfile, "division by zero in #if");
-                 v2 = 1;
+                 if (top->unsignedp)
+                   top->value = (unsigned HOST_WIDEST_INT) v1 % v2;
+                 else
+                   top->value = v1 % v2;
                }
-             top->unsignedp = unsigned1 || unsigned2;
-             if (top->unsignedp)
-               top->value = (unsigned HOST_WIDEST_INT) v1 % v2;
-             else
-               top->value = v1 % v2;
              break;
-           case '!':
-             if (top->flags & HAVE_VALUE)
-               {
-                 cpp_error (pfile, "syntax error");
-                 goto syntax_error;
-               }
-             top->value = ! v2;
-             top->unsignedp = 0;
-             top->flags |= HAVE_VALUE;
-             break;
-           case '~':
-             if (top->flags & HAVE_VALUE)
-               {
-                 cpp_error (pfile, "syntax error");
-                 goto syntax_error;
-               }
-             top->value = ~ v2;
-             top->unsignedp = unsigned2;
-             top->flags |= HAVE_VALUE;
-             break;
-           case '<':  COMPARE(<);  break;
-           case '>':  COMPARE(>);  break;
-           case LEQ:  COMPARE(<=); break;
-           case GEQ:  COMPARE(>=); break;
-           case EQUAL:
-             top->value = (v1 == v2);
+
+           case CPP_OR_OR:
+             top->value = v1 || v2;
              top->unsignedp = 0;
+             if (v1) skip_evaluation--;
              break;
-           case NOTEQUAL:
-             top->value = (v1 != v2);
+           case CPP_AND_AND:
+             top->value = v1 && v2;
              top->unsignedp = 0;
-             break;
-           case LSH:
-             if (skip_evaluation)
-               break;
-             top->unsignedp = unsigned1;
-             if (v2 < 0 && ! unsigned2)
-               top->value = right_shift (pfile, v1, unsigned1, -v2);
-             else
-               top->value = left_shift (pfile, v1, unsigned1, v2);
-             break;
-           case RSH:
-             if (skip_evaluation)
-               break;
-             top->unsignedp = unsigned1;
-             if (v2 < 0 && ! unsigned2)
-               top->value = left_shift (pfile, v1, unsigned1, -v2);
-             else
-               top->value = right_shift (pfile, v1, unsigned1, v2);
-             break;
-           case '&':  LOGICAL(&); break;
-           case '^':  LOGICAL(^);  break;
-           case '|':  LOGICAL(|);  break;
-           case ANDAND:
-             top->value = v1 && v2;  top->unsignedp = 0;
              if (!v1) skip_evaluation--;
              break;
-           case OROR:
-             top->value = v1 || v2;  top->unsignedp = 0;
-             if (v1) skip_evaluation--;
-             break;
-           case ',':
+           case CPP_COMMA:
              if (CPP_PEDANTIC (pfile))
-               cpp_pedwarn (pfile, "comma operator in operand of `#if'");
+               cpp_pedwarn (pfile, "comma operator in operand of #if");
              top->value = v2;
              top->unsignedp = unsigned2;
              break;
-           case '?':
-             cpp_error (pfile, "syntax error '?' without following ':'");
-             goto syntax_error;
-           case ':':
-             if (top[0].op != '?')
-               {
-                 cpp_error (pfile,
-                            "syntax error ':' without preceding '?'");
-                 goto syntax_error;
-               }
-             else if (! (top[1].flags & HAVE_VALUE)
-                      || !(top[-1].flags & HAVE_VALUE)
-                      || !(top[0].flags & HAVE_VALUE))
-               {
-                 cpp_error (pfile, "bad syntax for ?: operator");
-                 goto syntax_error;
-               }
-             else
-               {
-                 top--;
-                 if (top->value) skip_evaluation--;
-                 top->value = top->value ? v1 : v2;
-                 top->unsignedp = unsigned1 || unsigned2;
-               }
+           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_error (pfile, "missing '(' in expression");
-             goto syntax_error;
-           case '(':
-             if (op.op != ')')
-               {
-                 cpp_error (pfile, "missing ')' in expression");
-                 goto syntax_error;
-               }
-             if (!(top[1].flags & HAVE_VALUE))
-               {
-                 cpp_error (pfile, "void expression between '(' and ')'");
-                 goto syntax_error;
-               }
+           case CPP_OPEN_PAREN:
+             if (op.op != CPP_CLOSE_PAREN)
+               SYNTAX_ERROR ("missing ')' in expression");
              op.value = v2;
              op.unsignedp = unsigned2;
              goto push_immediate;
-           default:
-             if (ISGRAPH (top[1].op))
-               cpp_error (pfile, "unimplemented operator '%c'\n", top[1].op);
-             else
-               cpp_error (pfile, "unimplemented operator '\\%03o'\n",
-                          top[1].op);
+           case CPP_EOF:
+             /* Reducing this dummy operator indicates we've finished.  */
+             if (op.op == CPP_CLOSE_PAREN)
+               SYNTAX_ERROR ("missing '(' in expression");
+             goto done;
            }
        }
 
-      if (op.op == 0)
-       break;
+      /* Handle short-circuit evaluations.  */
+      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) /* Was '?' condition true?  */
+             skip_evaluation++;
+           else
+             skip_evaluation--;
+         default:
+           break;
+         }
 
     skip_reduction:
+      /* Check we have a left operand iff we need one.  */
+      if (flags & NO_L_OPERAND)
+       {
+         if (top->flags & HAVE_VALUE)
+           SYNTAX_ERROR2 ("missing binary operator before '%s'",
+                          op_as_text (pfile, top->op));
+       }
+      else
+       {
+         if (!(top->flags & HAVE_VALUE))
+           SYNTAX_ERROR2 ("operator '%s' has no left operand",
+                          op_as_text (pfile, top->op));
+       }
+
       /* Check for and handle stack overflow.  */
       top++;
       if (top == limit)
        {
-         struct operation *new_stack;
+         struct op *new_stack;
          int old_size = (char *) limit - (char *) stack;
          int new_size = 2 * old_size;
          if (stack != init_stack)
-           new_stack = (struct operation *) xrealloc (stack, new_size);
+           new_stack = (struct op *) xrealloc (stack, new_size);
          else
            {
-             new_stack = (struct operation *) xmalloc (new_size);
+             new_stack = (struct op *) xmalloc (new_size);
              memcpy (new_stack, stack, old_size);
            }
          stack = new_stack;
-         top = (struct operation *) ((char *) new_stack + old_size);
-         limit = (struct operation *) ((char *) new_stack + new_size);
+         top = (struct op *) ((char *) new_stack + old_size);
+         limit = (struct op *) ((char *) new_stack + new_size);
        }
       
       top->flags = flags;
-      top->prio = prio & ~RIGHT_ASSOC;
+      top->prio = prio & ~EXTRACT_PRIO(RIGHT_ASSOC);
       top->op = op.op;
-
-      /* Handle short circuiting.  */
-      if ((op.op == OROR && top[-1].value)
-         || (op.op == ANDAND && !top[-1].value)
-         || (op.op == '?' && !top[-1].value))
-       {
-         skip_evaluation++;
-       }
-      else if (op.op == ':')
-       {
-         if (top[-2].value) /* Was condition true? */
-           skip_evaluation++;
-         else
-           skip_evaluation--;
-       }
     }
 
+ done:
+  result = (top[1].value != 0);
   if (top != stack)
-    cpp_ice (pfile, "unbalanced stack in #if expression");
-  if (!(top->flags & HAVE_VALUE))
-    cpp_error (pfile, "#if with no expression");
-  result = (top->value != 0);
-
- tidy_up:
-  pfile->parsing_if_directive--;
-  CPP_SET_WRITTEN (pfile, old_written);
+    CPP_ICE ("unbalanced stack in #if");
+  else if (!(top[1].flags & HAVE_VALUE))
+    {
+      SYNTAX_ERROR ("#if with no expression");
+    syntax_error:
+      result = 0;  /* Return 0 on syntax error.  */
+    }
+
+  /* Free dynamic stack if we allocated one.  */
   if (stack != init_stack)
     free (stack);
   return result;
+}
 
- syntax_error:
-  _cpp_skip_rest_of_line (pfile);
-  result = 0;
-  goto tidy_up;
+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);
 }