//
// Copyright 2002 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.
//
#ifndef COMPILER_TRANSLATOR_PARSECONTEXT_H_
#define COMPILER_TRANSLATOR_PARSECONTEXT_H_

#include "compiler/preprocessor/Preprocessor.h"
#include "compiler/translator/Compiler.h"
#include "compiler/translator/Declarator.h"
#include "compiler/translator/Diagnostics.h"
#include "compiler/translator/DirectiveHandler.h"
#include "compiler/translator/FunctionLookup.h"
#include "compiler/translator/QualifierTypes.h"
#include "compiler/translator/SymbolTable.h"

namespace sh
{

struct TMatrixFields
{
    bool wholeRow;
    bool wholeCol;
    int row;
    int col;
};

//
// The following are extra variables needed during parsing, grouped together so
// they can be passed to the parser without needing a global.
//
class TParseContext : angle::NonCopyable
{
  public:
    TParseContext(TSymbolTable &symt,
                  TExtensionBehavior &ext,
                  sh::GLenum type,
                  ShShaderSpec spec,
                  const ShCompileOptions &options,
                  bool checksPrecErrors,
                  TDiagnostics *diagnostics,
                  const ShBuiltInResources &resources,
                  ShShaderOutput outputType);
    ~TParseContext();

    bool anyMultiviewExtensionAvailable();
    const angle::pp::Preprocessor &getPreprocessor() const { return mPreprocessor; }
    angle::pp::Preprocessor &getPreprocessor() { return mPreprocessor; }
    void *getScanner() const { return mScanner; }
    void setScanner(void *scanner) { mScanner = scanner; }
    int getShaderVersion() const { return mShaderVersion; }
    sh::GLenum getShaderType() const { return mShaderType; }
    ShShaderSpec getShaderSpec() const { return mShaderSpec; }
    int numErrors() const { return mDiagnostics->numErrors(); }
    void error(const TSourceLoc &loc, const char *reason, const char *token);
    void error(const TSourceLoc &loc, const char *reason, const ImmutableString &token);
    void warning(const TSourceLoc &loc, const char *reason, const char *token);

    // If isError is false, a warning will be reported instead.
    void outOfRangeError(bool isError,
                         const TSourceLoc &loc,
                         const char *reason,
                         const char *token);

    TIntermBlock *getTreeRoot() const { return mTreeRoot; }
    void setTreeRoot(TIntermBlock *treeRoot);

    bool getFragmentPrecisionHigh() const
    {
        return mFragmentPrecisionHighOnESSL1 || mShaderVersion >= 300;
    }
    void setFragmentPrecisionHighOnESSL1(bool fragmentPrecisionHigh)
    {
        mFragmentPrecisionHighOnESSL1 = fragmentPrecisionHigh;
    }

    bool isEarlyFragmentTestsSpecified() const { return mEarlyFragmentTestsSpecified; }
    bool hasDiscard() const { return mHasDiscard; }
    bool isSampleQualifierSpecified() const { return mSampleQualifierSpecified; }

    void setLoopNestingLevel(int loopNestintLevel) { mLoopNestingLevel = loopNestintLevel; }

    void incrLoopNestingLevel() { ++mLoopNestingLevel; }
    void decrLoopNestingLevel() { --mLoopNestingLevel; }

    void incrSwitchNestingLevel() { ++mSwitchNestingLevel; }
    void decrSwitchNestingLevel() { --mSwitchNestingLevel; }

    bool isComputeShaderLocalSizeDeclared() const { return mComputeShaderLocalSizeDeclared; }
    sh::WorkGroupSize getComputeShaderLocalSize() const;

    int getNumViews() const { return mNumViews; }

    const std::map<int, TLayoutImageInternalFormat> &pixelLocalStorageBindings() const
    {
        return mPLSBindings;
    }

    void enterFunctionDeclaration() { mDeclaringFunction = true; }

    void exitFunctionDeclaration() { mDeclaringFunction = false; }

    bool declaringFunction() const { return mDeclaringFunction; }

    TIntermConstantUnion *addScalarLiteral(const TConstantUnion *constantUnion,
                                           const TSourceLoc &line);

    // This method is guaranteed to succeed, even if no variable with 'name' exists.
    const TVariable *getNamedVariable(const TSourceLoc &location,
                                      const ImmutableString &name,
                                      const TSymbol *symbol);
    TIntermTyped *parseVariableIdentifier(const TSourceLoc &location,
                                          const ImmutableString &name,
                                          const TSymbol *symbol);

    // Look at a '.' field selector string and change it into offsets for a vector.
    bool parseVectorFields(const TSourceLoc &line,
                           const ImmutableString &compString,
                           int vecSize,
                           TVector<int> *fieldOffsets);

    void assignError(const TSourceLoc &line, const char *op, const TType &left, const TType &right);
    void unaryOpError(const TSourceLoc &line, const char *op, const TType &operand);
    void binaryOpError(const TSourceLoc &line,
                       const char *op,
                       const TType &left,
                       const TType &right);

