//
// Copyright (c) 2002-2014 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/translator/Compiler.h"
#include "compiler/translator/Diagnostics.h"
#include "compiler/translator/DirectiveHandler.h"
#include "compiler/translator/Intermediate.h"
#include "compiler/translator/SymbolTable.h"
#include "compiler/preprocessor/Preprocessor.h"

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,
                  TIntermediate &interm,
                  sh::GLenum type,
                  ShShaderSpec spec,
                  int options,
                  bool checksPrecErrors,
                  TInfoSink &is,
                  bool debugShaderPrecisionSupported)
        : intermediate(interm),
          symbolTable(symt),
          mDeferredSingleDeclarationErrorCheck(false),
          mShaderType(type),
          mShaderSpec(spec),
          mShaderVersion(100),
          mTreeRoot(nullptr),
          mLoopNestingLevel(0),
          mStructNestingLevel(0),
          mSwitchNestingLevel(0),
          mCurrentFunctionType(nullptr),
          mFunctionReturnsValue(false),
          mChecksPrecisionErrors(checksPrecErrors),
          mFragmentPrecisionHighOnESSL1(false),
          mDefaultMatrixPacking(EmpColumnMajor),
          mDefaultBlockStorage(EbsShared),
          mDiagnostics(is),
          mDirectiveHandler(ext, mDiagnostics, mShaderVersion, debugShaderPrecisionSupported),
          mPreprocessor(&mDiagnostics, &mDirectiveHandler),
          mScanner(nullptr),
          mUsesFragData(false),
          mUsesFragColor(false),
          mUsesSecondaryOutputs(false)
    {
    }

    const pp::Preprocessor &getPreprocessor() const { return mPreprocessor; }
    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(); }
    TInfoSink &infoSink() { return mDiagnostics.infoSink(); }
    void error(const TSourceLoc &loc, const char *reason, const char *token,
               const char *extraInfo="");
    void warning(const TSourceLoc &loc, const char *reason, const char *token,
                 const char *extraInfo="");

    void recover();
    TIntermNode *getTreeRoot() const { return mTreeRoot; }
    void setTreeRoot(TIntermNode *treeRoot) { mTreeRoot = treeRoot; }

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

    bool getFunctionReturnsValue() const { return mFunctionReturnsValue; }
    void setFunctionReturnsValue(bool functionReturnsValue)
    {
        mFunctionReturnsValue = functionReturnsValue;
    }

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

    const TType *getCurrentFunctionType() const { return mCurrentFunctionType; }
    void setCurrentFunctionType(const TType *currentFunctionType)
    {
        mCurrentFunctionType = currentFunctionType;
    }

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

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

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

    bool parseVectorFields(const TString&, int vecSize, TVectorFields&, const TSourceLoc &line);

    bool reservedErrorCheck(const TSourceLoc &line, const TString &identifier);
    void assignError(const TSourceLoc &line, const char *op, TString left, TString right);
    void unaryOpError(const TSourceLoc &line, const char *op, TString operand);
    void binaryOpError(const TSourceLoc &line, const char *op, TString left, TString right);
    bool precisionErrorCheck(const TSourceLoc &line, TPrecision precision, TBasicType type);
    bool lValueErrorCheck(const TSourceLoc &line, const char *op, TIntermTyped*);
    bool constErrorCheck(TIntermTyped *node);
    bool integerErrorCheck(TIntermTyped *node, const char *token);
    bool globalErrorCheck(const TSourceLoc &line, bool global, const char *token);
    bool constructorErrorCheck(const TSourceLoc &line, TIntermNode*, TFunction&, TOperator, TType*);
    bool arraySizeErrorCheck(const TSourceLoc &line, TIntermTyped *expr, int &size);
    bool arrayQualifierErrorCheck(const TSourceLoc &line, const TPublicType &type);
    bool arrayTypeErrorCheck(const TSourceLoc &line, const TPublicType &type);
    bool voidErrorCheck(const TSourceLoc &line, const TString &identifier, const TBasicType &type);
    bool boolErrorCheck(const TSourceLoc&, const TIntermTyped*);
    bool boolErrorCheck(const TSourceLoc&, const TPublicType&);
    bool samplerErrorCheck(const TSourceLoc &line, const TPublicType &pType, const char *reason);
    bool locationDeclaratorListCheck(const TSourceLoc &line, const TPublicType &pType);
    bool parameterSamplerErrorCheck(const TSourceLoc &line, TQualifier qualifier, const TType &type);
    bool paramErrorCheck(const TSourceLoc &line, TQualifier qualifier, TQualifier paramQualifier, TType *type);
    bool extensionErrorCheck(const TSourceLoc &line, const TString&);
    bool singleDeclarationErrorCheck(const TPublicType &publicType, const TSourceLoc &identifierLocation);
    bool layoutLocationErrorCheck(const TSourceLoc &location, const TLayoutQualifier &layoutQualifier);
    bool functionCallLValueErrorCheck(const TFunction *fnCandidate, TIntermAggregate *);
    void es3InvariantErrorCheck(const TQualifier qualifier, const TSourceLoc &invariantLocation);
    void es3InputOutputTypeCheck(const TQualifier qualifier,
                                 const TPublicType &type,
                                 const TSourceLoc &qualifierLocation);

    const TPragma &pragma() const { return mDirectiveHandler.pragma(); }
    const TExtensionBehavior &extensionBehavior() const { return mDirectiveHandler.extensionBehavior(); }
    bool supportsExtension(const char *extension);
    bool isExtensionEnabled(const char *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);

    bool containsSampler(const TType &type);
    bool areAllChildConst(TIntermAggregate *aggrNode);
    const TFunction* findFunction(
        const TSourceLoc &line, TFunction *pfnCall, int inputShaderVersion, bool *builtIn = 0);
    bool executeInitializer(const TSourceLoc &line,
                            const TString &identifier,
                            const TPublicType &pType,
                            TIntermTyped *initializer,
                            TIntermNode **intermNode);

    TPublicType addFullySpecifiedType(TQualifier qualifier,
                                      bool invariant,
                                      TLayoutQualifier layoutQualifier,
                                      const TPublicType &typeSpecifier);

    TIntermAggregate *parseSingleDeclaration(TPublicType &publicType,
                                             const TSourceLoc &identifierOrTypeLocation,
                                             const TString &identifier);
    TIntermAggregate *parseSingleArrayDeclaration(TPublicType &publicType,
                                                  const TSourceLoc &identifierLocation,
                                                  const TString &identifier,
                                                  const TSourceLoc &indexLocation,
                                                  TIntermTyped *indexExpression);
    TIntermAggregate *parseSingleInitDeclaration(const TPublicType &publicType,
                                                 const TSourceLoc &identifierLocation,
                                                 const TString &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"
    TIntermAggregate *parseSingleArrayInitDeclaration(TPublicType &publicType,
                                                      const TSourceLoc &identifierLocation,
                                                      const TString &identifier,
                                                      const TSourceLoc &indexLocation,
                                                      TIntermTyped *indexExpression,
                                                      const TSourceLoc &initLocation,
                                                      TIntermTyped *initializer);

    TIntermAggregate *parseInvariantDeclaration(const TSourceLoc &invariantLoc,
                                                const TSourceLoc &identifierLoc,
                                                const TString *identifier,
                                                const TSymbol *symbol);

    TIntermAggregate *parseDeclarator(TPublicType &publicType,
                                      TIntermAggregate *aggregateDeclaration,
                                      const TSourceLoc &identifierLocation,
                                      const TString &identifier);
    TIntermAggregate *parseArrayDeclarator(TPublicType &publicType,
                                           TIntermAggregate *aggregateDeclaration,
                                           const TSourceLoc &identifierLocation,
                                           const TString &identifier,
                                           const TSourceLoc &arrayLocation,
                                           TIntermTyped *indexExpression);
    TIntermAggregate *parseInitDeclarator(const TPublicType &publicType,
                                          TIntermAggregate *aggregateDeclaration,
                                          const TSourceLoc &identifierLocation,
                                          const TString &identifier,
                                          const TSourceLoc &initLocation,
                                          TIntermTyped *initializer);

    // Parse a declarator like "a[n] = initializer"
    TIntermAggregate *parseArrayInitDeclarator(const TPublicType &publicType,
                                               TIntermAggregate *aggregateDeclaration,
                                               const TSourceLoc &identifierLocation,
                                               const TString &identifier,
                                               const TSourceLoc &indexLocation,
                                               TIntermTyped *indexExpression,
                                               const TSourceLoc &initLocation,
                                               TIntermTyped *initializer);

    void parseGlobalLayoutQualifier(const TPublicType &typeQualifier);
    void parseFunctionPrototype(const TSourceLoc &location,
                                TFunction *function,
                                TIntermAggregate **aggregateOut);
    TFunction *parseFunctionDeclarator(const TSourceLoc &location,
                                       TFunction *function);
    TFunction *addConstructorFunc(const TPublicType &publicType);
    TIntermTyped *addConstructor(TIntermNode *arguments,
                                 TType *type,
                                 TOperator op,
                                 TFunction *fnCall,
                                 const TSourceLoc &line);
    TIntermTyped *foldConstConstructor(TIntermAggregate *aggrNode, const TType &type);
    TIntermTyped *addConstVectorNode(TVectorFields&, TIntermTyped*, const TSourceLoc&);
    TIntermTyped *addConstMatrixNode(int, TIntermTyped*, const TSourceLoc&);
    TIntermTyped *addConstArrayNode(int index, TIntermTyped *node, const TSourceLoc &line);
    TIntermTyped *addConstStruct(
        const TString &identifier, TIntermTyped *node, const TSourceLoc& line);
    TIntermTyped *addIndexExpression(TIntermTyped *baseExpression,
                                     const TSourceLoc& location,
                                     TIntermTyped *indexExpression);
    TIntermTyped* addFieldSelectionExpression(TIntermTyped *baseExpression,
                                              const TSourceLoc &dotLocation,
                                              const TString &fieldString,
                                              const TSourceLoc &fieldLocation);

    TFieldList *addStructDeclaratorList(const TPublicType &typeSpecifier, TFieldList *fieldList);
    TPublicType addStructure(const TSourceLoc &structLine,
                             const TSourceLoc &nameLine,
                             const TString *structName,
                             TFieldList *fieldList);

    TIntermAggregate* addInterfaceBlock(const TPublicType &typeQualifier,
                                        const TSourceLoc &nameLine,
                                        const TString &blockName,
                                        TFieldList *fieldList,
                                        const TString *instanceName,
                                        const TSourceLoc &instanceLine,
                                        TIntermTyped *arrayIndex,
                                        const TSourceLoc& arrayIndexLine);

    TLayoutQualifier parseLayoutQualifier(
        const TString &qualifierType, const TSourceLoc &qualifierTypeLine);
    TLayoutQualifier parseLayoutQualifier(const TString &qualifierType,
                                          const TSourceLoc &qualifierTypeLine,
                                          const TString &intValueString,
                                          int intValue,
                                          const TSourceLoc &intValueLine);
    TLayoutQualifier joinLayoutQualifiers(TLayoutQualifier leftQualifier, TLayoutQualifier rightQualifier);
    TPublicType joinInterpolationQualifiers(const TSourceLoc &interpolationLoc, TQualifier interpolationQualifier,
                                            const TSourceLoc &storageLoc, TQualifier storageQualifier);

    // Performs an error check for embedded struct declarations.
    // Returns true if an error was raised due to the declaration of
    // this struct.
    bool enterStructDeclaration(const TSourceLoc &line, const TString &identifier);
    void exitStructDeclaration();

    bool structNestingErrorCheck(const TSourceLoc &line, const TField &field);

    TIntermSwitch *addSwitch(TIntermTyped *init, TIntermAggregate *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);

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

    TIntermTyped *addFunctionCallOrMethod(TFunction *fnCall,
                                          TIntermNode *paramNode,
                                          TIntermNode *thisNode,
                                          const TSourceLoc &loc,
                                          bool *fatalError);

    TIntermTyped *addTernarySelection(
        TIntermTyped *cond, TIntermTyped *trueBlock, TIntermTyped *falseBlock, const TSourceLoc &line);

    // TODO(jmadill): make these private
    TIntermediate &intermediate; // to hold and build a parse tree
    TSymbolTable &symbolTable;   // symbol table that goes with the language currently being parsed

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

    bool nonInitErrorCheck(const TSourceLoc &line, const TString &identifier, TPublicType *type);

    TIntermTyped *addBinaryMathInternal(
        TOperator op, TIntermTyped *left, TIntermTyped *right, const TSourceLoc &loc);
    TIntermTyped *createAssign(
        TOperator op, TIntermTyped *left, TIntermTyped *right, const TSourceLoc &loc);
    // The funcReturnType parameter is expected to be non-null when the operation is a built-in function.
    // It is expected to be null for other unary operators.
    TIntermTyped *createUnaryMath(
        TOperator op, TIntermTyped *child, const TSourceLoc &loc, const TType *funcReturnType);

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

    // Set to true when the last/current declarator list was started with an empty declaration.
    bool mDeferredSingleDeclarationErrorCheck;

    sh::GLenum mShaderType;              // vertex or fragment language (future: pack or unpack)
    ShShaderSpec mShaderSpec;              // The language specification compiler conforms to - GLES2 or WebGL.
    int mShaderVersion;
    TIntermNode *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.
    TLayoutMatrixPacking mDefaultMatrixPacking;
    TLayoutBlockStorage mDefaultBlockStorage;
    TString mHashErrMsg;
    TDiagnostics mDiagnostics;
    TDirectiveHandler mDirectiveHandler;
    pp::Preprocessor mPreprocessor;
    void *mScanner;
    bool mUsesFragData; // track if we are using both gl_FragData and gl_FragColor
    bool mUsesFragColor;
    bool mUsesSecondaryOutputs;  // Track if we are using either gl_SecondaryFragData or
                                 // gl_Secondary FragColor or both.
};

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

#endif // COMPILER_TRANSLATOR_PARSECONTEXT_H_
