/*
//
// Copyright (c) 2002-2010 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) 2010 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!
}

%{
#include "compiler/glslang.h"
#include "compiler/ParseHelper.h"
#include "compiler/util.h"
#include "glslang_tab.h"

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

#define YY_USER_ACTION yylval->lex.line = yylineno;
#define YY_INPUT(buf, result, max_size) \
    result = string_input(buf, max_size, yyscanner);

static int string_input(char* buf, int max_size, yyscan_t yyscanner);
static int check_type(yyscan_t yyscanner);
static int reserved_word(yyscan_t yyscanner);
%}

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

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

%%

%{
    TParseContext* context = yyextra;
%}

    /* Single-line comments */
"//"[^\n]* ;

    /* Multi-line comments */
"/*"           { yy_push_state(COMMENT, yyscanner); }
<COMMENT>. |
<COMMENT>\n ;
<COMMENT>"*/"  { yy_pop_state(yyscanner); }

"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"        { context->lexAfterType = true; return(FLOAT_TYPE); }
"int"          { context->lexAfterType = true; return(INT_TYPE); }
"void"         { context->lexAfterType = true; return(VOID_TYPE); }
"bool"         { context->lexAfterType = true; return(BOOL_TYPE); }
"true"         { yylval->lex.b = true;  return(BOOLCONSTANT); }
"false"        { yylval->lex.b = false; return(BOOLCONSTANT); }

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

"mat2"         { context->lexAfterType = true; return(MATRIX2); }
"mat3"         { context->lexAfterType = true; return(MATRIX3); }
"mat4"         { context->lexAfterType = true; return(MATRIX4); }

"vec2"         { context->lexAfterType = true; return (VEC2); }
"vec3"         { context->lexAfterType = true; return (VEC3); }
"vec4"         { context->lexAfterType = true; return (VEC4); }
"ivec2"        { context->lexAfterType = true; return (IVEC2); }
"ivec3"        { context->lexAfterType = true; return (IVEC3); }
"ivec4"        { context->lexAfterType = true; return (IVEC4); }
"bvec2"        { context->lexAfterType = true; return (BVEC2); }
"bvec3"        { context->lexAfterType = true; return (BVEC3); }
"bvec4"        { context->lexAfterType = true; return (BVEC4); }

"sampler2D"       { context->lexAfterType = true; return SAMPLER2D; }
"samplerCube"     { context->lexAfterType = true; return SAMPLERCUBE; }
"samplerExternalOES" { context->lexAfterType = true; return SAMPLER_EXTERNAL_OES; }
"sampler2DRect" { context->lexAfterType = true; return SAMPLER2DRECT; }

"struct"       { context->lexAfterType = true; 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); }

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

"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); }
"fvec2"        { return reserved_word(yyscanner); }
"fvec3"        { return reserved_word(yyscanner); }
"fvec4"        { return reserved_word(yyscanner); }
"dvec2"        { return reserved_word(yyscanner); }
"dvec3"        { return reserved_word(yyscanner); }
"dvec4"        { 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}+         { yylval->lex.i = strtol(yytext, 0, 0); return(INTCONSTANT); }
0{O}+             { yylval->lex.i = strtol(yytext, 0, 0); return(INTCONSTANT); }
0{D}+             { context->error(yylineno, "Invalid Octal number.", yytext, "", ""); context->recover(); return 0;}
{D}+              { yylval->lex.i = strtol(yytext, 0, 0); return(INTCONSTANT); }

{D}+{E}           { yylval->lex.f = static_cast<float>(atof_dot(yytext)); return(FLOATCONSTANT); }
{D}+"."{D}*({E})? { yylval->lex.f = static_cast<float>(atof_dot(yytext)); return(FLOATCONSTANT); }
"."{D}+({E})?     { yylval->lex.f = static_cast<float>(atof_dot(yytext)); return(FLOATCONSTANT); }

"+="            {  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); }
";"             { context->lexAfterType = false; return(SEMICOLON); }
("{"|"<%")      { context->lexAfterType = false; return(LEFT_BRACE); }
("}"|"%>")      { return(RIGHT_BRACE); }
","         { if (context->inTypeParen) context->lexAfterType = false; return(COMMA); }
":"         { return(COLON); }
"="         { context->lexAfterType = false; return(EQUAL); }
"("         { context->lexAfterType = false; context->inTypeParen = true; return(LEFT_PAREN); }
")"         { context->inTypeParen = false; 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 = NewPoolTString(yytext); 
    return FIELD_SELECTION;
}
<FIELDS>[ \t\v\f\r] {}

[ \t\v\n\f\r]   {  }
<*><<EOF>>      { context->AfterEOF = true; yyterminate(); }
<*>.            { context->warning(yylineno, "Unknown char", yytext, ""); return 0; }

%%

