/*
//
// 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.
//

This file contains the Lex specification for GLSL ES.
Based on ANSI C grammar, Lex specification:
http://www.lysator.liu.se/c/ANSI-C-grammar-l.html

IF YOU MODIFY THIS FILE YOU ALSO NEED TO RUN scripts/run_code_generation.py
WHICH GENERATES THE GLSL ES LEXER (glslang_lex_autogen.cpp).
*/

%top{
// GENERATED FILE - DO NOT EDIT.
// Generated by generate_parser.py from glslang.l
//
// Copyright 2019 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.
//
// glslang.l:
//   Lexer for the OpenGL shading language.

// Ignore errors in auto-generated code.
#if defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wswitch-enum"
#pragma GCC diagnostic ignored "-Wunused-function"
#pragma GCC diagnostic ignored "-Wunused-variable"
#elif defined(_MSC_VER)
#pragma warning(disable: 4005)
#pragma warning(disable: 4065)
#pragma warning(disable: 4189)
#pragma warning(disable: 4244)
#pragma warning(disable: 4505)
#pragma warning(disable: 4701)
#pragma warning(disable: 4702)
#endif
#if defined(__clang__)
#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
#if defined(__APPLE__)
// Older clang versions don't have -Wextra-semi-stmt, and detecting Apple clang versions is
// difficult because they use different yet overlapping version numbers vs. regular clang.
#pragma clang diagnostic ignored "-Wunknown-warning-option"
#endif
// Flex isn't semi-colon clean.
#pragma clang diagnostic ignored "-Wextra-semi-stmt"
#pragma clang diagnostic ignored "-Wunreachable-code"
#endif
}

%{
#include "compiler/translator/glslang.h"
#include "compiler/translator/ParseContext.h"
#include "compiler/preprocessor/Token.h"
#include "compiler/translator/util.h"
#include "compiler/translator/length_limits.h"

using namespace sh;

#include "glslang_tab_autogen.h"

/* windows only pragma */
#ifdef _MSC_VER
#pragma warning(disable : 4102)
#endif

// Workaround for flex using the register keyword, deprecated in C++11.
#ifdef __cplusplus
#if __cplusplus > 199711L
#define register
#endif
#endif

#define YY_NO_INPUT
#define YY_USER_ACTION                                 \
    yylloc->first_file = yylloc->last_file = yycolumn; \
    yylloc->first_line = yylloc->last_line = yylineno;

#define YY_INPUT(buf, result, max_size) \
    result = string_input(buf, max_size, yyscanner);

static yy_size_t string_input(char* buf, yy_size_t max_size, yyscan_t yyscanner);
static int check_type(yyscan_t yyscanner);
static int reserved_word(yyscan_t yyscanner);
// Tests if an extension is enabled.  If the extension is promoted to core, this function returns true.
static bool is_extension_enabled_or_is_core(TParseContext *context,
        int extension_version, TExtension extension, int promotion_version);
// Helpers to determine if a symbol is reserved, keyword in extension or core, or identifier.
// Formatted as:
//
//    [V1_reserved_][V2_extension_][V3_keyword]
//
// which means in version V1, the symbol is reserved, and remains reserved until V3.  From versions
// V2 until V3, it's a keyword if the extension is enabled.  From version V3 on, it's a keyword in
// the spec itself.  Prior to V1, the symbol can be used as identifier.
static int ES2_reserved_ES3_keyword(TParseContext *context, int token);
static int ES2_keyword_ES3_reserved(TParseContext *context, int token);
static int ES3_keyword(TParseContext *context, int token);
static int ES3_reserved_ES3_1_keyword(TParseContext *context, int token);
static int ES2_reserved_ES3_1_keyword(TParseContext *context, int token);
static int ES3_1_keyword(TParseContext *context, int token);
static int ES2_reserved_ES2_extension_ES3_keyword(TParseContext *context, TExtension extension, int token);
static int ES3_extension(TParseContext *context, TExtension extension, int token);
static int ES3_reserved_ES3_1_extension_ES3_2_keyword(TParseContext *context, TExtension extension, int token);
static int ES3_reserved_ES3_extension_ES3_1_keyword(TParseContext *context, TExtension extension, int token);
static int ES3_1_reserved_ES3_1_extension_ES3_2_keyword(TParseContext *context, TExtension extension, int token);
static int WEBGL_video_texture_extension(TParseContext *context, int token);
static int uint_constant(TParseContext *context);
static int int_constant(TParseContext *context);
static int float_constant(yyscan_t yyscanner);
static int floatsuffix_check(TParseContext* context);
static int yuvcscstandardext_constant(TParseContext *context);
%}

