//
// Copyright (C) 2016 Google, Inc.
// Copyright (C) 2016 LunarG, 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.
//

//
// HLSL scanning, leveraging the scanning done by the preprocessor.
//

#include <cstring>
#include <unordered_map>
#include <unordered_set>

#include "../glslang/Include/Types.h"
#include "../glslang/MachineIndependent/SymbolTable.h"
#include "../glslang/MachineIndependent/ParseHelper.h"
#include "hlslScanContext.h"
#include "hlslTokens.h"

// preprocessor includes
#include "../glslang/MachineIndependent/preprocessor/PpContext.h"
#include "../glslang/MachineIndependent/preprocessor/PpTokens.h"

namespace {

struct str_eq
{
    bool operator()(const char* lhs, const char* rhs) const
    {
        return strcmp(lhs, rhs) == 0;
    }
};

struct str_hash
{
    size_t operator()(const char* str) const
    {
        // djb2
        unsigned long hash = 5381;
        int c;

        while ((c = *str++) != 0)
            hash = ((hash << 5) + hash) + c;

        return hash;
    }
};

using namespace QtShaderTools;

// A single global usable by all threads, by all versions, by all languages.
// After a single process-level initialization, this is read only and thread safe
std::unordered_map<const char*, glslang::EHlslTokenClass, str_hash, str_eq>* KeywordMap = nullptr;
std::unordered_set<const char*, str_hash, str_eq>* ReservedSet = nullptr;
std::unordered_map<const char*, glslang::TBuiltInVariable, str_hash, str_eq>* SemanticMap = nullptr;

};

