OSDN Git Service

Daily bump.
[pf3gnuchains/gcc-fork.git] / gcc / cppexp.c
index e35690c..58430c3 100644 (file)
@@ -1,5 +1,6 @@
-/* Parse C expressions for CCCP.
-   Copyright (C) 1987, 1992, 1994, 1995 Free Software Foundation.
+/* Parse C expressions for cpplib.
+   Copyright (C) 1987, 92, 94, 95, 97, 98, 1999, 2000 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
@@ -13,56 +14,15 @@ 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, 675 Mass Ave, Cambridge, MA 02139, 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. */
+Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
 
 /* Parse a C expression from text in a string  */
    
 #include "config.h"
+#include "system.h"
 #include "cpplib.h"
-
-extern char *xmalloc PARAMS ((unsigned));
-extern char *xrealloc PARAMS ((char *, unsigned));
-
-#ifdef MULTIBYTE_CHARS
-#include <stdlib.h>
-#include <locale.h>
-#endif
-
-#include <stdio.h>
-
-/* This is used for communicating lists of keywords with cccp.c.  */
-struct arglist {
-  struct arglist *next;
-  U_CHAR *name;
-  int length;
-  int argno;
-};
-
-/* Define a generic NULL if one hasn't already been defined.  */
-
-#ifndef NULL
-#define NULL 0
-#endif
-
-#ifndef GENERIC_PTR
-#if defined (USE_PROTOTYPES) ? USE_PROTOTYPES : defined (__STDC__)
-#define GENERIC_PTR void *
-#else
-#define GENERIC_PTR char *
-#endif
-#endif
-
-#ifndef NULL_PTR
-#define NULL_PTR ((GENERIC_PTR)0)
-#endif
-
-extern char *xmalloc ();
+#include "cpphash.h"
 
 #ifndef CHAR_TYPE_SIZE
 #define CHAR_TYPE_SIZE BITS_PER_UNIT
@@ -96,13 +56,37 @@ extern char *xmalloc ();
 #define MAX_WCHAR_TYPE_SIZE WCHAR_TYPE_SIZE
 #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)
+
+#define MAX_WCHAR_TYPE_MASK (MAX_WCHAR_TYPE_SIZE < HOST_BITS_PER_WIDEST_INT \
+                            ? ~(~(HOST_WIDEST_INT) 0 << MAX_WCHAR_TYPE_SIZE) \
+                            : ~ (HOST_WIDEST_INT) 0)
+
 /* Yield nonzero if adding two numbers with A's and B's signs can yield a
    number with SUM's sign, where A, B, and SUM are all C integers.  */
 #define possible_sum_sign(a, b, sum) ((((a) ^ (b)) | ~ ((a) ^ (sum))) < 0)
 
-static void integer_overflow ();
-static long left_shift ();
-static long right_shift ();
+typedef int op_t;
+
+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 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 struct operation parse_assertion 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));
+static const char * op_to_str PARAMS ((op_t, char *));
 
 #define ERROR 299
 #define OROR 300
@@ -116,153 +100,378 @@ static long right_shift ();
 #define NAME 308
 #define INT 309
 #define CHAR 310
+#define FINISHED 311
 