    // Check functions - the ones that return bool return false if an error was generated.

    bool checkIsNotReserved(const TSourceLoc &line, const ImmutableString &identifier);
    void checkPrecisionSpecified(const TSourceLoc &line, TPrecision precision, TBasicType type);
    bool checkCanBeLValue(const TSourceLoc &line, const char *op, TIntermTyped *node);
    void checkIsConst(TIntermTyped *node);
    void checkIsScalarInteger(TIntermTyped *node, const char *token);
    bool checkIsAtGlobalLevel(const TSourceLoc &line, const char *token);
    bool checkConstructorArguments(const TSourceLoc &line,
                                   const TIntermSequence &arguments,
                                   const TType &type);

    // Returns a sanitized array size to use (the size is at least 1).
    unsigned int checkIsValidArraySize(const TSourceLoc &line, TIntermTyped *expr);
    bool checkIsValidQualifierForArray(const TSourceLoc &line, const TPublicType &elementQualifier);
    bool checkArrayElementIsNotArray(const TSourceLoc &line, const TPublicType &elementType);
    bool checkArrayOfArraysInOut(const TSourceLoc &line,
                                 const TPublicType &elementType,
                                 const TType &arrayType);
    bool checkIsNonVoid(const TSourceLoc &line,
                        const ImmutableString &identifier,
                        const TBasicType &type);
    bool checkIsScalarBool(const TSourceLoc &line, const TIntermTyped *type);
    void checkIsScalarBool(const TSourceLoc &line, const TPublicType &pType);
    bool checkIsNotOpaqueType(const TSourceLoc &line,
                              const TTypeSpecifierNonArray &pType,
                              const char *reason);
    void checkDeclaratorLocationIsNotSpecified(const TSourceLoc &line, const TPublicType &pType);
    void checkLocationIsNotSpecified(const TSourceLoc &location,
                                     const TLayoutQualifier &layoutQualifier);
    void checkStd430IsForShaderStorageBlock(const TSourceLoc &location,
                                            const TLayoutBlockStorage &blockStorage,
                                            const TQualifier &qualifier);
    void checkIsParameterQualifierValid(const TSourceLoc &line,
                                        const TTypeQualifierBuilder &typeQualifierBuilder,
                                        TType *type);

    // Check if at least one of the specified extensions can be used, and generate error/warning as
    // appropriate according to the spec.
    // This function is only needed for a few different small constant sizes of extension array, and
    // we want to avoid unnecessary dynamic allocations. That's why checkCanUseOneOfExtensions is a
    // template function rather than one taking a vector.
    template <size_t size>
    bool checkCanUseOneOfExtensions(const TSourceLoc &line,
                                    const std::array<TExtension, size> &extensions);
    bool checkCanUseExtension(const TSourceLoc &line, TExtension extension);

    // Done for all declarations, whether empty or not.
    void declarationQualifierErrorCheck(const sh::TQualifier qualifier,
                                        const sh::TLayoutQualifier &layoutQualifier,
                                        const TSourceLoc &location);
    // Done for the first non-empty declarator in a declaration.
    void nonEmptyDeclarationErrorCheck(const TPublicType &publicType,
                                       const TSourceLoc &identifierLocation);
    // Done only for empty declarations.
    void emptyDeclarationErrorCheck(const TType &type, const TSourceLoc &location);

    void checkCanUseLayoutQualifier(const TSourceLoc &location);
    bool checkLayoutQualifierSupported(const TSourceLoc &location,
                                       const ImmutableString &layoutQualifierName,
                                       int versionRequired);
    bool checkWorkGroupSizeIsNotSpecified(const TSourceLoc &location,
                                          const TLayoutQualifier &layoutQualifier);
    void functionCallRValueLValueErrorCheck(const TFunction *fnCandidate, TIntermAggregate *fnCall);
    void checkInvariantVariableQualifier(bool invariant,
                                         const TQualifier qualifier,
                                         const TSourceLoc &invariantLocation);
    void checkInputOutputTypeIsValidES3(const TQualifier qualifier,
                                        const TPublicType &type,
                                        const TSourceLoc &qualifierLocation);
    void checkLocalVariableConstStorageQualifier(const TQualifierWrapperBase &qualifier);
    void checkTCSOutVarIndexIsValid(TIntermBinary *binaryExpression, const TSourceLoc &location);

    void checkAdvancedBlendEquationsNotSpecified(
        const TSourceLoc &location,
        const AdvancedBlendEquations &advancedBlendEquations,
        const TQualifier &qualifier);

    const TPragma &pragma() const { return mDirectiveHandler.pragma(); }
    const TExtensionBehavior &extensionBehavior() const
    {
        return mDirectiveHandler.extensionBehavior();
    }

