OSDN Git Service

* doc/fragments.texi, doc/trouble.texi: Remove links to old
[pf3gnuchains/gcc-fork.git] / gcc / cppexp.c
index 7e99292..f7fe054 100644 (file)
@@ -1,5 +1,6 @@
 /* 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
@@ -17,59 +18,15 @@ along with this program; if not, write to the Free Software
 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"
 #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
-
-#ifndef MAX_INT_TYPE_SIZE
-#define MAX_INT_TYPE_SIZE INT_TYPE_SIZE
-#endif
-
-#ifndef MAX_LONG_TYPE_SIZE
-#define MAX_LONG_TYPE_SIZE LONG_TYPE_SIZE
-#endif
-
-#ifndef MAX_WCHAR_TYPE_SIZE
-#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)
 
-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,
@@ -77,56 +34,76 @@ 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));
-static const char * op_to_str PARAMS ((op_t, char *));
-
-#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
-#define FINISHED 311
-
-struct operation
+static struct op parse_number PARAMS ((cpp_reader *, const cpp_token *));
+static struct op parse_defined PARAMS ((cpp_reader *));
+static struct op lex PARAMS ((cpp_reader *, int));
+static const unsigned char *op_as_text PARAMS ((cpp_reader *, enum cpp_ttype));
+
+struct op
 {
-  op_t op;
+  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.  */
 };
 
-/* Parse and convert an integer for #if.  Accepts decimal, hex, or octal
-   with or without size suffixes.  */
+/* 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)
+
+struct suffix
+{
+  const unsigned char s[4];
+  const unsigned char u;
+  const unsigned char l;
+};
+
+static const struct suffix vsuf_1[] = {
+  { "u", 1, 0 }, { "U", 1, 0 },
+  { "l", 0, 1 }, { "L", 0, 1 }
+};
+
+static const struct suffix vsuf_2[] = {
+  { "ul", 1, 1 }, { "UL", 1, 1 }, { "uL", 1, 1 }, { "Ul", 1, 1 },
+  { "lu", 1, 1 }, { "LU", 1, 1 }, { "Lu", 1, 1 }, { "lU", 1, 1 },
+  { "ll", 0, 2 }, { "LL", 0, 2 }
+};
+
+static const struct suffix vsuf_3[] = {
+  { "ull", 1, 2 }, { "ULL", 1, 2 }, { "uLL", 1, 2 }, { "Ull", 1, 2 },
+  { "llu", 1, 2 }, { "LLU", 1, 2 }, { "LLu", 1, 2 }, { "llU", 1, 2 }
+};
+#define Nsuff(tab) (sizeof tab / sizeof (struct suffix))
 
-static struct operation
-parse_number (pfile, start, end)
+/* Parse and convert an integer for #if.  Accepts decimal, hex, or
+   octal with or without size suffixes.  Returned op is CPP_ERROR on
+   error, otherwise it is a CPP_NUMBER.  */
+
+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;
 
@@ -148,49 +125,16 @@ 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++;
-
-      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;
-       }
+      c = *p;
+
+      if (ISDIGIT (c)
+         || (base == 16 && ISXDIGIT (c)))
+       digit = hex_value (c);
       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;
@@ -198,25 +142,46 @@ 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)
-    cpp_pedwarn (pfile,
-                "integer constant contains digits beyond the radix");
+    cpp_pedwarn (pfile, "integer constant contains digits beyond the radix");
 
   if (overflow)
     cpp_pedwarn (pfile, "integer constant out of range");
@@ -225,392 +190,186 @@ 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_NUMBER;
   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_defined (pfile)
      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 paren = 0;
+  cpp_hashnode *node = 0;
+  const cpp_token *token;
+  struct op op;
+  cpp_context *initial_context = pfile->context;
 
-  int c = -1;
+  /* Don't expand macros.  */
+  pfile->state.prevent_expansion++;
 
-  if (*ptr == 'L')
+  token = cpp_get_token (pfile);
+  if (token->type == CPP_OPEN_PAREN)
     {
-      ++ptr;
-      width = MAX_WCHAR_TYPE_SIZE, mask = MAX_WCHAR_TYPE_MASK;
+      paren = 1;
+      token = cpp_get_token (pfile);
     }
-  max_chars = MAX_LONG_TYPE_SIZE / width;
-
-  ++ptr;  /* skip initial quote */
 
