Add IntermNodePatternMatcher helper class

This will enable sharing code between different AST traversers that
apply transformations on similar node structures. This will make the
code more maintainable.

For now the helper class is used in UnfoldShortCircuitToIf and
SeparateExpressionsReturningArrays.

BUG=angleproject:1341
TEST=angle_end2end_tests, WebGL 2 conformance tests

Change-Id: Ib1e0d5a84fd05bcca983b34f18d47c53e86dc227
Reviewed-on: https://chromium-review.googlesource.com/361693
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
diff --git a/src/compiler/translator/IntermNodePatternMatcher.cpp b/src/compiler/translator/IntermNodePatternMatcher.cpp
new file mode 100644
index 0000000..a0cddac
--- /dev/null
+++ b/src/compiler/translator/IntermNodePatternMatcher.cpp
@@ -0,0 +1,84 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// IntermNodePatternMatcher is a helper class for matching node trees to given patterns.
+// It can be used whenever the same checks for certain node structures are common to multiple AST
+// traversers.
+//
+
+#include "compiler/translator/IntermNodePatternMatcher.h"
+
+#include "compiler/translator/IntermNode.h"
+
+namespace
+{
+
+bool IsNodeBlock(TIntermNode *node)
+{
+    ASSERT(node != nullptr);
+    return (node->getAsAggregate() && node->getAsAggregate()->getOp() == EOpSequence);
+}
+
+}  // anonymous namespace
+
+IntermNodePatternMatcher::IntermNodePatternMatcher(const unsigned int mask) : mMask(mask)
+{
+}
+
+bool IntermNodePatternMatcher::match(TIntermBinary *node, TIntermNode *parentNode)
+{
+    if ((mMask & kExpressionReturningArray) != 0)
+    {
+        if (node->isArray() && node->getOp() == EOpAssign && parentNode != nullptr &&
+            !IsNodeBlock(parentNode))
+        {
+            return true;
+        }
+    }
+
+    if ((mMask & kUnfoldedShortCircuitExpression) != 0)
+    {
+        if (node->getRight()->hasSideEffects() &&
+            (node->getOp() == EOpLogicalOr || node->getOp() == EOpLogicalAnd))
+        {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool IntermNodePatternMatcher::match(TIntermAggregate *node, TIntermNode *parentNode)
+{
+    if ((mMask & kExpressionReturningArray) != 0)
+    {
+        if (parentNode != nullptr)
+        {
+            TIntermBinary *parentBinary = parentNode->getAsBinaryNode();
+            bool parentIsAssignment =
+                (parentBinary != nullptr &&
+                 (parentBinary->getOp() == EOpAssign || parentBinary->getOp() == EOpInitialize));
+
+            if (node->getType().isArray() && !parentIsAssignment &&
+                (node->isConstructor() || node->getOp() == EOpFunctionCall) &&
+                !IsNodeBlock(parentNode))
+            {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+bool IntermNodePatternMatcher::match(TIntermSelection *node)
+{
+    if ((mMask & kUnfoldedShortCircuitExpression) != 0)
+    {
+        if (node->usesTernaryOperator())
+        {
+            return true;
+        }
+    }
+    return false;
+}