%option noyywrap nounput never-interactive
%option yylineno reentrant bison-bridge bison-locations
%option extra-type="TParseContext*"
%x FIELDS

D           [0-9]
L           [a-zA-Z_]
H           [a-fA-F0-9]
E           [Ee][+-]?{D}+
O           [0-7]

%%

%{
    TParseContext* context = yyextra;
%}

"invariant"    { return INVARIANT; }
"highp"        { return HIGH_PRECISION; }
"mediump"      { return MEDIUM_PRECISION; }
"lowp"         { return LOW_PRECISION; }
"precision"    { return PRECISION; }

"attribute"    { return ES2_keyword_ES3_reserved(context, ATTRIBUTE); }
"const"        { return CONST_QUAL; }
"uniform"      { return UNIFORM; }
"buffer"       { return ES3_1_keyword(context, BUFFER); }
"varying"      { return ES2_keyword_ES3_reserved(context, VARYING); }

"break"        { return BREAK; }
"continue"     { return CONTINUE; }
"do"           { return DO; }
"for"          { return FOR; }
"while"        { return WHILE; }

"if"           { return IF; }
"else"         { return ELSE; }
"switch"       { return ES2_reserved_ES3_keyword(context, SWITCH); }
"case"         { return ES3_keyword(context, CASE); }
"default"      { return ES2_reserved_ES3_keyword(context, DEFAULT); }

"centroid"     { return ES3_keyword(context, CENTROID); }
"flat"         { return ES2_reserved_ES3_keyword(context, FLAT); }
"smooth"       { return ES3_keyword(context, SMOOTH); }

"in"           { return IN_QUAL; }
"out"          { return OUT_QUAL; }
"inout"        { return INOUT_QUAL; }
"shared"       { return ES3_1_keyword(context, SHARED); }

"float"        { return FLOAT_TYPE; }
"int"          { return INT_TYPE; }
"uint"         { return ES3_keyword(context, UINT_TYPE); }
"void"         { return VOID_TYPE; }
"bool"         { return BOOL_TYPE; }
"true"         { yylval->lex.b = true;  return BOOLCONSTANT; }
"false"        { yylval->lex.b = false; return BOOLCONSTANT; }

"discard"      { return DISCARD; }
"return"       { return RETURN; }

"mat2"         { return MATRIX2; }
"mat3"         { return MATRIX3; }
"mat4"         { return MATRIX4; }

"mat2x2"         { return ES3_keyword(context, MATRIX2); }
"mat3x3"         { return ES3_keyword(context, MATRIX3); }
"mat4x4"         { return ES3_keyword(context, MATRIX4); }

"mat2x3"         { return ES3_keyword(context, MATRIX2x3); }
"mat3x2"         { return ES3_keyword(context, MATRIX3x2); }
"mat2x4"         { return ES3_keyword(context, MATRIX2x4); }
"mat4x2"         { return ES3_keyword(context, MATRIX4x2); }
"mat3x4"         { return ES3_keyword(context, MATRIX3x4); }
"mat4x3"         { return ES3_keyword(context, MATRIX4x3); }