    bool isExtensionEnabled(TExtension extension) const;
    void handleExtensionDirective(const TSourceLoc &loc, const char *extName, const char *behavior);
    void handlePragmaDirective(const TSourceLoc &loc,
                               const char *name,
                               const char *value,
                               bool stdgl);

    // For built-ins that can be redeclared, adjusts the type qualifier so transformations can
    // identify them correctly.
    void adjustRedeclaredBuiltInType(const TSourceLoc &line,
                                     const ImmutableString &identifier,
                                     TType *type);

    // Returns true on success. *initNode may still be nullptr on success in case the initialization
    // is not needed in the AST.
    bool executeInitializer(const TSourceLoc &line,
                            const ImmutableString &identifier,
                            TType *type,
                            TIntermTyped *initializer,
                            TIntermBinary **initNode);
    TIntermNode *addConditionInitializer(const TPublicType &pType,
                                         const ImmutableString &identifier,
                                         TIntermTyped *initializer,
                                         const TSourceLoc &loc);
    TIntermNode *addLoop(TLoopType type,
                         TIntermNode *init,
                         TIntermNode *cond,
                         TIntermTyped *expr,
                         TIntermNode *body,
                         const TSourceLoc &loc);

    // For "if" test nodes. There are three children: a condition, a true path, and a false path.
    // The two paths are in TIntermNodePair code.
    TIntermNode *addIfElse(TIntermTyped *cond, TIntermNodePair code, const TSourceLoc &loc);

    void addFullySpecifiedType(TPublicType *typeSpecifier);
    TPublicType addFullySpecifiedType(const TTypeQualifierBuilder &typeQualifierBuilder,
                                      const TPublicType &typeSpecifier);

    TIntermDeclaration *parseSingleDeclaration(TPublicType &publicType,
                                               const TSourceLoc &identifierOrTypeLocation,
                                               const ImmutableString &identifier);
    TIntermDeclaration *parseSingleArrayDeclaration(TPublicType &elementType,
                                                    const TSourceLoc &identifierLocation,
                                                    const ImmutableString &identifier,
                                                    const TSourceLoc &indexLocation,
                                                    const TVector<unsigned int> &arraySizes);
    TIntermDeclaration *parseSingleInitDeclaration(const TPublicType &publicType,
                                                   const TSourceLoc &identifierLocation,
                                                   const ImmutableString &identifier,
                                                   const TSourceLoc &initLocation,
                                                   TIntermTyped *initializer);

    // Parse a declaration like "type a[n] = initializer"
    // Note that this does not apply to declarations like "type[n] a = initializer"
    TIntermDeclaration *parseSingleArrayInitDeclaration(TPublicType &elementType,
                                                        const TSourceLoc &identifierLocation,
                                                        const ImmutableString &identifier,
                                                        const TSourceLoc &indexLocation,
                                                        const TVector<unsigned int> &arraySizes,
                                                        const TSourceLoc &initLocation,
                                                        TIntermTyped *initializer);

    TIntermGlobalQualifierDeclaration *parseGlobalQualifierDeclaration(
        const TTypeQualifierBuilder &typeQualifierBuilder,
        const TSourceLoc &identifierLoc,
        const ImmutableString &identifier,
        const TSymbol *symbol);

    void parseDeclarator(TPublicType &publicType,
                         const TSourceLoc &identifierLocation,
                         const ImmutableString &identifier,
                         TIntermDeclaration *declarationOut);
    void parseArrayDeclarator(TPublicType &elementType,
                              const TSourceLoc &identifierLocation,
                              const ImmutableString &identifier,
                              const TSourceLoc &arrayLocation,
                              const TVector<unsigned int> &arraySizes,
                              TIntermDeclaration *declarationOut);
    void parseInitDeclarator(const TPublicType &publicType,
                             const TSourceLoc &identifierLocation,
                             const ImmutableString &identifier,
                             const TSourceLoc &initLocation,
                             TIntermTyped *initializer,
                             TIntermDeclaration *declarationOut);

    // Parse a declarator like "a[n] = initializer"
    void parseArrayInitDeclarator(const TPublicType &elementType,
                                  const TSourceLoc &identifierLocation,
                                  const ImmutableString &identifier,
                                  const TSourceLoc &indexLocation,
                                  const TVector<unsigned int> &arraySizes,
                                  const TSourceLoc &initLocation,
                                  TIntermTyped *initializer,
                                  TIntermDeclaration *declarationOut);

    TIntermNode *addEmptyStatement(const TSourceLoc &location);

    void parseDefaultPrecisionQualifier(const TPrecision precision,
                                        const TPublicType &type,
                                        const TSourceLoc &loc);
    void parseGlobalLayoutQualifier(const TTypeQualifierBuilder &typeQualifierBuilder);

