/*
//
// 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 contains the Yacc grammar 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_glsl_parser.sh,
WHICH GENERATES THE GLSL ES PARSER.
*/

%{
//
// 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_glslang_parser.sh. DO NOT EDIT!

#if defined(__GNUC__)
#elif defined(_MSC_VER)
#pragma warning(disable: 4065)
#endif

#include "Context.h"

#define YYDEBUG 1
%}

%pure-parser
%name-prefix="pp"
%locations
%parse-param {pp::Context* context}
%lex-param {pp::Context* context}

%union {
    int ival;
    std::string* sval;
    pp::Token* tval;
    pp::TokenVector* tlist;
}

%{
static int yylex(YYSTYPE* lvalp, YYLTYPE* llocp, pp::Context* context);
static void yyerror(YYLTYPE* llocp,
                    pp::Context* context,
                    const char* reason);

static void pushConditionalBlock(pp::Context* context, bool condition);
static void popConditionalBlock(pp::Context* context);
%}

%token HASH HASH_UNDEF
%token HASH_IF HASH_IFDEF HASH_IFNDEF HASH_ELSE HASH_ELIF HASH_ENDIF DEFINED
%token HASH_ERROR HASH_PRAGMA HASH_EXTENSION HASH_VERSION HASH_LINE
%token <sval> HASH_DEFINE_OBJ HASH_DEFINE_FUNC
%token <sval> INT_CONSTANT FLOAT_CONSTANT IDENTIFIER
%type <ival> operator
%type <tval> conditional_token token
%type <tlist> parameter_list replacement_list conditional_list token_list
%%

input
    : /* empty */
    | input line
;

line
    : text_line
    | control_line
;

text_line
    : '\n'
    | token_list '\n' {
        // TODO(alokp): Expand macros.
        pp::TokenVector* out = context->output();
        out->insert(out->end(), $1->begin(), $1->end());
        delete $1;
    }
;

control_line
    : HASH '\n'
    | HASH_DEFINE_OBJ replacement_list '\n' {
        context->defineMacro(@1.first_line, pp::Macro::kTypeObj, $1, NULL, $2);
    }
    | HASH_DEFINE_FUNC '(' parameter_list ')' replacement_list '\n' {
        context->defineMacro(@1.first_line, pp::Macro::kTypeFunc, $1, $3, $5);
    }
    | HASH_UNDEF IDENTIFIER '\n' {
        context->undefineMacro($2);
    }
    | HASH_IF conditional_list '\n' {
        pushConditionalBlock(context, $2 ? true : false);
    }
    | HASH_IFDEF IDENTIFIER '\n' {
        pushConditionalBlock(context, context->isMacroDefined($2));
    }
    | HASH_IFNDEF IDENTIFIER '\n' {
        pushConditionalBlock(context, !context->isMacroDefined($2));
    }
    | HASH_ELIF conditional_list '\n' {
    }
    | HASH_ELSE '\n' {
    }
    | HASH_ENDIF '\n' {
        popConditionalBlock(context);
    }
    | HASH_ERROR '\n'
    | HASH_PRAGMA '\n'
    | HASH_EXTENSION '\n'
    | HASH_VERSION '\n'
    | HASH_LINE '\n'
;

replacement_list
    : /* empty */ { $$ = NULL; }
    | token_list

parameter_list
    : /* empty */ { $$ = NULL; }
    | IDENTIFIER {
        $$ = new pp::TokenVector;
        $$->push_back(new pp::Token(@1.first_line, IDENTIFIER, $1));
    }
    | parameter_list ',' IDENTIFIER {
        $$ = $1;
        $$->push_back(new pp::Token(@3.first_line, IDENTIFIER, $3));
    }

conditional_list
    : conditional_token {
        $$ = new pp::TokenVector;
        $$->push_back($1);
    }
    | conditional_list conditional_token {
        $$ = $1;
        $$->push_back($2);
    }
;

token_list
    : token {
        $$ = new pp::TokenVector;
        $$->push_back($1);
    }
    | token_list token {
        $$ = $1;
        $$->push_back($2);
    }
;

conditional_token
    : DEFINED IDENTIFIER {
    }
    | DEFINED '(' IDENTIFIER ')' {
    }
    | token
;

token
    : operator {
        $$ = new pp::Token(@1.first_line, $1, NULL);
    }
    | INT_CONSTANT {
        $$ = new pp::Token(@1.first_line, INT_CONSTANT, $1);
    }
    | FLOAT_CONSTANT {
        $$ = new pp::Token(@1.first_line, FLOAT_CONSTANT, $1);
    }
    | IDENTIFIER {
        $$ = new pp::Token(@1.first_line, IDENTIFIER, $1);
    }
;

operator
    : '[' { $$ = '['; }
    | ']' { $$ = ']'; }
    | '<' { $$ = '<'; }
    | '>' { $$ = '>'; }
    | '(' { $$ = '('; }
    | ')' { $$ = ')'; }
    | '{' { $$ = '{'; }
    | '}' { $$ = '}'; }
    | '.' { $$ = '.'; }
    | '+' { $$ = '+'; }
    | '-' { $$ = '-'; }
    | '/' { $$ = '/'; }
    | '*' { $$ = '*'; }
    | '%' { $$ = '%'; }
    | '^' { $$ = '^'; }
    | '|' { $$ = '|'; }
    | '&' { $$ = '&'; }
    | '~' { $$ = '~'; }
    | '=' { $$ = '='; }
    | '!' { $$ = '!'; }
    | ':' { $$ = ':'; }
    | ';' { $$ = ';'; }
    | ',' { $$ = ','; }
    | '?' { $$ = '?'; }
;

%%

int yylex(YYSTYPE* lvalp, YYLTYPE* llocp, pp::Context* context)
{
    return context->lex(lvalp, llocp);
}

void yyerror(YYLTYPE* llocp, pp::Context* context, const char* reason)
{
}

void pushConditionalBlock(pp::Context* context, bool condition)
{
}

void popConditionalBlock(pp::Context* context)
{
}

namespace pp {
bool Context::parse()
{
    yydebug = 1;
    return yyparse(this) == 0 ? true : false;
}
}  // namespace pp