extern "C" {
// Preprocessor interface.
#include "compiler/preprocessor/preprocess.h"

#define SETUP_CONTEXT(pp) \
    TParseContext* context = (TParseContext*) pp->pC; \
    struct yyguts_t* yyg = (struct yyguts_t*) context->scanner;

// Preprocessor callbacks.
void CPPDebugLogMsg(const char *msg)
{
    SETUP_CONTEXT(cpp);
    context->infoSink.debug.message(EPrefixNone, msg);
}

void CPPWarningToInfoLog(const char *msg)
{
    SETUP_CONTEXT(cpp);
    context->warning(yylineno, msg, "", "");
}

void CPPShInfoLogMsg(const char *msg)
{
    SETUP_CONTEXT(cpp);
    context->error(yylineno, msg, "", "");
    context->recover();
}

void CPPErrorToInfoLog(char *msg)
{
    SETUP_CONTEXT(cpp);
    context->error(yylineno, msg, "", "");
    context->recover();
}

void SetLineNumber(int line)
{
    SETUP_CONTEXT(cpp);
    int string = 0;
    DecodeSourceLoc(yylineno, &string, NULL);
    yylineno = EncodeSourceLoc(string, line);
}

void SetStringNumber(int string)
{
    SETUP_CONTEXT(cpp);
    int line = 0;
    DecodeSourceLoc(yylineno, NULL, &line);
    yylineno = EncodeSourceLoc(string, line);
}

int GetStringNumber()
{
    SETUP_CONTEXT(cpp);
    int string = 0;
    DecodeSourceLoc(yylineno, &string, NULL);
    return string;
}

int GetLineNumber()
{
    SETUP_CONTEXT(cpp);
    int line = 0;
    DecodeSourceLoc(yylineno, NULL, &line);
    return line;
}

void IncLineNumber()
{
    SETUP_CONTEXT(cpp);
    int string = 0, line = 0;
    DecodeSourceLoc(yylineno, &string, &line);
    yylineno = EncodeSourceLoc(string, ++line);
}

void DecLineNumber()
{
    SETUP_CONTEXT(cpp);
    int string = 0, line = 0;
    DecodeSourceLoc(yylineno, &string, &line);
    yylineno = EncodeSourceLoc(string, --line);
}

void HandlePragma(const char **tokens, int numTokens)
{
    SETUP_CONTEXT(cpp);
    if (!strcmp(tokens[0], "optimize")) {
        if (numTokens != 4) {
            CPPShInfoLogMsg("optimize pragma syntax is incorrect");
            return;
        }
        
        if (strcmp(tokens[1], "(")) {
            CPPShInfoLogMsg("\"(\" expected after 'optimize' keyword");
            return;
        }
            
        if (!strcmp(tokens[2], "on"))
            context->contextPragma.optimize = true;
        else if (!strcmp(tokens[2], "off"))
            context->contextPragma.optimize = false;
        else {
            CPPShInfoLogMsg("\"on\" or \"off\" expected after '(' for 'optimize' pragma");
            return;
        }
        
        if (strcmp(tokens[3], ")")) {
            CPPShInfoLogMsg("\")\" expected to end 'optimize' pragma");
            return;
        }
    } else if (!strcmp(tokens[0], "debug")) {
        if (numTokens != 4) {
            CPPShInfoLogMsg("debug pragma syntax is incorrect");
            return;
        }
        
        if (strcmp(tokens[1], "(")) {
            CPPShInfoLogMsg("\"(\" expected after 'debug' keyword");
            return;
        }
            
        if (!strcmp(tokens[2], "on"))
            context->contextPragma.debug = true;
        else if (!strcmp(tokens[2], "off"))
            context->contextPragma.debug = false;
        else {
            CPPShInfoLogMsg("\"on\" or \"off\" expected after '(' for 'debug' pragma");
            return;
        }
        
        if (strcmp(tokens[3], ")")) {
            CPPShInfoLogMsg("\")\" expected to end 'debug' pragma");
            return;
        }
    } else {
#ifdef PRAGMA_TABLE
        //
        // implementation specific pragma
        // use ((TParseContext *)cpp->pC)->contextPragma.pragmaTable to store the information about pragma
        // For now, just ignore the pragma that the implementation cannot recognize
        // An Example of one such implementation for a pragma that has a syntax like
        // #pragma pragmaname(pragmavalue)
        // This implementation stores the current pragmavalue against the pragma name in pragmaTable.
        //        
        if (numTokens == 4 && !strcmp(tokens[1], "(") && !strcmp(tokens[3], ")")) {              
            TPragmaTable& pragmaTable = ((TParseContext *)cpp->pC)->contextPragma.pragmaTable;
            TPragmaTable::iterator iter;
            iter = pragmaTable.find(TString(tokens[0]));
            if (iter != pragmaTable.end()) {
                iter->second = tokens[2];
            } else {
                pragmaTable[ tokens[0] ] = tokens[2];
            }        
        } else if (numTokens >= 2) {
            TPragmaTable& pragmaTable = ((TParseContext *)cpp->pC)->contextPragma.pragmaTable;
            TPragmaTable::iterator iter;
            iter = pragmaTable.find(TString(tokens[0]));
            if (iter != pragmaTable.end()) {
                iter->second = tokens[1];
            } else {
                pragmaTable[ tokens[0] ] = tokens[1];
            }
        }
#endif // PRAGMA_TABLE
    }
}

void StoreStr(char *string)
{
    SETUP_CONTEXT(cpp);
    TString strSrc;
    strSrc = TString(string);

    context->HashErrMsg = context->HashErrMsg + " " + strSrc;
}

const char* GetStrfromTStr(void)
{
    SETUP_CONTEXT(cpp);
    cpp->ErrMsg = context->HashErrMsg.c_str();
    return cpp->ErrMsg;
}

void ResetTString(void)
{
    SETUP_CONTEXT(cpp);
    context->HashErrMsg = "";
}

TBehavior GetBehavior(const char* behavior)
{
    if (!strcmp("require", behavior))
        return EBhRequire;
    else if (!strcmp("enable", behavior))
        return EBhEnable;
    else if (!strcmp("disable", behavior))
        return EBhDisable;
    else if (!strcmp("warn", behavior))
        return EBhWarn;
    else {
        CPPShInfoLogMsg((TString("behavior '") + behavior + "' is not supported").c_str());
        return EBhDisable;
    }        
}

void updateExtensionBehavior(const char* extName, const char* behavior)
{
    SETUP_CONTEXT(cpp);
    TBehavior behaviorVal = GetBehavior(behavior);
    TMap<TString, TBehavior>:: iterator iter;
    TString msg;
    
    // special cased for all extension
    if (!strcmp(extName, "all")) {
        if (behaviorVal == EBhRequire || behaviorVal == EBhEnable) {
            CPPShInfoLogMsg("extension 'all' cannot have 'require' or 'enable' behavior");  
            return;
        } else {
            for (iter = context->extensionBehavior.begin(); iter != context->extensionBehavior.end(); ++iter)
                iter->second = behaviorVal;
        }        
    } else {
        iter = context->extensionBehavior.find(TString(extName));
        if (iter == context->extensionBehavior.end()) {
            switch (behaviorVal) {
            case EBhRequire:
                CPPShInfoLogMsg((TString("extension '") + extName + "' is not supported").c_str());  
                break;
            case EBhEnable:
            case EBhWarn:
            case EBhDisable:
                msg = TString("extension '") + extName + "' is not supported";
                context->infoSink.info.message(EPrefixWarning, msg.c_str(), yylineno); 
                break;
            default:
                ;
            }
            return;
        } else
            iter->second = behaviorVal;
    }
}
}  // extern "C"

