Further _cpp_parse_expr() cleanup

Neil Booth neil@daikokuya.demon.co.uk
Sun Apr 28 09:45:00 GMT 2002


This separates out the operator stack reduction into a
separate routine, and removes storage of operator priority
and flags on the operator stack.  These are obtained purely
through the operator itself.

This simplifies the logic considerably, IMO, and allows removal
of a whole bunch of macros.  I spotted a poor diagnostic, that
if gcc-3.x sees

#if (

would complain about a void expression between parentheses, so
I corrected this and added a testcase.

Neil.

	* cppexp.c (lex): Update to use state.skip_eval.
	(struct op): Remove prio and flags members.
	(FLAG_BITS, FLAG_MASK, PRIO_SHIFT, EXTRACT_PRIO, EXTRACT_FLAGS,
	SHORT_CIRCUIT, RIGHT_ASSOC, ..._PRIO, op_to_prio): Remove.
	(LEFT_ASSOC): New macro.
	(optab): New table of operator priorities and flags.
	(SHIFT): Update.
	(_cpp_parse_expr): Clean up logic.  Return bool.  Use a
	malloc-ed parser stack.
	(reduce): New; reduce the operator stack.
	(_cpp_expand_op_stack): Expand the operator stack as necessary.
	* cpphash.h (struct op): Predeclare.
	(struct cpp_reader): New members op_stack, op_limit.
	(struct lexer_state): New member skip_eval.
	(_cpp_parse_expr): Update.
	(_cpp_expand_op_stack): New.
	* cpplib.c (do_if): Update.
	* cppinit.c (cpp_create_reader): Create op stack.
	(cpp_destroy): And destroy it.
	* cpplib.h (CPP_LAST_CPP_OP): Correct.
	(TTYPE_TABLE): Correct.
testsuite:
	* gcc.dg/cpp/if-mop.c: Update.
	* gcc.dg/cpp/if-mpar.c: Add test.
	* gcc.dg/cpp/if-oppr.c: Update.

============================================================
Index: gcc/cppexp.c
--- gcc/cppexp.c	26 Apr 2002 06:32:49 -0000	1.109
+++ gcc/cppexp.c	28 Apr 2002 16:31:31 -0000
@@ -36,14 +36,13 @@ static HOST_WIDEST_INT right_shift PARAM
 					    unsigned HOST_WIDEST_INT));
 static struct op parse_number PARAMS ((cpp_reader *, const cpp_token *));
 static struct op parse_defined PARAMS ((cpp_reader *));
-static struct op lex PARAMS ((cpp_reader *, int));
+static struct op lex PARAMS ((cpp_reader *));
 static const unsigned char *op_as_text PARAMS ((cpp_reader *, enum cpp_ttype));
+static struct op *reduce PARAMS ((cpp_reader *, struct op *, enum cpp_ttype));
 
 struct op
 {
   enum cpp_ttype op;
-  uchar prio;         /* Priority of op.  */
-  uchar flags;
   uchar unsignedp;    /* True if value should be treated as unsigned.  */
   HOST_WIDEST_INT value; /* The value logically "right" of op.  */
 };
@@ -281,9 +280,8 @@ parse_defined (pfile)
    result of the "defined" or "#" operators), CPP_ERROR on error,
    CPP_EOF, or the type of an operator token.  */
 static struct op
-lex (pfile, skip_evaluation)
+lex (pfile)
      cpp_reader *pfile;