"vec2"         { return VEC2; }
"vec3"         { return VEC3; }
"vec4"         { return VEC4; }
"ivec2"        { return IVEC2; }
"ivec3"        { return IVEC3; }
"ivec4"        { return IVEC4; }
"bvec2"        { return BVEC2; }
"bvec3"        { return BVEC3; }
"bvec4"        { return BVEC4; }
"uvec2"        { return ES3_keyword(context, UVEC2); }
"uvec3"        { return ES3_keyword(context, UVEC3); }
"uvec4"        { return ES3_keyword(context, UVEC4); }

"sampler2D"            { return SAMPLER2D; }
"samplerCube"          { return SAMPLERCUBE; }
"samplerExternalOES"   { return SAMPLER_EXTERNAL_OES; }
"sampler3D"            { return ES2_reserved_ES2_extension_ES3_keyword(context, TExtension::OES_texture_3D, SAMPLER3D); }
"sampler3DRect"        { return ES2_reserved_ES3_keyword(context, SAMPLER3DRECT); }
"sampler2DRect"        { return SAMPLER2DRECT; }
"sampler2DArray"       { return ES3_keyword(context, SAMPLER2DARRAY); }
"sampler2DMS"          { return ES3_reserved_ES3_extension_ES3_1_keyword(context, TExtension::ANGLE_texture_multisample, SAMPLER2DMS); }
"isampler2D"           { return ES3_keyword(context, ISAMPLER2D); }
"isampler3D"           { return ES3_keyword(context, ISAMPLER3D); }
"isamplerCube"         { return ES3_keyword(context, ISAMPLERCUBE); }
"isampler2DArray"      { return ES3_keyword(context, ISAMPLER2DARRAY); }
"isampler2DMS"         { return ES3_reserved_ES3_extension_ES3_1_keyword(context, TExtension::ANGLE_texture_multisample, ISAMPLER2DMS); }
"usampler2D"           { return ES3_keyword(context, USAMPLER2D); }
"usampler3D"           { return ES3_keyword(context, USAMPLER3D); }
"usamplerCube"         { return ES3_keyword(context, USAMPLERCUBE); }
"usampler2DArray"      { return ES3_keyword(context, USAMPLER2DARRAY); }
"usampler2DMS"         { return ES3_reserved_ES3_extension_ES3_1_keyword(context, TExtension::ANGLE_texture_multisample, USAMPLER2DMS); }
"sampler2DShadow"      { return ES2_reserved_ES3_keyword(context, SAMPLER2DSHADOW); }
"samplerCubeShadow"    { return ES3_keyword(context, SAMPLERCUBESHADOW); }
"sampler2DArrayShadow" { return ES3_keyword(context, SAMPLER2DARRAYSHADOW); }
"__samplerExternal2DY2YEXT"   { return ES3_extension(context, TExtension::EXT_YUV_target, SAMPLEREXTERNAL2DY2YEXT); }
"sampler2DMSArray"     { return ES3_reserved_ES3_1_extension_ES3_2_keyword(context, TExtension::OES_texture_storage_multisample_2d_array, SAMPLER2DMSARRAY); }
"isampler2DMSArray"    { return ES3_reserved_ES3_1_extension_ES3_2_keyword(context, TExtension::OES_texture_storage_multisample_2d_array, ISAMPLER2DMSARRAY); }
"usampler2DMSArray"    { return ES3_reserved_ES3_1_extension_ES3_2_keyword(context, TExtension::OES_texture_storage_multisample_2d_array, USAMPLER2DMSARRAY); }
"samplerVideoWEBGL"    { return WEBGL_video_texture_extension(context, SAMPLERVIDEOWEBGL); }
"struct"       { return STRUCT; }

"layout"  { return ES3_keyword(context, LAYOUT); }

"yuvCscStandardEXT"    { return ES3_extension(context, TExtension::EXT_YUV_target, YUVCSCSTANDARDEXT); }
"itu_601"              { return yuvcscstandardext_constant(context); }
"itu_601_full_range"   { return yuvcscstandardext_constant(context); }
"itu_709"              { return yuvcscstandardext_constant(context); }