-#define LEFT_OPERAND_REQUIRED 1
-#define RIGHT_OPERAND_REQUIRED 2
-#define HAVE_VALUE 4
-/*#define UNSIGNEDP 8*/
-
-#ifndef HOST_BITS_PER_WIDE_INT
-
-#if HOST_BITS_PER_LONG > HOST_BITS_PER_INT
-#define HOST_BITS_PER_WIDE_INT HOST_BITS_PER_LONG
-#define HOST_WIDE_INT long
-#else
-#define HOST_BITS_PER_WIDE_INT HOST_BITS_PER_INT
-#define HOST_WIDE_INT int
-#endif
-
-#endif
-
-struct operation {
-    short op;
-    char rprio; /* Priority of op (relative to it right operand). */
-    char flags;
-    char unsignedp;    /* true if value should be treated as unsigned */
-    HOST_WIDE_INT value;        /* The value logically "right" of op. */
+struct operation
+{
+  op_t 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.  */
 };
-\f
-/* Take care of parsing a number (anything that starts with a digit).
-   LEN is the number of characters in it.  */
 
-/* maybe needs to actually deal with floating point numbers */
+/* 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)
 
-struct operation
-parse_number (pfile, start, olen)
+/* Parse and convert an integer for #if.  Accepts decimal, hex, or octal
+   with or without size suffixes.  */
+
+static struct operation
+parse_number (pfile, start, end)
      cpp_reader *pfile;
-     char *start;
-     int olen;
+     U_CHAR *start;
+     U_CHAR *end;
 {
   struct operation op;
-  register char *p = start;
-  register int c;
-  register unsigned long n = 0, nd, ULONG_MAX_over_base;
-  register int base = 10;
-  register int len = olen;
-  register int overflow = 0;
-  register int digit, largest_digit = 0;
+  U_CHAR *p = start;
+  int c;
+  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;
 
   op.unsignedp = 0;
 
-  for (c = 0; c < len; c++)
-    if (p[c] == '.') {
-      /* It's a float since it contains a point.  */
-      cpp_error (pfile,
-                "floating point numbers not allowed in #if expressions");
-      op.op = ERROR;
-      return op;
+  if (p[0] == '0')
+    {
+      if (end - start >= 3 && (p[1] == 'x' || p[1] == 'X'))
+       {
+         p += 2;
+         base = 16;
+       }
+      else
+       {
+         p += 1;
+         base = 8;
+       }
     }
 
-  if (len >= 3 && (!strncmp (p, "0x", 2) || !strncmp (p, "0X", 2))) {
-    p += 2;
-    base = 16;
-    len -= 2;
-  }
-  else if (*p == '0')
-    base = 8;
-
-  ULONG_MAX_over_base = (unsigned long) -1 / base;
-/* start-sanitize-mpw */
-  /* Work around yet another MPW C bug. */
-#ifdef MPW_C
-  ULONG_MAX_over_base = ((unsigned long) -1) / ((unsigned long) base);
-#endif /* MPW_C */
-/* end-sanitize-mpw */
-
-  for (; len > 0; len--) {
-    c = *p++;
-
-    if (c >= '0' && c <= '9')
-      digit = c - '0';
-    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 {
-      /* `l' means long, and `u' means unsigned.  */
-      while (1) {
-       if (c == 'l' || c == 'L')
-         {
-           if (spec_long)
-             cpp_error (pfile, "two `l's in integer constant");
-           spec_long = 1;
-         }
-       else if (c == 'u' || c == 'U')
-         {
-           if (op.unsignedp)
-             cpp_error (pfile, "two `u's in integer constant");
-           op.unsignedp = 1;
-         }
-       else
-         break;
+  /* Some buggy compilers (e.g. MPW C) seem to need both casts.  */
+  MAX_over_base = (((unsigned HOST_WIDEST_INT) -1)
+                  / ((unsigned HOST_WIDEST_INT) base));
 
-       if (--len == 0)
+  while (p < end)
+    {
+      c = *p++;
+
+      if (c >= '0' && c <= '9')
+       digit = c - '0';
+      /* FIXME: assumes ASCII */
+      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;
-       c = *p++;
-      }
-      /* Don't look for any more digits after the suffixes.  */
-      break;
+       }
+      
+      if (largest_digit < digit)
+       largest_digit = digit;
+      nd = n * base + digit;
+      overflow |= MAX_over_base < n || nd < n;
+      n = nd;
     }
-    if (largest_digit < digit)
-      largest_digit = digit;
-    nd = n * base + digit;
-    overflow |= ULONG_MAX_over_base < n | nd < n;
-    n = nd;
-  }
 
-  if (len != 0)
+  if (p != end)
     {
-      cpp_error (pfile, "Invalid number in #if expression");
-      op.op = ERROR;
-      return op;
+      cpp_error (pfile, "invalid number in #if expression");
+      goto error;
     }
-
+  else if (spec_long > (CPP_OPTION (pfile, c89) ? 1 : 2))
+    {
+      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;
+    }
+  
   if (base <= largest_digit)
-    cpp_warning (pfile, "integer constant contains digits beyond the radix");
+    cpp_pedwarn (pfile,
+                "integer constant contains digits beyond the radix");
 
   if (overflow)
-    cpp_warning (pfile, "integer constant out of range");
+    cpp_pedwarn (pfile, "integer constant out of range");
 
   /* If too big to be signed, consider it unsigned.  */
-  if ((long) n < 0 && ! op.unsignedp)
+  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;
   return op;
+
+ error:
+  op.op = 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)
+     cpp_reader *pfile;
+     U_CHAR *start;
+     U_CHAR *end;
+{
+  struct operation 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;
+  int max_chars;
+  U_CHAR *ptr = start;
+
+  int c = -1;
+
+  if (*ptr == 'L')
+    {
+      ++ptr;
+      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 == '\'')
+       break;
+      else if (c == '\\')
+       {
+         c = parse_escape (pfile, &ptr, mask);
+         if (width < HOST_BITS_PER_INT
+             && (unsigned int) c >= (unsigned int)(1 << width))
+           cpp_pedwarn (pfile,
+                        "escape sequence out of range for character");
+       }
+         
+      /* Merge character into result; ignore excess chars.  */
+      if (++num_chars <= max_chars)
+       {
+         if (width < HOST_BITS_PER_INT)
+           result = (result << width) | (c & ((1 << width) - 1));
+         else
+           result = c;
+       }
+    }
+
+  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;
+    }
+  else if (num_chars > max_chars)
+    {
+      cpp_error (pfile, "character constant too long");
+      goto error;
+    }
+  else if (num_chars != 1 && ! CPP_TRADITIONAL (pfile))
+    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, U"__CHAR_UNSIGNED__",
+                  sizeof ("__CHAR_UNSIGNED__")-1)
+      || ((result >> (num_bits - 1)) & 1) == 0)
+    op.value = result & ((unsigned HOST_WIDEST_INT) ~0
+                        >> (HOST_BITS_PER_WIDEST_INT - num_bits));
+  else
+    op.value = result | ~((unsigned HOST_WIDEST_INT) ~0
+                         >> (HOST_BITS_PER_WIDEST_INT - num_bits));
+
+  /* This is always a signed type.  */
+  op.unsignedp = 0;
+  op.op = CHAR;
+  return op;
+
+ error:
+  op.op = ERROR;
+  return op;
+}
+
+static struct operation
+parse_defined (pfile)
+     cpp_reader *pfile;
+{
+  int paren = 0, len;
+  U_CHAR *tok;
+  enum cpp_ttype token;
+  struct operation op;
+  long old_written = CPP_WRITTEN (pfile);
+
+  op.unsignedp = 0;
+  op.op = INT;
+
+  pfile->no_macro_expand++;
+  token = _cpp_get_directive_token (pfile);
+  if (token == CPP_OPEN_PAREN)
+    {
+      paren++;
+      CPP_SET_WRITTEN (pfile, old_written);
+      token = _cpp_get_directive_token (pfile);
+    }
+
+  if (token != CPP_NAME)
+    goto oops;
+
+  tok = pfile->token_buffer + old_written;
+  len = CPP_PWRITTEN (pfile) - tok;
+  op.value = cpp_defined (pfile, tok, len);
+
+  if (paren)
+    {
+      if (_cpp_get_directive_token (pfile) != CPP_CLOSE_PAREN)
+       goto oops;
+    }
+  CPP_SET_WRITTEN (pfile, old_written);
+  pfile->no_macro_expand--;
+  return op;
+
+ oops:
+  CPP_SET_WRITTEN (pfile, old_written);
+  pfile->no_macro_expand--;
+  cpp_error (pfile, "'defined' without an identifier");
+
+  op.op = ERROR;
+  return op;
+}
+
+static struct operation
+parse_assertion (pfile)
+     cpp_reader *pfile;
+{
+  struct operation op;
+  cpp_hashnode *hp;
+  struct predicate *pred;
+  cpp_toklist query;
+  enum cpp_ttype type;
+  U_CHAR *tok;
+  size_t len;
+  unsigned int old_written;
+  int specific = 0;
+
+  old_written = CPP_WRITTEN (pfile);
+  CPP_PUTC (pfile, '#');
+  pfile->no_macro_expand++;
+  type = _cpp_get_directive_token (pfile);
+  if (type == CPP_VSPACE)
+    SYNTAX_ERROR ("assertion without predicate");
+  else if (type != CPP_NAME)
+    SYNTAX_ERROR ("assertion predicate is not an identifier");
+
+  tok = pfile->token_buffer + old_written;
+  len = CPP_WRITTEN (pfile) - old_written;
+  hp = cpp_lookup (pfile, tok, len);
+
+  /* Look ahead for an open paren.  */
+  _cpp_skip_hspace (pfile);
+  if (CPP_BUF_PEEK (CPP_BUFFER (pfile)) == '(')
+    {
+      if (_cpp_get_directive_token (pfile) != CPP_OPEN_PAREN)
+       CPP_ICE ("impossible token, expecting ( in parse_assertion");
+
+      _cpp_init_toklist (&query, NO_DUMMY_TOKEN);
+      specific = 1;
+      if (_cpp_scan_until (pfile, &query, CPP_CLOSE_PAREN) != CPP_CLOSE_PAREN)
+       SYNTAX_ERROR ("missing close paren on assertion answer");
+
+      if (_cpp_get_directive_token (pfile) != CPP_CLOSE_PAREN)
+       CPP_ICE ("impossible token, expecting ) in parse_assertion");
+    }
+
+  /* If we get here, the syntax is valid.  */
+  op.op = INT;
+  op.value = 0;
+  /* Has this predicate been asserted at all?  */
+  if (hp->type == T_ASSERTION)
+    {
+      if (specific)
+       {
+         for (pred = hp->value.pred; pred; pred = pred->next)
+           if (_cpp_equiv_toklists (&query, &pred->answer))
+             {
+               op.value = 1;
+               break;
+             }
+         _cpp_free_toklist (&query);
+       }
+      else
+       op.value = 1;
+    }
+
+ out:
+  pfile->no_macro_expand--;
+  CPP_SET_WRITTEN (pfile, old_written);
+  return op;
+
+ syntax_error:
+  if (specific)
+    _cpp_free_toklist (&query);
+  op.op = ERROR;
+  goto out;
 }
 