-     int skip_evaluation;
 {
   struct op op;
   const cpp_token *token = cpp_get_token (pfile);
@@ -343,7 +341,7 @@ lex (pfile, skip_evaluation)
 	  op.unsignedp = 0;
 	  op.value = 0;
 
-	  if (CPP_OPTION (pfile, warn_undef) && !skip_evaluation)
+	  if (CPP_OPTION (pfile, warn_undef) && !pfile->state.skip_eval)
 	    cpp_error (pfile, DL_WARNING, "\"%s\" is not defined",
 		       NODE_NAME (token->val.node));
 	  return op;
@@ -363,8 +361,7 @@ lex (pfile, skip_evaluation)
 
     default:
       if (((int) token->type > (int) CPP_EQ
-	   && (int) token->type < (int) CPP_PLUS_EQ)
-	  || token->type == CPP_EOF)
+	   && (int) token->type < (int) CPP_PLUS_EQ))
 	{
 	  op.op = token->type;
 	  return op;
@@ -435,100 +432,69 @@ right_shift (pfile, a, unsignedp, b)
 /* 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.
+than the operator on the top of the stack, we reduce the stack by one
+operator and repeat the test.  Since equal priorities do not reduce,
+this is naturally right-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.
+We handle left-associative operators by decrementing the priority of
+just-lexed operators by one, but retaining the priority of operators
+already on the stack.
 
 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.
+parenthesised expression.  If there is a matching '(', the routine
+reduce() exits immediately.  If the normal exit route sees a ')', then
+there cannot have been a matching '(' and an error message is output.
 
 The parser assumes all shifted operators require a left operand unless
 the flag NO_L_OPERAND is set.  These semantics are automatic; 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 NO_L_OPERAND   (1 << 0)
-#define SHORT_CIRCUIT  (1 << 1)
-
-/* Priority and flag combinations.  */
-#define RIGHT_ASSOC         (1 << FLAG_BITS)
-#define FORCE_REDUCE_PRIO   (0 << PRIO_SHIFT)
-#define CLOSE_PAREN_PRIO    (1 << PRIO_SHIFT)
-#define OPEN_PAREN_PRIO    ((2 << PRIO_SHIFT) | NO_L_OPERAND)
-#define COMMA_PRIO          (3 << PRIO_SHIFT)
-#define COND_PRIO          ((4 << PRIO_SHIFT) | RIGHT_ASSOC | SHORT_CIRCUIT)
-#define COLON_PRIO         ((5 << PRIO_SHIFT) | SHORT_CIRCUIT)
-#define OROR_PRIO          ((6 << PRIO_SHIFT) | SHORT_CIRCUIT)
-#define ANDAND_PRIO        ((7 << PRIO_SHIFT) | SHORT_CIRCUIT)
-#define OR_PRIO             (8 << PRIO_SHIFT)
-#define XOR_PRIO            (9 << PRIO_SHIFT)
-#define AND_PRIO           (10 << PRIO_SHIFT)
-#define MINMAX_PRIO	   (11 << PRIO_SHIFT)
-#define EQUAL_PRIO         (12 << PRIO_SHIFT)
-#define LESS_PRIO          (13 << PRIO_SHIFT)
-#define SHIFT_PRIO         (14 << PRIO_SHIFT)
-#define PLUS_PRIO          (15 << PRIO_SHIFT)
-#define MUL_PRIO           (16 << PRIO_SHIFT)
-#define UNARY_PRIO        ((17 << PRIO_SHIFT) | RIGHT_ASSOC | NO_L_OPERAND)
+#define NO_L_OPERAND	(1 << 0)
+#define LEFT_ASSOC	(1 << 1)
 
 /* 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[] =
+static const struct operator
 {
-  /* EQ */		0,		/* dummy entry - can't happen */
-  /* NOT */		UNARY_PRIO,
-  /* GREATER */		LESS_PRIO,
-  /* LESS */		LESS_PRIO,
-  /* PLUS */		PLUS_PRIO,
-  /* MINUS */		PLUS_PRIO,
-  /* 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 */
+  uchar prio;			/* Priorities are even.  */
+  uchar flags;
+} optab[] =
+{
+  /* EQ */		{0, 0},		/* Shouldn't happen.  */
+  /* NOT */		{16, NO_L_OPERAND},
+  /* GREATER */		{12, LEFT_ASSOC},
+  /* LESS */		{12, LEFT_ASSOC},
+  /* PLUS */		{14, LEFT_ASSOC},
+  /* MINUS */		{14, LEFT_ASSOC},
+  /* MULT */		{15, LEFT_ASSOC},
+  /* DIV */		{15, LEFT_ASSOC},
+  /* MOD */		{15, LEFT_ASSOC},
+  /* AND */		{9, LEFT_ASSOC},
+  /* OR */		{7, LEFT_ASSOC},
+  /* XOR */		{8, LEFT_ASSOC},
+  /* RSHIFT */		{13, LEFT_ASSOC},
+  /* LSHIFT */		{13, LEFT_ASSOC},
+  /* MIN */		{10, LEFT_ASSOC},	/* C++ specific */
+  /* MAX */		{10, LEFT_ASSOC},	/* 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,
-  /* EOF */		FORCE_REDUCE_PRIO,
-  /* UPLUS */		UNARY_PRIO,
-  /* UMINUS */		UNARY_PRIO
+  /* COMPL */		{16, NO_L_OPERAND},
+  /* AND_AND */		{6, LEFT_ASSOC},
+  /* OR_OR */		{5, LEFT_ASSOC},
+  /* QUERY */		{3, 0},
+  /* COLON */		{4, LEFT_ASSOC},
+  /* COMMA */		{2, LEFT_ASSOC},
+  /* OPEN_PAREN */	{1, NO_L_OPERAND},
+  /* CLOSE_PAREN */	{0, 0},
+  /* EOF */		{0, 0},
+  /* EQ_EQ */		{11, LEFT_ASSOC},
+  /* NOT_EQ */		{11, LEFT_ASSOC},
+  /* GREATER_EQ */	{12, LEFT_ASSOC},
+  /* LESS_EQ */		{12, LEFT_ASSOC},
+  /* UPLUS */		{16, NO_L_OPERAND},
+  /* UMINUS */		{16, NO_L_OPERAND}
 };
 
 #define COMPARE(OP) \
