text: //
  // Copyright (C) 2017 LunarG, Inc.
  // Copyright (C) 2018 Google, Inc.
  //
  // All rights reserved.
  //
  // Redistribution and use in source and binary forms, with or without
  // modification, are permitted provided that the following conditions
  // are met:
  //
  //    Redistributions of source code must retain the above copyright
  //    notice, this list of conditions and the following disclaimer.
  //
  //    Redistributions in binary form must reproduce the above
  //    copyright notice, this list of conditions and the following
  //    disclaimer in the documentation and/or other materials provided
  //    with the distribution.
  //
  //    Neither the name of Google, Inc., nor the names of its
  //    contributors may be used to endorse or promote products derived
  //    from this software without specific prior written permission.
  //
  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  // POSSIBILITY OF SUCH DAMAGE.
  //
  
  #include "attribute.h"
  #include "../Include/intermediate.h"
  #include "ParseHelper.h"
  
  namespace glslang {
  
  // extract integers out of attribute arguments stored in attribute aggregate
  bool TAttributeArgs::getInt(int& value, int argNum) const 
  {
      const TConstUnion* intConst = getConstUnion(EbtInt, argNum);
  
      if (intConst == nullptr)
          return false;
  
      value = intConst->getIConst();
      return true;
  }
  
  
  // extract strings out of attribute arguments stored in attribute aggregate.
  // convert to lower case if converToLower is true (for case-insensitive compare convenience)
  bool TAttributeArgs::getString(TString& value, int argNum, bool convertToLower) const 
  {
      const TConstUnion* stringConst = getConstUnion(EbtString, argNum);
  
      if (stringConst == nullptr)
          return false;
  
      value = *stringConst->getSConst();
  
      // Convenience.
      if (convertToLower)
          std::transform(value.begin(), value.end(), value.begin(), ::tolower);
  
      return true;
  }
  
  // How many arguments were supplied?
  int TAttributeArgs::size() const
  {
      return args == nullptr ? 0 : (int)args->getSequence().size();
  }
  
  // Helper to get attribute const union.  Returns nullptr on failure.
  const TConstUnion* TAttributeArgs::getConstUnion(TBasicType basicType, int argNum) const
  {
      if (args == nullptr)
          return nullptr;
  
      if (argNum >= (int)args->getSequence().size())
          return nullptr;
  
      if (args->getSequence()[argNum]->getAsConstantUnion() == nullptr)
          return nullptr;
  
      const TConstUnion* constVal = &args->getSequence()[argNum]->getAsConstantUnion()->getConstArray()[0];
      if (constVal == nullptr || constVal->getType() != basicType)
          return nullptr;
  
      return constVal;
  }
  
  // Implementation of TParseContext parts of attributes
  TAttributeType TParseContext::attributeFromName(const TString& name) const
  {
      if (name == "branch" || name == "dont_flatten")
          return EatBranch;
      else if (name == "flatten")
          return EatFlatten;
      else if (name == "unroll")
          return EatUnroll;
      else if (name == "loop" || name == "dont_unroll")
          return EatLoop;
      else if (name == "dependency_infinite")
          return EatDependencyInfinite;
      else if (name == "dependency_length")
          return EatDependencyLength;
      else if (name == "min_iterations")
          return EatMinIterations;
      else if (name == "max_iterations")
          return EatMaxIterations;
      else if (name == "iteration_multiple")
          return EatIterationMultiple;
      else if (name == "peel_count")
          return EatPeelCount;
      else if (name == "partial_count")
          return EatPartialCount;
      else
          return EatNone;
  }
  
  // Make an initial leaf for the grammar from a no-argument attribute
  TAttributes* TParseContext::makeAttributes(const TString& identifier) const
  {
      TAttributes *attributes = nullptr;
      attributes = NewPoolObject(attributes);
      TAttributeArgs args = { attributeFromName(identifier), nullptr };
      attributes->push_back(args);
      return attributes;
  }
  
  // Make an initial leaf for the grammar from a one-argument attribute
  TAttributes* TParseContext::makeAttributes(const TString& identifier, TIntermNode* node) const
  {
      TAttributes *attributes = nullptr;
      attributes = NewPoolObject(attributes);
  
      // for now, node is always a simple single expression, but other code expects
      // a list, so make it so
      TIntermAggregate* agg = intermediate.makeAggregate(node);
      TAttributeArgs args = { attributeFromName(identifier), agg };
      attributes->push_back(args);
      return attributes;
  }
  
  // Merge two sets of attributes into a single set.
  // The second argument is destructively consumed.
  TAttributes* TParseContext::mergeAttributes(TAttributes* attr1, TAttributes* attr2) const
  {
      attr1->splice(attr1->end(), *attr2);
      return attr1;
  }
  
  //
  // Selection attributes
  //
  void TParseContext::handleSelectionAttributes(const TAttributes& attributes, TIntermNode* node)
  {
      TIntermSelection* selection = node->getAsSelectionNode();
      if (selection == nullptr)
          return;
  
      for (auto it = attributes.begin(); it != attributes.end(); ++it) {
          if (it->size() > 0) {
              warn(node->getLoc(), "attribute with arguments not recognized, skipping", "", "");
              continue;
          }
  
          switch (it->name) {
          case EatFlatten:
              selection->setFlatten();
              break;
          case EatBranch:
              selection->setDontFlatten();
              break;
          default:
              warn(node->getLoc(), "attribute does not apply to a selection", "", "");
              break;
          }
      }
  }
  
  //
  // Switch attributes
  //
  void TParseContext::handleSwitchAttributes(const TAttributes& attributes, TIntermNode* node)
  {
      TIntermSwitch* selection = node->getAsSwitchNode();
      if (selection == nullptr)
          return;
  
      for (auto it = attributes.begin(); it != attributes.end(); ++it) {
          if (it->size() > 0) {
              warn(node->getLoc(), "attribute with arguments not recognized, skipping", "", "");
              continue;
          }
  
          switch (it->name) {
          case EatFlatten:
              selection->setFlatten();
              break;
          case EatBranch:
              selection->setDontFlatten();
              break;
          default:
              warn(node->getLoc(), "attribute does not apply to a switch", "", "");
              break;
          }
      }
  }
  
  //
  // Loop attributes
  //
  void TParseContext::handleLoopAttributes(const TAttributes& attributes, TIntermNode* node)
  {
      TIntermLoop* loop = node->getAsLoopNode();
      if (loop == nullptr) {
          // the actual loop might be part of a sequence
          TIntermAggregate* agg = node->getAsAggregate();
          if (agg == nullptr)
              return;
          for (auto it = agg->getSequence().begin(); it != agg->getSequence().end(); ++it) {
              loop = (*it)->getAsLoopNode();
              if (loop != nullptr)
                  break;
          }
          if (loop == nullptr)
              return;
      }
  
      for (auto it = attributes.begin(); it != attributes.end(); ++it) {
  
          const auto noArgument = [&](const char* feature) {
              if (it->size() > 0) {
                  warn(node->getLoc(), "expected no arguments", feature, "");
                  return false;
              }
              return true;
          };
  
          const auto positiveSignedArgument = [&](const char* feature, int& value) {
              if (it->size() == 1 && it->getInt(value)) {
                  if (value <= 0) {
                      error(node->getLoc(), "must be positive", feature, "");
                      return false;
                  }
              } else {
                  warn(node->getLoc(), "expected a single integer argument", feature, "");
                  return false;
              }
              return true;
          };
  
          const auto unsignedArgument = [&](const char* feature, unsigned int& uiValue) {
              int value;
              if (!(it->size() == 1 && it->getInt(value))) {
                  warn(node->getLoc(), "expected a single integer argument", feature, "");
                  return false;
              }
              uiValue = (unsigned int)value;
              return true;
          };
  
          const auto positiveUnsignedArgument = [&](const char* feature, unsigned int& uiValue) {
              int value;
              if (it->size() == 1 && it->getInt(value)) {
                  if (value == 0) {
                      error(node->getLoc(), "must be greater than or equal to 1", feature, "");
                      return false;
                  }
              } else {
                  warn(node->getLoc(), "expected a single integer argument", feature, "");
                  return false;
              }
              uiValue = (unsigned int)value;
              return true;
          };
  
          const auto spirv14 = [&](const char* feature) {
              if (spvVersion.spv > 0 && spvVersion.spv < EShTargetSpv_1_4)
                  warn(node->getLoc(), "attribute requires a SPIR-V 1.4 target-env", feature, "");
          };
  
          int value = 0;
          unsigned uiValue = 0;
          switch (it->name) {
          case EatUnroll:
              if (noArgument("unroll"))
                  loop->setUnroll();
              break;
          case EatLoop:
              if (noArgument("dont_unroll"))
                  loop->setDontUnroll();
              break;
          case EatDependencyInfinite:
              if (noArgument("dependency_infinite"))
                  loop->setLoopDependency(TIntermLoop::dependencyInfinite);
              break;
          case EatDependencyLength:
              if (positiveSignedArgument("dependency_length", value))
                  loop->setLoopDependency(value);
              break;
          case EatMinIterations:
              spirv14("min_iterations");
              if (unsignedArgument("min_iterations", uiValue))
                  loop->setMinIterations(uiValue);
              break;
          case EatMaxIterations:
              spirv14("max_iterations");
              if (unsignedArgument("max_iterations", uiValue))
                  loop->setMaxIterations(uiValue);
              break;
          case EatIterationMultiple:
              spirv14("iteration_multiple");
              if (positiveUnsignedArgument("iteration_multiple", uiValue))
                  loop->setIterationMultiple(uiValue);
              break;
          case EatPeelCount:
              spirv14("peel_count");
              if (unsignedArgument("peel_count", uiValue))
                  loop->setPeelCount(uiValue);
              break;
          case EatPartialCount:
              spirv14("partial_count");
              if (unsignedArgument("partial_count", uiValue))
                  loop->setPartialCount(uiValue);
              break;
          default:
              warn(node->getLoc(), "attribute does not apply to a loop", "", "");
              break;
          }
      }
  }
  
  
  } // end namespace glslang
  