-struct token {
-  char *operator;
-  int token;
+struct token
+{
+  const char *operator;
+  op_t token;
 };
 
-static struct token tokentab2[] = {
+static const struct token tokentab2[] =
+{
   {"&&", ANDAND},
   {"||", OROR},
   {"<<", LSH},
@@ -276,176 +485,59 @@ static struct token tokentab2[] = {
   {NULL, ERROR}
 };
 
-/* Read one token. */
+/* Read one token.  */
 
-struct operation
-cpp_lex (pfile)
-cpp_reader *pfile;
+static struct operation
+lex (pfile, skip_evaluation)
+     cpp_reader *pfile;
+     int skip_evaluation;
 {
-  register int c;
-  register int namelen;
-  register struct token *toktab;
-  enum cpp_token token;
+  const struct token *toktab;
+  enum cpp_ttype token;
   struct operation op;
   U_CHAR *tok_start, *tok_end;
-  int old_written;
-
- retry:
-
-  c = CPP_BUF_PEEK (CPP_BUFFER (pfile));
-  if (c == '#')
-    return parse_number (pfile,
-                        cpp_read_check_assertion (pfile) ? "1" : "0", 1);
+  long old_written;
 
   old_written = CPP_WRITTEN (pfile);
-  cpp_skip_hspace (pfile);
-  if (c == '\n')
-    {
-      op.op = 0;
-      return op;
-    }
+  token = _cpp_get_directive_token (pfile);
 
-  token = cpp_get_token (pfile);
   tok_start = pfile->token_buffer + old_written;
   tok_end = CPP_PWRITTEN (pfile);
-  pfile->limit = tok_start;
+  CPP_SET_WRITTEN (pfile, old_written);
   switch (token)
-  {
-    case CPP_EOF: /* Should not happen ... */
+    {
+    case CPP_EOF: /* Should not happen ...  */
+    case CPP_VSPACE:
       op.op = 0;
       return op;
-    case CPP_VSPACE:
-    case CPP_POP:
-      if (CPP_BUFFER (pfile)->fname != NULL)
-       {
-         op.op = 0;
-         return op;
-       }
-      goto retry;
-    case CPP_HSPACE:   case CPP_COMMENT: 
-      goto retry;
     case CPP_NUMBER:
-      return parse_number (pfile, tok_start, tok_end - tok_start);
+      return parse_number (pfile, tok_start, tok_end);
     case CPP_STRING:
-      cpp_error (pfile, "string constants not allowed in #if expressions");
+    case CPP_WSTRING:
+      cpp_error (pfile,
+                "string constants are not allowed in #if expressions");
       op.op = ERROR;
       return op;
-    case CPP_CHAR:
-      /* This code for reading a character constant
-        handles multicharacter constants and wide characters.
-        It is mostly copied from c-lex.c.  */
-      {
-        register int result = 0;
-       register num_chars = 0;
-       unsigned width = MAX_CHAR_TYPE_SIZE;
-       int wide_flag = 0;
-       int max_chars;
-       U_CHAR *ptr = tok_start;
-#ifdef MULTIBYTE_CHARS
-       char token_buffer[MAX_LONG_TYPE_SIZE/MAX_CHAR_TYPE_SIZE + MB_CUR_MAX];
-#else
-       char token_buffer[MAX_LONG_TYPE_SIZE/MAX_CHAR_TYPE_SIZE + 1];
-#endif
 
-       if (*ptr == 'L')
-         {
-           ptr++;
-           wide_flag = 1;
-           width = MAX_WCHAR_TYPE_SIZE;
-#ifdef MULTIBYTE_CHARS
-           max_chars = MB_CUR_MAX;
-#else
-           max_chars = 1;
-#endif
-         }
-       else
-           max_chars = MAX_LONG_TYPE_SIZE / width;
-
-       while (1)
-         {
-           if (ptr >= CPP_PWRITTEN (pfile) || (c = *ptr++) == '\'')
-             break;
-
-           if (c == '\\')
-             {
-               c = cpp_parse_escape (pfile, &ptr);
-               if (width < HOST_BITS_PER_INT
-                 && (unsigned) c >= (1 << width))
-                   cpp_pedwarn (pfile,
-                                "escape sequence out of range for character");
-             }
-
-           num_chars++;
-
-           /* Merge character into result; ignore excess chars.  */
-           if (num_chars < max_chars + 1)
-             {
-               if (width < HOST_BITS_PER_INT)
-                 result = (result << width) | (c & ((1 << width) - 1));
-               else
-                 result = c;
-               token_buffer[num_chars - 1] = c;
-             }
-         }
-
-       token_buffer[num_chars] = 0;
-
-       if (c != '\'')
-         cpp_error (pfile, "malformatted character constant");
-       else if (num_chars == 0)
-         cpp_error (pfile, "empty character constant");
-       else if (num_chars > max_chars)
-         {
-           num_chars = max_chars;
-           cpp_error (pfile, "character constant too long");
-         }
-       else if (num_chars != 1 && ! CPP_TRADITIONAL (pfile))
-         cpp_warning (pfile, "multi-character character constant");
-
-       /* If char type is signed, sign-extend the constant.  */
-       if (! wide_flag)
-         {
-           int num_bits = num_chars * width;
+    case CPP_CHAR:
+    case CPP_WCHAR:
+      return parse_charconst (pfile, tok_start, tok_end);
 
-           if (cpp_lookup (pfile, "__CHAR_UNSIGNED__",
-                           sizeof ("__CHAR_UNSIGNED__")-1, -1)
-               || ((result >> (num_bits - 1)) & 1) == 0)
-               op.value
-                   = result & ((unsigned long) ~0 >> (HOST_BITS_PER_LONG - num_bits));
-           else
-               op.value
-                   = result | ~((unsigned long) ~0 >> (HOST_BITS_PER_LONG - num_bits));
-         }
-       else
-         {
-#ifdef MULTIBYTE_CHARS
-           /* Set the initial shift state and convert the next sequence.  */
-             result = 0;
-             /* In all locales L'\0' is zero and mbtowc will return zero,
-                so don't use it.  */
-             if (num_chars > 1
-                 || (num_chars == 1 && token_buffer[0] != '\0'))
-               {
-                 wchar_t wc;
-                 (void) mbtowc (NULL_PTR, NULL_PTR, 0);
-                 if (mbtowc (& wc, token_buffer, num_chars) == num_chars)
-                   result = wc;
-                 else
-                   cpp_warning (pfile,"Ignoring invalid multibyte character");
-               }
-#endif
-             op.value = result;
-           }
-        }
+    case CPP_NAME:
+      if (!ustrncmp (tok_start, U"defined", 7))
+       return parse_defined (pfile);
 
-      /* This is always a signed type.  */
+      op.op = INT;
       op.unsignedp = 0;
-      op.op = CHAR;
-    
+      op.value = 0;
+
+      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_NAME:
-      return parse_number (pfile, "0", 0);
+    case CPP_HASH:
+      return parse_assertion (pfile);
 
     case CPP_OTHER:
       /* See if it is a special token of length 2.  */
@@ -456,11 +548,8 @@ cpp_reader *pfile;
                && tok_start[1] == toktab->operator[1])
                break;
          if (toktab->token == ERROR)
-           {
-             char *buf = (char *) alloca (40);
-             sprintf (buf, "`%s' not allowed in operand of `#if'", tok_start);
-             cpp_error (pfile, buf);
-           }
+           cpp_error (pfile, "'%.*s' is not allowed in #if expressions",
+                      (int) (tok_end - tok_start), tok_start);
          op.op = toktab->token; 
          return op;
        }
@@ -471,6 +560,27 @@ cpp_reader *pfile;
   }
 }
 
+/* Convert an operator ID to a string.  BUFF is a buffer at least 5
+   characters long which might be used to store the string.  */
+/* XXX FIXME: Remove BUFF when new lexer is implemented.  */
+static const char *
+op_to_str (op, buff)
+     op_t op;
+     char *buff;
+{
+  const struct token *toktab;
+
+  /* See if it is a special token of length 2.  */
+  for (toktab = tokentab2; toktab->operator != NULL; toktab++)
+    if (op == toktab->token)
+      return toktab->operator;
+
+  if (ISGRAPH (op))
+    sprintf (buff, "%c", (int) op);
+  else
+    sprintf (buff, "\\%03o", (int) op);
+  return buff;
+}
 
 /* Parse a C escape sequence.  STRING_PTR points to a variable
    containing a pointer to the string to parse.  That pointer
@@ -486,10 +596,11 @@ cpp_reader *pfile;
    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.  */
 
-int
-cpp_parse_escape (pfile, string_ptr)
+static HOST_WIDEST_INT
+parse_escape (pfile, string_ptr, result_mask)
      cpp_reader *pfile;
-     char **string_ptr;
+     U_CHAR **string_ptr;
+     HOST_WIDEST_INT result_mask;
 {
   register int c = *(*string_ptr)++;
   switch (c)
@@ -501,8 +612,8 @@ cpp_parse_escape (pfile, string_ptr)
     case 'e':
     case 'E':
       if (CPP_PEDANTIC (pfile))
-       cpp_pedwarn (pfile, "non-ANSI-standard escape sequence, `\\%c'", c);
-      return 033;
+       cpp_pedwarn (pfile, "non-ANSI-standard escape sequence, '\\%c'", c);
+      return TARGET_ESC;
     case 'f':
       return TARGET_FF;
     case 'n':
@@ -528,7 +639,7 @@ cpp_parse_escape (pfile, string_ptr)
     case '6':
     case '7':
       {
-       register int i = c - '0';
+       register HOST_WIDEST_INT i = c - '0';
        register int count = 0;
        while (++count < 3)
          {
@@ -541,17 +652,17 @@ cpp_parse_escape (pfile, string_ptr)
                break;
              }
          }
-       if ((i & ~((1 << MAX_CHAR_TYPE_SIZE) - 1)) != 0)
+       if (i != (i & result_mask))
          {
-           i &= (1 << MAX_CHAR_TYPE_SIZE) - 1;
-           cpp_warning (pfile,
-                         "octal character constant does not fit in a byte");
+           i &= result_mask;
+           cpp_pedwarn (pfile, "octal escape sequence out of range");
          }
        return i;
       }
     case 'x':
       {
-       register unsigned i = 0, overflow = 0, digits_found = 0, digit;
+       register unsigned HOST_WIDEST_INT i = 0, overflow = 0;
+       register int digits_found = 0, digit;
        for (;;)
          {
            c = *(*string_ptr)++;
@@ -572,11 +683,10 @@ cpp_parse_escape (pfile, string_ptr)
          }
        if (!digits_found)
          cpp_error (pfile, "\\x used with no following hex digits");
-       if (overflow | (i & ~((1 << BITS_PER_UNIT) - 1)))
+       if (overflow | (i != (i & result_mask)))
          {
-           i &= (1 << BITS_PER_UNIT) - 1;
-           cpp_warning (pfile,
-                        "hex character constant does not fit in a byte");
+           i &= result_mask;
+           cpp_pedwarn (pfile, "hex escape sequence out of range");
          }
        return i;
       }
@@ -593,75 +703,128 @@ integer_overflow (pfile)
     cpp_pedwarn (pfile, "integer overflow in preprocessor expression");
 }
 
-static long
+static HOST_WIDEST_INT
 left_shift (pfile, a, unsignedp, b)
      cpp_reader *pfile;
-     long a;
-     int unsignedp;
-     unsigned long b;
+     HOST_WIDEST_INT a;
+     unsigned int unsignedp;
+     unsigned HOST_WIDEST_INT b;
 {
-  if (b >= HOST_BITS_PER_LONG)
+  if (b >= HOST_BITS_PER_WIDEST_INT)
     {
       if (! unsignedp && a != 0)
        integer_overflow (pfile);
       return 0;
     }
   else if (unsignedp)
-    return (unsigned long) a << b;
+    return (unsigned HOST_WIDEST_INT) a << b;
   else
     {
-      long l = a << b;
+      HOST_WIDEST_INT l = a << b;
       if (l >> b != a)
        integer_overflow (pfile);
       return l;
     }
 }
 
-static long
+static HOST_WIDEST_INT
 right_shift (pfile, a, unsignedp, b)
-     cpp_reader *pfile;
-     long a;
-     int unsignedp;
-     unsigned long b;
+     cpp_reader *pfile ATTRIBUTE_UNUSED;
+     HOST_WIDEST_INT a;
+     unsigned int unsignedp;
+     unsigned HOST_WIDEST_INT b;
 {
-  if (b >= HOST_BITS_PER_LONG)
-    return unsignedp ? 0 : a >> (HOST_BITS_PER_LONG - 1);
+  if (b >= HOST_BITS_PER_WIDEST_INT)
+    return unsignedp ? 0 : a >> (HOST_BITS_PER_WIDEST_INT - 1);
   else if (unsignedp)
-    return (unsigned long) a >> b;
+    return (unsigned HOST_WIDEST_INT) a >> b;
   else
     return a >> b;
 }
 \f
-/* These priorities are all even, so we can handle associatively. */
-#define PAREN_INNER_PRIO 0
-#define COMMA_PRIO 4
-#define COND_PRIO (COMMA_PRIO+2)
-#define OROR_PRIO (COND_PRIO+2)
-#define ANDAND_PRIO (OROR_PRIO+2)
-#define OR_PRIO (ANDAND_PRIO+2)
-#define XOR_PRIO (OR_PRIO+2)
-#define AND_PRIO (XOR_PRIO+2)
-#define EQUAL_PRIO (AND_PRIO+2)
-#define LESS_PRIO (EQUAL_PRIO+2)
-#define SHIFT_PRIO (LESS_PRIO+2)
-#define PLUS_PRIO (SHIFT_PRIO+2)
-#define MUL_PRIO (PLUS_PRIO+2)
-#define UNARY_PRIO (MUL_PRIO+2)
-#define PAREN_OUTER_PRIO (UNARY_PRIO+2)
+/* 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 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 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 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 EQUAL_PRIO         (11 << PRIO_SHIFT)
+#define LESS_PRIO          (12 << PRIO_SHIFT)
+#define SHIFT_PRIO         (13 << PRIO_SHIFT)
+#define PLUS_PRIO          (14 << PRIO_SHIFT)
+#define MUL_PRIO           (15 << PRIO_SHIFT)
+#define UNARY_PRIO        ((16 << PRIO_SHIFT) | RIGHT_ASSOC | NO_L_OPERAND)
 
 #define COMPARE(OP) \
-  top->unsignedp = 0;\
-  top->value = (unsigned1 || unsigned2) ? (unsigned long) v1 OP v2 : (v1 OP v2)
+  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 LOGICAL(OP) \
+  top->value = v1 OP v2; \
+  top->unsignedp = unsigned1 | unsigned2;
 
 /* Parse and evaluate a C expression, reading from PFILE.
-   Returns the value of the expression.  */
+   Returns the truth value of the expression.  */
 
-HOST_WIDE_INT
-cpp_parse_expr (pfile)
+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),
@@ -674,107 +837,115 @@ cpp_parse_expr (pfile)
   struct operation init_stack[INIT_STACK_SIZE];
   struct operation *stack = init_stack;
   struct operation *limit = stack + INIT_STACK_SIZE;