@@ -549,7 +515,7 @@ op_to_prio[] =
   top->value = OP v2; \
   top->unsignedp = unsigned2;
 #define SHIFT(PSH, MSH) \
-  if (skip_evaluation)  \
+  if (pfile->state.skip_eval)  \
     break;		\
   top->unsignedp = unsigned1; \
   if (v2 < 0 && ! unsigned2)  \
@@ -558,48 +524,40 @@ op_to_prio[] =
     top->value = PSH (pfile, v1, unsigned1, v2);
 
 /* Parse and evaluate a C expression, reading from PFILE.
-   Returns the truth value of the expression.  */
-int
+   Returns the truth value of the expression.  
+
+   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 op_stack, and the current stack pointer is 'top'.
+   There is a stack element for each operator (only), and the most
+   recently pushed operator is 'top->op'.  An operand (value) is
+   stored in the 'value' field of the stack element of the operator
+   that precedes it.  */
+bool
 _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 stack base is 'stack', and the current stack pointer is 'top'.
-     There is a stack element for each operator (only),
-     and the most recently pushed operator is 'top->op'.
-     An operand (value) is stored in the 'value' field of the stack
-     element of the operator that precedes it.  */
+  struct op *top = pfile->op_stack;
+  unsigned int lex_count;
+  bool saw_leading_not, want_value = true;
 
-#define INIT_STACK_SIZE 20
-  struct op init_stack[INIT_STACK_SIZE];
-  struct op *stack = init_stack;
-  struct op *limit = stack + INIT_STACK_SIZE;
-  struct op *top = stack + 1;
-  int skip_evaluation = 0;
-  int result;
-  unsigned int lex_count, saw_leading_not;
-  bool want_value = true;
+  pfile->state.skip_eval = 0;
 
   /* Set up detection of #if ! defined().  */
   pfile->mi_ind_cmacro = 0;
-  saw_leading_not = 0;
+  saw_leading_not = false;
   lex_count = 0;
 
-  /* We've finished when we try to reduce this.  */
+  /* Lowest priority operator prevents further reductions.  */
   top->op = CPP_EOF;
-  /* Nifty way to catch missing '('.  */
-  top->prio = EXTRACT_PRIO(CLOSE_PAREN_PRIO);
 
   for (;;)
     {
-      unsigned int prio;
-      unsigned int flags;
       struct op op;
 
       /* Read a token */
-      op = lex (pfile, skip_evaluation);
+      op = lex (pfile);
       lex_count++;
 
       /* If the token is an operand, push its value and get next
@@ -614,7 +572,6 @@ _cpp_parse_expr (pfile)
 	  /* Push a value onto the stack.  */
 	  if (!want_value)
 	    SYNTAX_ERROR ("missing binary operator");
-	push_immediate:
 	  want_value = false;
 	  top->value = op.value;
 	  top->unsignedp = op.unsignedp;
@@ -635,244 +592,266 @@ _cpp_parse_expr (pfile)
 	  break;
 	}
 
