/*
//
// Copyright (c) 2002-2011 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 preprocessor.
Based on Microsoft Visual Studio 2010 Preprocessor Grammar:
http://msdn.microsoft.com/en-us/library/2scxys89.aspx

IF YOU MODIFY THIS FILE YOU ALSO NEED TO RUN generate_parser.sh.
*/

%top{
//
// Copyright (c) 2011 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/debug.h"
#include "Context.h"
#include "pp_tab.h"

#define YY_USER_ACTION                        \
    do {                                      \
        yylloc->first_line = yylineno;        \
        yylloc->first_column = yycolumn + 1;  \
        yycolumn += yyleng;                   \
    } while(0);

#define YY_INPUT(buf, result, maxSize) \
    result = yyextra->readInput(buf, maxSize);
    
static std::string* extractMacroName(const char* str, int len);
%}

%option noyywrap nounput never-interactive
%option yylineno reentrant bison-bridge bison-locations
%option stack
%option prefix="pp"
%option extra-type="pp::Context*"

HSPACE      [ \t]
HASH        ^{HSPACE}*#{HSPACE}*
IDENTIFIER  [_a-zA-Z][_a-zA-Z0-9]*
PUNCTUATOR  [][<>(){}.+-/*%^|&~=!:;,?]

DECIMAL_CONSTANT      [1-9][0-9]*
OCTAL_CONSTANT        0[0-7]*
HEXADECIMAL_CONSTANT  0[xX][0-9a-fA-F]+

DIGIT                [0-9]
EXPONENT_PART        [eE][+-]?{DIGIT}+
FRACTIONAL_CONSTANT  ({DIGIT}*"."{DIGIT}+)|({DIGIT}+".")

%%

{HASH} { return HASH; }

{HASH}define{HSPACE}+{IDENTIFIER}/[ \t\n] {
    yylval->sval = extractMacroName(yytext, yyleng);
    return HASH_DEFINE_OBJ;
}
{HASH}define{HSPACE}+{IDENTIFIER}/"(" {
    yylval->sval = extractMacroName(yytext, yyleng);
    return HASH_DEFINE_FUNC;
}
{HASH}undef{HSPACE}+ { return HASH_UNDEF; }

{HASH}if { return HASH_IF; }
{HASH}ifdef { return HASH_IFDEF; }
{HASH}ifndef { return HASH_IFNDEF; }
{HASH}else { return HASH_ELSE; }
{HASH}elif { return HASH_ELIF; }
{HASH}endif { return HASH_ENDIF; }
"defined" { return DEFINED; }

{HASH}error { return HASH_ERROR; }
{HASH}pragma { return HASH_PRAGMA; }
{HASH}extension { return HASH_EXTENSION; }
{HASH}version { return HASH_VERSION; }
{HASH}line { return HASH_LINE; }

{IDENTIFIER} {
    yylval->sval = new std::string(yytext, yyleng);
    return IDENTIFIER;
}

{DECIMAL_CONSTANT}|{OCTAL_CONSTANT}|{HEXADECIMAL_CONSTANT} {
    yylval->sval = new std::string(yytext, yyleng);
    return INT_CONSTANT;
}

({DIGIT}+{EXPONENT_PART})|({FRACTIONAL_CONSTANT}{EXPONENT_PART}?) {
    yylval->sval = new std::string(yytext, yyleng);
    return FLOAT_CONSTANT;
}

{PUNCTUATOR} { return yytext[0]; }

[ \t\v\f]+ { /* Ignore whitespace */ }

\n {
    ++yylineno; yycolumn = 0;
    return yytext[0];
}

<*><<EOF>>     { yyterminate(); }

%%

std::string* extractMacroName(const char* str, int len)
{
    // The input string is of the form {HASH}define{HSPACE}+{IDENTIFIER}
    // We just need to find the last HSPACE.
    ASSERT(str && (len > 8));  // strlen("#define ") == 8;

    std::string* name = NULL;
    for (int i = len - 1; i >= 0; --i)
    {
        if ((str[i] == ' ') || (str[i] == '\t'))
        {
            name = new std::string(str + i + 1, len - i - 1);
            break;
        }
    }
    ASSERT(name);
    return name;
}

namespace pp {

int Context::readInput(char* buf, int maxSize)
{
    int nread = YY_NULL;
    while (!mInput->eof() &&
           (mInput->error() == pp::Input::kErrorNone) &&
           (nread == YY_NULL))
    {
        int line = 0, file = 0;
        pp::Token::decodeLocation(yyget_lineno(mLexer), &line, &file);
        file = mInput->stringIndex();
        yyset_lineno(pp::Token::encodeLocation(line, file), mLexer);

        nread = mInput->read(buf, maxSize);

        if (mInput->error() == pp::Input::kErrorUnexpectedEOF)
        {
            // TODO(alokp): Report error.
        }
    }
    return nread;
}

bool Context::initLexer()
{
    ASSERT(mLexer == NULL);

    if (yylex_init_extra(this, &mLexer))
        return false;

    yyrestart(0, mLexer);
    return true;
}

void Context::destroyLexer()
{
    ASSERT(mLexer);

    yylex_destroy(mLexer);
    mLexer = NULL;
}

}  // namespace pp