int string_input(char* buf, int max_size, yyscan_t yyscanner) {
    int len;

    if ((len = yylex_CPP(buf, max_size)) == 0)
        return 0;
    if (len >= max_size) 
        YY_FATAL_ERROR("input buffer overflow, can't enlarge buffer because scanner uses REJECT");

    buf[len] = ' ';
    return len+1;
}

int check_type(yyscan_t yyscanner) {
    struct yyguts_t* yyg = (struct yyguts_t*) yyscanner;
    
    int token = IDENTIFIER;
    TSymbol* symbol = yyextra->symbolTable.find(yytext);
    if (yyextra->lexAfterType == false && symbol && symbol->isVariable()) {
        TVariable* variable = static_cast<TVariable*>(symbol);
        if (variable->isUserType()) {
            yyextra->lexAfterType = true;
            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(yylineno, "Illegal use of reserved word", yytext, "");
    yyextra->recover();
    return 0;
}

void yyerror(TParseContext* context, const char* reason) {
    struct yyguts_t* yyg = (struct yyguts_t*) context->scanner;

    if (context->AfterEOF) {
        context->error(yylineno, reason, "unexpected EOF", "");
    } else {
        context->error(yylineno, reason, yytext, "");
    }
    context->recover();
}

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;
    return yylex_destroy(scanner);
}

void glslang_scan(int count, const char* const string[], const int length[],
                  TParseContext* context) {
    yyrestart(NULL, context->scanner);
    yyset_lineno(EncodeSourceLoc(0, 1), context->scanner);
    context->AfterEOF = false;
    
    // Init preprocessor.
    cpp->pC = context;
    cpp->PaWhichStr = 0;
    cpp->PaArgv     = string;
    cpp->PaArgc     = count;
    cpp->PaStrLen   = length;
    cpp->pastFirstStatement = 0;
    ScanFromString(string[0]);
}