-      flags = EXTRACT_FLAGS (op_to_prio[op.op]);
-      prio = EXTRACT_PRIO (op_to_prio[op.op]);
-      if (prio == EXTRACT_PRIO (OPEN_PAREN_PRIO))
-	goto skip_reduction;
-
-      /* Check for reductions.  Then push the operator.  */
-      while (prio <= top->prio)
+      /* Check we have a value or operator as appropriate.  */
+      if (optab[op.op].flags & NO_L_OPERAND)
 	{
-	  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 (want_value)
+	  if (!want_value)
+	    SYNTAX_ERROR2 ("missing binary operator before '%s'",
+			   op_as_text (pfile, op.op));
+	}
+      else if (want_value)
+	{
+	  if (op.op == CPP_CLOSE_PAREN)
 	    {
 	      if (top->op == CPP_OPEN_PAREN)
 		SYNTAX_ERROR ("void expression between '(' and ')'");
-	      else if (top->op != CPP_EOF)
-		SYNTAX_ERROR2 ("operator '%s' has no right operand",
-			       op_as_text (pfile, top->op));
-	      else if (op.op != CPP_CLOSE_PAREN)
-		SYNTAX_ERROR ("#if with no expression");
-	    }
-
-	  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)
-	    {
-	    default:
-	      cpp_error (pfile, DL_ICE, "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_UPLUS:
-	      /* 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;
-	      if (CPP_WTRADITIONAL (pfile))
-		cpp_error (pfile, DL_WARNING,
-			   "traditional C rejects the unary plus operator");
-	      break;
-	    case CPP_UMINUS:
-	      UNARY(-);
-	      if (!skip_evaluation && (top->value & v2) < 0 && !unsigned2)
-		integer_overflow (pfile);
-	      break;
-
-	    case CPP_PLUS:
-	      top->value = v1 + v2;
-	      top->unsignedp = unsigned1 | unsigned2;
-	      if (! top->unsignedp && ! skip_evaluation
-		  && ! possible_sum_sign (v1, v2, top->value))
-		integer_overflow (pfile);
-	      break;
-	    case CPP_MINUS:
-	      top->value = v1 - v2;
-	      top->unsignedp = unsigned1 | unsigned2;
-	      if (! top->unsignedp && ! skip_evaluation
-		  && ! possible_sum_sign (top->value, v2, v1))
-		integer_overflow (pfile);
-	      break;
-	    case CPP_MULT:
-	      top->unsignedp = unsigned1 | unsigned2;
-	      if (top->unsignedp)
-		top->value = (unsigned HOST_WIDEST_INT) v1 * v2;
-	      else if (!skip_evaluation)
-		{
-		  top->value = v1 * v2;
-		  if (v1 && (top->value / v1 != v2
-		             || (top->value & v1 & v2) < 0))
-		    integer_overflow (pfile);
-		}
-	      break;
-	    case CPP_DIV:
-	    case CPP_MOD:
-	      if (skip_evaluation)
-		break;
-	      if (v2 == 0)
-		SYNTAX_ERROR ("division by zero in #if");
-	      top->unsignedp = unsigned1 | unsigned2;
-	      if (top[1].op == CPP_DIV)
-		{
-		  if (top->unsignedp)
-		    top->value = (unsigned HOST_WIDEST_INT) v1 / v2;
-		  else
-		    {
-		      top->value = v1 / v2;
-		      if ((top->value & v1 & v2) < 0)
-			integer_overflow (pfile);
-		    }
-		}
-	      else
-		{
-		  if (top->unsignedp)
-		    top->value = (unsigned HOST_WIDEST_INT) v1 % v2;
-		  else
-		    top->value = v1 % v2;
-		}
-	      break;
-
-	    case CPP_OR_OR:
-	      top->value = v1 || v2;
-	      top->unsignedp = 0;
-	      if (v1) skip_evaluation--;
-	      break;
-	    case CPP_AND_AND:
-	      top->value = v1 && v2;
-	      top->unsignedp = 0;
-	      if (!v1) skip_evaluation--;
-	      break;
-	    case CPP_COMMA:
-	      if (CPP_PEDANTIC (pfile))
-		cpp_error (pfile, DL_PEDWARN,
-			   "comma operator in operand of #if");
-	      top->value = v2;
-	      top->unsignedp = unsigned2;
-	      break;
-	    case CPP_QUERY:
-	      SYNTAX_ERROR ("syntax error '?' without following ':'");
-	    case CPP_COLON:
-	      if (top[0].op != CPP_QUERY)
-		SYNTAX_ERROR ("syntax error ':' without preceding '?'");
-	      top--;
-	      if (top->value) skip_evaluation--;
-	      top->value = top->value ? v1 : v2;
-	      top->unsignedp = unsigned1 | unsigned2;
-	      break;
-	    case CPP_OPEN_PAREN:
-	      if (op.op != CPP_CLOSE_PAREN)
-		SYNTAX_ERROR ("missing ')' in expression");
-	      op.value = v2;
-	      op.unsignedp = unsigned2;
-	      goto push_immediate;
-	    case CPP_EOF:
-	      /* Reducing this dummy operator indicates we've finished.  */
-	      if (op.op == CPP_CLOSE_PAREN)
-		SYNTAX_ERROR ("missing '(' in expression");
-	      goto done;
 	    }
+	  else if (top->op == CPP_EOF)
+	    SYNTAX_ERROR ("#if with no expression");
+	  if (top->op != CPP_EOF && top->op != CPP_OPEN_PAREN)
+	    SYNTAX_ERROR2 ("operator '%s' has no right operand",
+			   op_as_text (pfile, top->op));
 	}
 