-  register struct operation *top = stack;
-  int lprio, rprio;
+  register struct operation *top = stack + 1;
+  long old_written = CPP_WRITTEN (pfile);
+  int skip_evaluation = 0;
+  int result;
+  char buff[5];
+
+  /* Save parser state and set it to something sane.  */
+  int save_only_seen_white = pfile->only_seen_white;
+  int save_skipping = pfile->skipping;
+  pfile->only_seen_white = 0;
+  pfile->skipping = 0;
+
+  /* We've finished when we try to reduce this.  */
+  top->op = FINISHED;
+  /* Nifty way to catch missing '('.  */
+  top->prio = EXTRACT_PRIO(CLOSE_PAREN_PRIO);
+  /* Avoid missing right operand checks.  */
+  top->flags = NO_R_OPERAND;
 
-  top->rprio = 0;
-  top->flags = 0;
   for (;;)
     {
+      unsigned int prio;
+      unsigned int flags;
       struct operation op;
-      char flags = 0;
 
       /* Read a token */
-      op =  cpp_lex (pfile);
-
-      /* 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);
 
+      /* 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:
-         top->value = 0, top->unsignedp = 0;
-         goto set_value;
-       case INT:  case CHAR:
+         CPP_ICE ("lex returns a NAME");
+       case ERROR:
+         goto syntax_error;
+       default:
+         SYNTAX_ERROR ("invalid character in #if");
+
+       push_immediate:
+       case INT:
+       case CHAR:
+         /* Push a value onto the stack.  */
+         if (top->flags & HAVE_VALUE)
+           SYNTAX_ERROR ("missing binary operator");
          top->value = op.value;
          top->unsignedp = op.unsignedp;
-         goto set_value;
-       case 0:
-         lprio = 0;  goto maybe_reduce;
-       case '+':  case '-':
-         /* Is this correct if unary ? FIXME */
-         flags = RIGHT_OPERAND_REQUIRED;
-         lprio = PLUS_PRIO;  rprio = lprio + 1;  goto maybe_reduce;
-       case '!':  case '~':
-         flags = RIGHT_OPERAND_REQUIRED;
-         rprio = UNARY_PRIO;  lprio = rprio + 1;  goto maybe_reduce;
-       case '*':  case '/':  case '%':
-         lprio = MUL_PRIO;  goto binop;
-       case '<':  case '>':  case LEQ:  case GEQ:
-         lprio = LESS_PRIO;  goto binop;
-       case EQUAL:  case NOTEQUAL:
-         lprio = EQUAL_PRIO;  goto binop;
-       case LSH:  case RSH:
-         lprio = SHIFT_PRIO;  goto binop;
-       case '&':  lprio = AND_PRIO;  goto binop;
-       case '^':  lprio = XOR_PRIO;  goto binop;
-       case '|':  lprio = OR_PRIO;  goto binop;
-       case ANDAND:  lprio = ANDAND_PRIO;  goto binop;
-       case OROR:  lprio = OROR_PRIO;  goto binop;
-       case ',':
-         lprio = COMMA_PRIO;  goto binop;
-       case '(':
-         lprio = PAREN_OUTER_PRIO;  rprio = PAREN_INNER_PRIO;
-         goto maybe_reduce;
-       case ')':
-         lprio = PAREN_INNER_PRIO;  rprio = PAREN_OUTER_PRIO;
-         goto maybe_reduce;
-        case ':':
-         lprio = COND_PRIO;  rprio = COND_PRIO;
-         goto maybe_reduce;
-        case '?':
-         lprio = COND_PRIO + 1;  rprio = COND_PRIO;
-         goto maybe_reduce;
-       binop:
-         flags = LEFT_OPERAND_REQUIRED|RIGHT_OPERAND_REQUIRED;
-         rprio = lprio + 1;
-         goto maybe_reduce;
-       default:
-         cpp_error (pfile, "invalid character in #if");
-         goto syntax_error;
+         top->flags |= HAVE_VALUE;
+         continue;
+
+       case '+':
+       case '-':    prio = PLUS_PRIO;  if (top->flags & HAVE_VALUE) break;
+          /* else unary; fall through */
+       case '!':
+       case '~':    prio = UNARY_PRIO;  break;
+
+       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; break;
+       case ')':    prio = CLOSE_PAREN_PRIO;  break;
+        case ':':    prio = COLON_PRIO;  break;
+        case '?':    prio = COND_PRIO;  break;
+       case 0:      prio = FORCE_REDUCE_PRIO;  break;
        }
 