    TIntermFunctionPrototype *addFunctionPrototypeDeclaration(const TFunction &parsedFunction,
                                                              const TSourceLoc &location);
    TIntermFunctionDefinition *addFunctionDefinition(TIntermFunctionPrototype *functionPrototype,
                                                     TIntermBlock *functionBody,
                                                     const TSourceLoc &location);
    void parseFunctionDefinitionHeader(const TSourceLoc &location,
                                       const TFunction *function,
                                       TIntermFunctionPrototype **prototypeOut);
    TFunction *parseFunctionDeclarator(const TSourceLoc &location, TFunction *function);
    TFunction *parseFunctionHeader(const TPublicType &type,
                                   const ImmutableString &name,
                                   const TSourceLoc &location);

    TFunctionLookup *addNonConstructorFunc(const ImmutableString &name, const TSymbol *symbol);
    TFunctionLookup *addConstructorFunc(const TPublicType &publicType);

    TParameter parseParameterDeclarator(const TPublicType &publicType,
                                        const ImmutableString &name,
                                        const TSourceLoc &nameLoc);

    TParameter parseParameterArrayDeclarator(const ImmutableString &name,
                                             const TSourceLoc &nameLoc,
                                             const TVector<unsigned int> &arraySizes,
                                             const TSourceLoc &arrayLoc,
                                             TPublicType *elementType);

    TIntermTyped *addIndexExpression(TIntermTyped *baseExpression,
                                     const TSourceLoc &location,
                                     TIntermTyped *indexExpression);
    TIntermTyped *addFieldSelectionExpression(TIntermTyped *baseExpression,
                                              const TSourceLoc &dotLocation,
                                              const ImmutableString &fieldString,
                                              const TSourceLoc &fieldLocation);

    // Parse declarator for a single field
    TDeclarator *parseStructDeclarator(const ImmutableString &identifier, const TSourceLoc &loc);
    TDeclarator *parseStructArrayDeclarator(const ImmutableString &identifier,
                                            const TSourceLoc &loc,
                                            const TVector<unsigned int> *arraySizes);

    void checkDoesNotHaveDuplicateFieldName(const TFieldList::const_iterator begin,
                                            const TFieldList::const_iterator end,
                                            const ImmutableString &name,
                                            const TSourceLoc &location);
    TFieldList *addStructFieldList(TFieldList *fields, const TSourceLoc &location);
    TFieldList *combineStructFieldLists(TFieldList *processedFields,
                                        const TFieldList *newlyAddedFields,
                                        const TSourceLoc &location);
    TFieldList *addStructDeclaratorListWithQualifiers(
        const TTypeQualifierBuilder &typeQualifierBuilder,
        TPublicType *typeSpecifier,
        const TDeclaratorList *declaratorList);
    TFieldList *addStructDeclaratorList(const TPublicType &typeSpecifier,
                                        const TDeclaratorList *declaratorList);
    TTypeSpecifierNonArray addStructure(const TSourceLoc &structLine,
                                        const TSourceLoc &nameLine,
                                        const ImmutableString &structName,
                                        TFieldList *fieldList);

    TIntermDeclaration *addInterfaceBlock(const TTypeQualifierBuilder &typeQualifierBuilder,
                                          const TSourceLoc &nameLine,
                                          const ImmutableString &blockName,
                                          TFieldList *fieldList,
                                          const ImmutableString &instanceName,
                                          const TSourceLoc &instanceLine,
                                          const TVector<unsigned int> *arraySizes,
                                          const TSourceLoc &arraySizesLine);

    void parseLocalSize(const ImmutableString &qualifierType,
                        const TSourceLoc &qualifierTypeLine,
                        int intValue,
                        const TSourceLoc &intValueLine,
                        const std::string &intValueString,
                        size_t index,
                        sh::WorkGroupSize *localSize);
    void parseNumViews(int intValue,
                       const TSourceLoc &intValueLine,
                       const std::string &intValueString,
                       int *numViews);
    void parseInvocations(int intValue,
                          const TSourceLoc &intValueLine,
                          const std::string &intValueString,
                          int *numInvocations);
    void parseMaxVertices(int intValue,
                          const TSourceLoc &intValueLine,
                          const std::string &intValueString,
                          int *numMaxVertices);
    void parseVertices(int intValue,
                       const TSourceLoc &intValueLine,
                       const std::string &intValueString,
                       int *numVertices);
    void parseIndexLayoutQualifier(int intValue,
                                   const TSourceLoc &intValueLine,
                                   const std::string &intValueString,
                                   int *index);
    TLayoutQualifier parseLayoutQualifier(const ImmutableString &qualifierType,
                                          const TSourceLoc &qualifierTypeLine);
    TLayoutQualifier parseLayoutQualifier(const ImmutableString &qualifierType,
                                          const TSourceLoc &qualifierTypeLine,
                                          int intValue,
                                          const TSourceLoc &intValueLine);
    TTypeQualifierBuilder *createTypeQualifierBuilder(const TSourceLoc &loc);
    TStorageQualifierWrapper *parseGlobalStorageQualifier(TQualifier qualifier,
                                                          const TSourceLoc &loc);
    TStorageQualifierWrapper *parseVaryingQualifier(const TSourceLoc &loc);
    TStorageQualifierWrapper *parseInQualifier(const TSourceLoc &loc);
    TStorageQualifierWrapper *parseOutQualifier(const TSourceLoc &loc);
    TStorageQualifierWrapper *parseInOutQualifier(const TSourceLoc &loc);
    TLayoutQualifier joinLayoutQualifiers(TLayoutQualifier leftQualifier,
                                          TLayoutQualifier rightQualifier,
                                          const TSourceLoc &rightQualifierLocation);