-      /* Handle short-circuit evaluations.  */
-      if (flags & SHORT_CIRCUIT)
-	switch (op.op)
-	  {
-	  case CPP_OR_OR:    if (top->value) skip_evaluation++; break;
-	  case CPP_AND_AND:
-	  case CPP_QUERY:    if (!top->value) skip_evaluation++; break;
-	  case CPP_COLON:
-	    if (top[-1].value) /* Was '?' condition true?  */
-	      skip_evaluation++;
-	    else
-	      skip_evaluation--;
-	  default:
-	    break;
-	  }
+      top = reduce (pfile, top, op.op);
+      if (!top)
+	goto syntax_error;
 
-    skip_reduction:
-      /* Check we have a left operand iff we need one.  */
-      if (flags & NO_L_OPERAND)
-	{
-	  if (!want_value)
-	    SYNTAX_ERROR2 ("missing binary operator before '%s'",
-			   op_as_text (pfile, op.op));
-	}
-      else
+      switch (op.op)
 	{
-	  if (want_value)
-	    SYNTAX_ERROR2 ("operator '%s' has no left operand",
-			   op_as_text (pfile, op.op));
+	case CPP_CLOSE_PAREN:
+	  continue;
+	case CPP_EOF:
+	  goto done;
+	case CPP_OR_OR:
+	  if (top->value)
+	    pfile->state.skip_eval++;
+	  break;
+	case CPP_AND_AND:
+	case CPP_QUERY:
+	  if (!top->value)
+	    pfile->state.skip_eval++;
+	  break;
+	case CPP_COLON:
+	  if (top[-1].value) /* Was '?' condition true?  */
+	    pfile->state.skip_eval++;
+	  else
+	    pfile->state.skip_eval--;
+	default:
+	  break;
 	}
+
       want_value = true;
 
       /* Check for and handle stack overflow.  */
-      top++;
-      if (top == limit)
-	{
-	  struct op *new_stack;
-	  int old_size = (char *) limit - (char *) stack;
-	  int new_size = 2 * old_size;
-	  if (stack != init_stack)
-	    new_stack = (struct op *) xrealloc (stack, new_size);
-	  else
-	    {
-	      new_stack = (struct op *) xmalloc (new_size);
-	      memcpy (new_stack, stack, old_size);
-	    }
-	  stack = new_stack;
-	  top = (struct op *) ((char *) new_stack + old_size);
-	  limit = (struct op *) ((char *) new_stack + new_size);
-	}
+      if (++top == pfile->op_limit)
+	top = _cpp_expand_op_stack (pfile);
       
-      top->flags = flags;
-      top->prio = prio & ~EXTRACT_PRIO(RIGHT_ASSOC);
       top->op = op.op;
     }
 
- done:
+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)
+  if (top != pfile->op_stack)
     {
       cpp_error (pfile, DL_ICE, "unbalanced stack in #if");
     syntax_error:
-      result = 0;  /* Return 0 on syntax error.  */
+      return false;  /* Return false on syntax error.  */
     }
 