"image2D" { return ES3_reserved_ES3_1_keyword(context, IMAGE2D); }
"iimage2D" { return ES3_reserved_ES3_1_keyword(context, IIMAGE2D); }
"uimage2D" { return ES3_reserved_ES3_1_keyword(context, UIMAGE2D); }
"image2DArray" { return ES3_reserved_ES3_1_keyword(context, IMAGE2DARRAY); }
"iimage2DArray" { return ES3_reserved_ES3_1_keyword(context, IIMAGE2DARRAY); }
"uimage2DArray" { return ES3_reserved_ES3_1_keyword(context, UIMAGE2DARRAY); }
"image3D"  { return ES3_reserved_ES3_1_keyword(context, IMAGE3D); }
"uimage3D" { return ES3_reserved_ES3_1_keyword(context, UIMAGE3D); }
"iimage3D" { return ES3_reserved_ES3_1_keyword(context, IIMAGE3D); }
"iimageCube" { return ES3_reserved_ES3_1_keyword(context, IIMAGECUBE); }
"uimageCube" { return ES3_reserved_ES3_1_keyword(context, UIMAGECUBE); }
"imageCube" { return ES3_reserved_ES3_1_keyword(context, IMAGECUBE); }
"readonly" { return ES3_reserved_ES3_1_keyword(context, READONLY); }
"writeonly" { return ES3_reserved_ES3_1_keyword(context, WRITEONLY); }
"coherent" { return ES3_reserved_ES3_1_keyword(context, COHERENT); }
"restrict" { return ES3_reserved_ES3_1_keyword(context, RESTRICT); }
"volatile" { return ES2_reserved_ES3_1_keyword(context, VOLATILE); }
"atomic_uint" { return ES3_reserved_ES3_1_keyword(context, ATOMICUINT); }

"precise" { return ES3_1_reserved_ES3_1_extension_ES3_2_keyword(context, TExtension::EXT_gpu_shader5, PRECISE); }

    /* Reserved keywords for GLSL ES 3.00 that are not reserved for GLSL ES 1.00 */
"resource"          |
"noperspective"     |
"patch"             |
"sample"            |
"subroutine"        |
"common"            |
"partition"         |
"active"            |

"filter"            |
"image1D"           |
"iimage1D"          |
"uimage1D"          |
"image1DArray"      |
"iimage1DArray"     |
"uimage1DArray"     |
"image1DShadow"     |
"image2DShadow"     |
"image1DArrayShadow" |
"image2DArrayShadow" |
"imageBuffer"       |
"iimageBuffer"      |
"uimageBuffer"      |

"sampler1DArray"    |
"sampler1DArrayShadow" |
"isampler1D"        |
"isampler1DArray"   |
"usampler1D"        |
"usampler1DArray"   |
"isampler2DRect"    |
"usampler2DRect"    |
"samplerBuffer"     |
"isamplerBuffer"    |
"usamplerBuffer"    {
    if (context->getShaderVersion() < 300) {
        yylval->lex.string = AllocatePoolCharArray(yytext, yyleng);
        return check_type(yyscanner);
    }
    return reserved_word(yyscanner);
}

    /* Reserved keywords in GLSL ES 1.00 that are not reserved in GLSL ES 3.00 */
"packed"  {
    if (context->getShaderVersion() >= 300)
    {
        yylval->lex.string = AllocatePoolCharArray(yytext, yyleng);
        return check_type(yyscanner);
    }

    return reserved_word(yyscanner);
}

    /* Reserved keywords */
"asm"          |

"class"        |
"union"        |
"enum"         |
"typedef"      |
"template"     |
"this"         |

"goto"         |

"inline"       |
"noinline"     |
"public"       |
"static"       |
"extern"       |
"external"     |
"interface"    |

"long"         |
"short"        |
"double"       |
"half"         |
"fixed"        |
"unsigned"     |
"superp"       |

"input"        |
"output"       |