    // Performs an error check for embedded struct declarations.
    void enterStructDeclaration(const TSourceLoc &line, const ImmutableString &identifier);
    void exitStructDeclaration();

    void checkIsBelowStructNestingLimit(const TSourceLoc &line, const TField &field);

    TIntermSwitch *addSwitch(TIntermTyped *init,
                             TIntermBlock *statementList,
                             const TSourceLoc &loc);
    TIntermCase *addCase(TIntermTyped *condition, const TSourceLoc &loc);
    TIntermCase *addDefault(const TSourceLoc &loc);

    TIntermTyped *addUnaryMath(TOperator op, TIntermTyped *child, const TSourceLoc &loc);
    TIntermTyped *addUnaryMathLValue(TOperator op, TIntermTyped *child, const TSourceLoc &loc);
    TIntermTyped *addBinaryMath(TOperator op,
                                TIntermTyped *left,
                                TIntermTyped *right,
                                const TSourceLoc &loc);
    TIntermTyped *addBinaryMathBooleanResult(TOperator op,
                                             TIntermTyped *left,
                                             TIntermTyped *right,
                                             const TSourceLoc &loc);
    TIntermTyped *addAssign(TOperator op,
                            TIntermTyped *left,
                            TIntermTyped *right,
                            const TSourceLoc &loc);

    TIntermTyped *addComma(TIntermTyped *left, TIntermTyped *right, const TSourceLoc &loc);

    TIntermBranch *addBranch(TOperator op, const TSourceLoc &loc);
    TIntermBranch *addBranch(TOperator op, TIntermTyped *expression, const TSourceLoc &loc);

    void appendStatement(TIntermBlock *block, TIntermNode *statement);

    void checkTextureGather(TIntermAggregate *functionCall);
    void checkTextureOffset(TIntermAggregate *functionCall);
    void checkImageMemoryAccessForBuiltinFunctions(TIntermAggregate *functionCall);
    void checkImageMemoryAccessForUserDefinedFunctions(const TFunction *functionDefinition,
                                                       const TIntermAggregate *functionCall);
    void checkAtomicMemoryBuiltinFunctions(TIntermAggregate *functionCall);
    void checkInterpolationFS(TIntermAggregate *functionCall);

    // fnCall is only storing the built-in op, and function name or constructor type. arguments
    // has the arguments.
    TIntermTyped *addFunctionCallOrMethod(TFunctionLookup *fnCall, const TSourceLoc &loc);

    TIntermTyped *addTernarySelection(TIntermTyped *cond,
                                      TIntermTyped *trueExpression,
                                      TIntermTyped *falseExpression,
                                      const TSourceLoc &line);

    int getGeometryShaderMaxVertices() const { return mGeometryShaderMaxVertices; }
    int getGeometryShaderInvocations() const
    {
        return (mGeometryShaderInvocations > 0) ? mGeometryShaderInvocations : 1;
    }
    TLayoutPrimitiveType getGeometryShaderInputPrimitiveType() const
    {
        return mGeometryShaderInputPrimitiveType;
    }
    TLayoutPrimitiveType getGeometryShaderOutputPrimitiveType() const
    {
        return mGeometryShaderOutputPrimitiveType;
    }
    int getTessControlShaderOutputVertices() const { return mTessControlShaderOutputVertices; }
    TLayoutTessEvaluationType getTessEvaluationShaderInputPrimitiveType() const
    {
        return mTessEvaluationShaderInputPrimitiveType;
    }
    TLayoutTessEvaluationType getTessEvaluationShaderInputVertexSpacingType() const
    {
        return mTessEvaluationShaderInputVertexSpacingType;
    }
    TLayoutTessEvaluationType getTessEvaluationShaderInputOrderingType() const
    {
        return mTessEvaluationShaderInputOrderingType;
    }
    TLayoutTessEvaluationType getTessEvaluationShaderInputPointType() const
    {
        return mTessEvaluationShaderInputPointType;
    }

    const TVector<TType *> &getDeferredArrayTypesToSize() const
    {
        return mDeferredArrayTypesToSize;
    }

    void markShaderHasPrecise() { mHasAnyPreciseType = true; }
    bool hasAnyPreciseType() const { return mHasAnyPreciseType; }
    AdvancedBlendEquations getAdvancedBlendEquations() const { return mAdvancedBlendEquations; }

    ShShaderOutput getOutputType() const { return mOutputType; }