namespace glslang {

void HlslScanContext::fillInKeywordMap()
{
    if (KeywordMap != nullptr) {
        // this is really an error, as this should called only once per process
        // but, the only risk is if two threads called simultaneously
        return;
    }
    KeywordMap = new std::unordered_map<const char*, EHlslTokenClass, str_hash, str_eq>;

    (*KeywordMap)["static"] =                  EHTokStatic;
    (*KeywordMap)["const"] =                   EHTokConst;
    (*KeywordMap)["unorm"] =                   EHTokUnorm;
    (*KeywordMap)["snorm"] =                   EHTokSNorm;
    (*KeywordMap)["extern"] =                  EHTokExtern;
    (*KeywordMap)["uniform"] =                 EHTokUniform;
    (*KeywordMap)["volatile"] =                EHTokVolatile;
    (*KeywordMap)["precise"] =                 EHTokPrecise;
    (*KeywordMap)["shared"] =                  EHTokShared;
    (*KeywordMap)["groupshared"] =             EHTokGroupShared;
    (*KeywordMap)["linear"] =                  EHTokLinear;
    (*KeywordMap)["centroid"] =                EHTokCentroid;
    (*KeywordMap)["nointerpolation"] =         EHTokNointerpolation;
    (*KeywordMap)["noperspective"] =           EHTokNoperspective;
    (*KeywordMap)["sample"] =                  EHTokSample;
    (*KeywordMap)["row_major"] =               EHTokRowMajor;
    (*KeywordMap)["column_major"] =            EHTokColumnMajor;
    (*KeywordMap)["packoffset"] =              EHTokPackOffset;
    (*KeywordMap)["in"] =                      EHTokIn;
    (*KeywordMap)["out"] =                     EHTokOut;
    (*KeywordMap)["inout"] =                   EHTokInOut;
    (*KeywordMap)["layout"] =                  EHTokLayout;
    (*KeywordMap)["globallycoherent"] =        EHTokGloballyCoherent;
    (*KeywordMap)["inline"] =                  EHTokInline;

    (*KeywordMap)["point"] =                   EHTokPoint;
    (*KeywordMap)["line"] =                    EHTokLine;
    (*KeywordMap)["triangle"] =                EHTokTriangle;
    (*KeywordMap)["lineadj"] =                 EHTokLineAdj;
    (*KeywordMap)["triangleadj"] =             EHTokTriangleAdj;

    (*KeywordMap)["PointStream"] =             EHTokPointStream;
    (*KeywordMap)["LineStream"] =              EHTokLineStream;
    (*KeywordMap)["TriangleStream"] =          EHTokTriangleStream;

    (*KeywordMap)["InputPatch"] =              EHTokInputPatch;
    (*KeywordMap)["OutputPatch"] =             EHTokOutputPatch;

    (*KeywordMap)["Buffer"] =                  EHTokBuffer;
    (*KeywordMap)["vector"] =                  EHTokVector;
    (*KeywordMap)["matrix"] =                  EHTokMatrix;

    (*KeywordMap)["void"] =                    EHTokVoid;
    (*KeywordMap)["string"] =                  EHTokString;
    (*KeywordMap)["bool"] =                    EHTokBool;
    (*KeywordMap)["int"] =                     EHTokInt;
    (*KeywordMap)["uint"] =                    EHTokUint;
    (*KeywordMap)["uint64_t"] =                EHTokUint64;
    (*KeywordMap)["dword"] =                   EHTokDword;
    (*KeywordMap)["half"] =                    EHTokHalf;
    (*KeywordMap)["float"] =                   EHTokFloat;
    (*KeywordMap)["double"] =                  EHTokDouble;
    (*KeywordMap)["min16float"] =              EHTokMin16float;
    (*KeywordMap)["min10float"] =              EHTokMin10float;
    (*KeywordMap)["min16int"] =                EHTokMin16int;
    (*KeywordMap)["min12int"] =                EHTokMin12int;
    (*KeywordMap)["min16uint"] =               EHTokMin16uint;

    (*KeywordMap)["bool1"] =                   EHTokBool1;
    (*KeywordMap)["bool2"] =                   EHTokBool2;
    (*KeywordMap)["bool3"] =                   EHTokBool3;
    (*KeywordMap)["bool4"] =                   EHTokBool4;
    (*KeywordMap)["float1"] =                  EHTokFloat1;
    (*KeywordMap)["float2"] =                  EHTokFloat2;
    (*KeywordMap)["float3"] =                  EHTokFloat3;
    (*KeywordMap)["float4"] =                  EHTokFloat4;
    (*KeywordMap)["int1"] =                    EHTokInt1;
    (*KeywordMap)["int2"] =                    EHTokInt2;
    (*KeywordMap)["int3"] =                    EHTokInt3;
    (*KeywordMap)["int4"] =                    EHTokInt4;
    (*KeywordMap)["double1"] =                 EHTokDouble1;
    (*KeywordMap)["double2"] =                 EHTokDouble2;
    (*KeywordMap)["double3"] =                 EHTokDouble3;
    (*KeywordMap)["double4"] =                 EHTokDouble4;
    (*KeywordMap)["uint1"] =                   EHTokUint1;
    (*KeywordMap)["uint2"] =                   EHTokUint2;
    (*KeywordMap)["uint3"] =                   EHTokUint3;
    (*KeywordMap)["uint4"] =                   EHTokUint4;

    (*KeywordMap)["half1"] =                   EHTokHalf1;
    (*KeywordMap)["half2"] =                   EHTokHalf2;
    (*KeywordMap)["half3"] =                   EHTokHalf3;
    (*KeywordMap)["half4"] =                   EHTokHalf4;
    (*KeywordMap)["min16float1"] =             EHTokMin16float1;
    (*KeywordMap)["min16float2"] =             EHTokMin16float2;
    (*KeywordMap)["min16float3"] =             EHTokMin16float3;
    (*KeywordMap)["min16float4"] =             EHTokMin16float4;
    (*KeywordMap)["min10float1"] =             EHTokMin10float1;
    (*KeywordMap)["min10float2"] =             EHTokMin10float2;
    (*KeywordMap)["min10float3"] =             EHTokMin10float3;
    (*KeywordMap)["min10float4"] =             EHTokMin10float4;
    (*KeywordMap)["min16int1"] =               EHTokMin16int1;
    (*KeywordMap)["min16int2"] =               EHTokMin16int2;
    (*KeywordMap)["min16int3"] =               EHTokMin16int3;
    (*KeywordMap)["min16int4"] =               EHTokMin16int4;
    (*KeywordMap)["min12int1"] =               EHTokMin12int1;
    (*KeywordMap)["min12int2"] =               EHTokMin12int2;
    (*KeywordMap)["min12int3"] =               EHTokMin12int3;
    (*KeywordMap)["min12int4"] =               EHTokMin12int4;
    (*KeywordMap)["min16uint1"] =              EHTokMin16uint1;
    (*KeywordMap)["min16uint2"] =              EHTokMin16uint2;
    (*KeywordMap)["min16uint3"] =              EHTokMin16uint3;
    (*KeywordMap)["min16uint4"] =              EHTokMin16uint4;

    (*KeywordMap)["bool1x1"] =                 EHTokBool1x1;
    (*KeywordMap)["bool1x2"] =                 EHTokBool1x2;
    (*KeywordMap)["bool1x3"] =                 EHTokBool1x3;
    (*KeywordMap)["bool1x4"] =                 EHTokBool1x4;
    (*KeywordMap)["bool2x1"] =                 EHTokBool2x1;
    (*KeywordMap)["bool2x2"] =                 EHTokBool2x2;
    (*KeywordMap)["bool2x3"] =                 EHTokBool2x3;
    (*KeywordMap)["bool2x4"] =                 EHTokBool2x4;
    (*KeywordMap)["bool3x1"] =                 EHTokBool3x1;
    (*KeywordMap)["bool3x2"] =                 EHTokBool3x2;
    (*KeywordMap)["bool3x3"] =                 EHTokBool3x3;
    (*KeywordMap)["bool3x4"] =                 EHTokBool3x4;
    (*KeywordMap)["bool4x1"] =                 EHTokBool4x1;
    (*KeywordMap)["bool4x2"] =                 EHTokBool4x2;
    (*KeywordMap)["bool4x3"] =                 EHTokBool4x3;
    (*KeywordMap)["bool4x4"] =                 EHTokBool4x4;
    (*KeywordMap)["int1x1"] =                  EHTokInt1x1;
    (*KeywordMap)["int1x2"] =                  EHTokInt1x2;
    (*KeywordMap)["int1x3"] =                  EHTokInt1x3;
    (*KeywordMap)["int1x4"] =                  EHTokInt1x4;
    (*KeywordMap)["int2x1"] =                  EHTokInt2x1;
    (*KeywordMap)["int2x2"] =                  EHTokInt2x2;
    (*KeywordMap)["int2x3"] =                  EHTokInt2x3;
    (*KeywordMap)["int2x4"] =                  EHTokInt2x4;
    (*KeywordMap)["int3x1"] =                  EHTokInt3x1;
    (*KeywordMap)["int3x2"] =                  EHTokInt3x2;
    (*KeywordMap)["int3x3"] =                  EHTokInt3x3;
    (*KeywordMap)["int3x4"] =                  EHTokInt3x4;
    (*KeywordMap)["int4x1"] =                  EHTokInt4x1;
    (*KeywordMap)["int4x2"] =                  EHTokInt4x2;
    (*KeywordMap)["int4x3"] =                  EHTokInt4x3;
    (*KeywordMap)["int4x4"] =                  EHTokInt4x4;
    (*KeywordMap)["uint1x1"] =                 EHTokUint1x1;
    (*KeywordMap)["uint1x2"] =                 EHTokUint1x2;
    (*KeywordMap)["uint1x3"] =                 EHTokUint1x3;
    (*KeywordMap)["uint1x4"] =                 EHTokUint1x4;
    (*KeywordMap)["uint2x1"] =                 EHTokUint2x1;
    (*KeywordMap)["uint2x2"] =                 EHTokUint2x2;
    (*KeywordMap)["uint2x3"] =                 EHTokUint2x3;
    (*KeywordMap)["uint2x4"] =                 EHTokUint2x4;
    (*KeywordMap)["uint3x1"] =                 EHTokUint3x1;
    (*KeywordMap)["uint3x2"] =                 EHTokUint3x2;
    (*KeywordMap)["uint3x3"] =                 EHTokUint3x3;
    (*KeywordMap)["uint3x4"] =                 EHTokUint3x4;
    (*KeywordMap)["uint4x1"] =                 EHTokUint4x1;
    (*KeywordMap)["uint4x2"] =                 EHTokUint4x2;
    (*KeywordMap)["uint4x3"] =                 EHTokUint4x3;
    (*KeywordMap)["uint4x4"] =                 EHTokUint4x4;
    (*KeywordMap)["bool1x1"] =                 EHTokBool1x1;
    (*KeywordMap)["bool1x2"] =                 EHTokBool1x2;
    (*KeywordMap)["bool1x3"] =                 EHTokBool1x3;
    (*KeywordMap)["bool1x4"] =                 EHTokBool1x4;
    (*KeywordMap)["bool2x1"] =                 EHTokBool2x1;
    (*KeywordMap)["bool2x2"] =                 EHTokBool2x2;
    (*KeywordMap)["bool2x3"] =                 EHTokBool2x3;
    (*KeywordMap)["bool2x4"] =                 EHTokBool2x4;
    (*KeywordMap)["bool3x1"] =                 EHTokBool3x1;
    (*KeywordMap)["bool3x2"] =                 EHTokBool3x2;
    (*KeywordMap)["bool3x3"] =                 EHTokBool3x3;
    (*KeywordMap)["bool3x4"] =                 EHTokBool3x4;
    (*KeywordMap)["bool4x1"] =                 EHTokBool4x1;
    (*KeywordMap)["bool4x2"] =                 EHTokBool4x2;
    (*KeywordMap)["bool4x3"] =                 EHTokBool4x3;
    (*KeywordMap)["bool4x4"] =                 EHTokBool4x4;
    (*KeywordMap)["float1x1"] =                EHTokFloat1x1;
    (*KeywordMap)["float1x2"] =                EHTokFloat1x2;
    (*KeywordMap)["float1x3"] =                EHTokFloat1x3;
    (*KeywordMap)["float1x4"] =                EHTokFloat1x4;
    (*KeywordMap)["float2x1"] =                EHTokFloat2x1;
    (*KeywordMap)["float2x2"] =                EHTokFloat2x2;
    (*KeywordMap)["float2x3"] =                EHTokFloat2x3;
    (*KeywordMap)["float2x4"] =                EHTokFloat2x4;
    (*KeywordMap)["float3x1"] =                EHTokFloat3x1;
    (*KeywordMap)["float3x2"] =                EHTokFloat3x2;
    (*KeywordMap)["float3x3"] =                EHTokFloat3x3;
    (*KeywordMap)["float3x4"] =                EHTokFloat3x4;
    (*KeywordMap)["float4x1"] =                EHTokFloat4x1;
    (*KeywordMap)["float4x2"] =                EHTokFloat4x2;
    (*KeywordMap)["float4x3"] =                EHTokFloat4x3;
    (*KeywordMap)["float4x4"] =                EHTokFloat4x4;
    (*KeywordMap)["half1x1"] =                 EHTokHalf1x1;
    (*KeywordMap)["half1x2"] =                 EHTokHalf1x2;
    (*KeywordMap)["half1x3"] =                 EHTokHalf1x3;
    (*KeywordMap)["half1x4"] =                 EHTokHalf1x4;
    (*KeywordMap)["half2x1"] =                 EHTokHalf2x1;
    (*KeywordMap)["half2x2"] =                 EHTokHalf2x2;
    (*KeywordMap)["half2x3"] =                 EHTokHalf2x3;
    (*KeywordMap)["half2x4"] =                 EHTokHalf2x4;
    (*KeywordMap)["half3x1"] =                 EHTokHalf3x1;
    (*KeywordMap)["half3x2"] =                 EHTokHalf3x2;
    (*KeywordMap)["half3x3"] =                 EHTokHalf3x3;
    (*KeywordMap)["half3x4"] =                 EHTokHalf3x4;
    (*KeywordMap)["half4x1"] =                 EHTokHalf4x1;
    (*KeywordMap)["half4x2"] =                 EHTokHalf4x2;
    (*KeywordMap)["half4x3"] =                 EHTokHalf4x3;
    (*KeywordMap)["half4x4"] =                 EHTokHalf4x4;
    (*KeywordMap)["double1x1"] =               EHTokDouble1x1;
    (*KeywordMap)["double1x2"] =               EHTokDouble1x2;
    (*KeywordMap)["double1x3"] =               EHTokDouble1x3;
    (*KeywordMap)["double1x4"] =               EHTokDouble1x4;
    (*KeywordMap)["double2x1"] =               EHTokDouble2x1;
    (*KeywordMap)["double2x2"] =               EHTokDouble2x2;
    (*KeywordMap)["double2x3"] =               EHTokDouble2x3;
    (*KeywordMap)["double2x4"] =               EHTokDouble2x4;
    (*KeywordMap)["double3x1"] =               EHTokDouble3x1;
    (*KeywordMap)["double3x2"] =               EHTokDouble3x2;
    (*KeywordMap)["double3x3"] =               EHTokDouble3x3;
    (*KeywordMap)["double3x4"] =               EHTokDouble3x4;
    (*KeywordMap)["double4x1"] =               EHTokDouble4x1;
    (*KeywordMap)["double4x2"] =               EHTokDouble4x2;
    (*KeywordMap)["double4x3"] =               EHTokDouble4x3;
    (*KeywordMap)["double4x4"] =               EHTokDouble4x4;

    (*KeywordMap)["sampler"] =                 EHTokSampler;
    (*KeywordMap)["sampler1D"] =               EHTokSampler1d;
    (*KeywordMap)["sampler2D"] =               EHTokSampler2d;
    (*KeywordMap)["sampler3D"] =               EHTokSampler3d;
    (*KeywordMap)["samplerCube"] =             EHTokSamplerCube;
    (*KeywordMap)["sampler_state"] =           EHTokSamplerState;
    (*KeywordMap)["SamplerState"] =            EHTokSamplerState;
    (*KeywordMap)["SamplerComparisonState"] =  EHTokSamplerComparisonState;
    (*KeywordMap)["texture"] =                 EHTokTexture;
    (*KeywordMap)["Texture1D"] =               EHTokTexture1d;
    (*KeywordMap)["Texture1DArray"] =          EHTokTexture1darray;
    (*KeywordMap)["Texture2D"] =               EHTokTexture2d;
    (*KeywordMap)["Texture2DArray"] =          EHTokTexture2darray;
    (*KeywordMap)["Texture3D"] =               EHTokTexture3d;
    (*KeywordMap)["TextureCube"] =             EHTokTextureCube;
    (*KeywordMap)["TextureCubeArray"] =        EHTokTextureCubearray;
    (*KeywordMap)["Texture2DMS"] =             EHTokTexture2DMS;
    (*KeywordMap)["Texture2DMSArray"] =        EHTokTexture2DMSarray;
    (*KeywordMap)["RWTexture1D"] =             EHTokRWTexture1d;
    (*KeywordMap)["RWTexture1DArray"] =        EHTokRWTexture1darray;
    (*KeywordMap)["RWTexture2D"] =             EHTokRWTexture2d;
    (*KeywordMap)["RWTexture2DArray"] =        EHTokRWTexture2darray;
    (*KeywordMap)["RWTexture3D"] =             EHTokRWTexture3d;
    (*KeywordMap)["RWBuffer"] =                EHTokRWBuffer;
    (*KeywordMap)["SubpassInput"] =            EHTokSubpassInput;
    (*KeywordMap)["SubpassInputMS"] =          EHTokSubpassInputMS;

    (*KeywordMap)["AppendStructuredBuffer"] =  EHTokAppendStructuredBuffer;
    (*KeywordMap)["ByteAddressBuffer"] =       EHTokByteAddressBuffer;
    (*KeywordMap)["ConsumeStructuredBuffer"] = EHTokConsumeStructuredBuffer;
    (*KeywordMap)["RWByteAddressBuffer"] =     EHTokRWByteAddressBuffer;
    (*KeywordMap)["RWStructuredBuffer"] =      EHTokRWStructuredBuffer;
    (*KeywordMap)["StructuredBuffer"] =        EHTokStructuredBuffer;
    (*KeywordMap)["TextureBuffer"] =           EHTokTextureBuffer;

    (*KeywordMap)["class"] =                   EHTokClass;
    (*KeywordMap)["struct"] =                  EHTokStruct;
    (*KeywordMap)["cbuffer"] =                 EHTokCBuffer;
    (*KeywordMap)["ConstantBuffer"] =          EHTokConstantBuffer;
    (*KeywordMap)["tbuffer"] =                 EHTokTBuffer;
    (*KeywordMap)["typedef"] =                 EHTokTypedef;
    (*KeywordMap)["this"] =                    EHTokThis;
    (*KeywordMap)["namespace"] =               EHTokNamespace;

    (*KeywordMap)["true"] =                    EHTokBoolConstant;
    (*KeywordMap)["false"] =                   EHTokBoolConstant;

    (*KeywordMap)["for"] =                     EHTokFor;
    (*KeywordMap)["do"] =                      EHTokDo;
    (*KeywordMap)["while"] =                   EHTokWhile;
    (*KeywordMap)["break"] =                   EHTokBreak;
    (*KeywordMap)["continue"] =                EHTokContinue;
    (*KeywordMap)["if"] =                      EHTokIf;
    (*KeywordMap)["else"] =                    EHTokElse;
    (*KeywordMap)["discard"] =                 EHTokDiscard;
    (*KeywordMap)["return"] =                  EHTokReturn;
    (*KeywordMap)["switch"] =                  EHTokSwitch;
    (*KeywordMap)["case"] =                    EHTokCase;
    (*KeywordMap)["default"] =                 EHTokDefault;

    // TODO: get correct set here
    ReservedSet = new std::unordered_set<const char*, str_hash, str_eq>;

    ReservedSet->insert("auto");
    ReservedSet->insert("catch");
    ReservedSet->insert("char");
    ReservedSet->insert("const_cast");
    ReservedSet->insert("enum");
    ReservedSet->insert("explicit");
    ReservedSet->insert("friend");
    ReservedSet->insert("goto");
    ReservedSet->insert("long");
    ReservedSet->insert("mutable");
    ReservedSet->insert("new");
    ReservedSet->insert("operator");
    ReservedSet->insert("private");
    ReservedSet->insert("protected");
    ReservedSet->insert("public");
    ReservedSet->insert("reinterpret_cast");
    ReservedSet->insert("short");
    ReservedSet->insert("signed");
    ReservedSet->insert("sizeof");
    ReservedSet->insert("static_cast");
    ReservedSet->insert("template");
    ReservedSet->insert("throw");
    ReservedSet->insert("try");
    ReservedSet->insert("typename");
    ReservedSet->insert("union");
    ReservedSet->insert("unsigned");
    ReservedSet->insert("using");
    ReservedSet->insert("virtual");

    SemanticMap = new std::unordered_map<const char*, glslang::TBuiltInVariable, str_hash, str_eq>;

    // in DX9, all outputs had to have a semantic associated with them, that was either consumed
    // by the system or was a specific register assignment
    // in DX10+, only semantics with the SV_ prefix have any meaning beyond decoration
    // Fxc will only accept DX9 style semantics in compat mode
    // Also, in DX10 if a SV value is present as the input of a stage, but isn't appropriate for that
    // stage, it would just be ignored as it is likely there as part of an output struct from one stage
    // to the next
    bool bParseDX9 = false;
    if (bParseDX9) {
        (*SemanticMap)["PSIZE"] = EbvPointSize;
        (*SemanticMap)["FOG"] =   EbvFogFragCoord;
        (*SemanticMap)["DEPTH"] = EbvFragDepth;
        (*SemanticMap)["VFACE"] = EbvFace;
        (*SemanticMap)["VPOS"] =  EbvFragCoord;
    }

    (*SemanticMap)["SV_POSITION"] =               EbvPosition;
    (*SemanticMap)["SV_VERTEXID"] =               EbvVertexIndex;
    (*SemanticMap)["SV_VIEWPORTARRAYINDEX"] =     EbvViewportIndex;
    (*SemanticMap)["SV_TESSFACTOR"] =             EbvTessLevelOuter;
    (*SemanticMap)["SV_SAMPLEINDEX"] =            EbvSampleId;
    (*SemanticMap)["SV_RENDERTARGETARRAYINDEX"] = EbvLayer;
    (*SemanticMap)["SV_PRIMITIVEID"] =            EbvPrimitiveId;
    (*SemanticMap)["SV_OUTPUTCONTROLPOINTID"] =   EbvInvocationId;
    (*SemanticMap)["SV_ISFRONTFACE"] =            EbvFace;
    (*SemanticMap)["SV_INSTANCEID"] =             EbvInstanceIndex;
    (*SemanticMap)["SV_INSIDETESSFACTOR"] =       EbvTessLevelInner;
    (*SemanticMap)["SV_GSINSTANCEID"] =           EbvInvocationId;
    (*SemanticMap)["SV_DISPATCHTHREADID"] =       EbvGlobalInvocationId;
    (*SemanticMap)["SV_GROUPTHREADID"] =          EbvLocalInvocationId;
    (*SemanticMap)["SV_GROUPINDEX"] =             EbvLocalInvocationIndex;
    (*SemanticMap)["SV_GROUPID"] =                EbvWorkGroupId;
    (*SemanticMap)["SV_DOMAINLOCATION"] =         EbvTessCoord;
    (*SemanticMap)["SV_DEPTH"] =                  EbvFragDepth;
    (*SemanticMap)["SV_COVERAGE"] =               EbvSampleMask;
    (*SemanticMap)["SV_DEPTHGREATEREQUAL"] =      EbvFragDepthGreater;
    (*SemanticMap)["SV_DEPTHLESSEQUAL"] =         EbvFragDepthLesser;
    (*SemanticMap)["SV_STENCILREF"] =             EbvFragStencilRef;
}

void HlslScanContext::deleteKeywordMap()
{
    delete KeywordMap;
    KeywordMap = nullptr;
    delete ReservedSet;
    ReservedSet = nullptr;
    delete SemanticMap;
    SemanticMap = nullptr;
}

// Wrapper for tokenizeClass() to get everything inside the token.
void HlslScanContext::tokenize(HlslToken& token)
{
    EHlslTokenClass tokenClass = tokenizeClass(token);
    token.tokenClass = tokenClass;
}

glslang::TBuiltInVariable HlslScanContext::mapSemantic(const char* upperCase)
{
    auto it = SemanticMap->find(upperCase);
    if (it != SemanticMap->end())
        return it->second;
    else
        return glslang::EbvNone;
}

//
// Fill in token information for the next token, except for the token class.
// Returns the enum value of the token class of the next token found.
// Return 0 (EndOfTokens) on end of input.
//
EHlslTokenClass HlslScanContext::tokenizeClass(HlslToken& token)
{
    do {
        parserToken = &token;
        TPpToken ppToken;
        int token = ppContext.tokenize(ppToken);
        if (token == EndOfInput)
            return EHTokNone;

        tokenText = ppToken.name;
        loc = ppToken.loc;
        parserToken->loc = loc;
        switch (token) {
        case ';':                       return EHTokSemicolon;
        case ',':                       return EHTokComma;
        case ':':                       return EHTokColon;
        case '=':                       return EHTokAssign;
        case '(':                       return EHTokLeftParen;
        case ')':                       return EHTokRightParen;
        case '.':                       return EHTokDot;
        case '!':                       return EHTokBang;
        case '-':                       return EHTokDash;
        case '~':                       return EHTokTilde;
        case '+':                       return EHTokPlus;
        case '*':                       return EHTokStar;
        case '/':                       return EHTokSlash;
        case '%':                       return EHTokPercent;
        case '<':                       return EHTokLeftAngle;
        case '>':                       return EHTokRightAngle;
        case '|':                       return EHTokVerticalBar;
        case '^':                       return EHTokCaret;
        case '&':                       return EHTokAmpersand;
        case '?':                       return EHTokQuestion;
        case '[':                       return EHTokLeftBracket;
        case ']':                       return EHTokRightBracket;
        case '{':                       return EHTokLeftBrace;
        case '}':                       return EHTokRightBrace;
        case '\\':
            parseContext.error(loc, "illegal use of escape character", "\\", "");
            break;

        case PPAtomAddAssign:          return EHTokAddAssign;
        case PPAtomSubAssign:          return EHTokSubAssign;
        case PPAtomMulAssign:          return EHTokMulAssign;
        case PPAtomDivAssign:          return EHTokDivAssign;
        case PPAtomModAssign:          return EHTokModAssign;

        case PpAtomRight:              return EHTokRightOp;
        case PpAtomLeft:               return EHTokLeftOp;

        case PpAtomRightAssign:        return EHTokRightAssign;
        case PpAtomLeftAssign:         return EHTokLeftAssign;
        case PpAtomAndAssign:          return EHTokAndAssign;
        case PpAtomOrAssign:           return EHTokOrAssign;
        case PpAtomXorAssign:          return EHTokXorAssign;

        case PpAtomAnd:                return EHTokAndOp;
        case PpAtomOr:                 return EHTokOrOp;
        case PpAtomXor:                return EHTokXorOp;

        case PpAtomEQ:                 return EHTokEqOp;
        case PpAtomGE:                 return EHTokGeOp;
        case PpAtomNE:                 return EHTokNeOp;
        case PpAtomLE:                 return EHTokLeOp;

        case PpAtomDecrement:          return EHTokDecOp;
        case PpAtomIncrement:          return EHTokIncOp;

        case PpAtomColonColon:         return EHTokColonColon;

        case PpAtomConstInt:           parserToken->i = ppToken.ival;       return EHTokIntConstant;
        case PpAtomConstUint:          parserToken->i = ppToken.ival;       return EHTokUintConstant;
        case PpAtomConstFloat16:       parserToken->d = ppToken.dval;       return EHTokFloat16Constant;
        case PpAtomConstFloat:         parserToken->d = ppToken.dval;       return EHTokFloatConstant;
        case PpAtomConstDouble:        parserToken->d = ppToken.dval;       return EHTokDoubleConstant;
        case PpAtomIdentifier:
        {
            EHlslTokenClass token = tokenizeIdentifier();
            return token;
        }

        case PpAtomConstString: {
            parserToken->string = NewPoolTString(tokenText);
            return EHTokStringConstant;
        }

        case EndOfInput:               return EHTokNone;

        default:
            if (token < PpAtomMaxSingle) {
                char buf[2];
                buf[0] = (char)token;
                buf[1] = 0;
                parseContext.error(loc, "unexpected token", buf, "");
            } else if (tokenText[0] != 0)
                parseContext.error(loc, "unexpected token", tokenText, "");
            else
                parseContext.error(loc, "unexpected token", "", "");
            break;
        }
    } while (true);
}

EHlslTokenClass HlslScanContext::tokenizeIdentifier()
{
    if (ReservedSet->find(tokenText) != ReservedSet->end())
        return reservedWord();

    auto it = KeywordMap->find(tokenText);
    if (it == KeywordMap->end()) {
        // Should have an identifier of some sort
        return identifierOrType();
    }
    keyword = it->second;

    switch (keyword) {

    // qualifiers
    case EHTokStatic:
    case EHTokConst:
    case EHTokSNorm:
    case EHTokUnorm:
    case EHTokExtern:
    case EHTokUniform:
    case EHTokVolatile:
    case EHTokShared:
    case EHTokGroupShared:
    case EHTokLinear:
    case EHTokCentroid:
    case EHTokNointerpolation:
    case EHTokNoperspective:
    case EHTokSample:
    case EHTokRowMajor:
    case EHTokColumnMajor:
    case EHTokPackOffset:
    case EHTokIn:
    case EHTokOut:
    case EHTokInOut:
    case EHTokPrecise:
    case EHTokLayout:
    case EHTokGloballyCoherent:
    case EHTokInline:
        return keyword;

    // primitive types
    case EHTokPoint:
    case EHTokLine:
    case EHTokTriangle:
    case EHTokLineAdj:
    case EHTokTriangleAdj:
        return keyword;

    // stream out types
    case EHTokPointStream:
    case EHTokLineStream:
    case EHTokTriangleStream:
        return keyword;

    // Tessellation patches
    case EHTokInputPatch:
    case EHTokOutputPatch:
        return keyword;

    case EHTokBuffer:
    case EHTokVector:
    case EHTokMatrix:
        return keyword;

    // scalar types
    case EHTokVoid:
    case EHTokString:
    case EHTokBool:
    case EHTokInt:
    case EHTokUint:
    case EHTokUint64:
    case EHTokDword:
    case EHTokHalf:
    case EHTokFloat:
    case EHTokDouble:
    case EHTokMin16float:
    case EHTokMin10float:
    case EHTokMin16int:
    case EHTokMin12int:
    case EHTokMin16uint:

    // vector types
    case EHTokBool1:
    case EHTokBool2:
    case EHTokBool3:
    case EHTokBool4:
    case EHTokFloat1:
    case EHTokFloat2:
    case EHTokFloat3:
    case EHTokFloat4:
    case EHTokInt1:
    case EHTokInt2:
    case EHTokInt3:
    case EHTokInt4:
    case EHTokDouble1:
    case EHTokDouble2:
    case EHTokDouble3:
    case EHTokDouble4:
    case EHTokUint1:
    case EHTokUint2:
    case EHTokUint3:
    case EHTokUint4:
    case EHTokHalf1:
    case EHTokHalf2:
    case EHTokHalf3:
    case EHTokHalf4:
    case EHTokMin16float1:
    case EHTokMin16float2:
    case EHTokMin16float3:
    case EHTokMin16float4:
    case EHTokMin10float1:
    case EHTokMin10float2:
    case EHTokMin10float3:
    case EHTokMin10float4:
    case EHTokMin16int1:
    case EHTokMin16int2:
    case EHTokMin16int3:
    case EHTokMin16int4:
    case EHTokMin12int1:
    case EHTokMin12int2:
    case EHTokMin12int3:
    case EHTokMin12int4:
    case EHTokMin16uint1:
    case EHTokMin16uint2:
    case EHTokMin16uint3:
    case EHTokMin16uint4:

    // matrix types
    case EHTokBool1x1:
    case EHTokBool1x2:
    case EHTokBool1x3:
    case EHTokBool1x4:
    case EHTokBool2x1:
    case EHTokBool2x2:
    case EHTokBool2x3:
    case EHTokBool2x4:
    case EHTokBool3x1:
    case EHTokBool3x2:
    case EHTokBool3x3:
    case EHTokBool3x4:
    case EHTokBool4x1:
    case EHTokBool4x2:
    case EHTokBool4x3:
    case EHTokBool4x4:
    case EHTokInt1x1:
    case EHTokInt1x2:
    case EHTokInt1x3:
    case EHTokInt1x4:
    case EHTokInt2x1:
    case EHTokInt2x2:
    case EHTokInt2x3:
    case EHTokInt2x4:
    case EHTokInt3x1:
    case EHTokInt3x2:
    case EHTokInt3x3:
    case EHTokInt3x4:
    case EHTokInt4x1:
    case EHTokInt4x2:
    case EHTokInt4x3:
    case EHTokInt4x4:
    case EHTokUint1x1:
    case EHTokUint1x2:
    case EHTokUint1x3:
    case EHTokUint1x4:
    case EHTokUint2x1:
    case EHTokUint2x2:
    case EHTokUint2x3:
    case EHTokUint2x4:
    case EHTokUint3x1:
    case EHTokUint3x2:
    case EHTokUint3x3:
    case EHTokUint3x4:
    case EHTokUint4x1:
    case EHTokUint4x2:
    case EHTokUint4x3:
    case EHTokUint4x4:
    case EHTokFloat1x1:
    case EHTokFloat1x2:
    case EHTokFloat1x3:
    case EHTokFloat1x4:
    case EHTokFloat2x1:
    case EHTokFloat2x2:
    case EHTokFloat2x3:
    case EHTokFloat2x4:
    case EHTokFloat3x1:
    case EHTokFloat3x2:
    case EHTokFloat3x3:
    case EHTokFloat3x4:
    case EHTokFloat4x1:
    case EHTokFloat4x2:
    case EHTokFloat4x3:
    case EHTokFloat4x4:
    case EHTokHalf1x1:
    case EHTokHalf1x2:
    case EHTokHalf1x3:
    case EHTokHalf1x4:
    case EHTokHalf2x1:
    case EHTokHalf2x2:
    case EHTokHalf2x3:
    case EHTokHalf2x4:
    case EHTokHalf3x1:
    case EHTokHalf3x2:
    case EHTokHalf3x3:
    case EHTokHalf3x4:
    case EHTokHalf4x1:
    case EHTokHalf4x2:
    case EHTokHalf4x3:
    case EHTokHalf4x4:
    case EHTokDouble1x1:
    case EHTokDouble1x2:
    case EHTokDouble1x3:
    case EHTokDouble1x4:
    case EHTokDouble2x1:
    case EHTokDouble2x2:
    case EHTokDouble2x3:
    case EHTokDouble2x4:
    case EHTokDouble3x1:
    case EHTokDouble3x2:
    case EHTokDouble3x3:
    case EHTokDouble3x4:
    case EHTokDouble4x1:
    case EHTokDouble4x2:
    case EHTokDouble4x3:
    case EHTokDouble4x4:
        return keyword;

    // texturing types
    case EHTokSampler:
    case EHTokSampler1d:
    case EHTokSampler2d:
    case EHTokSampler3d:
    case EHTokSamplerCube:
    case EHTokSamplerState:
    case EHTokSamplerComparisonState:
    case EHTokTexture:
    case EHTokTexture1d:
    case EHTokTexture1darray:
    case EHTokTexture2d:
    case EHTokTexture2darray:
    case EHTokTexture3d:
    case EHTokTextureCube:
    case EHTokTextureCubearray:
    case EHTokTexture2DMS:
    case EHTokTexture2DMSarray:
    case EHTokRWTexture1d:
    case EHTokRWTexture1darray:
    case EHTokRWTexture2d:
    case EHTokRWTexture2darray:
    case EHTokRWTexture3d:
    case EHTokRWBuffer:
    case EHTokAppendStructuredBuffer:
    case EHTokByteAddressBuffer:
    case EHTokConsumeStructuredBuffer:
    case EHTokRWByteAddressBuffer:
    case EHTokRWStructuredBuffer:
    case EHTokStructuredBuffer:
    case EHTokTextureBuffer:
    case EHTokSubpassInput:
    case EHTokSubpassInputMS:
        return keyword;

    // variable, user type, ...
    case EHTokClass:
    case EHTokStruct:
    case EHTokTypedef:
    case EHTokCBuffer:
    case EHTokConstantBuffer:
    case EHTokTBuffer:
    case EHTokThis:
    case EHTokNamespace:
        return keyword;

    case EHTokBoolConstant:
        if (strcmp("true", tokenText) == 0)
            parserToken->b = true;
        else
            parserToken->b = false;
        return keyword;

    // control flow
    case EHTokFor:
    case EHTokDo:
    case EHTokWhile:
    case EHTokBreak:
    case EHTokContinue:
    case EHTokIf:
    case EHTokElse:
    case EHTokDiscard:
    case EHTokReturn:
    case EHTokCase:
    case EHTokSwitch:
    case EHTokDefault:
        return keyword;

    default:
        parseContext.infoSink.info.message(EPrefixInternalError, "Unknown glslang keyword", loc);
        return EHTokNone;
    }
}

EHlslTokenClass HlslScanContext::identifierOrType()
{
    parserToken->string = NewPoolTString(tokenText);

    return EHTokIdentifier;
}

// Give an error for use of a reserved symbol.
// However, allow built-in declarations to use reserved words, to allow
// extension support before the extension is enabled.
EHlslTokenClass HlslScanContext::reservedWord()
{
    if (! parseContext.symbolTable.atBuiltInLevel())
        parseContext.error(loc, "Reserved word.", tokenText, "", "");

    return EHTokNone;
}

} // end namespace glslang