-  while (ptr < end)
+  if (token->type == CPP_NAME)
     {
-      c = *ptr++;
-      if (c == '\'')
-       break;
-      else if (c == '\\')
+      node = token->val.node;
+      if (paren && cpp_get_token (pfile)->type != CPP_CLOSE_PAREN)
        {
-         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");
+         cpp_error (pfile, "missing ')' after \"defined\"");
+         node = 0;
        }
-         
-      /* 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)
+  else
     {
-      cpp_error (pfile, "character constant too long");
-      goto error;
+      cpp_error (pfile, "operator \"defined\" requires an identifier");
+      if (token->flags & NAMED_OP)
+       {
+         cpp_token op;
+
+         op.flags = 0;
+         op.type = token->type;
+         cpp_error (pfile,
+                    "(\"%s\" is an alternative token for \"%s\" in C++)",
+                    cpp_token_as_text (pfile, token),
+                    cpp_token_as_text (pfile, &op));
+       }
     }
-  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));
+  if (!node)
+    op.op = CPP_ERROR;
   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 (pfile->context != initial_context)
+       cpp_warning (pfile, "this use of \"defined\" may not be portable");
 
-  if (token != CPP_NAME)
-    goto oops;
-
-  tok = pfile->token_buffer + old_written;
-  len = CPP_PWRITTEN (pfile) - tok;
-  op.value = cpp_defined (pfile, tok, len);
+      op.value = node->type == NT_MACRO;
+      op.unsignedp = 0;
+      op.op = CPP_NUMBER;
 
-  if (paren)
-    {
-      if (_cpp_get_directive_token (pfile) != CPP_CLOSE_PAREN)
-       goto oops;
+      /* A possible controlling macro of the form #if !defined ().
+        _cpp_parse_expr checks there was no other junk on the line.  */
+      pfile->mi_ind_cmacro = node;
     }
-  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;
+  pfile->state.prevent_expansion--;
   return op;
 }
 
-struct token
-{
-  const char *operator;
-  op_t token;
-};
-
-static const struct token tokentab2[] =
-{
-  {"&&", ANDAND},
-  {"||", OROR},
-  {"<<", LSH},
-  {">>", RSH},
-  {"==", EQUAL},
-  {"!=", NOTEQUAL},
-  {"<=", LEQ},
-  {">=", GEQ},
-  {"++", ERROR},
-  {"--", ERROR},
-  {NULL, ERROR}
-};
-
-/* Read one token.  */
+/* Read a token.  The returned type is CPP_NUMBER for a valid number
+   (an interpreted preprocessing number or character constant, or the
+   result of the "defined" or "#" operators), CPP_ERROR on error,
+   CPP_EOF, or the type of an operator token.  */
 
-static struct operation
+static struct op
 lex (pfile, skip_evaluation)
      cpp_reader *pfile;
      int skip_evaluation;
 {
-  const struct token *toktab;
-  enum cpp_ttype 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;
+  const cpp_token *token = cpp_get_token (pfile);
+
+  switch (token->type)
     {
-    case CPP_EOF: /* Should not happen ...  */
-    case CPP_VSPACE:
-      op.op = 0;
-      return op;
     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);
-
-    case CPP_NAME:
-      if (!ustrncmp (tok_start, U"defined", 7))
-       return parse_defined (pfile);
-
-      op.op = INT;
-      op.unsignedp = 0;
-      op.value = 0;
+      {
+       unsigned int chars_seen;
 
-      if (CPP_OPTION (pfile, warn_undef) && !skip_evaluation)
-       cpp_warning (pfile, "'%.*s' is not defined",
-                    (int) (tok_end - tok_start), tok_start);
-      return op;
+       /* This is always a signed type.  */
+       op.unsignedp = 0;
+       op.op = CPP_NUMBER;
+       op.value = cpp_interpret_charconst (pfile, token, 1, 0, &chars_seen);
+       return op;
+      }
 
-    case CPP_ASSERTION:
-      op.op = INT;
-      op.unsignedp = 0;
-      op.value = cpp_defined (pfile, tok_start, tok_end - tok_start);
-      return op;
+    case CPP_STRING:
+    case CPP_WSTRING:
+      SYNTAX_ERROR ("string constants are not valid in #if");
 
     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' is not allowed in #if expressions",
-                      (int) (tok_end - tok_start), tok_start);
-         op.op = toktab->token; 
+      if (ISGRAPH (token->val.c))
+       SYNTAX_ERROR2 ("invalid character '%c' in #if", token->val.c);
+      else
+       SYNTAX_ERROR2 ("invalid character '\\%03o' in #if", token->val.c);
+
+    case CPP_NAME:
+      if (token->val.node == pfile->spec_nodes.n_defined)
+       return parse_defined (pfile);
+      else if (CPP_OPTION (pfile, cplusplus)
+              && (token->val.node == pfile->spec_nodes.n_true
+                  || token->val.node == pfile->spec_nodes.n_false))
+       {
+         op.op = CPP_NUMBER;
+         op.unsignedp = 0;
+         op.value = (token->val.node == pfile->spec_nodes.n_true);
+
+         /* 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",
+                        NODE_NAME (token->val.node));
          return op;
        }
-      /* fall through */
-    default:
-      op.op = *tok_start;
-      return op;
-  }
-}
-
-/* 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
-   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.
+      else
+       {
+         op.op = CPP_NUMBER;
+         op.unsignedp = 0;
+         op.value = 0;
 
-   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.  */
+         if (CPP_OPTION (pfile, warn_undef) && !skip_evaluation)
+           cpp_warning (pfile, "\"%s\" is not defined",
+                        NODE_NAME (token->val.node));
+         return op;
+       }
 
