/*
//
// Copyright (c) 2002-2012 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 generate_parser.sh,
WHICH GENERATES THE GLSL ES LEXER (glslang_lex.cpp).
*/

%top{
//
// Copyright (c) 2012 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 is auto-generated by generate_parser.sh. DO NOT EDIT!

// Ignore errors in auto-generated code.
#if defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wunused-function"
#pragma GCC diagnostic ignored "-Wunused-variable"
#pragma GCC diagnostic ignored "-Wswitch-enum"
#elif defined(_MSC_VER)
#pragma warning(disable: 4065)
#pragma warning(disable: 4189)
#pragma warning(disable: 4505)
#pragma warning(disable: 4701)
#endif

#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunknown-pragmas"
#pragma clang diagnostic ignored "-Wdeprecated-register"
#endif
}

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

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

#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);
static int int_constant(yyscan_t yyscanner);
static int float_constant(yyscan_t yyscanner);
%}

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

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

%%

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

"attribute"    { return ATTRIBUTE; }
"const"        { return CONST_QUAL; }
"uniform"      { return UNIFORM; }
"varying"      { return VARYING; }

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

"if"           { return IF; }
"else"         { return ELSE; }

"in"           { return IN_QUAL; }
"out"          { return OUT_QUAL; }
"inout"        { return INOUT_QUAL; }

"float"        { return FLOAT_TYPE; }
"int"          { return INT_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; }

"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; }

"sampler2D"          { return SAMPLER2D; }
"samplerCube"        { return SAMPLERCUBE; }
"samplerExternalOES" { return SAMPLER_EXTERNAL_OES; }
"sampler2DRect"      { return SAMPLER2DRECT; }

"struct"       { return STRUCT; }

"asm"          { return reserved_word(yyscanner); }

"class"        { return reserved_word(yyscanner); }
"union"        { return reserved_word(yyscanner); }
"enum"         { return reserved_word(yyscanner); }
"typedef"      { return reserved_word(yyscanner); }
"template"     { return reserved_word(yyscanner); }
"this"         { return reserved_word(yyscanner); }
"packed"       { return reserved_word(yyscanner); }

"goto"         { return reserved_word(yyscanner); }
"switch"       { return reserved_word(yyscanner); }
"default"      { return reserved_word(yyscanner); }

"inline"       { return reserved_word(yyscanner); }
"noinline"     { return reserved_word(yyscanner); }
"volatile"     { return reserved_word(yyscanner); }
"public"       { return reserved_word(yyscanner); }
"static"       { return reserved_word(yyscanner); }
"extern"       { return reserved_word(yyscanner); }
"external"     { return reserved_word(yyscanner); }
"interface"    { return reserved_word(yyscanner); }
"flat"         { return reserved_word(yyscanner); }

"long"         { return reserved_word(yyscanner); }
"short"        { return reserved_word(yyscanner); }
"double"       { return reserved_word(yyscanner); }
"half"         { return reserved_word(yyscanner); }
"fixed"        { return reserved_word(yyscanner); }
"unsigned"     { return reserved_word(yyscanner); }
"superp"       { return reserved_word(yyscanner); }

"input"        { return reserved_word(yyscanner); }
"output"       { return reserved_word(yyscanner); }

"hvec2"        { return reserved_word(yyscanner); }
"hvec3"        { return reserved_word(yyscanner); }
"hvec4"        { return reserved_word(yyscanner); }
"dvec2"        { return reserved_word(yyscanner); }
"dvec3"        { return reserved_word(yyscanner); }
"dvec4"        { return reserved_word(yyscanner); }
"fvec2"        { return reserved_word(yyscanner); }
"fvec3"        { return reserved_word(yyscanner); }
"fvec4"        { return reserved_word(yyscanner); }

"sampler1D"           { return reserved_word(yyscanner); }
"sampler3D"           { return reserved_word(yyscanner); }
"sampler1DShadow"     { return reserved_word(yyscanner); }
"sampler2DShadow"     { return reserved_word(yyscanner); }
"sampler3DRect"       { return reserved_word(yyscanner); }
"sampler2DRectShadow" { return reserved_word(yyscanner); }

"sizeof"       { return reserved_word(yyscanner); }
"cast"         { return reserved_word(yyscanner); }

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

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

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

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

"+="            { 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; }
"."         { 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; }

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

%%

#if defined(__clang__)
#pragma clang diagnostic pop
#endif

yy_size_t string_input(char* buf, yy_size_t max_size, yyscan_t yyscanner) {
    pp::Token token;
    yyget_extra(yyscanner)->preprocessor.lex(&token);
    yy_size_t len = token.type == 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;
    TSymbol* symbol = yyextra->symbolTable.find(yytext);
    if (symbol && symbol->isVariable()) {
        TVariable* variable = static_cast<TVariable*>(symbol);
        if (variable->isUserType())
            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, "");
    yyextra->recover();
    return 0;
}

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

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

    if (!atoi_clamp(yytext, &(yylval->lex.i)))
        yyextra->warning(*yylloc, "Integer overflow", yytext, "");
    return INTCONSTANT;
}

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

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

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

    context->scanner = scanner;
    return 0;
}

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

    return 0;
}

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

    // Initialize preprocessor.
    if (!context->preprocessor.init(count, string, length))
        return 1;
    context->preprocessor.setMaxTokenLength(SH_MAX_TOKEN_LENGTH);

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

    return 0;
}