"hvec2"        |
"hvec3"        |
"hvec4"        |
"dvec2"        |
"dvec3"        |
"dvec4"        |
"fvec2"        |
"fvec3"        |
"fvec4"        |

"sampler1D"    |
"sampler1DShadow" |
"sampler2DRectShadow" |

"sizeof"       |
"cast"         |

"namespace"    |
"using"        { return reserved_word(yyscanner); }

{L}({L}|{D})*       {
   yylval->lex.string = AllocatePoolCharArray(yytext, yyleng);
   return check_type(yyscanner);
}

0[xX]{H}+         { return int_constant(context); }
0{O}+             { return int_constant(context); }
{D}+              { return int_constant(context); }

0[xX]{H}+[uU]     { return uint_constant(context); }
0{O}+[uU]         { return uint_constant(context); }
{D}+[uU]          { return uint_constant(context); }

{D}+{E}           { return float_constant(yyscanner); }
{D}+"."{D}*({E})? { return float_constant(yyscanner); }
"."{D}+({E})?     { return float_constant(yyscanner); }

{D}+{E}[fF]           { return floatsuffix_check(context); }
{D}+"."{D}*({E})?[fF] { return floatsuffix_check(context); }
"."{D}+({E})?[fF]     { return floatsuffix_check(context); }

"+="            { return ADD_ASSIGN; }
"-="            { return SUB_ASSIGN; }
"*="            { return MUL_ASSIGN; }
"/="            { return DIV_ASSIGN; }
"%="            { return MOD_ASSIGN; }
"<<="           { return LEFT_ASSIGN; }
">>="           { return RIGHT_ASSIGN; }
"&="            { return AND_ASSIGN; }
"^="            { return XOR_ASSIGN; }
"|="            { return OR_ASSIGN; }

"++"            { return INC_OP; }
"--"            { return DEC_OP; }
"&&"            { return AND_OP; }
"||"            { return OR_OP; }
"^^"            { return XOR_OP; }
"<="            { return LE_OP; }
">="            { return GE_OP; }
"=="            { return EQ_OP; }
"!="            { return NE_OP; }
"<<"            { return LEFT_OP; }
">>"            { return RIGHT_OP; }
";"             { return SEMICOLON; }
("{"|"<%")      { return LEFT_BRACE; }
("}"|"%>")      { return RIGHT_BRACE; }
","             { return COMMA; }
":"             { return COLON; }
"="             { return EQUAL; }
"("             { return LEFT_PAREN; }
")"             { return RIGHT_PAREN; }
("["|"<:")      { return LEFT_BRACKET; }
("]"|":>")      { return RIGHT_BRACKET; }
"."             { BEGIN(FIELDS); return DOT; }
"!"             { return BANG; }
"-"             { return DASH; }
"~"             { return TILDE; }
"+"             { return PLUS; }
"*"             { return STAR; }
"/"             { return SLASH; }
"%"             { return PERCENT; }
"<"             { return LEFT_ANGLE; }
">"             { return RIGHT_ANGLE; }
"|"             { return VERTICAL_BAR; }
"^"             { return CARET; }
"&"             { return AMPERSAND; }
"?"             { return QUESTION; }

<FIELDS>{L}({L}|{D})* {
    BEGIN(INITIAL);
    yylval->lex.string = AllocatePoolCharArray(yytext, yyleng);
    return FIELD_SELECTION;
}
<FIELDS>[ \t\v\f\r] {}
<FIELDS>. {
    yyextra->error(*yylloc, "Illegal character at fieldname start", yytext);
    return 0;
}

[ \t\v\n\f\r] { }
<*><<EOF>>    { yyterminate(); }
<*>.          { assert(false); return 0; }

%%

yy_size_t string_input(char* buf, yy_size_t max_size, yyscan_t yyscanner) {
    angle::pp::Token token;
    yyget_extra(yyscanner)->getPreprocessor().lex(&token);
    yy_size_t len = token.type == angle::pp::Token::LAST ? 0 : token.text.size();
    if (len < max_size)
        memcpy(buf, token.text.c_str(), len);
    yyset_column(token.location.file, yyscanner);
    yyset_lineno(token.location.line, yyscanner);

    if (len >= max_size)
        YY_FATAL_ERROR("Input buffer overflow");
    else if (len > 0)
        buf[len++] = ' ';
    return len;
}