-    set_value:
-      /* Push a value onto the stack. */
-      if (top->flags & HAVE_VALUE)
-       {
-         cpp_error (pfile, "syntax error in #if");
-         goto syntax_error;
-       }
-      top->flags |= HAVE_VALUE;
-      continue;
+      /* Separate the operator's code into priority and flags.  */
+      flags = EXTRACT_FLAGS(prio);
+      prio = EXTRACT_PRIO(prio);
+      if (op.op == '(')
+       goto skip_reduction;
 
-    maybe_reduce:
-      /* Push an operator, and check if we can reduce now. */
-      while (top->rprio > lprio)
+      /* Check for reductions.  Then push the operator.  */
+      while (prio <= top->prio)
        {
-         long v1 = top[-1].value, v2 = top[0].value;
-         int unsigned1 = top[-1].unsignedp, 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 == '(')
+               SYNTAX_ERROR ("void expression between '(' and ')'");
+             else
+               SYNTAX_ERROR2 ("operator '%s' has no right operand",
+                              op_to_str (top->op, buff));
            }
-         /* 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 '+':
@@ -787,8 +958,8 @@ cpp_parse_expr (pfile)
              else
                {
                  top->value = v1 + v2;
-                 top->unsignedp = unsigned1 || unsigned2;
-                 if (! top->unsignedp
+                 top->unsignedp = unsigned1 | unsigned2;
+                 if (! top->unsignedp && ! skip_evaluation
                      && ! possible_sum_sign (v1, v2, top->value))
                    integer_overflow (pfile);
                }
@@ -797,7 +968,8 @@ cpp_parse_expr (pfile)
              if (!(top->flags & HAVE_VALUE))
                { /* Unary '-' */
                  top->value = - v2;