-static HOST_WIDEST_INT
-parse_escape (pfile, string_ptr, result_mask)
-     cpp_reader *pfile;
-     U_CHAR **string_ptr;
-     HOST_WIDEST_INT result_mask;
-{
-  register int c = *(*string_ptr)++;
-  switch (c)
-    {
-    case 'a':
-      return TARGET_BELL;
-    case 'b':
-      return TARGET_BS;
-    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;
-      
-    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;
-       while (++count < 3)
-         {
-           c = *(*string_ptr)++;
-           if (c >= '0' && c <= '7')
-             i = (i << 3) + c - '0';
-           else
-             {
-               (*string_ptr)--;
-               break;
-             }
-         }
-       if (i != (i & result_mask))
-         {
-           i &= result_mask;
-           cpp_pedwarn (pfile, "octal escape sequence out of range");
-         }
-       return i;
-      }
-    case 'x':
+    case CPP_HASH:
       {
-       register unsigned HOST_WIDEST_INT i = 0, overflow = 0;
-       register int digits_found = 0, digit;
-       for (;;)
-         {
-           c = *(*string_ptr)++;
-           if (c >= '0' && c <= '9')
-             digit = c - '0';
-           else if (c >= 'a' && c <= 'f')
-             digit = c - 'a' + 10;
-           else if (c >= 'A' && c <= 'F')
-             digit = c - 'A' + 10;
-           else
-             {
-               (*string_ptr)--;
-               break;
-             }
-           overflow |= i ^ (i << 4 >> 4);
-           i = (i << 4) + digit;
-           digits_found = 1;
-         }
-       if (!digits_found)
-         cpp_error (pfile, "\\x used with no following hex digits");
-       if (overflow | (i != (i & result_mask)))
-         {
-           i &= result_mask;
-           cpp_pedwarn (pfile, "hex escape sequence out of range");
-         }
-       return i;
+       int temp;
+
+       op.op = CPP_NUMBER;
+       if (_cpp_test_assertion (pfile, &temp))
+         op.op = CPP_ERROR;
+       op.unsignedp = 0;
+       op.value = temp;
+       return op;
       }
+
     default:
-      return c;
+      if (((int) token->type > (int) CPP_EQ
+          && (int) token->type < (int) CPP_PLUS_EQ)
+         || token->type == CPP_EOF)
+       {
+         op.op = token->type;
+         return op;
+       }
+
+      SYNTAX_ERROR2 ("\"%s\" is not valid in #if expressions",
+                    cpp_token_as_text (pfile, token));
     }
+
+ syntax_error:
+  op.op = CPP_ERROR;
+  return op;
 }
 
 static void
@@ -693,8 +452,8 @@ 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)
+#define EXTRACT_PRIO(CNST) ((CNST) >> FLAG_BITS)
+#define EXTRACT_FLAGS(CNST) ((CNST) & FLAG_MASK)
 
 /* Flags.  */
 #define HAVE_VALUE     (1 << 0)
