/*
 * Copyright 2009, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

//
// Parse HLSL shaders and separate the shader code from the Technique block
// parse the technique block into shader declarations and stateassignments
// using a Parser and Lexer generated by Antlr3.0
//
// Compile from the command line using:
//
//   $ set CLASSPATH=%CLASSPATH%;c:\bin\antlr\antlr-3.1.jar
//   $ java org.antlr.Tool Technique.g3pl
//   $ cl *.cc *.c /TP -I c:\bin\antlr\libantlr3c-3.1\include /c /nologo
//   $ link *.obj /LTCG /out:technique.exe c:\bin\antlr\antlr3c.lib \
//                /NODEFAULTLIB:libcmt.lib /nologo


grammar Technique;

options {
   language = C;
}

@lexer::preincludes {
#include <vector>
#include "base/logging.h"
#include "core/cross/types.h"

#ifdef OS_WIN
// These need to be defined here so that when antlr includes
// winsock.h, it doesn't barf.  For some reason these are undefined by
// some header (and it appears to be a windows header).
#define IN
#define OUT
#endif
}

@lexer::postinclude {
  namespace o3d {
  void TechniqueError(pANTLR3_BASE_RECOGNIZER recognizer,
                      pANTLR3_UINT8 * tokenNames);
  }
}

@lexer::apifuncs
{
  // Install the custom error reporting function.
  RECOGNIZER->displayRecognitionError = o3d::TechniqueError;
}

@lexer::members {
  static int toInt(const pANTLR3_STRING string) {
    return atoi(reinterpret_cast<const char*>(string->chars));
  }
  static o3d::String toStringL(const pANTLR3_STRING string) {
    return o3d::String(reinterpret_cast<const char*>(string->chars));
  }
  // Removes escape sequences, as well as leading and trailing quotes.
  static ANTLR3_STRING* unescape(const pANTLR3_STRING string) {
    if (!string || !string->factory) return NULL;
    ANTLR3_STRING* s = string->factory->newRaw(string->factory);
    const char *f = reinterpret_cast<const char*>(string->chars);
    if (!f) return s;
    if (*f == '"') f++;
    for (; *f; f++) {
      if (*f == '\\') {
        switch (*++f) {
          case '\0':  return s;
          case 'b':   s->addc(s, '\b'); break;
          case 't':   s->addc(s, '\t'); break;
          case 'n':   s->addc(s, '\n'); break;
          case 'f':   s->addc(s, '\f'); break;
          case 'r':   s->addc(s, '\r'); break;
          default:    s->addc(s, *f);   break;
        }
      } else if (*f == '"') {
        return s;
      } else {
        s->addc(s, *f);
      }
    }
    return s;
  }
  static void setFilename(pANTLR3_BASE_RECOGNIZER rec,
                          const pANTLR3_STRING filename) {
    if (filename) {
      // We store the filename into the "custom" field in the lexer state,
      // which gets copied into each token.  If we use the fileName field
      // in the stream itself, when the lexer runs ahead of the parser
      // (which is often), the file name can be incorrect.  By storing it
      // in the token itself, the error code can retrieve the filename
      // associated with that token.
      rec->state->custom = unescape(filename);
    }
  }
}

@parser::preincludes {
// NOTE: this header must be included before the "antlr.h" header
// because on Windows it includes <windows.h> which will define <winsock.h>
// before <winsock2.h>, causing a cascade of struct redefinition errors.
#ifdef OS_WIN
// NOTE: disable compiler warning about C function returning a
// struct. This is caused by the Antlr generated functions being declared
// extern "C" but being compiled and used as C++.
#pragma warning( disable: 4190 )
#endif
#include <vector>
#include "base/logging.h"
#include "base/string_util.h"
#include "core/cross/types.h"
#include "compiler/technique/technique_structures.h"
}

@parser::includes {
// NOTE: includes that occur after the "antlr3.h" header has been
// declared which therefore can use the Antlr3 datatypes.
}

@parser::postinclude {
  namespace o3d {
  void TechniqueError(pANTLR3_BASE_RECOGNIZER recognizer,
                      pANTLR3_UINT8 * tokenNames);
  void TechniqueSetErrorString(String* e);
  }
}

@parser::apifuncs
{
  // Install the custom error reporting function.
  RECOGNIZER->displayRecognitionError = o3d::TechniqueError;
}

@parser::members {
  o3d::String *shader_string_;
  o3d::TechniqueDeclarationList *technique_list_;
  o3d::SamplerStateList *sampler_list_;
  o3d::String *error_string_;
  // NOTE: the reinterpret casts below are
  // to cast from Antlr3's internal ANTLR_UINT8 strings to the UTF8
  // char* that O3D uses.
  o3d::String toString(const pANTLR3_STRING string) {
    return o3d::String(reinterpret_cast<const char*>(string->chars));
  }
  void addString(const pANTLR3_STRING string) {
    shader_string_->append(toString(string));
  }
  void addText(const pANTLR3_STRING string) {
    if (string && string->chars) {
      addString(string);
      shader_string_->append("\n");
    }
  }
  void sampler_state_assignment(const pANTLR3_STRING id,
                                const pANTLR3_STRING value) {
    const char* idc = reinterpret_cast<const char *>(id->chars);
    if (!base::strcasecmp(idc, "MinFilter")) {
      sampler_list_->back().min_filter = toString(value);
    } else if (!base::strcasecmp(idc, "MagFilter")) {
      sampler_list_->back().mag_filter = toString(value);
    } else if (!base::strcasecmp(idc, "MipFilter")) {
      sampler_list_->back().mip_filter = toString(value);
    } else if (!base::strcasecmp(idc, "AddressU")) {
      sampler_list_->back().address_u = toString(value);
    } else if (!base::strcasecmp(idc, "AddressV")) {
      sampler_list_->back().address_v = toString(value);
    } else if (!base::strcasecmp(idc, "AddressW")) {
      sampler_list_->back().address_w = toString(value);
    } else if (!base::strcasecmp(idc, "BorderColor")) {
      sampler_list_->back().border_color = toString(value);
    } else if (!base::strcasecmp(idc, "MaxAnisotropy")) {
      sampler_list_->back().max_anisotropy = toString(value);
    }
  }
}

// rules -----------------------------------------------------------------------

// This is the entry rule - zero or more global declarations followed by an
// end-of-file token.
translation_unit [o3d::TechniqueDeclarationList *technique_list,
                  o3d::SamplerStateList *sampler_list,
                  o3d::String *shader_string,
                  o3d::String *error_string]
  returns [bool success]
@init {
  // On entry, reset the list of error strings.
  technique_list_ = technique_list;
  sampler_list_ = sampler_list;
  shader_string_ = shader_string;
  error_string_ = error_string;
  o3d::TechniqueSetErrorString(error_string_);
}
@after {
  // On exit, set the return value.
  success = error_string_->length() == 0;
  technique_list_ = NULL;
  sampler_list_ = NULL;
  shader_string_ = NULL;
  error_string_ = NULL;
  o3d::TechniqueSetErrorString(NULL);
}
  :
    ( noise global_declaration )* EOF
  ;

noise
  :
    ( COMMENT | WHITESPACE | MULTILINE_COMMENT )* { addText($text); }
    | LINE_DIRECTIVE
  ;

global_declaration
  : (function_storage_class? function_type_specifier IDENTIFIER LPAREN) => function_declaration
    { addText($text); }
  | sampler_declaration
  | texture_declaration
  | struct_definition
    { addText($text); }
  | typedef_definition
    { addText($text); }
  | var_declaration
    { addText($text); }
  | technique_definition
  ;

// variables -------------------------------------------

var_declaration
  : var_storage_class*
    var_type_modifier?
    var_datatype id_declaration
    semantic?
    annotation_list?
    ('=' initializer)?
    var_packoffset?
    var_register_bind?
    SEMI
  ;

var_storage_class
  : EXTERN
  | NOINTERPOLATION
  | SHARED
  | STATIC
  | UNIFORM
  | VOLATILE
  ;

var_type_modifier
  : T_CONST
  | ROW_MAJOR
  | COLUMN_MAJOR;

var_datatype
  : buffer_type_specifier
  | scalar_type_or_string_specifier
  | vector_type_specifier
  | matrix_type_specifier
  | struct_type_specifier
  ;

var_packoffset
  : 'packoffset' LPAREN register_name (DOT IDENTIFIER)? RPAREN
  ;

var_register_bind
  : COLON register_name
  ;

id_declaration
  : IDENTIFIER ( LBRACKET constant_expression RBRACKET )?
  ;

// function --------------------------------------------

function_declaration
  : function_storage_class?
    function_type_specifier IDENTIFIER LPAREN argument_list RPAREN semantic?
    function_body (SEMI)?
  ;

function_storage_class
  : INLINE   // ignoring platform target
  ;

function_type_specifier
  : scalar_type_specifier
  | vector_type_specifier
  | matrix_type_specifier
  | struct_type_specifier
  | T_VOID
  ;

semantic
  :    COLON semantic_specifier ;

param_type_specifier
  : scalar_type_specifier
  | vector_type_specifier
  | matrix_type_specifier
  | struct_type_specifier
  | string_type_specifier
  | sampler_type_specifier
  ;

basic_type_specifier
  : scalar_type_specifier
  | vector_type_specifier
  | matrix_type_specifier
  | string_type_specifier
  ;

// typedef ---------------------------------------

typedef_definition
  : TYPEDEF
  ;

// basic datatypes -------------------------------

buffer_type_specifier
  : (BUFFER '<' var_datatype '>' IDENTIFIER)
  ;

// effects ---------------------------------------------------------------------

technique_definition
@declarations {
  o3d::TechniqueDeclaration technique_decl;
}
@init {
  // clear the technique declaration before use
  technique_decl.clear();
}
  : TECHNIQUE IDENTIFIER
    {
      technique_decl.name = toString($IDENTIFIER.text);
    }
    annotation_list? '{' ( pass[technique_decl] )+  '}' SEMI?
    {
      technique_list_->push_back(technique_decl);
    }

  | TECHNIQUE
    {
      technique_decl.name = "";
    }
    annotation_list? '{' ( pass[technique_decl] )+  '}' SEMI?
    {
      technique_list_->push_back(technique_decl);
    }
  ;

pass [o3d::TechniqueDeclaration& technique_decl]
@declarations {
  o3d::PassDeclaration pass_decl;
}
@after {
  $technique_decl.pass.push_back(pass_decl);
}
  : PASS IDENTIFIER?
    {
      if ($IDENTIFIER != NULL && $IDENTIFIER.text->chars != NULL) {
        pass_decl.name =  toString($IDENTIFIER.text);
      } else {
        *error_string_ += "Bad pass identifier, line ";
        *error_string_ += $PASS.line;
      }
    }
    annotation_list? '{' ( state_assignment[pass_decl] )*  '}' SEMI?
  ;

state_assignment [o3d::PassDeclaration& pass]
  : (VERTEXSHADER) =>
      x=VERTEXSHADER '=' 'compile' IDENTIFIER fn=variable_or_call_expression
      {
        $pass.vertex_shader_entry = $fn.identifier;
        $pass.vertex_shader_profile = toString($IDENTIFIER.text);
        $pass.vertex_shader_arguments = $fn.arglist;
      }
      SEMI
  | (FRAGMENTSHADER) =>
      x=FRAGMENTSHADER '=' 'compile' IDENTIFIER fn=variable_or_call_expression
      {
        $pass.fragment_shader_entry = $fn.identifier;
        $pass.fragment_shader_profile = toString($IDENTIFIER.text);
        $pass.fragment_shader_arguments = $fn.arglist;
      }
    SEMI
  | IDENTIFIER '=' val=primary_expression
      {
        if ($IDENTIFIER.text->chars == NULL) {
          *error_string_ += "Bad state assigment identifier, line ";
          *error_string_ += $IDENTIFIER.line;

        } else if ($val.text->chars == NULL) {
          *error_string_ = "Bad state assigment value, line ";
          *error_string_ += $IDENTIFIER.line;
        } else {
          o3d::StateAssignment state;
          state.name = toString($IDENTIFIER.text);
          state.value = toString($val.text);
          $pass.state_assignment.push_back(state);
        }
      }
    SEMI
  ;

// data types ------------------------------------------------------------------

scalar_type_specifier
  : 'bool'
  | 'int'
  | 'uint'
  | 'half'
  | FLOAT
  | 'double'
  ;

scalar_type_or_string_specifier
  : scalar_type_specifier
  | string_type_specifier
  ;

vector_type_specifier
  : 'bool1'
  | 'bool2'
  | 'bool3'
  | 'bool4'
  | 'int1'
  | 'int2'
  | 'int3'
  | 'int4'
  | 'uint1'
  | 'uint2'
  | 'uint3'
  | 'uint4'
  | 'half1'
  | 'half2'
  | 'half3'
  | 'half4'
  | 'float1'
  | 'float2'
  | 'float3'
  | 'float4'
  | 'double1'
  | 'double2'
  | 'double3'
  | 'double4'
  | VECTOR '<' scalar_type_specifier ',' DECIMAL_LITERAL '>'
  ;

matrix_type_specifier
  : 'float1x1'
  | 'float1x2'
  | 'float1x3'
  | 'float1x4'
  | 'float2x1'
  | 'float2x2'
  | 'float2x3'
  | 'float2x4'
  | 'float3x1'
  | 'float3x2'
  | 'float3x3'
  | 'float3x4'
  | 'float4x1'
  | 'float4x2'
  | 'float4x3'
  | 'float4x4'
  | MATRIX '<' scalar_type_specifier ','
    DECIMAL_LITERAL ',' DECIMAL_LITERAL '>'
  ;

string_type_specifier
  : STRING
  ;

// Sampler declarations ----------------------------

sampler_declaration
  : sampler_type_specifier id_declaration
    {
      o3d::SamplerState s;
      s.name = toString($id_declaration.text);
      sampler_list_->push_back(s);
      addString($sampler_type_specifier.text);
      shader_string_->append(" ");
      addString($id_declaration.text);
      shader_string_->append(";\n");
    }
    ( '=' 'sampler_state' '{' sampler_state_declaration+ '}' )? SEMI
  ;

sampler_state_declaration
  : TEXTURE '=' '<' IDENTIFIER '>' SEMI
    { sampler_list_->back().texture = toString($IDENTIFIER.text); }
  | TEXTURE '=' '(' IDENTIFIER ')' SEMI
    { sampler_list_->back().texture = toString($IDENTIFIER.text); }
  | IDENTIFIER '=' initializer SEMI
    { sampler_state_assignment($IDENTIFIER.text, $initializer.text); }
  ;

sampler_type_specifier
  : 'sampler'
  | 'sampler1D'
  | 'sampler2D'
  | 'sampler3D'
  | 'samplerCUBE'
  | 'sampler_state'
  | 'SamplerComparisonState' | 'samplercomparisonstate'  // DX10 only
  ;

 // texture declaration ----------------------------

texture_declaration
  : texture_type_specifier IDENTIFIER semantic? annotation_list? SEMI
  ;

texture_type_specifier
  : TEXTURE
  | TEXTURE1D
  | TEXTURE2D
  | TEXTURE3D
  | TEXTURECUBE
  | TEXTURERECT
  ;

 // struct declaration -----------------------------

struct_type_specifier
  : IDENTIFIER
  | ( STRUCT ( IDENTIFIER )? LCURLY ) => struct_definition
  | STRUCT IDENTIFIER
  ;

annotation_list
  : '<' annotation* '>'
  ;

annotation
  : basic_type_specifier IDENTIFIER '=' initializer SEMI
  ;

initializer
  : expression
  | '{' expression ( ',' expression )* '}'
  ;

register_name
  // registers for VS_3_0
  :    REGISTER '(' input_register_name | output_register_name ')';

input_register_name
  : IDENTIFIER DECIMAL_LITERAL
    // IDENTIFIER must be v, r, c, b, i, s, o (check semantically)
  | IDENTIFIER
    // IDENTIFIER must be a0 aL p0 (check semantically)
  ;

output_register_name
  : IDENTIFIER
  // must be one of:  'oD0', 'oD1', 'oFog', 'oPos', 'oPts',
  //                  'oT0', 'oT1', 'oT2', 'oT3', 'oT4', 'oT5', 'oT6', 'oT7'
  // (check semantically)
  ;

pack_offset
  : .+ ;   // no idea what this field looks like.

argument_list
  : ( param_declaration ( COMMA param_declaration )* )?
  ;

param_declaration
  : param_direction? param_variability? param_type_specifier id_declaration semantic?
//    | FUNCTION type_specifier IDENTIFIER
  ;

param_variability
  : T_CONST
  | UNIFORM
  ;

param_direction
  : T_IN
  | T_OUT
  | T_INOUT
  ;

function_body
  : LCURLY ( decl_or_statement )* RCURLY
  ;

decl_or_statement
  // We copied the following sub-rule here to expedite the parsing time
  // as this is a much more common case than the "Id init_declarator_list"
  // case which would normally spend a lot of time in exception handling.
  : (lvalue_expression assignment_operator ) => assignment_statement
  | ( ( T_CONST )? vector_type_specifier ) => ( T_CONST )? vector_type_specifier init_declarator_list SEMI
  | ( ( T_CONST )? scalar_type_specifier ) => ( T_CONST )? scalar_type_specifier init_declarator_list SEMI
  | ( ( T_CONST )? matrix_type_specifier ) => ( T_CONST )? matrix_type_specifier init_declarator_list SEMI
  | ( STRUCT ( IDENTIFIER )? LCURLY ) => struct_definition ( init_declarator_list )? SEMI
  | STRUCT IDENTIFIER init_declarator_list SEMI
  | ( IDENTIFIER init_declarator_list ) => IDENTIFIER init_declarator_list SEMI
  | statement
  ;

init_declarator_list
  : init_declarator ( COMMA init_declarator )* ;

init_declarator
  : declarator ( ASSIGN expression )? ;

declarator
  : IDENTIFIER ( LBRACKET ( constant_expression )? RBRACKET )*
  ;

struct_definition
  : STRUCT ( IDENTIFIER )? LCURLY struct_declaration_list RCURLY IDENTIFIER? SEMI
  ;

struct_declaration_list
    // We currently don't support nested structs so the field type
    // can only be either a scalar or a vector.
  : ( struct_interpolation_modifier?
    (scalar_type_specifier|vector_type_specifier) IDENTIFIER
    (COLON semantic_specifier)? SEMI )+
  ;

struct_interpolation_modifier   // DX10 only
  : T_LINEAR
  | CENTROID
  | NOINTERPOLATION
  | NOPERSPECTIVE
  ;

semantic_specifier
  : IDENTIFIER
  ;

statement
  : ( lvalue_expression assignment_operator ) => assignment_statement
  | ( lvalue_expression self_modify_operator ) => post_modify_statement
  | pre_modify_statement
  | expression_statement
  | compound_statement
  | selection_statement
  | iteration_statement
  | jump_statement
  | SEMI
  ;

assignment_statement
  : lvalue_expression assignment_operator expression SEMI
  ;

pre_modify_statement
  : pre_modify_expression SEMI ;

pre_modify_expression
  : self_modify_operator lvalue_expression ;

post_modify_statement
  : post_modify_expression SEMI ;

post_modify_expression
  : lvalue_expression self_modify_operator ;

self_modify_operator
  : PLUS_PLUS | MINUS_MINUS ;

expression_statement
  : expression SEMI ;

compound_statement
  : LCURLY (
          ( IDENTIFIER init_declarator_list) => IDENTIFIER init_declarator_list SEMI
        | ( ( T_CONST )? vector_type_specifier ) => ( T_CONST )? vector_type_specifier init_declarator_list SEMI
        | ( ( T_CONST )? scalar_type_specifier ) => ( T_CONST )? scalar_type_specifier init_declarator_list SEMI
        | ( STRUCT ( IDENTIFIER )? LCURLY ) => struct_definition ( init_declarator_list )? SEMI
        | STRUCT IDENTIFIER init_declarator_list SEMI
        | statement
        )*
    RCURLY
  ;

selection_statement
  : IF LPAREN expression RPAREN statement ( ELSE statement )?
  ;

iteration_statement
  : WHILE LPAREN expression RPAREN statement
  | FOR LPAREN assignment_statement
      equality_expression SEMI modify_expression RPAREN statement
  | DO statement WHILE LPAREN expression RPAREN SEMI
  ;

modify_expression
  : (lvalue_expression assignment_operator ) =>
      lvalue_expression assignment_operator expression
  | pre_modify_expression
  | post_modify_expression
  ;

jump_statement
  : BREAK SEMI
  | CONTINUE
  | RETURN ( expression )? SEMI
  | DISCARD
  ;

expression
  : conditional_expression
  ;

assignment_operator
  : ASSIGN
  | MUL_ASSIGN
  | DIV_ASSIGN
  | ADD_ASSIGN
  | SUB_ASSIGN
  | BITWISE_AND_ASSIGN
  | BITWISE_OR_ASSIGN
  | BITWISE_XOR_ASSIGN
  | BITWISE_SHIFTL_ASSIGN
  | BITWISE_SHIFTR_ASSIGN
  ;

constant_expression
  : (IDENTIFIER) => variable_expression
  | literal_value
  ;

conditional_expression
  : logical_or_expression ( QUESTION expression COLON conditional_expression )?
  ;

logical_or_expression
  : exclusive_or_expression ( OR exclusive_or_expression )*
  ;

// We remove the NOT operator from the unary expression and stick it here
// so that it has a lower precedence than relational operations.
logical_and_expression
  : ( NOT )? inclusive_or_expression ( AND ( NOT )? inclusive_or_expression )*
  ;

inclusive_or_expression
  : exclusive_or_expression (BITWISE_OR exclusive_or_expression )*
  ;

exclusive_or_expression
  : and_expression ( BITWISE_XOR and_expression )*
  ;

and_expression
  : equality_expression ( BITWISE_AND equality_expression )*
  ;

equality_expression
  : relational_expression ( (EQUAL|NOT_EQUAL) relational_expression )*
  ;

relational_expression
  : shift_expression ( (LESS|GREATER|LESSEQUAL|GREATEREQUAL) shift_expression )*
  ;

shift_expression
  : additive_expression ( (BITWISE_SHIFTL|BITWISE_SHIFTR) additive_expression )*
  ;

additive_expression
  : multiplicative_expression ( (PLUS|MINUS) multiplicative_expression )*
  ;

multiplicative_expression
  : cast_expression ( (MUL|DIV|MOD) cast_expression )*
  ;

cast_expression
  : LPAREN IDENTIFIER RPAREN postfix_expression
  | LPAREN basic_type_specifier RPAREN postfix_expression
  | unary_expression
  ;

unary_expression
  : (PLUS|MINUS) unary_expression
  | postfix_expression
  ;

postfix_expression
  : primary_expression ( postfix_suffix )?
  ;

lvalue_expression
  : variable_expression ( postfix_suffix )?
  ;

postfix_suffix
  // choosing between struct field access or vector swizzling is a semantic choice.
  : ( DOT primary_expression )+
  ;

primary_expression
  : constructor
  | variable_or_call_expression
  | literal_value
  | LPAREN expression RPAREN
  ;

variable_expression
  : IDENTIFIER ( LBRACKET expression RBRACKET )?
  ;

// Combine variable expression and call expression here to get rid of the
// syntactic predicate we used to use in the primary_expression rule. Using
// predicates results in the parser spending a lot of time in exception
// handling (when lookahead fails).
variable_or_call_expression
  returns [o3d::String identifier, o3d::String arglist]
  : IDENTIFIER
    (
      ( ( LBRACKET expression RBRACKET )? )
      {
        $identifier = toString($IDENTIFIER.text);
        $arglist = "";
      }
      |
      ( LPAREN argument_expression_list RPAREN )
      {
        $identifier = toString($IDENTIFIER.text);
        if ($argument_expression_list.text->chars) {
          $arglist = toString($argument_expression_list.text);
        } else {
          $arglist = "";
        }
      }
    )
  ;

constructor
  : ( vector_type_specifier | matrix_type_specifier )
    LPAREN expression ( COMMA expression )* RPAREN
  ;

argument_expression_list
  : ( expression ( COMMA expression )* )?
  ;

int_literal
  : DECIMAL_LITERAL
//  | OCT_LITERAL
//  | HEX_LITERAL
  ;

literal_value
  : DECIMAL_LITERAL
  | FLOAT_LITERAL
  | STRING_LITERAL+
  | ( T_FALSE | T_TRUE )
  ;

float_literal
  : FLOAT_LITERAL
  ;

// lexical elements ------------------------------------------------------------

NOT           : '!' ;
NOT_EQUAL     : '!=' ;
AND           : '&&' ;
LPAREN        : '(' ;
RPAREN        : ')' ;
MUL           : '*' ;
MUL_ASSIGN    : '*=' ;
PLUS          : '+' ;
PLUS_PLUS     : '++' ;
ADD_ASSIGN    : '+=' ;
COMMA         : ',' ;
MINUS         : '-' ;
MINUS_MINUS   : '--' ;
SUB_ASSIGN    : '-=' ;
DIV           : '/' ;
DIV_ASSIGN    : '/=' ;
MOD           : '%';
MOD_ASSIGN    :  '%=';
COLON         : ':' ;
SEMI          : ';' ;
LESS          : '<' ;
LESSEQUAL     : '<=' ;
ASSIGN        : '=' ;
EQUAL         : '==' ;
GREATER       : '>' ;
GREATEREQUAL  : '>=' ;
QUESTION      : '?' ;
LBRACKET      : '[' ;
RBRACKET      : ']' ;
LCURLY        : '{' ;
OR            : '||' ;
RCURLY        : '}' ;
DOT           : '.' ;
BITWISE_NOT    : '~';
BITWISE_SHIFTL : '<<';
BITWISE_SHIFTR : '>>';
BITWISE_AND    : '&';
BITWISE_OR     : '|';
BITWISE_XOR    : '^';
BITWISE_SHIFTL_ASSIGN : '<<=';
BITWISE_SHIFTR_ASSIGN : '>>=';
BITWISE_AND_ASSIGN    : '&=';
BITWISE_OR_ASSIGN     : '|=';
BITWISE_XOR_ASSIGN    : '^=';
// keywords ----------------------------

BREAK            : 'break';
BUFFER           : 'buffer';
COLUMN_MAJOR     : 'column_major';
CBUFFER          : 'cbuffer';
CENTROID         : 'centroid';
T_CONST          : 'const';
CONTINUE         : 'continue';
DISCARD          : 'discard';
DO               : 'do';
ELSE             : 'else';
EXTERN           : 'extern';
T_FALSE          : 'false';
FLOAT            : (('f'|'F')('l'|'L')('o'|'O')('a'|'A')('t'|'T'));
FOR              : 'for';
IF               : 'if';
T_IN             : 'in';
INLINE           : 'inline';
T_INOUT          : 'inout';
T_LINEAR         : 'linear';
MATRIX           : ('m'|'M')('a'|'A')('t'|'T')('r'|'R')('i'|'I')('x'|'X');
NAMESPACE        : 'namespace';
NOINTERPOLATION  : 'nointerpolation';
NOPERSPECTIVE    : 'noperspective';
T_OUT            : 'out';
RETURN           : 'return';
REGISTER         : 'register';
ROW_MAJOR        : 'row_major';
SHARED           : 'shared';
STATEBLOCK       : 'stateblock';
STATEBLOCK_STATE : 'stateblock_state';
STATIC           : 'static';
STRING           : ('s'|'S')('t'|'T')('r'|'R')('i'|'I')('n'|'N')('g'|'G');
STRUCT           : 'struct';
SWITCH           : 'switch';
TBUFFER          : 'tbuffer';
TEXTURE          :
  ('t'|'T')('e'|'E')('x'|'X')('t'|'T')('u'|'U')('r'|'R')('e'|'E');
TEXTURE1D        : 'Texture1D';
TEXTURE1DARRAY   : 'Texture1DArray';
TEXTURE2D        : 'Texture2D';
TEXTURE2DARRAY   : 'Texture2DArray';
TEXTURE2DMS      : 'Texture2DMS';
TEXTURE2DMSARRAY : 'Texture2DMSArray';
TEXTURE3D        : 'Texture3D';
TEXTURECUBE      : 'TextureCUBE';
TEXTURECUBEARRAY : 'TextureCUBEArray';
TEXTURERECT      : 'TextureRECT';
T_TRUE           : 'true';
TYPEDEF          : 'typedef';
UNIFORM          : 'uniform';
VECTOR           : ('v'|'V')('e'|'E')('c'|'C')('t'|'T')('o'|'O')('r'|'R');
T_VOID           : 'void';
VOLATILE         : 'volatile';
WHILE            : 'while';

// sampler state tokens

PASS           : ('p'|'P')('a'|'A')('s'|'S')('s'|'S');

TECHNIQUE      : ('t'|'T')('e'|'E')('c'|'C')('h'|'H')
                 ('n'|'N')('i'|'I')('q'|'Q')('u'|'U')('e'|'E')
               ;

VERTEXSHADER
  : (('v'|'V')('e'|'E')('r'|'R')('t'|'T')('e'|'E')('x'|'X'))
    ((('s'|'S')('h'|'H')('a'|'A')('d'|'D')('e'|'E')('r'|'R')) |
     (('p'|'P')('r'|'R')('o'|'O')('g'|'G')('r'|'R')('a'|'A')('m'|'M')))
  ;

FRAGMENTSHADER
  : (('f'|'F')('r'|'R')('a'|'A')('g'|'G')('m'|'M')('e'|'E')('n'|'N')('t'|'T')
     ('p'|'P')('r'|'R')('o'|'O')('g'|'G')('r'|'R')('a'|'A')('m'|'M'))
  | (('p'|'P')('i'|'I')('x'|'X')('e'|'E')('l'|'L')
     ('s'|'S')('h'|'H')('a'|'A')('d'|'D')('e'|'E')('r'|'R'))
  ;

RESERVED_WORDS
  : (('a'|'A')('S'|'s')('m'|'M'))
  | 'asm_fragment'
  | 'auto'
  | 'case'
  | 'catch'
  | 'char'
  | 'class'
  | 'const_cast'
  | (('d'|'D')('e'|'E')('c'|'C')('l'|'L'))
  | 'default'
  | 'delete'
  | (('d'|'D')('w'|'W')('o'|'O')('r'|'R')('d'|'D'))
  | 'dynamic_cast'
  | 'emit'
  | 'enum'
  | 'explicit'
  | 'fixed'
  | 'friend'
  | 'get'
  | 'goto'
  | 'interface'
  | 'long'
  | 'mutable'
  | 'new'
  | 'operator'
  | 'packed'
  | (('p'|'P')('i'|'I')('x'|'X')('e'|'E')('l'|'L')
     ('f'|'F')('r'|'R')('a'|'A')('g'|'G')('m'|'M')('e'|'E')('n'|'N')('t'|'T'))
  | 'private'
  | 'protected'
  | 'public'
  | 'reinterpret_cast'
  | 'short'
  | 'signed'
  | 'sizeof'
  | 'snorm'
  | 'static_cast'
  | 'template'
  | 'this'
  | 'throw'
  | 'try'
  | 'typeid'
  | 'typename'
  | 'union'
  | 'unorm'
  | 'unsigned'
  | 'using'
  | (('v'|'V')('e'|'E')('r'|'R')('t'|'T')('e'|'E')('x'|'X')
     ('f'|'F')('r'|'R')('a'|'A')
     ('g'|'G')('m'|'M')('e'|'E')('n'|'N')('t'|'T'))
  | 'virtual'
  ;

IDENTIFIER
  : ('a'..'z'|'A'..'Z'|'_')('a'..'z'|'A'..'Z'|'_'|'0'..'9')*
  ;

DECIMAL_LITERAL
  : ('0'..'9')+
  ;

fragment ESCAPE_SEQUENCE
  : '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\')
  ;

CHARACTER_LITERAL
  :   '\'' ( ESCAPE_SEQUENCE | ~('\''|'\\') ) '\''
  ;

STRING_LITERAL
  :  '"' ( ESCAPE_SEQUENCE | ~('\\'|'"') )* '"'
  ;

fragment EXPONENT : ('e'|'E') (PLUS | MINUS)? ('0'..'9')+ ;

fragment FLOATSUFFIX : ('f'|'F'|'h'|'H') ;

FLOAT_LITERAL
  : ('0'..'9')+ '.' ('0'..'9')* (EXPONENT)? (FLOATSUFFIX)?
  | '.' ('0'..'9')+ (EXPONENT)? (FLOATSUFFIX)?
  ;

// skipped elements ------------------------------------------------------------

LINE_DIRECTIVE
  : '#line' (' '|'\t')* line_num=DECIMAL_LITERAL '\r'? '\n'
    { INPUT->setLine(INPUT, toInt($line_num.text)); $channel = HIDDEN; }
  | '#line' (' '|'\t')* line_num=DECIMAL_LITERAL
    (' '|'\t')* file=STRING_LITERAL '\r'? '\n'
    { INPUT->setLine(INPUT, toInt($line_num.text)); $channel = HIDDEN;
      setFilename(RECOGNIZER, $file.text); }
  ;

WHITESPACE
  : (' '|'\r'|'\t'|'\u000C'|'\n')   { $channel = HIDDEN; }
  ;

COMMENT
  : '//' ~('\n'|'\r')* '\r'? '\n'   { $channel = HIDDEN; }
  ;

MULTILINE_COMMENT
  : '/*' ( options {greedy=false;} : . )* '*/'  { $channel = HIDDEN; }
  ;

// -----------------------------------------------------------------------------