/*
//
// 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 "Lexer.h"
#include "Token.h"

typedef std::string YYSTYPE;
typedef pp::Token::Location YYLTYPE;

// Use the unused yycolumn variable to track file (string) number.
#define yyfileno yycolumn

#define YY_USER_INIT  \
    do {              \
        yyfileno = 0; \
        yylineno = 1; \
    } while(0);

#define YY_USER_ACTION                                            \
    do {                                                          \
        pp::Input* input = &yyextra->input;                       \
        pp::Input::Location* scanLoc = &yyextra->scanLoc;         \
        while (scanLoc->cIndex >= input->length(scanLoc->sIndex)) \
        {                                                         \
            scanLoc->cIndex -= input->length(scanLoc->sIndex++);  \
            ++yyfileno; yylineno = 1;                             \
        }                                                         \
        yylloc->file = yyfileno;                                  \
        yylloc->line = yylineno;                                  \
        scanLoc->cIndex += yyleng;                                \
    } while(0);

#define YY_INPUT(buf, result, maxSize) \
    result = yyextra->input.read(buf, maxSize);

%}

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

NEWLINE     \n|\r|\r\n
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}+".")

%%

    /* Line comment */
"//"[^\r\n]*

    /* Block comment */
    /* Line breaks are just counted - not returned. */
    /* The comment is replaced by a single space. */ 
"/*"                { BEGIN(COMMENT); }
<COMMENT>[^*\r\n]+
<COMMENT>"*"
<COMMENT>{NEWLINE}  { ++yylineno; }
<COMMENT><<EOF>>    { return pp::Token::EOF_IN_COMMENT; }
<COMMENT>"*/"       { BEGIN(INITIAL); return ' '; }

# { return yytext[0]; }

{IDENTIFIER} {
    yylval->assign(yytext, yyleng);
    return pp::Token::IDENTIFIER;
}

{DECIMAL_CONSTANT}|{OCTAL_CONSTANT}|{HEXADECIMAL_CONSTANT} {
    yylval->assign(yytext, yyleng);
    return pp::Token::CONST_INT;
}

({DIGIT}+{EXPONENT_PART})|({FRACTIONAL_CONSTANT}{EXPONENT_PART}?) {
    yylval->assign(yytext, yyleng);
    return pp::Token::CONST_FLOAT;
}

    /* Anything that starts with a {DIGIT} or .{DIGIT} must be a number. */
    /* Rule to catch all invalid integers and floats. */
({DIGIT}+[_a-zA-Z0-9.]*)|("."{DIGIT}+[_a-zA-Z0-9.]*) {
    yylval->assign(yytext, yyleng);
    return pp::Token::INVALID_NUMBER;
}

"++"  { return pp::Token::OP_INC; }
"--"  { return pp::Token::OP_DEC; }
"<<"  { return pp::Token::OP_LEFT; }
">>"  { return pp::Token::OP_RIGHT; }
"<="  { return pp::Token::OP_LE; }
">="  { return pp::Token::OP_GE; }
"=="  { return pp::Token::OP_EQ; }
"!="  { return pp::Token::OP_NE; }
"&&"  { return pp::Token::OP_AND; }
"^^"  { return pp::Token::OP_XOR; }
"||"  { return pp::Token::OP_OR; }
"+="  {  return pp::Token::OP_ADD_ASSIGN; }
"-="  {  return pp::Token::OP_SUB_ASSIGN; }
"*="  {  return pp::Token::OP_MUL_ASSIGN; }
"/="  {  return pp::Token::OP_DIV_ASSIGN; }
"%="  {  return pp::Token::OP_MOD_ASSIGN; }
"<<=" {  return pp::Token::OP_LEFT_ASSIGN; }
">>=" {  return pp::Token::OP_RIGHT_ASSIGN; }
"&="  {  return pp::Token::OP_AND_ASSIGN; }
"^="  {  return pp::Token::OP_XOR_ASSIGN; }
"|="  {  return pp::Token::OP_OR_ASSIGN; }

{PUNCTUATOR} { return yytext[0]; }
[ \t\v\f]+   { return ' '; }

{NEWLINE} {
    ++yylineno;
    return '\n';
}

. {
    yylval->assign(yytext, yyleng);
    return pp::Token::INVALID_CHARACTER;
}

<<EOF>>   { yyterminate(); }

%%

namespace pp {

int Lexer::lex(Token* token)
{
    bool leadingSpace = false;
    token->type = yylex(&token->value, &token->location, mHandle);
    while (token->type == ' ')
    {
        leadingSpace = true;
        token->type = yylex(&token->value, &token->location, mHandle);
    }
    token->setHasLeadingSpace(leadingSpace);

    return token->type;
}

bool Lexer::initLexer()
{
    if ((mHandle == NULL) && yylex_init_extra(&mContext, &mHandle))
        return false;

    yyrestart(0, mHandle);
    return true;
}

void Lexer::destroyLexer()
{
    if (mHandle == NULL)
        return;

    yylex_destroy(mHandle);
    mHandle = NULL;
}

}  // namespace pp