int check_type(yyscan_t yyscanner) {
    struct yyguts_t* yyg = (struct yyguts_t*) yyscanner;

    int token = IDENTIFIER;
    // Note that the ImmutableString used here isn't static or pool allocated - but it's fine since yytext is valid for the duration of its use.
    const TSymbol* symbol = yyextra->symbolTable.find(ImmutableString(yytext, yyleng), yyextra->getShaderVersion());
    if (symbol && symbol->isStruct())
    {
        token = TYPE_NAME;
    }
    yylval->lex.symbol = symbol;
    return token;
}

int reserved_word(yyscan_t yyscanner) {
    struct yyguts_t* yyg = (struct yyguts_t*) yyscanner;

    yyextra->error(*yylloc, "Illegal use of reserved word", yytext);
    return 0;
}

static bool is_extension_enabled_or_is_core(TParseContext *context,
        int extension_version, TExtension extension, int promotion_version)
{
    int version = context->getShaderVersion();

    // If version is at least promotion_version, symbol is definitely keyword.  Otherwise it's a
    // keyword if version is at least extension_version (where the extension was introduced) and
    // the extension is enabled.
    return version >= promotion_version ||
        (version >= extension_version && context->isExtensionEnabled(extension));
}

int ES2_reserved_ES3_keyword(TParseContext *context, int token)
{
    yyscan_t yyscanner = (yyscan_t) context->getScanner();

    if (context->getShaderVersion() < 300)
    {
        return reserved_word(yyscanner);
    }

    return token;
}

int ES2_keyword_ES3_reserved(TParseContext *context, int token)
{
    yyscan_t yyscanner = (yyscan_t) context->getScanner();

    if (context->getShaderVersion() >= 300)
    {
        return reserved_word(yyscanner);
    }

    return token;
}

int ES3_reserved_ES3_1_keyword(TParseContext *context, int token)
{
    struct yyguts_t* yyg = (struct yyguts_t*) context->getScanner();
    yyscan_t yyscanner = (yyscan_t) context->getScanner();

    if (context->getShaderVersion() < 300)
    {
        yylval->lex.string = AllocatePoolCharArray(yytext, yyleng);
        return check_type(yyscanner);
    }
    else if (context->getShaderVersion() == 300)
    {
        return reserved_word(yyscanner);
    }

    return token;
}

int ES3_keyword(TParseContext *context, int token)
{
    struct yyguts_t* yyg = (struct yyguts_t*) context->getScanner();
    yyscan_t yyscanner = (yyscan_t) context->getScanner();

    // not a reserved word in GLSL ES 1.00, so could be used as an identifier/type name
    if (context->getShaderVersion() < 300)
    {
        yylval->lex.string = AllocatePoolCharArray(yytext, yyleng);
        return check_type(yyscanner);
    }

    return token;
}

int ES2_reserved_ES3_1_keyword(TParseContext *context, int token)
{
    yyscan_t yyscanner = (yyscan_t) context->getScanner();

    if (context->getShaderVersion() < 310)
    {
        return reserved_word(yyscanner);
    }

    return token;
}

int ES3_1_keyword(TParseContext *context, int token)
{
    struct yyguts_t* yyg = (struct yyguts_t*) context->getScanner();
    yyscan_t yyscanner = (yyscan_t) context->getScanner();

    // A keyword in GLSL ES 3.10.
    if (context->getShaderVersion() >= 310)
    {
        return token;
    }

    // Otherwise can be used as an identifier/type name
    yylval->lex.string = AllocatePoolCharArray(yytext, yyleng);
    return check_type(yyscanner);
}