    // TODO(jmadill): make this private
    TSymbolTable &symbolTable;  // symbol table that goes with the language currently being parsed

  private:
    class AtomicCounterBindingState;
    constexpr static size_t kAtomicCounterSize = 4;
    // UNIFORM_ARRAY_STRIDE for atomic counter arrays is an implementation-dependent value which
    // can be queried after a program is linked according to ES 3.10 section 7.7.1. This is
    // controversial with the offset inheritance as described in ESSL 3.10 section 4.4.6. Currently
    // we treat it as always 4 in favour of the original interpretation in
    // "ARB_shader_atomic_counters".
    // TODO(jie.a.chen@intel.com): Double check this once the spec vagueness is resolved.
    // Note that there may be tests in AtomicCounter_test that will need to be updated as well.
    constexpr static size_t kAtomicCounterArrayStride = 4;

    void markStaticReadIfSymbol(TIntermNode *node);

    // Returns a clamped index. If it prints out an error message, the token is "[]".
    int checkIndexLessThan(bool outOfRangeIndexIsError,
                           const TSourceLoc &location,
                           int index,
                           int arraySize,
                           const char *reason);

    bool declareVariable(const TSourceLoc &line,
                         const ImmutableString &identifier,
                         const TType *type,
                         TVariable **variable);

    void checkCanBeDeclaredWithoutInitializer(const TSourceLoc &line,
                                              const ImmutableString &identifier,
                                              TType *type);

    TParameter parseParameterDeclarator(TType *type,
                                        const ImmutableString &name,
                                        const TSourceLoc &nameLoc);

    bool checkIsValidTypeAndQualifierForArray(const TSourceLoc &indexLocation,
                                              const TPublicType &elementType);
    // Done for all atomic counter declarations, whether empty or not.
    void atomicCounterQualifierErrorCheck(const TPublicType &publicType,
                                          const TSourceLoc &location);

    // Assumes that multiplication op has already been set based on the types.
    bool isMultiplicationTypeCombinationValid(TOperator op, const TType &left, const TType &right);

    void checkOutParameterIsNotOpaqueType(const TSourceLoc &line,
                                          TQualifier qualifier,
                                          const TType &type);

    void checkInternalFormatIsNotSpecified(const TSourceLoc &location,
                                           TLayoutImageInternalFormat internalFormat);
    void checkMemoryQualifierIsNotSpecified(const TMemoryQualifier &memoryQualifier,
                                            const TSourceLoc &location);
    void checkAtomicCounterOffsetDoesNotOverlap(bool forceAppend,
                                                const TSourceLoc &loc,
                                                TType *type);
    void checkAtomicCounterOffsetAlignment(const TSourceLoc &location, const TType &type);

    void checkIndexIsNotSpecified(const TSourceLoc &location, int index);
    void checkBindingIsValid(const TSourceLoc &identifierLocation, const TType &type);
    void checkBindingIsNotSpecified(const TSourceLoc &location, int binding);
    void checkOffsetIsNotSpecified(const TSourceLoc &location, int offset);
    void checkImageBindingIsValid(const TSourceLoc &location,
                                  int binding,
                                  int arrayTotalElementCount);
    void checkSamplerBindingIsValid(const TSourceLoc &location,
                                    int binding,
                                    int arrayTotalElementCount);
    void checkBlockBindingIsValid(const TSourceLoc &location,
                                  const TQualifier &qualifier,
                                  int binding,
                                  int arraySize);
    void checkAtomicCounterBindingIsValid(const TSourceLoc &location, int binding);
    void checkPixelLocalStorageBindingIsValid(const TSourceLoc &, const TType &);

    void checkUniformLocationInRange(const TSourceLoc &location,
                                     int objectLocationCount,
                                     const TLayoutQualifier &layoutQualifier);
    void checkAttributeLocationInRange(const TSourceLoc &location,
                                       int objectLocationCount,
                                       const TLayoutQualifier &layoutQualifier);

    void checkDepthIsNotSpecified(const TSourceLoc &location, TLayoutDepth depth);

    void checkYuvIsNotSpecified(const TSourceLoc &location, bool yuv);

    void checkEarlyFragmentTestsIsNotSpecified(const TSourceLoc &location, bool earlyFragmentTests);

    void checkNoncoherentIsSpecified(const TSourceLoc &location, bool noncoherent);

    void checkNoncoherentIsNotSpecified(const TSourceLoc &location, bool noncoherent);

    bool checkUnsizedArrayConstructorArgumentDimensionality(const TIntermSequence &arguments,
                                                            TType type,
                                                            const TSourceLoc &line);

    // Check texture offset is within range.
    void checkSingleTextureOffset(const TSourceLoc &line,
                                  const TConstantUnion *values,
                                  size_t size,
                                  int minOffsetValue,
                                  int maxOffsetValue);

