text: //
  // Copyright (C) 2002-2005  3Dlabs Inc. Ltd.
  // Copyright (C) 2016 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 3Dlabs Inc. Ltd. 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.
  //
  
  // Implement the TParseContextBase class.
  
  #include <cstdarg>
  
  #include "ParseHelper.h"
  
  extern int yyparse(glslang::TParseContext*);
  
  namespace glslang {
  
  //
  // Used to output syntax, parsing, and semantic errors.
  //
  
  void TParseContextBase::outputMessage(const TSourceLoc& loc, const char* szReason,
                                        const char* szToken,
                                        const char* szExtraInfoFormat,
                                        TPrefixType prefix, va_list args)
  {
      const int maxSize = MaxTokenLength + 200;
      char szExtraInfo[maxSize];
  
      safe_vsprintf(szExtraInfo, maxSize, szExtraInfoFormat, args);
  
      infoSink.info.prefix(prefix);
      infoSink.info.location(loc);
      infoSink.info << "'" << szToken <<  "' : " << szReason << " " << szExtraInfo << "\n";
  
      if (prefix == EPrefixError) {
          ++numErrors;
      }
  }
  
  void C_DECL TParseContextBase::error(const TSourceLoc& loc, const char* szReason, const char* szToken,
                                       const char* szExtraInfoFormat, ...)
  {
      if (messages & EShMsgOnlyPreprocessor)
          return;
      va_list args;
      va_start(args, szExtraInfoFormat);
      outputMessage(loc, szReason, szToken, szExtraInfoFormat, EPrefixError, args);
      va_end(args);
  
      if ((messages & EShMsgCascadingErrors) == 0)
          currentScanner->setEndOfInput();
  }
  
  void C_DECL TParseContextBase::warn(const TSourceLoc& loc, const char* szReason, const char* szToken,
                                      const char* szExtraInfoFormat, ...)
  {
      if (suppressWarnings())
          return;
      va_list args;
      va_start(args, szExtraInfoFormat);
      outputMessage(loc, szReason, szToken, szExtraInfoFormat, EPrefixWarning, args);
      va_end(args);
  }
  
  void C_DECL TParseContextBase::ppError(const TSourceLoc& loc, const char* szReason, const char* szToken,
                                         const char* szExtraInfoFormat, ...)
  {
      va_list args;
      va_start(args, szExtraInfoFormat);
      outputMessage(loc, szReason, szToken, szExtraInfoFormat, EPrefixError, args);
      va_end(args);
  
      if ((messages & EShMsgCascadingErrors) == 0)
          currentScanner->setEndOfInput();
  }
  
  void C_DECL TParseContextBase::ppWarn(const TSourceLoc& loc, const char* szReason, const char* szToken,
                                        const char* szExtraInfoFormat, ...)
  {
      va_list args;
      va_start(args, szExtraInfoFormat);
      outputMessage(loc, szReason, szToken, szExtraInfoFormat, EPrefixWarning, args);
      va_end(args);
  }
  
  //
  // Both test and if necessary, spit out an error, to see if the node is really
  // an l-value that can be operated on this way.
  //
  // Returns true if there was an error.
  //
  bool TParseContextBase::lValueErrorCheck(const TSourceLoc& loc, const char* op, TIntermTyped* node)
  {
      TIntermBinary* binaryNode = node->getAsBinaryNode();
  
      if (binaryNode) {
          switch(binaryNode->getOp()) {
          case EOpIndexDirect:
          case EOpIndexIndirect:     // fall through
          case EOpIndexDirectStruct: // fall through
          case EOpVectorSwizzle:
          case EOpMatrixSwizzle:
              return lValueErrorCheck(loc, op, binaryNode->getLeft());
          default:
              break;
          }
          error(loc, " l-value required", op, "", "");
  
          return true;
      }
  
      const char* symbol = nullptr;
      TIntermSymbol* symNode = node->getAsSymbolNode();
      if (symNode != nullptr)
          symbol = symNode->getName().c_str();
  
      const char* message = nullptr;
      switch (node->getQualifier().storage) {
      case EvqConst:          message = "can't modify a const";        break;
      case EvqConstReadOnly:  message = "can't modify a const";        break;
      case EvqUniform:        message = "can't modify a uniform";      break;
      case EvqBuffer:
          if (node->getQualifier().readonly)
              message = "can't modify a readonly buffer";
          break;
  
      default:
          //
          // Type that can't be written to?
          //
          switch (node->getBasicType()) {
          case EbtSampler:
              message = "can't modify a sampler";
              break;
          case EbtAtomicUint:
              message = "can't modify an atomic_uint";
              break;
          case EbtVoid:
              message = "can't modify void";
              break;
          default:
              break;
          }
      }
  
      if (message == nullptr && binaryNode == nullptr && symNode == nullptr) {
          error(loc, " l-value required", op, "", "");
  
          return true;
      }
  
      //
      // Everything else is okay, no error.
      //
      if (message == nullptr)
          return false;
  
      //
      // If we get here, we have an error and a message.
      //
      if (symNode)
          error(loc, " l-value required", op, "\"%s\" (%s)", symbol, message);
      else
          error(loc, " l-value required", op, "(%s)", message);
  
      return true;
  }
  
  // Test for and give an error if the node can't be read from.
  void TParseContextBase::rValueErrorCheck(const TSourceLoc& loc, const char* op, TIntermTyped* node)
  {
      if (! node)
          return;
  
      TIntermBinary* binaryNode = node->getAsBinaryNode();
      if (binaryNode) {
          switch(binaryNode->getOp()) {
          case EOpIndexDirect:
          case EOpIndexIndirect:
          case EOpIndexDirectStruct:
          case EOpVectorSwizzle:
          case EOpMatrixSwizzle:
              rValueErrorCheck(loc, op, binaryNode->getLeft());
          default:
              break;
          }
  
          return;
      }
  
      TIntermSymbol* symNode = node->getAsSymbolNode();
      if (symNode && symNode->getQualifier().writeonly)
          error(loc, "can't read from writeonly object: ", op, symNode->getName().c_str());
  }
  
  // Add 'symbol' to the list of deferred linkage symbols, which
  // are later processed in finish(), at which point the symbol
  // must still be valid.
  // It is okay if the symbol's type will be subsequently edited;
  // the modifications will be tracked.
  void TParseContextBase::trackLinkage(TSymbol& symbol)
  {
      if (!parsingBuiltins)
          linkageSymbols.push_back(&symbol);
  }
  
  // Make a shared symbol have a non-shared version that can be edited by the current
  // compile, such that editing its type will not change the shared version and will
  // effect all nodes already sharing it (non-shallow type),
  // or adopting its full type after being edited (shallow type).
  void TParseContextBase::makeEditable(TSymbol*& symbol)
  {
      // copyUp() does a deep copy of the type.
      symbol = symbolTable.copyUp(symbol);
  
      // Save it (deferred, so it can be edited first) in the AST for linker use.
      if (symbol)
          trackLinkage(*symbol);
  }
  
  // Return a writable version of the variable 'name'.
  //
  // Return nullptr if 'name' is not found.  This should mean
  // something is seriously wrong (e.g., compiler asking self for
  // built-in that doesn't exist).
  TVariable* TParseContextBase::getEditableVariable(const char* name)
  {
      bool builtIn;
      TSymbol* symbol = symbolTable.find(name, &builtIn);
  
      assert(symbol != nullptr);
      if (symbol == nullptr)
          return nullptr;
  
      if (builtIn)
          makeEditable(symbol);
  
      return symbol->getAsVariable();
  }
  
  // Select the best matching function for 'call' from 'candidateList'.
  //
  // Assumptions
  //
  // There is no exact match, so a selection algorithm needs to run. That is, the
  // language-specific handler should check for exact match first, to
  // decide what to do, before calling this selector.
  //
  // Input
  //
  //  * list of candidate signatures to select from
  //  * the call
  //  * a predicate function convertible(from, to) that says whether or not type
  //    'from' can implicitly convert to type 'to' (it includes the case of what
  //    the calling language would consider a matching type with no conversion
  //    needed)
  //  * a predicate function better(from1, from2, to1, to2) that says whether or
  //    not a conversion from <-> to2 is considered better than a conversion
  //    from <-> to1 (both in and out directions need testing, as declared by the
  //    formal parameter)
  //
  // Output
  //
  //  * best matching candidate (or none, if no viable candidates found)
  //  * whether there was a tie for the best match (ambiguous overload selection,
  //    caller's choice for how to report)
  //
  const TFunction* TParseContextBase::selectFunction(
      const TVector<const TFunction*> candidateList,
      const TFunction& call,
      std::function<bool(const TType& from, const TType& to, TOperator op, int arg)> convertible,
      std::function<bool(const TType& from, const TType& to1, const TType& to2)> better,
      /* output */ bool& tie)
  {
  //
  // Operation
  //
  // 1. Prune the input list of candidates down to a list of viable candidates,
  // where each viable candidate has
  //
  //  * at least as many parameters as there are calling arguments, with any
  //    remaining parameters being optional or having default values
  //  * each parameter is true under convertible(A, B), where A is the calling
  //    type for in and B is the formal type, and in addition, for out B is the
  //    calling type and A is the formal type
  //
  // 2. If there are no viable candidates, return with no match.
  //
  // 3. If there is only one viable candidate, it is the best match.
  //
  // 4. If there are multiple viable candidates, select the first viable candidate
  // as the incumbent. Compare the incumbent to the next viable candidate, and if
  // that candidate is better (bullets below), make it the incumbent. Repeat, with
  // a linear walk through the viable candidate list. The final incumbent will be
  // returned as the best match. A viable candidate is better than the incumbent if
  //
  //  * it has a function argument with a better(...) conversion than the incumbent,
  //    for all directions needed by in and out
  //  * the incumbent has no argument with a better(...) conversion then the
  //    candidate, for either in or out (as needed)
  //
  // 5. Check for ambiguity by comparing the best match against all other viable
  // candidates. If any other viable candidate has a function argument with a
  // better(...) conversion than the best candidate (for either in or out
  // directions), return that there was a tie for best.
  //
  
      tie = false;
  
      // 1. prune to viable...
      TVector<const TFunction*> viableCandidates;
      for (auto it = candidateList.begin(); it != candidateList.end(); ++it) {
          const TFunction& candidate = *(*it);
  
          // to even be a potential match, number of arguments must be >= the number of
          // fixed (non-default) parameters, and <= the total (including parameter with defaults).
          if (call.getParamCount() < candidate.getFixedParamCount() ||
              call.getParamCount() > candidate.getParamCount())
              continue;
  
          // see if arguments are convertible
          bool viable = true;
  
          // The call can have fewer parameters than the candidate, if some have defaults.
          const int paramCount = std::min(call.getParamCount(), candidate.getParamCount());
          for (int param = 0; param < paramCount; ++param) {
              if (candidate[param].type->getQualifier().isParamInput()) {
                  if (! convertible(*call[param].type, *candidate[param].type, candidate.getBuiltInOp(), param)) {
                      viable = false;
                      break;
                  }
              }
              if (candidate[param].type->getQualifier().isParamOutput()) {
                  if (! convertible(*candidate[param].type, *call[param].type, candidate.getBuiltInOp(), param)) {
                      viable = false;
                      break;
                  }
              }
          }
  
          if (viable)
              viableCandidates.push_back(&candidate);
      }
  
      // 2. none viable...
      if (viableCandidates.size() == 0)
          return nullptr;
  
      // 3. only one viable...
      if (viableCandidates.size() == 1)
          return viableCandidates.front();
  
      // 4. find best...
      const auto betterParam = [&call, &better](const TFunction& can1, const TFunction& can2) -> bool {
          // is call -> can2 better than call -> can1 for any parameter
          bool hasBetterParam = false;
          for (int param = 0; param < call.getParamCount(); ++param) {
              if (better(*call[param].type, *can1[param].type, *can2[param].type)) {
                  hasBetterParam = true;
                  break;
              }
          }
          return hasBetterParam;
      };
  
      const auto equivalentParams = [&call, &better](const TFunction& can1, const TFunction& can2) -> bool {
          // is call -> can2 equivalent to call -> can1 for all the call parameters?
          for (int param = 0; param < call.getParamCount(); ++param) {
              if (better(*call[param].type, *can1[param].type, *can2[param].type) ||
                  better(*call[param].type, *can2[param].type, *can1[param].type))
                  return false;
          }
          return true;
      };
  
      const TFunction* incumbent = viableCandidates.front();
      for (auto it = viableCandidates.begin() + 1; it != viableCandidates.end(); ++it) {
          const TFunction& candidate = *(*it);
          if (betterParam(*incumbent, candidate) && ! betterParam(candidate, *incumbent))
              incumbent = &candidate;
      }
  
      // 5. ambiguity...
      for (auto it = viableCandidates.begin(); it != viableCandidates.end(); ++it) {
          if (incumbent == *it)
              continue;
          const TFunction& candidate = *(*it);
  
          // In the case of default parameters, it may have an identical initial set, which is
          // also ambiguous
          if (betterParam(*incumbent, candidate) || equivalentParams(*incumbent, candidate))
              tie = true;
      }
  
      return incumbent;
  }
  
  //
  // Look at a '.' field selector string and change it into numerical selectors
  // for a vector or scalar.
  //
  // Always return some form of swizzle, so the result is always usable.
  //
  void TParseContextBase::parseSwizzleSelector(const TSourceLoc& loc, const TString& compString, int vecSize,
                                               TSwizzleSelectors<TVectorSelector>& selector)
  {
      // Too long?
      if (compString.size() > MaxSwizzleSelectors)
          error(loc, "vector swizzle too long", compString.c_str(), "");
  
      // Use this to test that all swizzle characters are from the same swizzle-namespace-set
      enum {
          exyzw,
          ergba,
          estpq,
      } fieldSet[MaxSwizzleSelectors];
  
      // Decode the swizzle string.
      int size = std::min(MaxSwizzleSelectors, (int)compString.size());
      for (int i = 0; i < size; ++i) {
          switch (compString[i])  {
          case 'x':
              selector.push_back(0);
              fieldSet[i] = exyzw;
              break;
          case 'r':
              selector.push_back(0);
              fieldSet[i] = ergba;
              break;
          case 's':
              selector.push_back(0);
              fieldSet[i] = estpq;
              break;
  
          case 'y':
              selector.push_back(1);
              fieldSet[i] = exyzw;
              break;
          case 'g':
              selector.push_back(1);
              fieldSet[i] = ergba;
              break;
          case 't':
              selector.push_back(1);
              fieldSet[i] = estpq;
              break;
  
          case 'z':
              selector.push_back(2);
              fieldSet[i] = exyzw;
              break;
          case 'b':
              selector.push_back(2);
              fieldSet[i] = ergba;
              break;
          case 'p':
              selector.push_back(2);
              fieldSet[i] = estpq;
              break;
  
          case 'w':
              selector.push_back(3);
              fieldSet[i] = exyzw;
              break;
          case 'a':
              selector.push_back(3);
              fieldSet[i] = ergba;
              break;
          case 'q':
              selector.push_back(3);
              fieldSet[i] = estpq;
              break;
  
          default:
              error(loc, "unknown swizzle selection", compString.c_str(), "");
              break;
          }
      }
  
      // Additional error checking.
      for (int i = 0; i < selector.size(); ++i) {
          if (selector[i] >= vecSize) {
              error(loc, "vector swizzle selection out of range",  compString.c_str(), "");
              selector.resize(i);
              break;
          }
  
          if (i > 0 && fieldSet[i] != fieldSet[i-1]) {
              error(loc, "vector swizzle selectors not from the same set", compString.c_str(), "");
              selector.resize(i);
              break;
          }
      }
  
      // Ensure it is valid.
      if (selector.size() == 0)
          selector.push_back(0);
  }
  
  //
  // Make the passed-in variable information become a member of the
  // global uniform block.  If this doesn't exist yet, make it.
  //
  void TParseContextBase::growGlobalUniformBlock(TSourceLoc& loc, TType& memberType, TString& memberName, TTypeList* typeList)
  {
      // Make the global block, if not yet made.
      if (globalUniformBlock == nullptr) {
          TQualifier blockQualifier;
          blockQualifier.clear();
          blockQualifier.storage = EvqUniform;
          TType blockType(new TTypeList, *NewPoolTString(getGlobalUniformBlockName()), blockQualifier);
          setUniformBlockDefaults(blockType);
          globalUniformBlock = new TVariable(NewPoolTString(""), blockType, true);
          firstNewMember = 0;
      }
  
      // Add the requested member as a member to the global block.
      TType* type = new TType;
      type->shallowCopy(memberType);
      type->setFieldName(memberName);
      if (typeList)
          type->setStruct(typeList);
      TTypeLoc typeLoc = {type, loc};
      globalUniformBlock->getType().getWritableStruct()->push_back(typeLoc);
  
      // Insert into the symbol table.
      if (firstNewMember == 0) {
          // This is the first request; we need a normal symbol table insert
          if (symbolTable.insert(*globalUniformBlock))
              trackLinkage(*globalUniformBlock);
          else
              error(loc, "failed to insert the global constant buffer", "uniform", "");
      } else {
          // This is a follow-on request; we need to amend the first insert
          symbolTable.amend(*globalUniformBlock, firstNewMember);
      }
  
      ++firstNewMember;
  }
  
  void TParseContextBase::finish()
  {
      if (parsingBuiltins)
          return;
  
      // Transfer the linkage symbols to AST nodes
      TIntermAggregate* linkage = new TIntermAggregate;
      for (auto i = linkageSymbols.begin(); i != linkageSymbols.end(); ++i)
          intermediate.addSymbolLinkageNode(linkage, **i);
      intermediate.addSymbolLinkageNodes(linkage, getLanguage(), symbolTable);
  }
  
  } // end namespace glslang
  