int WEBGL_video_texture_extension(TParseContext *context, int token)
{
    // Available with WEBGL_video_texture_extension
    if (context->isExtensionEnabled(TExtension::WEBGL_video_texture))
    {
        return token;
    }

    // Otherwise can be used as an identifier/type name
    struct yyguts_t* yyg = (struct yyguts_t*) context->getScanner();
    yyscan_t yyscanner = (yyscan_t) context->getScanner();

    yylval->lex.string = AllocatePoolCharArray(yytext, yyleng);
    return check_type(yyscanner);
}

int ES2_reserved_ES2_extension_ES3_keyword(TParseContext *context, TExtension extension, int token)
{
    yyscan_t yyscanner = (yyscan_t) context->getScanner();

    // A keyword in GLSL ES 3.00 or GLSL ES 1.00 with enabled extension.
    if (is_extension_enabled_or_is_core(context, 100, extension, 300))
    {
        return token;
    }

    // Reserved otherwise.
    return reserved_word(yyscanner);
}

int ES3_extension(TParseContext *context, TExtension extension, int token)
{
    struct yyguts_t* yyg = (struct yyguts_t*) context->getScanner();
    yyscan_t yyscanner = (yyscan_t) context->getScanner();

    // a keyword word in GLSL ES 3.00 with enabled extension.
    if (context->getShaderVersion() >= 300 && context->isExtensionEnabled(extension))
    {
        return token;
    }

    // Otherwise can be used as an identifier/type name
    yylval->lex.string = AllocatePoolCharArray(yytext, yyleng);
    return check_type(yyscanner);
}

int ES3_reserved_ES3_1_extension_ES3_2_keyword(TParseContext *context, TExtension extension, int token)
{
    struct yyguts_t* yyg = (struct yyguts_t*) context->getScanner();
    yyscan_t yyscanner = (yyscan_t) context->getScanner();

    // a keyword in GLSL ES 3.10 with enabled extension
    if (is_extension_enabled_or_is_core(context, 310, extension, 320))
    {
        return token;
    }
    // a reserved word in GLSL ES 3.00+
    if (context->getShaderVersion() >= 300)
    {
        return reserved_word(yyscanner);
    }

    // Otherwise can be used as an identifier/type name
    yylval->lex.string = AllocatePoolCharArray(yytext, yyleng);
    return check_type(yyscanner);
}

int ES3_reserved_ES3_extension_ES3_1_keyword(TParseContext *context, TExtension extension, int token)
{
    struct yyguts_t* yyg = (struct yyguts_t*) context->getScanner();
    yyscan_t yyscanner = (yyscan_t) context->getScanner();

    // A keyword in GLSL ES 3.00 with enabled extension or in GLSL ES 3.10
    if (is_extension_enabled_or_is_core(context, 300, extension, 310))
    {
        return token;
    }

    if(context->getShaderVersion() == 300)
    {
        return reserved_word(yyscanner);
    }

    yylval->lex.string = AllocatePoolCharArray(yytext, yyleng);
    return check_type(yyscanner);
}

static int ES3_1_reserved_ES3_1_extension_ES3_2_keyword(TParseContext *context, TExtension extension, int token)
{
    struct yyguts_t* yyg = (struct yyguts_t*) context->getScanner();
    yyscan_t yyscanner = (yyscan_t) context->getScanner();

    // A keyword in GLSL ES 3.20 or GLSL ES 3.10 with enabled extension.
    if (is_extension_enabled_or_is_core(context, 310, extension, 320))
    {
        return token;
    }

    // A reserved word in GLSL ES 3.10
    if (context->getShaderVersion() == 310)
    {
        return reserved_word(yyscanner);
    }

    yylval->lex.string = AllocatePoolCharArray(yytext, yyleng);
    return check_type(yyscanner);
}

