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

// Token location (file and line) is encoded into a single int so that
// we can use yylineno macro.

const unsigned int kLocationLineSize = 16;  // in bits.
const unsigned int kLocationLineMask = (1 << kLocationLineSize) - 1;

static int location(int file, int line)
{
    return (file << kLocationLineSize) | (line & kLocationLineMask);
}

static int locationFile(int loc)
{
    return loc >> kLocationLineSize;
}

static int locationLine(int loc)
{
    return loc & kLocationLineMask;
}

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

#define YY_USER_ACTION                          \
    do {                                        \
        yylloc->file = locationFile(yylineno);  \
        yylloc->line = locationLine(yylineno);  \
    } while(0);

// Suppress the default implementation of YY_INPUT which generated
// compiler warnings.
#define YY_INPUT

%}

%option nounput never-interactive
%option reentrant bison-bridge bison-locations
%option prefix="pp"
%option extra-type="pp::Input*"
%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(); }

%%

int ppwrap(yyscan_t scanner)
{
    pp::Input* input = yyget_extra(scanner);

    int file = -1;
    // Delete the current buffer before switching to the next one.
    YY_BUFFER_STATE buffer = static_cast<YY_BUFFER_STATE>(input->buffer);
    if (buffer != NULL)
    {
        // Save the current token location before they get lost when the
        // current input buffer is deleted.
        file = locationFile(yyget_lineno(scanner));

        yy_delete_buffer(buffer, scanner);
        input->buffer = NULL;
    }

    int index = std::min(input->index + 1, input->count);
    if (index == input->count)
        return 1;  // EOF reached.

    int length = input->length ? input->length[index] : -1;
    if (length < 0)  // NULL terminated string.
        buffer = yy_scan_string(input->string[index], scanner);
    else
        buffer = yy_scan_bytes(input->string[index], length, scanner);

    // Increment file number and reset line number.
    // This can be done only after we have created a new input buffer.
    yyset_lineno(location(file + 1, 1), scanner);

    input->index = index;
    input->buffer = buffer;
    return 0;
}

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(&mInput, &mHandle))
        return false;

    // Setup first scan string.
    mInput.index = -1;
    ppwrap(mHandle);

    return true;
}

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

    YY_BUFFER_STATE buffer = static_cast<YY_BUFFER_STATE>(mInput.buffer);
    if (buffer != NULL)
    {
        yy_delete_buffer(buffer, mHandle);
        mInput.buffer = NULL;
    }
 
    yylex_destroy(mHandle);
    mHandle = NULL;
}

}  // namespace pp