-  /* Free dynamic stack if we allocated one.  */
-  if (stack != init_stack)
-    free (stack);
-  return result;
+  return top->value != 0;
+}
+
+/* Reduce the operator / value stack if possible, in preparation for
+   pushing operator OP.  Returns NULL on error, otherwise the top of
+   the stack.  */
+static struct op *
+reduce (pfile, top, op)
+     cpp_reader *pfile;
+     struct op *top;
+     enum cpp_ttype op;
+{
+  unsigned int prio;
+
+  if (op == CPP_OPEN_PAREN)
+    return top;
+
+  /* Decrement the priority of left-associative operators to force a
+     reduction with operators of otherwise equal priority.  */
+  prio = optab[op].prio - ((optab[op].flags & LEFT_ASSOC) != 0);
+  while (prio < optab[top->op].prio)
+    {
+      HOST_WIDEST_INT v1, v2;
+      unsigned int unsigned1, unsigned2;
+
+      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)
+	{
+	default:
+	  cpp_error (pfile, DL_ICE, "impossible operator '%s'",
+		     op_as_text (pfile, top[1].op));
+	  return 0;
+
+	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_UPLUS:
+	  /* 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;
+	  if (CPP_WTRADITIONAL (pfile))
+	    cpp_error (pfile, DL_WARNING,
+		       "traditional C rejects the unary plus operator");
+	  break;
+	case CPP_UMINUS:
+	  UNARY(-);
+	  if (!pfile->state.skip_eval && (top->value & v2) < 0 && !unsigned2)
+	    integer_overflow (pfile);
+	  break;
+
+	case CPP_PLUS:
+	  top->value = v1 + v2;
+	  top->unsignedp = unsigned1 | unsigned2;
+	  if (! top->unsignedp && ! pfile->state.skip_eval
+	      && ! possible_sum_sign (v1, v2, top->value))
+	    integer_overflow (pfile);
+	  break;
+	case CPP_MINUS:
+	  top->value = v1 - v2;
+	  top->unsignedp = unsigned1 | unsigned2;
+	  if (! top->unsignedp && ! pfile->state.skip_eval
+	      && ! possible_sum_sign (top->value, v2, v1))
+	    integer_overflow (pfile);
+	  break;
+	case CPP_MULT:
+	  top->unsignedp = unsigned1 | unsigned2;
+	  if (top->unsignedp)
+	    top->value = (unsigned HOST_WIDEST_INT) v1 * v2;
+	  else if (!pfile->state.skip_eval)
+	    {
+	      top->value = v1 * v2;
+	      if (v1 && (top->value / v1 != v2
+			 || (top->value & v1 & v2) < 0))
+		integer_overflow (pfile);
+	    }
+	  break;
+	case CPP_DIV:
+	case CPP_MOD:
+	  if (pfile->state.skip_eval)
+	    break;
+	  if (v2 == 0)
+	    {
+	      cpp_error (pfile, DL_ERROR, "division by zero in #if");
+	      return 0;
+	    }
+	  top->unsignedp = unsigned1 | unsigned2;
+	  if (top[1].op == CPP_DIV)
+	    {
+	      if (top->unsignedp)
+		top->value = (unsigned HOST_WIDEST_INT) v1 / v2;
+	      else
+		{
+		  top->value = v1 / v2;
+		  if ((top->value & v1 & v2) < 0)
+		    integer_overflow (pfile);
+		}
+	    }
+	  else
+	    {
+	      if (top->unsignedp)
+		top->value = (unsigned HOST_WIDEST_INT) v1 % v2;
+	      else
+		top->value = v1 % v2;
+	    }
+	  break;
+
+	case CPP_OR_OR:
+	  top->value = v1 || v2;
+	  top->unsignedp = 0;
+	  if (v1) pfile->state.skip_eval--;
+	  break;
+	case CPP_AND_AND:
+	  top->value = v1 && v2;
+	  top->unsignedp = 0;
+	  if (!v1) pfile->state.skip_eval--;
+	  break;
+	case CPP_COMMA:
+	  if (CPP_PEDANTIC (pfile))
+	    cpp_error (pfile, DL_PEDWARN,
+		       "comma operator in operand of #if");
+	  top->value = v2;
+	  top->unsignedp = unsigned2;
+	  break;
+	case CPP_QUERY:
+	  cpp_error (pfile, DL_ERROR, "'?' without following ':'");
+	  return 0;
+	case CPP_COLON:
+	  if (top->op != CPP_QUERY)
+	    {
+	      cpp_error (pfile, DL_ERROR, " ':' without preceding '?'");
+	      return 0;
+	    }
+	  top--;
+	  if (top->value) pfile->state.skip_eval--;
+	  top->value = top->value ? v1 : v2;
+	  top->unsignedp = unsigned1 | unsigned2;
+	  break;
+	case CPP_OPEN_PAREN:
+	  if (op != CPP_CLOSE_PAREN)
+	    {
+	      cpp_error (pfile, DL_ERROR, "missing ')' in expression");
+	      return 0;
+	    }
+	  top->value = v2;
+	  top->unsignedp = unsigned2;
+	  return top;
+	}
+    }
+
+  if (op == CPP_CLOSE_PAREN)
+    {
+      cpp_error (pfile, DL_ERROR, "missing '(' in expression");
+      return 0;
+    }
+
+  return top;
+}
+
+/* Returns the position of the old top of stack after expansion.  */
+struct op *
+_cpp_expand_op_stack (pfile)
+     cpp_reader *pfile;
+{
+  size_t n = (size_t) (pfile->op_limit - pfile->op_stack);
+
+  pfile->op_stack = (struct op *) xrealloc (pfile->op_stack,
+					    (n * 2 + 20) * sizeof (struct op));
+
+  return pfile->op_stack + n;
 }
 
 /* Output OP as text for diagnostics.  */
============================================================
Index: gcc/cpphash.h
--- gcc/cpphash.h	22 Apr 2002 17:48:02 -0000	1.145
+++ gcc/cpphash.h	28 Apr 2002 16:31:36 -0000
@@ -27,6 +27,7 @@ Foundation, 59 Temple Place - Suite 330,
 
 struct directive;		/* Deliberately incomplete.  */
 struct pending_option;
+struct op;
 
 /* Test if a sign is valid within a preprocessing number.  */
 #define VALID_SIGN(c, prevc) \
@@ -153,6 +154,9 @@ struct lexer_state
 
   /* Nonzero when parsing arguments to a function-like macro.  */
   unsigned char parsing_args;
+
+  /* Nonzero to skip evaluating part of an expression.  */
+  unsigned int skip_eval;
 };
 
 /* Special nodes - identifiers with predefined significance.  */
@@ -312,6 +316,9 @@ struct cpp_reader
   /* Identifier hash table.  */ 
   struct ht *hash_table;
 
+  /* Expression parser stack.  */
+  struct op *op_stack, *op_limit;
+
   /* User visible options.  */
   struct cpp_options opts;
 
@@ -391,7 +398,8 @@ extern void _cpp_pop_file_buffer	PARAMS 
 						 struct include_file *));
 
 /* In cppexp.c */