int uint_constant(TParseContext *context)
{
    struct yyguts_t* yyg = (struct yyguts_t*) context->getScanner();

    if (context->getShaderVersion() < 300)
    {
        context->error(*yylloc, "Unsigned integers are unsupported prior to GLSL ES 3.00", yytext);
        return 0;
    }

    if (!atoi_clamp(yytext, &(yylval->lex.u)))
        yyextra->error(*yylloc, "Integer overflow", yytext);

    return UINTCONSTANT;
}

int floatsuffix_check(TParseContext* context)
{
    struct yyguts_t* yyg = (struct yyguts_t*) context->getScanner();

    if (context->getShaderVersion() < 300)
    {
        context->error(*yylloc, "Floating-point suffix unsupported prior to GLSL ES 3.00", yytext);
        return 0;
    }

    std::string text = yytext;
    text.resize(text.size() - 1);
    if (!strtof_clamp(text, &(yylval->lex.f)))
        yyextra->warning(*yylloc, "Float overflow", yytext);

    return(FLOATCONSTANT);
}

void yyerror(YYLTYPE* lloc, TParseContext* context, void *scanner, const char* reason) {
    context->error(*lloc, reason, yyget_text(scanner));
}

int int_constant(TParseContext *context) {
    struct yyguts_t* yyg = (struct yyguts_t*) context->getScanner();

    unsigned int u;
    if (!atoi_clamp(yytext, &u))
    {
        if (context->getShaderVersion() >= 300)
            yyextra->error(*yylloc, "Integer overflow", yytext);
        else
            yyextra->warning(*yylloc, "Integer overflow", yytext);
    }
    yylval->lex.i = static_cast<int>(u);
    return INTCONSTANT;
}

int float_constant(yyscan_t yyscanner) {
    struct yyguts_t* yyg = (struct yyguts_t*) yyscanner;

    if (!strtof_clamp(yytext, &(yylval->lex.f)))
        yyextra->warning(*yylloc, "Float overflow", yytext);
    return FLOATCONSTANT;
}

int yuvcscstandardext_constant(TParseContext *context)
{
    struct yyguts_t* yyg = (struct yyguts_t*) context->getScanner();
    yyscan_t yyscanner = (yyscan_t) context->getScanner();

    // a reserved word in GLSL ES 3.00 with enabled extension, otherwise could be used as an identifier/type name
    if (context->getShaderVersion() >= 300 && context->isExtensionEnabled(TExtension::EXT_YUV_target))
    {
        yylval->lex.string = AllocatePoolCharArray(yytext, yyleng);
        return YUVCSCSTANDARDEXTCONSTANT;
    }

    yylval->lex.string = AllocatePoolCharArray(yytext, yyleng);
    return check_type(yyscanner);
}

int glslang_initialize(TParseContext* context) {
    yyscan_t scanner = NULL;
    if (yylex_init_extra(context, &scanner))
        return 1;

    context->setScanner(scanner);
    return 0;
}

int glslang_finalize(TParseContext* context) {
    yyscan_t scanner = context->getScanner();
    if (scanner == NULL) return 0;
    
    context->setScanner(NULL);
    yylex_destroy(scanner);

    return 0;
}

int glslang_scan(size_t count, const char* const string[], const int length[],
                 TParseContext* context) {
    yyrestart(NULL, context->getScanner());
    yyset_column(0, context->getScanner());
    yyset_lineno(1, context->getScanner());

    // Initialize preprocessor.
    angle::pp::Preprocessor *preprocessor = &context->getPreprocessor();

    if (!preprocessor->init(count, string, length))
        return 1;

    // Define extension macros.
    const TExtensionBehavior& extBehavior = context->extensionBehavior();
    for (TExtensionBehavior::const_iterator iter = extBehavior.begin();
         iter != extBehavior.end(); ++iter) {
        preprocessor->predefineMacro(GetExtensionNameString(iter->first), 1);
    }
    if (context->getFragmentPrecisionHigh())
        preprocessor->predefineMacro("GL_FRAGMENT_PRECISION_HIGH", 1);

    preprocessor->setMaxTokenSize(sh::GetGlobalMaxTokenSize(context->getShaderSpec()));

    return 0;
}