@@ -715,12 +474,49 @@ be handled with operator-specific code.  */
 #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 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; \
@@ -730,18 +526,24 @@ be handled with operator-specific code.  */
 #define EQUALITY(OP) \
   top->value = v1 OP v2; \
   top->unsignedp = 0;
-#define LOGICAL(OP) \
+#define BITWISE(OP) \
   top->value = v1 OP v2; \
   top->unsignedp = unsigned1 | unsigned2;
-
-/* 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)
+#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.  */
@@ -761,18 +563,21 @@ _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 + 1;
-  long old_written = CPP_WRITTEN (pfile);
+  struct op init_stack[INIT_STACK_SIZE];
+  struct op *stack = init_stack;
+  struct op *limit = stack + INIT_STACK_SIZE;
+  struct op *top = stack + 1;
   int skip_evaluation = 0;
   int result;
-  char buff[5];
+  unsigned int lex_count, saw_leading_not;
+
+  /* Set up detection of #if ! defined().  */
+  pfile->mi_ind_cmacro = 0;
+  saw_leading_not = 0;
+  lex_count = 0;
 
-  pfile->parsing_if_directive++;
   /* We've finished when we try to reduce this.  */
-  top->op = FINISHED;
+  top->op = CPP_EOF;
   /* Nifty way to catch missing '('.  */
   top->prio = EXTRACT_PRIO(CLOSE_PAREN_PRIO);
   /* Avoid missing right operand checks.  */
@@ -782,31 +587,21 @@ _cpp_parse_expr (pfile)
     {
       unsigned int prio;
       unsigned int flags;
-      struct operation op;
+      struct op op;
 
       /* Read a token */
       op = lex (pfile, skip_evaluation);
+      lex_count++;
 
       /* 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 ("lex returns a NAME");
-       case ERROR:
-         goto syntax_error;
-       case '#':
-         /* We get '#' when get_directive_token hits a syntactically
-            invalid assertion predicate.  _cpp_parse_assertion has
-            already issued an error.  */
+       case CPP_ERROR:
          goto syntax_error;
-       default:
-         SYNTAX_ERROR ("invalid character in #if");
-
        push_immediate:
-       case INT:
-       case CHAR:
+       case CPP_NUMBER:
          /* Push a value onto the stack.  */
          if (top->flags & HAVE_VALUE)
            SYNTAX_ERROR ("missing binary operator");
@@ -815,40 +610,22 @@ _cpp_parse_expr (pfile)
          top->flags |= HAVE_VALUE;
          continue;
 
-       case '+':
-       case '-':    prio = PLUS_PRIO;  if (top->flags & HAVE_VALUE) break;
+       case CPP_EOF:   prio = FORCE_REDUCE_PRIO;       break;
+
+       case CPP_NOT:
+         saw_leading_not = lex_count == 1;
+         prio = op_to_prio[op.op];
+         break;
+       case CPP_PLUS:
+       case CPP_MINUS: prio = PLUS_PRIO;  if (top->flags & HAVE_VALUE) break;
           /* 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;
+       default:        prio = op_to_prio[op.op];       break;
        }
 
       /* Separate the operator's code into priority and flags.  */
       flags = EXTRACT_FLAGS(prio);
       prio = EXTRACT_PRIO(prio);
-      if (op.op == '(')
+      if (prio == EXTRACT_PRIO(OPEN_PAREN_PRIO))
        goto skip_reduction;
 
       /* Check for reductions.  Then push the operator.  */
@@ -861,11 +638,11 @@ _cpp_parse_expr (pfile)
             right operand.  Check this before trying to reduce.  */
          if ((top->flags & (HAVE_VALUE | NO_R_OPERAND)) == 0)
            {
-             if (top->op == '(')
+             if (top->op == CPP_OPEN_PAREN)
                SYNTAX_ERROR ("void expression between '(' and ')'");
              else
                SYNTAX_ERROR2 ("operator '%s' has no right operand",
-                              op_to_str (top->op, buff));
+                              op_as_text (pfile, top->op));
            }
 
          unsigned2 = top->unsignedp, v2 = top->value;