    // Will set the size of the outermost array according to geometry shader input layout.
    void checkGeometryShaderInputAndSetArraySize(const TSourceLoc &location,
                                                 const ImmutableString &token,
                                                 TType *type);

    // Similar, for tessellation shaders.
    void checkTessellationShaderUnsizedArraysAndSetSize(const TSourceLoc &location,
                                                        const ImmutableString &token,
                                                        TType *type);

    // Will size any unsized array type so unsized arrays won't need to be taken into account
    // further along the line in parsing.
    void checkIsNotUnsizedArray(const TSourceLoc &line,
                                const char *errorMessage,
                                const ImmutableString &token,
                                TType *arrayType);

    TIntermTyped *addBinaryMathInternal(TOperator op,
                                        TIntermTyped *left,
                                        TIntermTyped *right,
                                        const TSourceLoc &loc);
    TIntermTyped *createUnaryMath(TOperator op,
                                  TIntermTyped *child,
                                  const TSourceLoc &loc,
                                  const TFunction *func);

    TIntermTyped *addMethod(TFunctionLookup *fnCall, const TSourceLoc &loc);
    TIntermTyped *addConstructor(TFunctionLookup *fnCall, const TSourceLoc &line);
    TIntermTyped *addNonConstructorFunctionCall(TFunctionLookup *fnCall, const TSourceLoc &loc);

    // Return either the original expression or the folded version of the expression in case the
    // folded node will validate the same way during subsequent parsing.
    TIntermTyped *expressionOrFoldedResult(TIntermTyped *expression);

    // Return true if the checks pass
    bool binaryOpCommonCheck(TOperator op,
                             TIntermTyped *left,
                             TIntermTyped *right,
                             const TSourceLoc &loc);

    TIntermFunctionPrototype *createPrototypeNodeFromFunction(const TFunction &function,
                                                              const TSourceLoc &location,
                                                              bool insertParametersToSymbolTable);

    void setAtomicCounterBindingDefaultOffset(const TPublicType &declaration,
                                              const TSourceLoc &location);

    bool checkPrimitiveTypeMatchesTypeQualifier(const TTypeQualifier &typeQualifier);
    bool parseGeometryShaderInputLayoutQualifier(const TTypeQualifier &typeQualifier);
    bool parseGeometryShaderOutputLayoutQualifier(const TTypeQualifier &typeQualifier);
    void setGeometryShaderInputArraySize(unsigned int inputArraySize, const TSourceLoc &line);

    bool parseTessControlShaderOutputLayoutQualifier(const TTypeQualifier &typeQualifier);
    bool parseTessEvaluationShaderInputLayoutQualifier(const TTypeQualifier &typeQualifier);

    // Certain operations become illegal only iff the shader declares pixel local storage uniforms.
    enum class PLSIllegalOperations
    {
        // When polyfilled with shader images, pixel local storage requires early_fragment_tests,
        // which causes discard to interact differently with the depth and stencil tests.
        //
        // To ensure identical behavior across all backends (some of which may not have access to
        // early_fragment_tests), we disallow discard if pixel local storage uniforms have been
        // declared.
        Discard,

        // ARB_fragment_shader_interlock functions cannot be called within flow control, which
        // includes any code that might execute after a return statement. To keep things simple, and
        // since these "interlock" calls are automatically injected by the compiler inside of
        // main(), we disallow return from main() if pixel local storage uniforms have been
        // declared.
        ReturnFromMain,

        // When polyfilled with shader images, pixel local storage requires early_fragment_tests,
        // which causes assignments to gl_FragDepth(EXT) and gl_SampleMask to be ignored.
        //
        // To ensure identical behavior across all backends, we disallow assignment to these values
        // if pixel local storage uniforms have been declared.
        AssignFragDepth,
        AssignSampleMask,

        // EXT_blend_func_extended may restrict the number of draw buffers with a nonzero output
        // index, which can invalidate a PLS implementation.
        FragDataIndexNonzero,

        // KHR_blend_equation_advanced is incompatible with multiple draw buffers, which is a
        // required feature for many PLS implementations.
        EnableAdvancedBlendEquation,
    };

    // Generates an error if any pixel local storage uniforms have been declared (more specifically,
    // if mPLSBindings is not empty).
    //
    // If no pixel local storage uniforms have been declared, and if the PLS extension is enabled,
    // saves the potential error to mPLSPotentialErrors in case we encounter a PLS uniform later.
    void errorIfPLSDeclared(const TSourceLoc &, PLSIllegalOperations);

    // Set to true when the last/current declarator list was started with an empty declaration. The
    // non-empty declaration error check will need to be performed if the empty declaration is
    // followed by a declarator.
    bool mDeferredNonEmptyDeclarationErrorCheck;

