Don't evaluate short-circuited preprocessor expressions

Resubmit with clang build issue fixed. The result of a short-circuited
operation is now either 0 or 1.

ESSL 3.00 spec section 3.4 mentions that the second operand in a logical
&& or || preprocessor operation is evaluated only if the first operand
doesn't short-circuit the expression. The non-evaluated part of a
preprocessor expression may also have undefined identifiers.

Make the expression parser follow the spec by ignoring errors that are
generated inside short-circuited expressions. This includes undefined
identifiers and divide by zero.

BUG=angleproject:347
TEST=dEQP-GLES3.functional.shaders.preprocessor.undefined_identifiers.*
     angle_unittests

Change-Id: I4163f96ec46d40ac859ffb39d91b89490041e44d
Reviewed-on: https://chromium-review.googlesource.com/297252
Tested-by: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
diff --git a/src/compiler/preprocessor/ExpressionParser.y b/src/compiler/preprocessor/ExpressionParser.y
index 0933736..d82c767 100644
--- a/src/compiler/preprocessor/ExpressionParser.y
+++ b/src/compiler/preprocessor/ExpressionParser.y
@@ -64,6 +64,13 @@
     pp::Lexer* lexer;
     pp::Token* token;
     int* result;
+
+    void startIgnoreErrors() { ++ignoreErrors; }
+    void endIgnoreErrors() { --ignoreErrors; }
+
+    bool isIgnoringErrors() { return ignoreErrors > 0; }
+
+    int ignoreErrors;
 };
 }  // namespace
 %}
@@ -79,6 +86,7 @@
 %}
 
 %token TOK_CONST_INT
+%token TOK_IDENTIFIER
 %left TOK_OP_OR
 %left TOK_OP_AND
 %left '|'
@@ -96,17 +104,58 @@
 input
     : expression {
         *(context->result) = static_cast<int>($1);
-        YYACCEPT;
     }
 ;
 
 expression
     : TOK_CONST_INT
-    | expression TOK_OP_OR expression {
-        $$ = $1 || $3;
+    | TOK_IDENTIFIER {
+        if (!context->isIgnoringErrors())
+        {
+            YYABORT;
+        }
     }
-    | expression TOK_OP_AND expression {
-        $$ = $1 && $3;
+    | expression TOK_OP_OR {
+        if ($1 != 0)
+        {
+            // Ignore errors in the short-circuited part of the expression.
+            // ESSL3.00 section 3.4:
+            // If an operand is not evaluated, the presence of undefined identifiers
+            // in the operand will not cause an error.
+            // Unevaluated division by zero should not cause an error either.
+            context->startIgnoreErrors();
+        }
+    } expression {
+        if ($1 != 0)
+        {
+            context->endIgnoreErrors();
+            $$ = static_cast<YYSTYPE>(1);
+        }
+        else
+        {
+            $$ = $1 || $4;
+        }
+    }
+    | expression TOK_OP_AND {
+        if ($1 == 0)
+        {
+            // Ignore errors in the short-circuited part of the expression.
+            // ESSL3.00 section 3.4:
+            // If an operand is not evaluated, the presence of undefined identifiers
+            // in the operand will not cause an error.
+            // Unevaluated division by zero should not cause an error either.
+            context->startIgnoreErrors();
+        }
+    } expression {
+        if ($1 == 0)
+        {
+            context->endIgnoreErrors();
+            $$ = static_cast<YYSTYPE>(0);
+        }
+        else
+        {
+            $$ = $1 && $4;
+        }
     }
     | expression '|' expression {
         $$ = $1 | $3;
@@ -148,28 +197,48 @@
         $$ = $1 + $3;
     }
     | expression '%' expression {
-        if ($3 == 0) {
-            std::ostringstream stream;
-            stream << $1 << " % " << $3;
-            std::string text = stream.str();
-            context->diagnostics->report(pp::Diagnostics::PP_DIVISION_BY_ZERO,
-                                         context->token->location,
-                                         text.c_str());
-            YYABORT;
-        } else {
+        if ($3 == 0)
+        {
+            if (context->isIgnoringErrors())
+            {
+                $$ = static_cast<YYSTYPE>(0);
+            }
+            else
+            {
+                std::ostringstream stream;
+                stream << $1 << " % " << $3;
+                std::string text = stream.str();
+                context->diagnostics->report(pp::Diagnostics::PP_DIVISION_BY_ZERO,
+                                             context->token->location,
+                                             text.c_str());
+                YYABORT;
+            }
+        }
+        else
+        {
             $$ = $1 % $3;
         }
     }
     | expression '/' expression {
-        if ($3 == 0) {
-            std::ostringstream stream;
-            stream << $1 << " / " << $3;
-            std::string text = stream.str();
-            context->diagnostics->report(pp::Diagnostics::PP_DIVISION_BY_ZERO,
-                                         context->token->location,
-                                         text.c_str());
-            YYABORT;
-        } else {
+        if ($3 == 0)
+        {
+            if (context->isIgnoringErrors())
+            {
+                $$ = static_cast<YYSTYPE>(0);
+            }
+            else
+            {
+                std::ostringstream stream;
+                stream << $1 << " / " << $3;
+                std::string text = stream.str();
+                context->diagnostics->report(pp::Diagnostics::PP_DIVISION_BY_ZERO,
+                                            context->token->location,
+                                            text.c_str());
+                YYABORT;
+            }
+        }
+        else
+        {
             $$ = $1 / $3;
         }
     }
@@ -213,6 +282,15 @@
         type = TOK_CONST_INT;
         break;
       }
+      case pp::Token::IDENTIFIER:
+        if (!context->isIgnoringErrors())
+        {
+            context->diagnostics->report(pp::Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN,
+                                         token->location, token->text);
+        }
+        *lvalp = static_cast<YYSTYPE>(-1);
+        type = TOK_IDENTIFIER;
+        break;
       case pp::Token::OP_OR:
         type = TOK_OP_OR;
         break;
@@ -287,6 +365,7 @@
     context.lexer = mLexer;
     context.token = token;
     context.result = result;
+    context.ignoreErrors = 0;
     int ret = yyparse(&context);
     switch (ret)
     {