-extern int _cpp_parse_expr		PARAMS ((cpp_reader *));
+extern bool _cpp_parse_expr		PARAMS ((cpp_reader *));
+extern struct op *_cpp_expand_op_stack	PARAMS ((cpp_reader *));
 
 /* In cpplex.c */
 extern cpp_token *_cpp_temp_token	PARAMS ((cpp_reader *));
============================================================
Index: gcc/cpplib.c
--- gcc/cpplib.c	22 Apr 2002 17:48:01 -0000	1.298
+++ gcc/cpplib.c	28 Apr 2002 16:31:41 -0000
@@ -1366,7 +1366,7 @@ do_if (pfile)
   int skip = 1;
 
   if (! pfile->state.skipping)
-    skip = _cpp_parse_expr (pfile) == 0;
+    skip = _cpp_parse_expr (pfile) == false;
 
   push_conditional (pfile, skip, T_IF, pfile->mi_ind_cmacro);
 }
============================================================
Index: gcc/cppinit.c
--- gcc/cppinit.c	22 Apr 2002 17:48:01 -0000	1.213
+++ gcc/cppinit.c	28 Apr 2002 16:31:48 -0000
@@ -535,6 +535,9 @@ cpp_create_reader (lang)
   pfile->a_buff = _cpp_get_buff (pfile, 0);
   pfile->u_buff = _cpp_get_buff (pfile, 0);
 