-                 if ((top->value & v2) < 0 && ! unsigned2)
+                 if (!skip_evaluation && (top->value & v2) < 0
+                     && !unsigned2)
                    integer_overflow (pfile);
                  top->unsignedp = unsigned2;
                  top->flags |= HAVE_VALUE;
@@ -805,86 +977,69 @@ cpp_parse_expr (pfile)
              else
                { /* Binary '-' */
                  top->value = v1 - v2;
-                 top->unsignedp = unsigned1 || unsigned2;
-                 if (! top->unsignedp
+                 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;
+             top->unsignedp = unsigned1 | unsigned2;
              if (top->unsignedp)
-               top->value = (unsigned long) v1 * v2;
-             else
+               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 '%':
+             if (skip_evaluation)
+               break;
              if (v2 == 0)
+               SYNTAX_ERROR ("division by zero in #if");
+             top->unsignedp = unsigned1 | unsigned2;
+             if (top[1].op == '/')
                {
-                 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 long) v1 / v2;
              else
                {
-                 top->value = v1 / v2;
-                 if ((top->value & v1 & v2) < 0)
-                   integer_overflow (pfile);
-               }
-             break;
-           case '%':
-             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 long) 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);
-             top->unsignedp = 0;
-             break;
-           case NOTEQUAL:
-             top->value = (v1 != v2);
-             top->unsignedp = 0;
-             break;
+           case LEQ:  COMPARE(<=);  break;
+           case GEQ:  COMPARE(>=);  break;
+           case EQUAL:    EQUALITY(==);  break;
+           case NOTEQUAL: EQUALITY(!=);  break;
            case LSH:
+             if (skip_evaluation)
+               break;
              top->unsignedp = unsigned1;
              if (v2 < 0 && ! unsigned2)
                top->value = right_shift (pfile, v1, unsigned1, -v2);
@@ -892,112 +1047,128 @@ cpp_parse_expr (pfile)
                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;
-#define LOGICAL(OP) \
-             top->value = v1 OP v2;\
-             top->unsignedp = unsigned1 || unsigned2;
            case '&':  LOGICAL(&); break;
            case '^':  LOGICAL(^);  break;
            case '|':  LOGICAL(|);  break;
            case ANDAND:
-             top->value = v1 && v2;  top->unsignedp = 0;  break;
+             top->value = v1 && v2;  top->unsignedp = 0;
+             if (!v1) skip_evaluation--;
+             break;
            case OROR:
-             top->value = v1 || v2;  top->unsignedp = 0;  break;
+             top->value = v1 || v2;  top->unsignedp = 0;
+             if (v1) skip_evaluation--;
+             break;
            case ',':
              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 '(':  case '?':
-             cpp_error (pfile, "syntax error in #if");
-             goto syntax_error;
+           case '?':
+             SYNTAX_ERROR ("syntax error '?' without following ':'");
            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--;
-                 top->value = top->value ? v1 : v2;
-                 top->unsignedp = unsigned1 || unsigned2;
-               }
-             break;
-           case ')':
-             if ((top[1].flags & HAVE_VALUE)
-                 || ! (top[0].flags & HAVE_VALUE)
-                 || top[0].op != '('
-                 || (top[-1].flags & HAVE_VALUE))
-               {
-                 cpp_error (pfile, "mismatched parentheses in #if");
-                 goto syntax_error;
-               }
-             else
-               {
-                 top--;
-                 top->value = v1;
-                 top->unsignedp = unsigned1;
-                 top->flags |= HAVE_VALUE;
-               }
+               SYNTAX_ERROR ("syntax error ':' without preceding '?'");
+             top--;
+             if (top->value) skip_evaluation--;
+             top->value = top->value ? v1 : v2;
+             top->unsignedp = unsigned1 | unsigned2;
              break;