@@ -875,12 +652,40 @@ _cpp_parse_expr (pfile)
          /* 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
                {
@@ -891,15 +696,12 @@ _cpp_parse_expr (pfile)
                    integer_overflow (pfile);
                }
              break;
-           case '-':
+           case CPP_MINUS:
              if (!(top->flags & HAVE_VALUE))
-               { /* Unary '-' */
-                 top->value = - v2;
-                 if (!skip_evaluation && (top->value & v2) < 0
-                     && !unsigned2)
+               {
+                 UNARY(-);
+                 if (!skip_evaluation && (top->value & v2) < 0 && !unsigned2)
                    integer_overflow (pfile);
-                 top->unsignedp = unsigned2;
-                 top->flags |= HAVE_VALUE;
                }
              else
                { /* Binary '-' */
@@ -910,7 +712,7 @@ _cpp_parse_expr (pfile)
                    integer_overflow (pfile);
                }
              break;
-           case '*':
+           case CPP_MULT:
              top->unsignedp = unsigned1 | unsigned2;
              if (top->unsignedp)
                top->value = (unsigned HOST_WIDEST_INT) v1 * v2;
@@ -922,14 +724,14 @@ _cpp_parse_expr (pfile)
                    integer_overflow (pfile);
                }
              break;
-           case '/':
-           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 == '/')
+             if (top[1].op == CPP_DIV)
                {
                  if (top->unsignedp)
                    top->value = (unsigned HOST_WIDEST_INT) v1 / v2;
@@ -948,79 +750,42 @@ _cpp_parse_expr (pfile)
                    top->value = v1 % v2;
                }
              break;
-           case '!':
-             top->value = ! v2;
+
+           case CPP_OR_OR:
+             top->value = v1 || v2;
              top->unsignedp = 0;
-             top->flags |= HAVE_VALUE;
-             break;
-           case '~':
-             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:    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);
-             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);
+             if (v1) skip_evaluation--;
              break;
-           case '&':  LOGICAL(&); break;
-           case '^':  LOGICAL(^);  break;
-           case '|':  LOGICAL(|);  break;
-           case ANDAND:
-             top->value = v1 && v2;  top->unsignedp = 0;
+           case CPP_AND_AND:
+             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");
              top->value = v2;
              top->unsignedp = unsigned2;
              break;
-           case '?':
+           case CPP_QUERY:
              SYNTAX_ERROR ("syntax error '?' without following ':'");
-           case ':':
-             if (top[0].op != '?')
+           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 '(':
-             if (op.op != ')')
+           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:
-             SYNTAX_ERROR2 ("unimplemented operator '%s'",
-                            op_to_str (top[1].op, buff));
-           case FINISHED:
+           case CPP_EOF:
              /* Reducing this dummy operator indicates we've finished.  */
-             if (op.op == ')')
+             if (op.op == CPP_CLOSE_PAREN)
                SYNTAX_ERROR ("missing '(' in expression");
              goto done;
            }
@@ -1030,14 +795,16 @@ _cpp_parse_expr (pfile)
       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 ':':
+         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:
@@ -1046,32 +813,32 @@ _cpp_parse_expr (pfile)
        {
          if (top->flags & HAVE_VALUE)
            SYNTAX_ERROR2 ("missing binary operator before '%s'",
-                          op_to_str (op.op, buff));
+                          op_as_text (pfile, op.op));
        }
       else
        {
          if (!(top->flags & HAVE_VALUE))
            SYNTAX_ERROR2 ("operator '%s' has no left operand",
-                          op_to_str (op.op, buff));
+                          op_as_text (pfile, op.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;
@@ -1080,21 +847,37 @@ _cpp_parse_expr (pfile)
     }
 
  done:
+  /* The controlling macro expression is only valid if we called lex 3
+     times: <!> <defined expression> and <EOF>.  push_conditional ()
+     checks that we are at top-of-file.  */
+  if (pfile->mi_ind_cmacro && !(saw_leading_not && lex_count == 3))
+    pfile->mi_ind_cmacro = 0;
+
   result = (top[1].value != 0);
+
   if (top != stack)
-    CPP_ICE ("unbalanced stack in #if expression");
+    CPP_ICE ("unbalanced stack in #if");
   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);
-  pfile->parsing_if_directive--;
-  CPP_SET_WRITTEN (pfile, old_written);
   return result;
 }
+
+static const unsigned char *
+op_as_text (pfile, op)
+     cpp_reader *pfile;
+     enum cpp_ttype op;
+{
+  cpp_token token;
+
+  token.type = op;
+  token.flags = 0;
+  return cpp_token_as_text (pfile, &token);
+}