    sh::GLenum mShaderType;    // vertex/fragment/geometry/etc shader
    ShShaderSpec mShaderSpec;  // The language specification compiler conforms to - GLES/WebGL/etc.
    ShCompileOptions mCompileOptions;  // Options passed to TCompiler
    int mShaderVersion;
    TIntermBlock *mTreeRoot;  // root of parse tree being created
    int mLoopNestingLevel;    // 0 if outside all loops
    int mStructNestingLevel;  // incremented while parsing a struct declaration
    int mSwitchNestingLevel;  // 0 if outside all switch statements
    const TType
        *mCurrentFunctionType;    // the return type of the function that's currently being parsed
    bool mFunctionReturnsValue;   // true if a non-void function has a return
    bool mChecksPrecisionErrors;  // true if an error will be generated when a variable is declared
                                  // without precision, explicit or implicit.
    bool mFragmentPrecisionHighOnESSL1;  // true if highp precision is supported when compiling
                                         // ESSL1.
    bool mEarlyFragmentTestsSpecified;   // true if layout(early_fragment_tests) in; is specified.
    bool mHasDiscard;                    // true if |discard| is encountered in the shader.
    bool mSampleQualifierSpecified;      // true if the |sample| qualifier is used
    bool mPositionRedeclaredForSeparateShaderObject;       // true if EXT_separate_shader_objects is
                                                           // enabled and gl_Position is redefined.
    bool mPointSizeRedeclaredForSeparateShaderObject;      // true if EXT_separate_shader_objects is
                                                           // enabled and gl_PointSize is redefined.
    bool mPositionOrPointSizeUsedForSeparateShaderObject;  // true if gl_Position or gl_PointSize
                                                           // has been referenced.
    TLayoutMatrixPacking mDefaultUniformMatrixPacking;
    TLayoutBlockStorage mDefaultUniformBlockStorage;
    TLayoutMatrixPacking mDefaultBufferMatrixPacking;
    TLayoutBlockStorage mDefaultBufferBlockStorage;
    TString mHashErrMsg;
    TDiagnostics *mDiagnostics;
    TDirectiveHandler mDirectiveHandler;
    angle::pp::Preprocessor mPreprocessor;
    void *mScanner;
    int mMinProgramTexelOffset;
    int mMaxProgramTexelOffset;

    int mMinProgramTextureGatherOffset;
    int mMaxProgramTextureGatherOffset;

    // keep track of local group size declared in layout. It should be declared only once.
    bool mComputeShaderLocalSizeDeclared;
    sh::WorkGroupSize mComputeShaderLocalSize;
    // keep track of number of views declared in layout.
    int mNumViews;
    int mMaxNumViews;
    int mMaxImageUnits;
    int mMaxCombinedTextureImageUnits;
    int mMaxUniformLocations;
    int mMaxUniformBufferBindings;
    int mMaxVertexAttribs;
    int mMaxAtomicCounterBindings;
    int mMaxShaderStorageBufferBindings;

    // keeps track whether we are declaring / defining a function
    bool mDeclaringFunction;

    // keeps track whether we are declaring / defining the function main().
    bool mDeclaringMain;

    // Track the state of each atomic counter binding.
    std::map<int, AtomicCounterBindingState> mAtomicCounterBindingStates;

    // Track the format of each pixel local storage binding.
    std::map<int, TLayoutImageInternalFormat> mPLSBindings;

    // Potential errors to generate immediately upon encountering a pixel local storage uniform.
    std::vector<std::tuple<const TSourceLoc, PLSIllegalOperations>> mPLSPotentialErrors;

    // Track the geometry shader global parameters declared in layout.
    TLayoutPrimitiveType mGeometryShaderInputPrimitiveType;
    TLayoutPrimitiveType mGeometryShaderOutputPrimitiveType;
    int mGeometryShaderInvocations;
    int mGeometryShaderMaxVertices;
    int mMaxGeometryShaderInvocations;
    int mMaxGeometryShaderMaxVertices;
    unsigned int mGeometryInputArraySize;

    int mMaxPatchVertices;
    int mTessControlShaderOutputVertices;
    TLayoutTessEvaluationType mTessEvaluationShaderInputPrimitiveType;
    TLayoutTessEvaluationType mTessEvaluationShaderInputVertexSpacingType;
    TLayoutTessEvaluationType mTessEvaluationShaderInputOrderingType;
    TLayoutTessEvaluationType mTessEvaluationShaderInputPointType;
    // List of array declarations without an explicit size that have come before layout(vertices=N).
    // Once the vertex count is specified, these arrays are sized.
    TVector<TType *> mDeferredArrayTypesToSize;
    // Whether the |precise| keyword has been seen in the shader.
    bool mHasAnyPreciseType;

    AdvancedBlendEquations mAdvancedBlendEquations;

    // Track when we add new scope for func body in ESSL 1.00 spec
    bool mFunctionBodyNewScope;

    ShShaderOutput mOutputType;
};

int PaParseStrings(size_t count,
                   const char *const string[],
                   const int length[],
                   TParseContext *context);

}  // namespace sh

#endif  // COMPILER_TRANSLATOR_PARSECONTEXT_H_