+           case '(':
+             if (op.op != ')')
+               SYNTAX_ERROR ("missing ')' in expression");
+             op.value = v2;
+             op.unsignedp = unsigned2;
+             goto push_immediate;
            default:
-             fprintf (stderr,
-                      top[1].op >= ' ' && top[1].op <= '~'
-                      ? "unimplemented operator '%c'\n"
-                      : "unimplemented operator '\\%03o'\n",
-                      top[1].op);
+             SYNTAX_ERROR2 ("unimplemented operator '%s'",
+                            op_to_str (top[1].op, buff));
+           case FINISHED:
+             /* Reducing this dummy operator indicates we've finished.  */
+             if (op.op == ')')
+               SYNTAX_ERROR ("missing '(' in expression");
+             goto done;
            }
        }
-      if (op.op == 0)
+
+      /* Handle short-circuit evaluations.  */
+      if (flags & SHORT_CIRCUIT)
+       switch (op.op)
+         {
+         case OROR:    if (top->value) skip_evaluation++; break;
+         case ANDAND:
+         case '?':     if (!top->value) skip_evaluation++; break;
+         case ':':
+           if (top[-1].value) /* Was '?' condition true?  */
+             skip_evaluation++;
+           else
+             skip_evaluation--;
+         }
+
+    skip_reduction:
+      /* Check we have a left operand iff we need one.  */
+      if (flags & NO_L_OPERAND)
        {
-         if (top != stack)
-           cpp_error (pfile, "internal error in #if expression");
-         if (stack != init_stack)
-           free (stack);
-         return top->value;
+         if (top->flags & HAVE_VALUE)
+           SYNTAX_ERROR2 ("missing binary operator before '%s'",
+                          op_to_str (op.op, buff));
        }
+      else
+       {
+         if (!(top->flags & HAVE_VALUE))
+           SYNTAX_ERROR2 ("operator '%s' has no left operand",
+                          op_to_str (op.op, buff));
+       }
+
+      /* Check for and handle stack overflow.  */
       top++;
-      
-      /* Check for and handle stack overflow. */
       if (top == limit)
        {
          struct operation *new_stack;
-         int old_size = (char*)limit - (char*)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 operation *) xrealloc (stack, new_size);
          else
            {
-             new_stack = (struct operation*) xmalloc (new_size);
-             bcopy ((char *) stack, (char *) new_stack, old_size);
+             new_stack = (struct operation *) 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 operation *) ((char *) new_stack + old_size);
+         limit = (struct operation *) ((char *) new_stack + new_size);
        }
       
       top->flags = flags;
-      top->rprio = rprio;
+      top->prio = prio & ~EXTRACT_PRIO(RIGHT_ASSOC);
       top->op = op.op;
     }
- syntax_error:
+
+ done:
+  result = (top[1].value != 0);
+  if (top != stack)
+    CPP_ICE ("unbalanced stack in #if expression");
+  else if (!(top[1].flags & HAVE_VALUE))
+    {
+      SYNTAX_ERROR ("#if with no expression");
+    syntax_error:
+      _cpp_skip_rest_of_line (pfile);
+      result = 0;  /* Return 0 on syntax error.  */
+    }
+
+  /* Free dynamic stack if we allocated one.  */
   if (stack != init_stack)
     free (stack);
-  skip_rest_of_line (pfile);
-  return 0;
+  CPP_SET_WRITTEN (pfile, old_written);
+  pfile->only_seen_white = save_only_seen_white;
+  pfile->skipping = save_skipping;
+  return result;
 }