+  /* The expression parser stack.  */
+  _cpp_expand_op_stack (pfile);
+
   /* Initialise the buffer obstack.  */
   gcc_obstack_init (&pfile->buffer_ob);
 
@@ -556,6 +559,7 @@ cpp_destroy (pfile)
 
   free_chain (CPP_OPTION (pfile, pending)->include_head);
   free (CPP_OPTION (pfile, pending));
+  free (pfile->op_stack);
 
   while (CPP_BUFFER (pfile) != NULL)
     _cpp_pop_buffer (pfile);
============================================================
Index: gcc/cpplib.h
--- gcc/cpplib.h	26 Apr 2002 06:32:50 -0000	1.209
+++ gcc/cpplib.h	28 Apr 2002 16:31:50 -0000
@@ -58,7 +58,7 @@ struct file_name_map_list;
 #define CPP_LAST_EQ CPP_MAX
 #define CPP_FIRST_DIGRAPH CPP_HASH
 #define CPP_LAST_PUNCTUATOR CPP_DOT_STAR
-#define CPP_LAST_CPP_OP CPP_EOF
+#define CPP_LAST_CPP_OP CPP_LESS_EQ
 
 #define TTYPE_TABLE				\
   OP(CPP_EQ = 0,	"=")			\
@@ -86,13 +86,13 @@ struct file_name_map_list;
   OP(CPP_COMMA,		",")	/* grouping */	\
   OP(CPP_OPEN_PAREN,	"(")			\
   OP(CPP_CLOSE_PAREN,	")")			\
+  TK(CPP_EOF,		SPELL_NONE)		\
   OP(CPP_EQ_EQ,		"==")	/* compare */	\
   OP(CPP_NOT_EQ,	"!=")			\
   OP(CPP_GREATER_EQ,	">=")			\
   OP(CPP_LESS_EQ,	"<=")			\
 \
-  /* These 3 are special in preprocessor expressions.  */ \
-  TK(CPP_EOF,		SPELL_NONE)		\
+  /* These two are unary + / - in preprocessor expressions.  */ \
   OP(CPP_PLUS_EQ,	"+=")	/* math */	\
   OP(CPP_MINUS_EQ,	"-=")			\
 \
============================================================
Index: gcc/testsuite/gcc.dg/cpp/if-mop.c
--- gcc/testsuite/gcc.dg/cpp/if-mop.c	28 Oct 2000 18:01:40 -0000	1.2
+++ gcc/testsuite/gcc.dg/cpp/if-mop.c	28 Apr 2002 16:31:50 -0000
@@ -12,7 +12,7 @@
 #if ~		/* { dg-error "no right op" "no unary operand" } */
 #endif
 
-#if 3 + * 6 + 4  /* { dg-error "no left op" "no left operand" } */
+#if 3 + * 6 + 4  /* { dg-error "no right op" "no left operand" } */
 #endif
 
 #if 2 ~2	/* { dg-error "missing bin" "no binary operator" } */
============================================================
Index: gcc/testsuite/gcc.dg/cpp/if-mpar.c
--- gcc/testsuite/gcc.dg/cpp/if-mpar.c	27 Jun 2000 22:26:11 -0000	1.1
+++ gcc/testsuite/gcc.dg/cpp/if-mpar.c	28 Apr 2002 16:31:50 -0000
@@ -19,3 +19,6 @@
 
 #if 4)		/* { dg-error "missing '\\('" "missing '(' no. 3" } */
 #endif
+
+#if (		/* { dg-error "missing '\\)'" "missing '(' no. 3" } */
+#endif
============================================================
Index: gcc/testsuite/gcc.dg/cpp/if-oppr.c
--- gcc/testsuite/gcc.dg/cpp/if-oppr.c	27 Jun 2000 22:26:11 -0000	1.1
+++ gcc/testsuite/gcc.dg/cpp/if-oppr.c	28 Apr 2002 16:31:50 -0000
@@ -20,7 +20,7 @@
 #endif
 
 /* , not higher than ?.  This is not a syntax error if it is.  */
-#if 1 ? 0, 1: 1	/* { dg-error "syntax" "? higher precedence than ," } */
+#if 1 ? 0, 1: 1	/* { dg-error "without" "? higher precedence than ," } */
 #error
 #endif
 



More information about the Gcc-patches mailing list