hlsl2glslfork/hlslang/MachineIndependent/hlslang.y

2460 строки
83 KiB
Plaintext

// Copyright (c) The HLSL2GLSLFork Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE.txt file.
// Bison grammar and production code for parsing HLSL
%{
/* Based on:
ANSI C Yacc grammar
In 1985, Jeff Lee published his Yacc grammar (which is accompanied by a
matching Lex specification) for the April 30, 1985 draft version of the
ANSI C standard. Tom Stockfisch reposted it to net.sources in 1987; that
original, as mentioned in the answer to question 17.25 of the comp.lang.c
FAQ, can be ftp'ed from ftp.uu.net, file usenet/net.sources/ansi.c.grammar.Z.
I intend to keep this version as close to the current C Standard grammar as
possible; please let me know if you discover discrepancies.
Jutta Degener, 1995
*/
#include "SymbolTable.h"
#include "ParseHelper.h"
#include "../../include/hlsl2glsl.h"
#ifdef _WIN32
#define YYPARSE_PARAM parseContext
#define YYPARSE_PARAM_DECL TParseContext&
#define YY_DECL int yylex(YYSTYPE* pyylval, TParseContext& parseContext)
#define YYLEX_PARAM parseContext
#else
#define YYPARSE_PARAM parseContextLocal
#define parseContext (*((TParseContext*)(parseContextLocal)))
#define YY_DECL int yylex(YYSTYPE* pyylval, void* parseContextLocal)
#define YYLEX_PARAM (void*)(parseContextLocal)
extern void yyerror(const char*);
#endif
#define FRAG_ONLY(S, L) { \
if (parseContext.language != EShLangFragment) { \
parseContext.error(L, " supported in fragment shaders only ", S, "", ""); \
parseContext.recover(); \
} \
}
#define NONSQUARE_MATRIX_CHECK(S, L) { \
if (parseContext.targetVersion < ETargetGLSL_120) { \
parseContext.error(L, " not supported in pre-GLSL1.20", S, "", ""); \
parseContext.recover(); \
} \
}
#define UNSUPPORTED_FEATURE(S, L) { \
parseContext.error(L, " not supported ", S, "", ""); \
parseContext.recover(); \
}
#define SET_BASIC_TYPE(RES,PAR,T,PREC) \
TQualifier qual = parseContext.getDefaultQualifier(); \
(RES).setBasic(T, qual, (PAR).line); \
(RES).precision = PREC
%}
%union {
struct {
TSourceLoc line;
union {
TString *string;
float f;
int i;
bool b;
};
TSymbol* symbol;
} lex;
struct {
TSourceLoc line;
TOperator op;
union {
TIntermNode* intermNode;
TIntermNodePair nodePair;
TIntermTyped* intermTypedNode;
TIntermAggregate* intermAggregate;
TIntermTyped* intermDeclaration;
};
union {
TPublicType type;
TQualifier qualifier;
TFunction* function;
TParameter param;
TTypeLine typeLine;
TTypeList* typeList;
TAnnotation* ann;
TTypeInfo* typeInfo;
};
} interm;
}
%{
#ifndef _WIN32
extern int yylex(YYSTYPE*, void*);
#endif
%}
%pure_parser /* Just in case is called from multiple threads */
%expect 1 /* One shift reduce conflict because of if | else */
%token <lex> CONST_QUAL STATIC_QUAL BOOL_TYPE FLOAT_TYPE INT_TYPE STRING_TYPE FIXED_TYPE HALF_TYPE
%token <lex> BREAK CONTINUE DO ELSE FOR IF DISCARD RETURN
%token <lex> BVEC2 BVEC3 BVEC4 IVEC2 IVEC3 IVEC4 VEC2 VEC3 VEC4 HVEC2 HVEC3 HVEC4 FVEC2 FVEC3 FVEC4
%token <lex> MATRIX2x2 MATRIX2x3 MATRIX2x4 MATRIX3x2 MATRIX3x3 MATRIX3x4 MATRIX4x2 MATRIX4x3 MATRIX4x4
%token <lex> HMATRIX2x2 HMATRIX2x3 HMATRIX2x4 HMATRIX3x2 HMATRIX3x3 HMATRIX3x4 HMATRIX4x2 HMATRIX4x3 HMATRIX4x4
%token <lex> FMATRIX2x2 FMATRIX2x3 FMATRIX2x4 FMATRIX3x2 FMATRIX3x3 FMATRIX3x4 FMATRIX4x2 FMATRIX4x3 FMATRIX4x4
%token <lex> IN_QUAL OUT_QUAL INOUT_QUAL UNIFORM
%token <lex> STRUCT VOID_TYPE WHILE
%token <lex> SAMPLER1D SAMPLER2D SAMPLER3D SAMPLERCUBE SAMPLER1DSHADOW SAMPLER2DSHADOW SAMPLERRECTSHADOW SAMPLERRECT
%token <lex> SAMPLER2D_HALF SAMPLER2D_FLOAT SAMPLERCUBE_HALF SAMPLERCUBE_FLOAT
%token <lex> SAMPLERGENERIC VECTOR MATRIX REGISTER TEXTURE SAMPLERSTATE
%token <lex> IDENTIFIER TYPE_NAME FLOATCONSTANT INTCONSTANT BOOLCONSTANT STRINGCONSTANT
%token <lex> FIELD_SELECTION
%token <lex> LEFT_OP RIGHT_OP
%token <lex> INC_OP DEC_OP LE_OP GE_OP EQ_OP NE_OP
%token <lex> AND_OP OR_OP XOR_OP MUL_ASSIGN DIV_ASSIGN ADD_ASSIGN
%token <lex> MOD_ASSIGN LEFT_ASSIGN RIGHT_ASSIGN AND_ASSIGN XOR_ASSIGN OR_ASSIGN
%token <lex> SUB_ASSIGN
%type <lex> ann_numerical_constant
%token <lex> LEFT_PAREN RIGHT_PAREN LEFT_BRACKET RIGHT_BRACKET LEFT_BRACE RIGHT_BRACE DOT
%token <lex> COMMA COLON EQUAL SEMICOLON BANG DASH TILDE PLUS STAR SLASH PERCENT
%token <lex> LEFT_ANGLE RIGHT_ANGLE VERTICAL_BAR CARET AMPERSAND QUESTION
%type <interm> assignment_operator unary_operator
%type <interm.intermTypedNode> variable_identifier primary_expression postfix_expression
%type <interm.intermTypedNode> expression int_expression assign_expression
%type <interm.intermTypedNode> unary_expression mul_expression add_expression
%type <interm.intermTypedNode> rel_expression eq_expression
%type <interm.intermTypedNode> cond_expression const_expression
%type <interm.intermTypedNode> log_or_expression log_xor_expression log_and_expression
%type <interm.intermTypedNode> shift_expression and_expression xor_expression or_expression
%type <interm.intermTypedNode> function_call initializer condition conditionopt
%type <interm.intermTypedNode> initialization_list sampler_initializer
%type <interm.intermAggregate> initializer_list
%type <interm.intermNode> translation_unit function_definition
%type <interm.intermNode> statement simple_statement
%type <interm.intermAggregate> statement_list compound_statement
%type <interm.intermNode> declaration_statement selection_statement expression_statement
%type <interm.intermNode> external_declaration
%type <interm.intermDeclaration> declaration
%type <interm.intermNode> for_init_statement compound_statement_no_new_scope
%type <interm.nodePair> selection_rest_statement for_rest_statement
%type <interm.intermNode> iteration_statement jump_statement statement_no_new_scope
%type <interm.intermDeclaration> single_declaration init_declarator_list
%type <interm> parameter_declaration parameter_declarator parameter_type_specifier
%type <interm.qualifier> parameter_qualifier
%type <interm.type> type_qualifier fully_specified_type type_specifier
%type <interm.type> type_specifier_nonarray
%type <interm.type> struct_specifier
%type <interm.typeLine> struct_declarator
%type <interm.typeList> struct_declarator_list struct_declaration struct_declaration_list
%type <interm.function> function_header function_declarator function_identifier
%type <interm.function> function_header_with_parameters function_call_header
%type <interm> function_call_header_with_parameters function_call_header_no_parameters function_call_generic function_prototype
%type <interm> function_call_or_method
%type <interm.ann> annotation annotation_list
%type <interm.typeInfo> type_info
%type <lex> annotation_item semantic
%start translation_unit
%%
variable_identifier
: IDENTIFIER {
// The symbol table search was done in the lexical phase
const TSymbol* symbol = $1.symbol;
const TVariable* variable;
if (symbol == 0) {
parseContext.error($1.line, "undeclared identifier", $1.string->c_str(), "");
parseContext.recover();
TType type(EbtFloat, EbpUndefined);
TVariable* fakeVariable = new TVariable($1.string, type);
parseContext.symbolTable.insert(*fakeVariable);
variable = fakeVariable;
} else {
// This identifier can only be a variable type symbol
if (! symbol->isVariable()) {
parseContext.error($1.line, "variable expected", $1.string->c_str(), "");
parseContext.recover();
}
variable = static_cast<const TVariable*>(symbol);
}
// don't delete $1.string, it's used by error recovery, and the pool
// pop will reclaim the memory
if (variable->getType().getQualifier() == EvqConst && variable->constValue)
{
TIntermConstant* c = ir_add_constant(variable->getType(), $1.line);
c->copyValuesFrom(*variable->constValue);
$$ = c;
}
else
{
TIntermSymbol* sym = ir_add_symbol(variable, $1.line);
$$ = sym;
}
}
;
primary_expression
: variable_identifier {
$$ = $1;
}
| INTCONSTANT {
TIntermConstant* constant = ir_add_constant(TType(EbtInt, EbpUndefined, EvqConst), $1.line);
constant->setValue($1.i);
$$ = constant;
}
| FLOATCONSTANT {
TIntermConstant* constant = ir_add_constant(TType(EbtFloat, EbpUndefined, EvqConst), $1.line);
constant->setValue($1.f);
$$ = constant;
}
| BOOLCONSTANT {
TIntermConstant* constant = ir_add_constant(TType(EbtBool, EbpUndefined, EvqConst), $1.line);
constant->setValue($1.b);
$$ = constant;
}
| LEFT_PAREN expression RIGHT_PAREN {
$$ = $2;
}
;
postfix_expression
: primary_expression {
$$ = $1;
}
| postfix_expression LEFT_BRACKET int_expression RIGHT_BRACKET {
if (!$1) {
parseContext.error($2.line, " left of '[' is null ", "expression", "");
YYERROR;
}
if (!$1->isArray() && !$1->isMatrix() && !$1->isVector()) {
if ($1->getAsSymbolNode())
parseContext.error($2.line, " left of '[' is not of type array, matrix, or vector ", $1->getAsSymbolNode()->getSymbol().c_str(), "");
else
parseContext.error($2.line, " left of '[' is not of type array, matrix, or vector ", "expression", "");
parseContext.recover();
}
if ($3->getQualifier() == EvqConst) {
if (($1->isVector() || $1->isMatrix()) && $1->getType().getRowsCount() <= $3->getAsConstant()->toInt() && !$1->isArray() ) {
parseContext.error($2.line, "", "[", "field selection out of range '%d'", $3->getAsConstant()->toInt());
parseContext.recover();
} else {
if ($1->isArray()) {
if ($1->getType().getArraySize() == 0) {
if ($1->getType().getMaxArraySize() <= $3->getAsConstant()->toInt()) {
if (parseContext.arraySetMaxSize($1->getAsSymbolNode(), $1->getTypePointer(), $3->getAsConstant()->toInt(), true, $2.line))
parseContext.recover();
} else {
if (parseContext.arraySetMaxSize($1->getAsSymbolNode(), $1->getTypePointer(), 0, false, $2.line))
parseContext.recover();
}
} else if ( $3->getAsConstant()->toInt() >= $1->getType().getArraySize()) {
parseContext.error($2.line, "", "[", "array index out of range '%d'", $3->getAsConstant()->toInt());
parseContext.recover();
}
}
$$ = ir_add_index(EOpIndexDirect, $1, $3, $2.line);
}
} else {
if ($1->isArray() && $1->getType().getArraySize() == 0) {
parseContext.error($2.line, "", "[", "array must be redeclared with a size before being indexed with a variable");
parseContext.recover();
}
$$ = ir_add_index(EOpIndexIndirect, $1, $3, $2.line);
}
if ($$ == 0) {
TIntermConstant* constant = ir_add_constant(TType(EbtFloat, EbpUndefined, EvqConst), $2.line);
constant->setValue(0.f);
$$ = constant;
} else if ($1->isArray()) {
if ($1->getType().getStruct())
$$->setType(TType($1->getType().getStruct(), $1->getType().getTypeName(), EbpUndefined, $1->getLine()));
else
$$->setType(TType($1->getBasicType(), $1->getPrecision(), EvqTemporary, $1->getColsCount(),$1->getRowsCount(), $1->isMatrix()));
if ($1->getType().getQualifier() == EvqConst)
$$->getTypePointer()->changeQualifier(EvqConst);
} else if ($1->isMatrix() && $1->getType().getQualifier() == EvqConst)
$$->setType(TType($1->getBasicType(), $1->getPrecision(), EvqConst, 1, $1->getColsCount()));
else if ($1->isMatrix())
$$->setType(TType($1->getBasicType(), $1->getPrecision(), EvqTemporary, 1, $1->getColsCount()));
else if ($1->isVector() && $1->getType().getQualifier() == EvqConst)
$$->setType(TType($1->getBasicType(), $1->getPrecision(), EvqConst));
else if ($1->isVector())
$$->setType(TType($1->getBasicType(), $1->getPrecision(), EvqTemporary));
else
$$->setType($1->getType());
}
| function_call {
$$ = $1;
}
| postfix_expression DOT FIELD_SELECTION {
if (!$1) {
parseContext.error($3.line, "field selection on null object", ".", "");
YYERROR;
}
if ($1->isArray()) {
parseContext.error($3.line, "cannot apply dot operator to an array", ".", "");
parseContext.recover();
}
if ($1->isVector()) {
TVectorFields fields;
if (! parseContext.parseVectorFields(*$3.string, $1->getRowsCount(), fields, $3.line)) {
fields.num = 1;
fields.offsets[0] = 0;
parseContext.recover();
}
$$ = ir_add_vector_swizzle(fields, $1, $2.line, $3.line);
} else if ($1->isMatrix()) {
TVectorFields fields;
if (!parseContext.parseMatrixFields(*$3.string, $1->getColsCount(), $1->getRowsCount(), fields, $3.line)) {
fields.num = 1;
fields.offsets[0] = 0;
parseContext.recover();
}
TString vectorString = *$3.string;
TIntermTyped* index = ir_add_swizzle(fields, $3.line);
$$ = ir_add_index(EOpMatrixSwizzle, $1, index, $2.line);
$$->setType(TType($1->getBasicType(), $1->getPrecision(), EvqTemporary, 1, fields.num));
} else if ($1->getBasicType() == EbtStruct) {
bool fieldFound = false;
TTypeList* fields = $1->getType().getStruct();
if (fields == 0) {
parseContext.error($2.line, "structure has no fields", "Internal Error", "");
parseContext.recover();
$$ = $1;
} else {
unsigned int i;
for (i = 0; i < fields->size(); ++i) {
if ((*fields)[i].type->getFieldName() == *$3.string) {
fieldFound = true;
break;
}
}
if (fieldFound) {
TIntermConstant* index = ir_add_constant(TType(EbtInt, EbpUndefined, EvqConst), $3.line);
index->setValue(i);
$$ = ir_add_index(EOpIndexDirectStruct, $1, index, $2.line);
$$->setType(*(*fields)[i].type);
} else {
parseContext.error($2.line, " no such field in structure", $3.string->c_str(), "");
parseContext.recover();
$$ = $1;
}
}
} else if ($1->isScalar()) {
// HLSL allows ".xxxx" field selection on single component floats. Handle that here.
TVectorFields fields;
// Check to make sure only the "x" component is accessed.
if (! parseContext.parseVectorFields(*$3.string, 1, fields, $3.line)) {
fields.num = 1;
fields.offsets[0] = 0;
parseContext.recover();
}
// Create the appropriate constructor based on the number of ".x"'s there are in the selection field
TString vectorString = *$3.string;
TQualifier qualifier = $1->getType().getQualifier() == EvqConst ? EvqConst : EvqTemporary;
TType type($1->getBasicType(), $1->getPrecision(), qualifier, 1, (int) vectorString.size());
$$ = parseContext.constructBuiltIn(&type, parseContext.getConstructorOp(type),
$$, $1->getLine(), false);
} else {
parseContext.error($2.line, " field selection requires structure, vector, or matrix on left hand side", $3.string->c_str(), "");
parseContext.recover();
$$ = $1;
}
// don't delete $3.string, it's from the pool
}
| postfix_expression INC_OP {
if (parseContext.lValueErrorCheck($2.line, "++", $1))
parseContext.recover();
$$ = ir_add_unary_math(EOpPostIncrement, $1, $2.line, parseContext);
if ($$ == 0) {
parseContext.unaryOpError($2.line, "++", $1->getCompleteString());
parseContext.recover();
$$ = $1;
}
}
| postfix_expression DEC_OP {
if (parseContext.lValueErrorCheck($2.line, "--", $1))
parseContext.recover();
$$ = ir_add_unary_math(EOpPostDecrement, $1, $2.line, parseContext);
if ($$ == 0) {
parseContext.unaryOpError($2.line, "--", $1->getCompleteString());
parseContext.recover();
$$ = $1;
}
}
;
int_expression
: expression {
if (parseContext.scalarErrorCheck($1, "[]"))
parseContext.recover();
TType type(EbtInt, EbpUndefined);
$$ = parseContext.constructBuiltIn(&type, EOpConstructInt, $1, $1->getLine(), true);
if ($$ == 0) {
parseContext.error($1->getLine(), "cannot convert to index", "[]", "");
parseContext.recover();
$$ = $1;
}
}
;
function_call
: function_call_or_method {
TFunction* fnCall = $1.function;
TOperator op = fnCall->getBuiltInOp();
if (op == EOpArrayLength) {
if ($1.intermNode->getAsTyped() == 0 || $1.intermNode->getAsTyped()->getType().getArraySize() == 0) {
parseContext.error($1.line, "", fnCall->getName().c_str(), "array must be declared with a size before using this method");
parseContext.recover();
}
TIntermConstant* constant = ir_add_constant(TType(EbtInt, EbpUndefined, EvqConst), $1.line);
constant->setValue($1.intermNode->getAsTyped()->getType().getArraySize());
$$ = constant;
} else if (op != EOpNull) {
//
// Then this should be a constructor.
// Don't go through the symbol table for constructors.
// Their parameters will be verified algorithmically.
//
TType type(EbtVoid, EbpUndefined); // use this to get the type back
if (parseContext.constructorErrorCheck($1.line, $1.intermNode, *fnCall, op, &type)) {
$$ = 0;
} else {
//
// It's a constructor, of type 'type'.
//
$$ = parseContext.addConstructor($1.intermNode, &type, op, fnCall, $1.line);
}
if ($$ == 0) {
parseContext.recover();
$$ = ir_set_aggregate_op(0, op, $1.line);
$$->setType(type);
}
} else {
//
// Not a constructor. Find it in the symbol table.
//
const TFunction* fnCandidate;
bool builtIn;
fnCandidate = parseContext.findFunction($1.line, fnCall, &builtIn);
if ( fnCandidate && fnCandidate->getMangledName() != fnCall->getMangledName()) {
//add constructors to arguments to ensure that they have proper types
TIntermNode *temp = parseContext.promoteFunctionArguments( $1.intermNode,
fnCandidate);
if (temp)
$1.intermNode = temp;
else {
parseContext.error( $1.intermNode->getLine(), " unable to suitably promote arguments to function",
fnCandidate->getName().c_str(), "");
fnCandidate = 0;
}
}
if (fnCandidate) {
//
// A declared function. But, it might still map to a built-in
// operation.
//
op = fnCandidate->getBuiltInOp();
if (builtIn && op != EOpNull) {
//
// A function call mapped to a built-in operation.
//
if (fnCandidate->getParamCount() == 1) {
//
// Treat it like a built-in unary operator.
//
$$ = ir_add_unary_math(op, $1.intermNode, gNullSourceLoc, parseContext);
if ($$ == 0) {
parseContext.error($1.intermNode->getLine(), " wrong operand type", "Internal Error",
"built in unary operator function. Type: %s",
static_cast<TIntermTyped*>($1.intermNode)->getCompleteString().c_str());
YYERROR;
}
} else {
$$ = ir_set_aggregate_op($1.intermAggregate, op, $1.line);
$$->setType(fnCandidate->getReturnType());
}
} else {
// This is a real function call
$$ = ir_set_aggregate_op($1.intermAggregate, EOpFunctionCall, $1.line);
$$->setType(fnCandidate->getReturnType());
$$->getAsAggregate()->setName(fnCandidate->getMangledName());
$$->getAsAggregate()->setPlainName(fnCandidate->getName());
TQualifier qual;
for (int i = 0; i < fnCandidate->getParamCount(); ++i) {
qual = (*fnCandidate)[i].type->getQualifier();
if (qual == EvqOut || qual == EvqInOut) {
if (parseContext.lValueErrorCheck($$->getLine(), "assign", $$->getAsAggregate()->getNodes()[i]->getAsTyped())) {
parseContext.error($1.intermNode->getLine(), "Constant value cannot be passed for 'out' or 'inout' parameters.", "Error", "");
parseContext.recover();
}
}
}
}
$$->setType(fnCandidate->getReturnType());
} else {
// error message was put out by PaFindFunction()
// Put on a dummy node for error recovery
TIntermConstant* constant = ir_add_constant(TType(EbtFloat, EbpUndefined, EvqConst), $1.line);
constant->setValue(0.f);
$$ = constant;
parseContext.recover();
}
}
delete fnCall;
}
;
function_call_or_method
: function_call_generic {
$$ = $1;
}
| postfix_expression DOT function_call_generic {
if ($1->isArray() && $3.function->getName() == "length") {
//
// implement array.length()
//
$$ = $3;
$$.intermNode = $1;
$$.function->relateToOperator(EOpArrayLength);
} else {
parseContext.error($3.line, "methods are not supported", "", "");
parseContext.recover();
$$ = $3;
}
}
;
function_call_generic
: function_call_header_with_parameters RIGHT_PAREN {
$$ = $1;
$$.line = $2.line;
}
| function_call_header_no_parameters RIGHT_PAREN {
$$ = $1;
$$.line = $2.line;
}
;
function_call_header_no_parameters
: function_call_header VOID_TYPE {
$$.function = $1;
$$.intermNode = 0;
}
| function_call_header {
$$.function = $1;
$$.intermNode = 0;
}
;
function_call_header_with_parameters
: function_call_header assign_expression {
if (!$2) {
YYERROR;
}
TParameter param = { 0, 0, new TType($2->getType()) };
$1->addParameter(param);
$$.function = $1;
$$.intermNode = $2;
}
| function_call_header_with_parameters COMMA assign_expression {
if (!$3) {
YYERROR;
}
TParameter param = { 0, 0, new TType($3->getType()) };
$1.function->addParameter(param);
$$.function = $1.function;
$$.intermNode = ir_grow_aggregate($1.intermNode, $3, $2.line);
}
;
function_call_header
: function_identifier LEFT_PAREN {
$$ = $1;
}
;
// Grammar Note: Constructors look like functions, but are recognized as types.
function_identifier
: type_specifier {
//
// Constructor
//
if ($1.array) {
//TODO : figure out how to deal with array constructors
}
if ($1.userDef) {
TString tempString = "";
TType type($1);
TFunction *function = new TFunction(&tempString, type, EOpConstructStruct);
$$ = function;
} else {
TOperator op = ir_get_constructor_op($1, parseContext, false);
if (op == EOpNull) {
parseContext.error($1.line, "cannot construct this type", TType::getBasicString($1.type), "");
parseContext.recover();
$1.type = EbtFloat;
op = EOpConstructFloat;
}
TString tempString = "";
TType type($1);
TFunction *function = new TFunction(&tempString, type, op);
$$ = function;
}
}
| IDENTIFIER {
if (parseContext.reservedErrorCheck($1.line, *$1.string))
parseContext.recover();
TType type(EbtVoid, EbpUndefined);
const TString *mangled;
if ( *$1.string == "main")
mangled = NewPoolTString("xlat_main");
else
mangled = $1.string;
TFunction *function = new TFunction( mangled, type);
$$ = function;
}
| FIELD_SELECTION {
if (parseContext.reservedErrorCheck($1.line, *$1.string))
parseContext.recover();
TType type(EbtVoid, EbpUndefined);
TFunction *function = new TFunction($1.string, type);
$$ = function;
}
;
unary_expression
: postfix_expression {
$$ = $1;
}
| INC_OP unary_expression {
if (parseContext.lValueErrorCheck($1.line, "++", $2))
parseContext.recover();
$$ = ir_add_unary_math(EOpPreIncrement, $2, $1.line, parseContext);
if ($$ == 0) {
parseContext.unaryOpError($1.line, "++", $2->getCompleteString());
parseContext.recover();
$$ = $2;
}
}
| DEC_OP unary_expression {
if (parseContext.lValueErrorCheck($1.line, "--", $2))
parseContext.recover();
$$ = ir_add_unary_math(EOpPreDecrement, $2, $1.line, parseContext);
if ($$ == 0) {
parseContext.unaryOpError($1.line, "--", $2->getCompleteString());
parseContext.recover();
$$ = $2;
}
}
| unary_operator unary_expression {
if ($1.op != EOpNull) {
$$ = ir_add_unary_math($1.op, $2, $1.line, parseContext);
if ($$ == 0) {
const char* errorOp = "";
switch($1.op) {
case EOpNegative: errorOp = "-"; break;
case EOpLogicalNot: errorOp = "!"; break;
case EOpBitwiseNot: errorOp = "~"; break;
default: break;
}
parseContext.unaryOpError($1.line, errorOp, $2->getCompleteString());
parseContext.recover();
$$ = $2;
}
} else
$$ = $2;
}
| LEFT_PAREN type_specifier_nonarray RIGHT_PAREN unary_expression {
// cast operator, insert constructor
TOperator op = ir_get_constructor_op($2, parseContext, true);
if (op == EOpNull) {
parseContext.error($2.line, "cannot cast this type", TType::getBasicString($2.type), "");
parseContext.recover();
$2.type = EbtFloat;
op = EOpConstructFloat;
}
TString tempString = "";
TType type($2);
TFunction *function = new TFunction(&tempString, type, op);
TParameter param = { 0, 0, new TType($4->getType()) };
function->addParameter(param);
TType type2(EbtVoid, EbpUndefined); // use this to get the type back
if (parseContext.constructorErrorCheck($2.line, $4, *function, op, &type2)) {
$$ = 0;
} else {
//
// It's a constructor, of type 'type'.
//
$$ = parseContext.addConstructor($4, &type2, op, function, $2.line);
}
if ($$ == 0) {
parseContext.recover();
$$ = ir_set_aggregate_op(0, op, $2.line);
} else {
$$->setType(type2);
}
}
;
// Grammar Note: No traditional style type casts.
unary_operator
: PLUS { $$.line = $1.line; $$.op = EOpNull; }
| DASH { $$.line = $1.line; $$.op = EOpNegative; }
| BANG { $$.line = $1.line; $$.op = EOpLogicalNot; }
| TILDE { UNSUPPORTED_FEATURE("~", $1.line);
$$.line = $1.line; $$.op = EOpBitwiseNot; }
;
// Grammar Note: No '*' or '&' unary ops. Pointers are not supported.
mul_expression
: unary_expression { $$ = $1; }
| mul_expression STAR unary_expression { $$ = parseContext.add_binary(EOpMul, $1, $3, $2.line, "*", false); }
| mul_expression SLASH unary_expression { $$ = parseContext.add_binary(EOpDiv, $1, $3, $2.line, "/", false); }
| mul_expression PERCENT unary_expression { $$ = parseContext.add_binary(EOpMod, $1, $3, $2.line, "%", false); }
;
add_expression
: mul_expression { $$ = $1; }
| add_expression PLUS mul_expression { $$ = parseContext.add_binary(EOpAdd, $1, $3, $2.line, "+", false); }
| add_expression DASH mul_expression { $$ = parseContext.add_binary(EOpSub, $1, $3, $2.line, "-", false); }
;
shift_expression
: add_expression { $$ = $1; }
| shift_expression LEFT_OP add_expression { $$ = parseContext.add_binary(EOpLeftShift, $1, $3, $2.line, "<<", false); }
| shift_expression RIGHT_OP add_expression { $$ = parseContext.add_binary(EOpRightShift, $1, $3, $2.line, ">>", false); }
;
rel_expression
: shift_expression { $$ = $1; }
| rel_expression LEFT_ANGLE shift_expression { $$ = parseContext.add_binary(EOpLessThan, $1, $3, $2.line, "<", true); }
| rel_expression RIGHT_ANGLE shift_expression { $$ = parseContext.add_binary(EOpGreaterThan, $1, $3, $2.line, ">", true); }
| rel_expression LE_OP shift_expression { $$ = parseContext.add_binary(EOpLessThanEqual, $1, $3, $2.line, "<=", true); }
| rel_expression GE_OP shift_expression { $$ = parseContext.add_binary(EOpGreaterThanEqual, $1, $3, $2.line, ">=", true); }
;
eq_expression
: rel_expression { $$ = $1; }
| eq_expression EQ_OP rel_expression { $$ = parseContext.add_binary(EOpEqual, $1, $3, $2.line, "==", true); }
| eq_expression NE_OP rel_expression { $$ = parseContext.add_binary(EOpNotEqual, $1, $3, $2.line, "!=", true); }
;
and_expression
: eq_expression { $$ = $1; }
| and_expression AMPERSAND eq_expression { $$ = parseContext.add_binary(EOpAnd, $1, $3, $2.line, "&", false); }
;
xor_expression
: and_expression { $$ = $1; }
| xor_expression CARET and_expression { $$ = parseContext.add_binary(EOpExclusiveOr, $1, $3, $2.line, "^", false); }
;
or_expression
: xor_expression { $$ = $1; }
| or_expression VERTICAL_BAR xor_expression { $$ = parseContext.add_binary(EOpInclusiveOr, $1, $3, $2.line, "|", false); }
;
log_and_expression
: or_expression { $$ = $1; }
| log_and_expression AND_OP or_expression { $$ = parseContext.add_binary(EOpLogicalAnd, $1, $3, $2.line, "&&", true); }
;
log_xor_expression
: log_and_expression { $$ = $1; }
| log_xor_expression XOR_OP log_and_expression { $$ = parseContext.add_binary(EOpLogicalXor, $1, $3, $2.line, "^^", true); }
;
log_or_expression
: log_xor_expression { $$ = $1; }
| log_or_expression OR_OP log_xor_expression { $$ = parseContext.add_binary(EOpLogicalOr, $1, $3, $2.line, "||", true); }
;
cond_expression
: log_or_expression { $$ = $1; }
| log_or_expression QUESTION expression COLON assign_expression {
if (parseContext.boolOrVectorErrorCheck($2.line, $1))
parseContext.recover();
$$ = ir_add_selection($1, $3, $5, $2.line, parseContext.infoSink);
if ($$ == 0) {
parseContext.binaryOpError($2.line, ":", $3->getCompleteString(), $5->getCompleteString());
parseContext.recover();
$$ = $5;
}
}
;
assign_expression
: cond_expression { $$ = $1; }
| unary_expression assignment_operator assign_expression {
if (parseContext.lValueErrorCheck($2.line, "assign", $1))
parseContext.recover();
$$ = parseContext.addAssign($2.op, $1, $3, $2.line);
if ($$ == 0) {
parseContext.assignError($2.line, "assign", $1->getCompleteString(), $3->getCompleteString());
parseContext.recover();
$$ = $1;
} else if (($1->isArray() || $3->isArray()))
parseContext.recover();
}
;
assignment_operator
: EQUAL { $$.line = $1.line; $$.op = EOpAssign; }
| MUL_ASSIGN { $$.line = $1.line; $$.op = EOpMulAssign; }
| DIV_ASSIGN { $$.line = $1.line; $$.op = EOpDivAssign; }
| MOD_ASSIGN { UNSUPPORTED_FEATURE("%=", $1.line); $$.line = $1.line; $$.op = EOpModAssign; }
| ADD_ASSIGN { $$.line = $1.line; $$.op = EOpAddAssign; }
| SUB_ASSIGN { $$.line = $1.line; $$.op = EOpSubAssign; }
| LEFT_ASSIGN { UNSUPPORTED_FEATURE("<<=", $1.line); $$.line = $1.line; $$.op = EOpLeftShiftAssign; }
| RIGHT_ASSIGN { UNSUPPORTED_FEATURE("<<=", $1.line); $$.line = $1.line; $$.op = EOpRightShiftAssign; }
| AND_ASSIGN { UNSUPPORTED_FEATURE("&=", $1.line); $$.line = $1.line; $$.op = EOpAndAssign; }
| XOR_ASSIGN { UNSUPPORTED_FEATURE("^=", $1.line); $$.line = $1.line; $$.op = EOpExclusiveOrAssign; }
| OR_ASSIGN { UNSUPPORTED_FEATURE("|=", $1.line); $$.line = $1.line; $$.op = EOpInclusiveOrAssign; }
;
expression
: assign_expression {
$$ = $1;
}
| expression COMMA assign_expression {
$$ = ir_add_comma($1, $3, $2.line);
if ($$ == 0) {
parseContext.binaryOpError($2.line, ",", $1->getCompleteString(), $3->getCompleteString());
parseContext.recover();
$$ = $3;
}
}
;
const_expression
: cond_expression {
if (parseContext.constErrorCheck($1))
parseContext.recover();
$$ = $1;
}
;
declaration
: function_prototype SEMICOLON { $$ = 0; }
| init_declarator_list SEMICOLON { $$ = $1; }
;
function_prototype
: function_declarator RIGHT_PAREN {
//
// Multiple declarations of the same function are allowed.
//
// If this is a definition, the definition production code will check for redefinitions
// (we don't know at this point if it's a definition or not).
//
// Redeclarations are allowed. But, return types and parameter qualifiers must match.
//
TFunction* prevDec = static_cast<TFunction*>(parseContext.symbolTable.find($1->getMangledName()));
if (prevDec) {
if (prevDec->getReturnType() != $1->getReturnType()) {
parseContext.error($2.line, "overloaded functions must have the same return type", $1->getReturnType().getBasicString(), "");
parseContext.recover();
}
for (int i = 0; i < prevDec->getParamCount(); ++i) {
if ((*prevDec)[i].type->getQualifier() != (*$1)[i].type->getQualifier()) {
parseContext.error($2.line, "overloaded functions must have the same parameter qualifiers", (*$1)[i].type->getQualifierString(), "");
parseContext.recover();
}
}
}
//
// If this is a redeclaration, it could also be a definition,
// in which case, we want to use the variable names from this one, and not the one that's
// being redeclared. So, pass back up this declaration, not the one in the symbol table.
//
$$.function = $1;
$$.line = $2.line;
parseContext.symbolTable.insert(*$$.function);
}
| function_declarator RIGHT_PAREN COLON IDENTIFIER {
//
// Multiple declarations of the same function are allowed.
//
// If this is a definition, the definition production code will check for redefinitions
// (we don't know at this point if it's a definition or not).
//
// Redeclarations are allowed. But, return types and parameter qualifiers must match.
//
TFunction* prevDec = static_cast<TFunction*>(parseContext.symbolTable.find($1->getMangledName()));
if (prevDec) {
if (prevDec->getReturnType() != $1->getReturnType()) {
parseContext.error($2.line, "overloaded functions must have the same return type", $1->getReturnType().getBasicString(), "");
parseContext.recover();
}
for (int i = 0; i < prevDec->getParamCount(); ++i) {
if ((*prevDec)[i].type->getQualifier() != (*$1)[i].type->getQualifier()) {
parseContext.error($2.line, "overloaded functions must have the same parameter qualifiers", (*$1)[i].type->getQualifierString(), "");
parseContext.recover();
}
}
}
//
// If this is a redeclaration, it could also be a definition,
// in which case, we want to use the variable names from this one, and not the one that's
// being redeclared. So, pass back up this declaration, not the one in the symbol table.
//
$$.function = $1;
$$.line = $2.line;
$$.function->setInfo(new TTypeInfo(*$4.string, 0));
parseContext.symbolTable.insert(*$$.function);
}
;
function_declarator
: function_header {
$$ = $1;
}
| function_header_with_parameters {
$$ = $1;
}
;
function_header_with_parameters
: function_header parameter_declaration {
// Add the parameter
$$ = $1;
if ($2.param.type->getBasicType() != EbtVoid)
$1->addParameter($2.param);
else
delete $2.param.type;
}
| function_header_with_parameters COMMA parameter_declaration {
//
// Only first parameter of one-parameter functions can be void
// The check for named parameters not being void is done in parameter_declarator
//
if ($3.param.type->getBasicType() == EbtVoid) {
//
// This parameter > first is void
//
parseContext.error($2.line, "cannot be an argument type except for '(void)'", "void", "");
parseContext.recover();
delete $3.param.type;
} else {
// Add the parameter
$$ = $1;
$1->addParameter($3.param);
}
}
;
function_header
: fully_specified_type IDENTIFIER LEFT_PAREN {
if ($1.qualifier != EvqGlobal && $1.qualifier != EvqTemporary) {
parseContext.error($2.line, "no qualifiers allowed for function return", getQualifierString($1.qualifier), "");
parseContext.recover();
}
// make sure a sampler is not involved as well...
if (parseContext.structQualifierErrorCheck($2.line, $1))
parseContext.recover();
// Add the function as a prototype after parsing it (we do not support recursion)
TFunction *function;
TType type($1);
const TString* mangled = 0;
if ( *$2.string == "main")
mangled = NewPoolTString( "xlat_main");
else
mangled = $2.string;
function = new TFunction(mangled, type);
$$ = function;
}
;
parameter_declarator
// Type + name
: type_specifier IDENTIFIER {
if ($1.type == EbtVoid) {
parseContext.error($2.line, "illegal use of type 'void'", $2.string->c_str(), "");
parseContext.recover();
}
if (parseContext.reservedErrorCheck($2.line, *$2.string))
parseContext.recover();
TParameter param = {$2.string, 0, new TType($1)};
$$.line = $2.line;
$$.param = param;
}
| type_specifier IDENTIFIER EQUAL initializer {
if ($1.type == EbtVoid) {
parseContext.error($2.line, "illegal use of type 'void'", $2.string->c_str(), "");
parseContext.recover();
}
if (parseContext.reservedErrorCheck($2.line, *$2.string))
parseContext.recover();
TParameter param = {$2.string, 0, new TType($1)};
$$.line = $2.line;
$$.param = param;
//TODO: add initializer support
}
| type_specifier IDENTIFIER register_specifier {
// register is being ignored
if ($1.type == EbtVoid) {
parseContext.error($2.line, "illegal use of type 'void'", $2.string->c_str(), "");
parseContext.recover();
}
if (parseContext.reservedErrorCheck($2.line, *$2.string))
parseContext.recover();
TParameter param = {$2.string, 0, new TType($1)};
$$.line = $2.line;
$$.param = param;
}
| type_specifier IDENTIFIER COLON IDENTIFIER {
//Parameter with semantic
if ($1.type == EbtVoid) {
parseContext.error($2.line, "illegal use of type 'void'", $2.string->c_str(), "");
parseContext.recover();
}
if (parseContext.reservedErrorCheck($2.line, *$2.string))
parseContext.recover();
TParameter param = {$2.string, new TTypeInfo(*$4.string, 0), new TType($1)};
$$.line = $2.line;
$$.param = param;
}
| type_specifier IDENTIFIER LEFT_BRACKET const_expression RIGHT_BRACKET {
// Check that we can make an array out of this type
if (parseContext.arrayTypeErrorCheck($3.line, $1))
parseContext.recover();
if (parseContext.reservedErrorCheck($2.line, *$2.string))
parseContext.recover();
int size;
if (parseContext.arraySizeErrorCheck($3.line, $4, size))
parseContext.recover();
$1.setArray(true, size);
TType* type = new TType($1);
TParameter param = { $2.string, 0, type };
$$.line = $2.line;
$$.param = param;
}
| type_specifier IDENTIFIER LEFT_BRACKET const_expression RIGHT_BRACKET COLON IDENTIFIER {
// Check that we can make an array out of this type
if (parseContext.arrayTypeErrorCheck($3.line, $1))
parseContext.recover();
if (parseContext.reservedErrorCheck($2.line, *$2.string))
parseContext.recover();
int size;
if (parseContext.arraySizeErrorCheck($3.line, $4, size))
parseContext.recover();
$1.setArray(true, size);
TType* type = new TType($1);
TParameter param = { $2.string, new TTypeInfo(*$7.string, 0), type };
$$.line = $2.line;
$$.param = param;
}
;
parameter_declaration
//
// The only parameter qualifier a parameter can have are
// IN_QUAL, OUT_QUAL, INOUT_QUAL, or CONST.
//
//
// Type + name
//
: type_qualifier parameter_qualifier parameter_declarator {
$$ = $3;
if (parseContext.paramErrorCheck($3.line, $1.qualifier, $2, $$.param.type))
parseContext.recover();
}
| parameter_qualifier parameter_declarator {
$$ = $2;
if (parseContext.parameterSamplerErrorCheck($2.line, $1, *$2.param.type))
parseContext.recover();
if (parseContext.paramErrorCheck($2.line, EvqTemporary, $1, $$.param.type))
parseContext.recover();
}
//
// Only type
//
| type_qualifier parameter_qualifier parameter_type_specifier {
$$ = $3;
if (parseContext.paramErrorCheck($3.line, $1.qualifier, $2, $$.param.type))
parseContext.recover();
}
| parameter_qualifier parameter_type_specifier {
$$ = $2;
if (parseContext.parameterSamplerErrorCheck($2.line, $1, *$2.param.type))
parseContext.recover();
if (parseContext.paramErrorCheck($2.line, EvqTemporary, $1, $$.param.type))
parseContext.recover();
}
;
parameter_qualifier
: /* empty */ {
$$ = EvqIn;
}
| IN_QUAL {
$$ = EvqIn;
}
| OUT_QUAL {
$$ = EvqOut;
}
| INOUT_QUAL {
$$ = EvqInOut;
}
;
parameter_type_specifier
: type_specifier {
TParameter param = { 0, 0, new TType($1) };
$$.param = param;
}
;
init_declarator_list
: single_declaration {
$$ = $1;
}
| init_declarator_list COMMA IDENTIFIER type_info {
TPublicType type = ir_get_decl_type_noarray($1);
if (parseContext.structQualifierErrorCheck($3.line, type))
parseContext.recover();
if (parseContext.nonInitConstErrorCheck($3.line, *$3.string, type))
parseContext.recover();
if (parseContext.nonInitErrorCheck($3.line, *$3.string, $4, type))
parseContext.recover();
TSymbol* sym = parseContext.symbolTable.find(*$3.string);
if (!sym)
$$ = $1;
else
$$ = ir_grow_declaration($1, sym, NULL, parseContext);
}
| init_declarator_list COMMA IDENTIFIER LEFT_BRACKET RIGHT_BRACKET type_info {
TPublicType type = ir_get_decl_type_noarray($1);
if (parseContext.structQualifierErrorCheck($3.line, type))
parseContext.recover();
if (parseContext.nonInitConstErrorCheck($3.line, *$3.string, type))
parseContext.recover();
if (parseContext.arrayTypeErrorCheck($4.line, type) || parseContext.arrayQualifierErrorCheck($4.line, type))
parseContext.recover();
else {
TVariable* variable;
if (parseContext.arrayErrorCheck($4.line, *$3.string, $6, type, variable))
parseContext.recover();
if (!variable)
$$ = $1;
else {
variable->getType().setArray(true);
$$ = ir_grow_declaration($1, variable, NULL, parseContext);
}
}
}
| init_declarator_list COMMA IDENTIFIER LEFT_BRACKET const_expression RIGHT_BRACKET type_info {
TPublicType type = ir_get_decl_type_noarray($1);
if (parseContext.structQualifierErrorCheck($3.line, type))
parseContext.recover();
if (parseContext.nonInitConstErrorCheck($3.line, *$3.string, type))
parseContext.recover();
if (parseContext.arrayTypeErrorCheck($4.line, type) || parseContext.arrayQualifierErrorCheck($4.line, type))
parseContext.recover();
else {
int size;
if (parseContext.arraySizeErrorCheck($4.line, $5, size))
parseContext.recover();
type.setArray(true, size);
TVariable* variable;
if (parseContext.arrayErrorCheck($4.line, *$3.string, $7, type, variable))
parseContext.recover();
if (!variable)
$$ = $1;
else {
$$ = ir_grow_declaration($1, variable, NULL, parseContext);
}
}
}
| init_declarator_list COMMA IDENTIFIER LEFT_BRACKET RIGHT_BRACKET type_info EQUAL initializer {
TPublicType type = ir_get_decl_type_noarray($1);
if (parseContext.structQualifierErrorCheck($3.line, type))
parseContext.recover();
TVariable* variable = 0;
if (parseContext.arrayTypeErrorCheck($4.line, type) || parseContext.arrayQualifierErrorCheck($4.line, type))
parseContext.recover();
else if (parseContext.arrayErrorCheck($4.line, *$3.string, type, variable))
parseContext.recover();
{
TIntermSymbol* symbol;
type.setArray(true, $8->getType().getArraySize());
if (!parseContext.executeInitializer($3.line, *$3.string, $6, type, $8, symbol, variable)) {
if (!variable)
$$ = $1;
else {
$$ = ir_grow_declaration($1, variable, $8, parseContext);
}
} else {
parseContext.recover();
$$ = 0;
}
}
}
| init_declarator_list COMMA IDENTIFIER LEFT_BRACKET const_expression RIGHT_BRACKET type_info EQUAL initializer {
TPublicType type = ir_get_decl_type_noarray($1);
int array_size;
if (parseContext.structQualifierErrorCheck($3.line, type))
parseContext.recover();
TVariable* variable = 0;
if (parseContext.arrayTypeErrorCheck($4.line, type) || parseContext.arrayQualifierErrorCheck($4.line, type))
parseContext.recover();
else {
if (parseContext.arraySizeErrorCheck($4.line, $5, array_size))
parseContext.recover();
type.setArray(true, array_size);
if (parseContext.arrayErrorCheck($4.line, *$3.string, $7, type, variable))
parseContext.recover();
}
{
TIntermSymbol* symbol;
if (!parseContext.executeInitializer($3.line, *$3.string, $7, type, $9, symbol, variable)) {
if (!variable)
$$ = $1;
else {
$$ = ir_grow_declaration($1, variable, $9, parseContext);
}
} else {
parseContext.recover();
$$ = 0;
}
}
}
| init_declarator_list COMMA IDENTIFIER type_info EQUAL initializer {
TPublicType type = ir_get_decl_type_noarray($1);
if (parseContext.structQualifierErrorCheck($3.line, type))
parseContext.recover();
TIntermSymbol* symbol;
if ( !IsSampler(type.type)) {
if (!parseContext.executeInitializer($3.line, *$3.string, $4, type, $6, symbol)) {
TSymbol* variable = parseContext.symbolTable.find(*$3.string);
if (!variable)
$$ = $1;
else
$$ = ir_grow_declaration($1, variable, $6, parseContext);
} else {
parseContext.recover();
$$ = 0;
}
} else {
//Special code to skip initializers for samplers
$$ = $1;
if (parseContext.structQualifierErrorCheck($3.line, type))
parseContext.recover();
if (parseContext.nonInitConstErrorCheck($3.line, *$3.string, type))
parseContext.recover();
if (parseContext.nonInitErrorCheck($3.line, *$3.string, $4, type))
parseContext.recover();
}
}
;
single_declaration
: fully_specified_type {
$$ = 0;
}
| fully_specified_type IDENTIFIER type_info {
bool error = false;
if (error &= parseContext.structQualifierErrorCheck($2.line, $1))
parseContext.recover();
if (error &= parseContext.nonInitConstErrorCheck($2.line, *$2.string, $1))
parseContext.recover();
if (error &= parseContext.nonInitErrorCheck($2.line, *$2.string, $3, $1))
parseContext.recover();
TSymbol* symbol = parseContext.symbolTable.find(*$2.string);
if (!error && symbol) {
$$ = ir_add_declaration(symbol, NULL, $2.line, parseContext);
} else {
$$ = 0;
}
}
| fully_specified_type IDENTIFIER LEFT_BRACKET RIGHT_BRACKET type_info {
if (parseContext.structQualifierErrorCheck($2.line, $1))
parseContext.recover();
if (parseContext.nonInitConstErrorCheck($2.line, *$2.string, $1))
parseContext.recover();
if (parseContext.arrayTypeErrorCheck($3.line, $1) || parseContext.arrayQualifierErrorCheck($3.line, $1))
parseContext.recover();
else {
$1.setArray(true);
TVariable* variable;
if (parseContext.arrayErrorCheck($3.line, *$2.string, $5, $1, variable))
parseContext.recover();
}
TSymbol* symbol = parseContext.symbolTable.find(*$2.string);
if (symbol) {
$$ = ir_add_declaration(symbol, NULL, $2.line, parseContext);
} else {
$$ = 0;
}
}
| fully_specified_type IDENTIFIER LEFT_BRACKET const_expression RIGHT_BRACKET type_info {
if (parseContext.structQualifierErrorCheck($2.line, $1))
parseContext.recover();
if (parseContext.nonInitConstErrorCheck($2.line, *$2.string, $1))
parseContext.recover();
TVariable* variable;
if (parseContext.arrayTypeErrorCheck($3.line, $1) || parseContext.arrayQualifierErrorCheck($3.line, $1))
parseContext.recover();
else {
int size;
if (parseContext.arraySizeErrorCheck($3.line, $4, size))
parseContext.recover();
$1.setArray(true, size);
if (parseContext.arrayErrorCheck($3.line, *$2.string, $6, $1, variable))
parseContext.recover();
if (variable) {
$$ = ir_add_declaration(variable, NULL, $2.line, parseContext);
} else {
$$ = 0;
}
}
}
| fully_specified_type IDENTIFIER LEFT_BRACKET RIGHT_BRACKET type_info EQUAL initializer {
if (parseContext.structQualifierErrorCheck($2.line, $1))
parseContext.recover();
TVariable* variable = 0;
if (parseContext.arrayTypeErrorCheck($3.line, $1) || parseContext.arrayQualifierErrorCheck($3.line, $1))
parseContext.recover();
else {
$1.setArray(true, $7->getType().getArraySize());
if (parseContext.arrayErrorCheck($3.line, *$2.string, $5, $1, variable))
parseContext.recover();
}
{
TIntermSymbol* symbol;
if (!parseContext.executeInitializer($2.line, *$2.string, $5, $1, $7, symbol, variable)) {
if (variable)
$$ = ir_add_declaration(symbol, $7, $6.line, parseContext);
else
$$ = 0;
} else {
parseContext.recover();
$$ = 0;
}
}
}
| fully_specified_type IDENTIFIER LEFT_BRACKET const_expression RIGHT_BRACKET type_info EQUAL initializer {
if (parseContext.structQualifierErrorCheck($2.line, $1))
parseContext.recover();
TVariable* variable = 0;
if (parseContext.arrayTypeErrorCheck($3.line, $1) || parseContext.arrayQualifierErrorCheck($3.line, $1))
parseContext.recover();
else {
int size;
if (parseContext.arraySizeErrorCheck($3.line, $4, size))
parseContext.recover();
$1.setArray(true, size);
if (parseContext.arrayErrorCheck($3.line, *$2.string, $6, $1, variable))
parseContext.recover();
}
{
TIntermSymbol* symbol;
if (!parseContext.executeInitializer($2.line, *$2.string, $6, $1, $8, symbol, variable)) {
if (variable)
$$ = ir_add_declaration(symbol, $8, $7.line, parseContext);
else
$$ = 0;
} else {
parseContext.recover();
$$ = 0;
}
}
}
| fully_specified_type IDENTIFIER type_info EQUAL initializer {
if (parseContext.structQualifierErrorCheck($2.line, $1))
parseContext.recover();
if (!IsSampler($1.type)) {
TIntermSymbol* symbol;
if (!parseContext.executeInitializer($2.line, *$2.string, $3, $1, $5, symbol)) {
if (symbol)
$$ = ir_add_declaration(symbol, $5, $4.line, parseContext);
else
$$ = 0;
} else {
parseContext.recover();
$$ = 0;
}
} else {
if (parseContext.structQualifierErrorCheck($2.line, $1))
parseContext.recover();
if (parseContext.nonInitConstErrorCheck($2.line, *$2.string, $1))
parseContext.recover();
if (parseContext.nonInitErrorCheck($2.line, *$2.string, $3, $1))
parseContext.recover();
TSymbol* symbol = parseContext.symbolTable.find(*$2.string);
if (symbol) {
$$ = ir_add_declaration(symbol, NULL, $2.line, parseContext);
} else {
$$ = 0;
}
}
}
;
// Grammar Note: No 'enum', or 'typedef'.
//
// Input/output semantics:
// float must be 16 or 32 bits
// float alignment restrictions?
// check for only one input and only one output
// sum of bitfields has to be multiple of 32
//
fully_specified_type
: type_specifier {
$$ = $1;
}
| type_qualifier type_specifier {
if ($2.array && parseContext.arrayQualifierErrorCheck($2.line, $1)) {
parseContext.recover();
$2.setArray(false);
}
if ($1.qualifier == EvqAttribute &&
($2.type == EbtBool || $2.type == EbtInt)) {
parseContext.error($2.line, "cannot be bool or int", getQualifierString($1.qualifier), "");
parseContext.recover();
}
$$ = $2;
$$.qualifier = $1.qualifier;
}
;
type_qualifier
: CONST_QUAL {
$$.setBasic(EbtVoid, EvqConst, $1.line);
}
| STATIC_QUAL {
$$.setBasic(EbtVoid, EvqStatic, $1.line);
}
| STATIC_QUAL CONST_QUAL {
$$.setBasic(EbtVoid, EvqConst, $1.line); // same as "const" really
}
| UNIFORM {
if (parseContext.globalErrorCheck($1.line, parseContext.symbolTable.atGlobalLevel(), "uniform"))
parseContext.recover();
$$.setBasic(EbtVoid, EvqUniform, $1.line);
}
;
type_specifier
: type_specifier_nonarray {
$$ = $1;
}
| type_specifier_nonarray LEFT_BRACKET const_expression RIGHT_BRACKET {
$$ = $1;
if (parseContext.arrayTypeErrorCheck($2.line, $1))
parseContext.recover();
else {
int size;
if (parseContext.arraySizeErrorCheck($2.line, $3, size))
parseContext.recover();
$$.setArray(true, size);
}
}
;
type_specifier_nonarray
: VOID_TYPE {
SET_BASIC_TYPE($$,$1,EbtVoid,EbpUndefined);
}
| FLOAT_TYPE {
SET_BASIC_TYPE($$,$1,EbtFloat,EbpHigh);
}
| HALF_TYPE {
SET_BASIC_TYPE($$,$1,EbtFloat,EbpMedium);
}
| FIXED_TYPE {
SET_BASIC_TYPE($$,$1,EbtFloat,EbpLow);
}
| INT_TYPE {
SET_BASIC_TYPE($$,$1,EbtInt,EbpHigh);
}
| BOOL_TYPE {
SET_BASIC_TYPE($$,$1,EbtBool,EbpHigh);
}
| VECTOR LEFT_ANGLE FLOAT_TYPE COMMA INTCONSTANT RIGHT_ANGLE {
TQualifier qual = parseContext.getDefaultQualifier();
if ( $5.i > 4 || $5.i < 1 ) {
parseContext.error($2.line, "vector dimension out of range", "", "");
parseContext.recover();
$$.setBasic(EbtFloat, qual, $1.line);
} else {
$$.setBasic(EbtFloat, qual, $1.line);
$$.setVector($5.i);
}
}
| VECTOR LEFT_ANGLE INT_TYPE COMMA INTCONSTANT RIGHT_ANGLE {
TQualifier qual = parseContext.getDefaultQualifier();
if ( $5.i > 4 || $5.i < 1 ) {
parseContext.error($2.line, "vector dimension out of range", "", "");
parseContext.recover();
$$.setBasic(EbtInt, qual, $1.line);
} else {
$$.setBasic(EbtInt, qual, $1.line);
$$.setVector($5.i);
}
}
| VECTOR LEFT_ANGLE BOOL_TYPE COMMA INTCONSTANT RIGHT_ANGLE {
TQualifier qual = parseContext.getDefaultQualifier();
if ( $5.i > 4 || $5.i < 1 ) {
parseContext.error($2.line, "vector dimension out of range", "", "");
parseContext.recover();
$$.setBasic(EbtBool, qual, $1.line);
} else {
$$.setBasic(EbtBool, qual, $1.line);
$$.setVector($5.i);
}
}
| VEC2 {
SET_BASIC_TYPE($$,$1,EbtFloat,EbpHigh);
$$.setVector(2);
}
| VEC3 {
SET_BASIC_TYPE($$,$1,EbtFloat,EbpHigh);
$$.setVector(3);
}
| VEC4 {
SET_BASIC_TYPE($$,$1,EbtFloat,EbpHigh);
$$.setVector(4);
}
| HVEC2 {
SET_BASIC_TYPE($$,$1,EbtFloat,EbpMedium);
$$.setVector(2);
}
| HVEC3 {
SET_BASIC_TYPE($$,$1,EbtFloat,EbpMedium);
$$.setVector(3);
}
| HVEC4 {
SET_BASIC_TYPE($$,$1,EbtFloat,EbpMedium);
$$.setVector(4);
}
| FVEC2 {
SET_BASIC_TYPE($$,$1,EbtFloat,EbpLow);
$$.setVector(2);
}
| FVEC3 {
SET_BASIC_TYPE($$,$1,EbtFloat,EbpLow);
$$.setVector(3);
}
| FVEC4 {
SET_BASIC_TYPE($$,$1,EbtFloat,EbpLow);
$$.setVector(4);
}
| BVEC2 {
SET_BASIC_TYPE($$,$1,EbtBool,EbpHigh);
$$.setVector(2);
}
| BVEC3 {
SET_BASIC_TYPE($$,$1,EbtBool,EbpHigh);
$$.setVector(3);
}
| BVEC4 {
SET_BASIC_TYPE($$,$1,EbtBool,EbpHigh);
$$.setVector(4);
}
| IVEC2 {
SET_BASIC_TYPE($$,$1,EbtInt,EbpHigh);
$$.setVector(2);
}
| IVEC3 {
SET_BASIC_TYPE($$,$1,EbtInt,EbpHigh);
$$.setVector(3);
}
| IVEC4 {
SET_BASIC_TYPE($$,$1,EbtInt,EbpHigh);
$$.setVector(4);
}
| MATRIX2x2 {
SET_BASIC_TYPE($$,$1,EbtFloat,EbpHigh);
$$.setMatrix(2, 2);
}
| MATRIX2x3 {
NONSQUARE_MATRIX_CHECK("float2x3", $1.line);
SET_BASIC_TYPE($$,$1,EbtFloat,EbpHigh);
$$.setMatrix(3, 2);
}
| MATRIX2x4 {
NONSQUARE_MATRIX_CHECK("float2x4", $1.line);
SET_BASIC_TYPE($$,$1,EbtFloat,EbpHigh);
$$.setMatrix(4, 2);
}
| MATRIX3x2 {
NONSQUARE_MATRIX_CHECK("float3x2", $1.line);
SET_BASIC_TYPE($$,$1,EbtFloat,EbpHigh);
$$.setMatrix(2, 3);
}
| MATRIX3x3 {
SET_BASIC_TYPE($$,$1,EbtFloat,EbpHigh);
$$.setMatrix(3, 3);
}
| MATRIX3x4 {
NONSQUARE_MATRIX_CHECK("float3x4", $1.line);
SET_BASIC_TYPE($$,$1,EbtFloat,EbpHigh);
$$.setMatrix(4, 3);
}
| MATRIX4x2 {
NONSQUARE_MATRIX_CHECK("float4x2", $1.line);
SET_BASIC_TYPE($$,$1,EbtFloat,EbpHigh);
$$.setMatrix(2, 4);
}
| MATRIX4x3 {
NONSQUARE_MATRIX_CHECK("float4x3", $1.line);
SET_BASIC_TYPE($$,$1,EbtFloat,EbpHigh);
$$.setMatrix(3, 4);
}
| MATRIX4x4 {
SET_BASIC_TYPE($$,$1,EbtFloat,EbpHigh);
$$.setMatrix(4, 4);
}
| HMATRIX2x2 {
SET_BASIC_TYPE($$,$1,EbtFloat,EbpMedium);
$$.setMatrix(2, 2);
}
| HMATRIX2x3 {
NONSQUARE_MATRIX_CHECK("half2x3", $1.line);
SET_BASIC_TYPE($$,$1,EbtFloat,EbpMedium);
$$.setMatrix(3, 2);
}
| HMATRIX2x4 {
NONSQUARE_MATRIX_CHECK("half2x4", $1.line);
SET_BASIC_TYPE($$,$1,EbtFloat,EbpMedium);
$$.setMatrix(4, 2);
}
| HMATRIX3x2 {
NONSQUARE_MATRIX_CHECK("half3x2", $1.line);
SET_BASIC_TYPE($$,$1,EbtFloat,EbpMedium);
$$.setMatrix(2, 3);
}
| HMATRIX3x3 {
SET_BASIC_TYPE($$,$1,EbtFloat,EbpMedium);
$$.setMatrix(3, 3);
}
| HMATRIX3x4 {
NONSQUARE_MATRIX_CHECK("half3x4", $1.line);
SET_BASIC_TYPE($$,$1,EbtFloat,EbpMedium);
$$.setMatrix(4, 3);
}
| HMATRIX4x2 {
NONSQUARE_MATRIX_CHECK("half4x2", $1.line);
SET_BASIC_TYPE($$,$1,EbtFloat,EbpMedium);
$$.setMatrix(2, 4);
}
| HMATRIX4x3 {
NONSQUARE_MATRIX_CHECK("half4x3", $1.line);
SET_BASIC_TYPE($$,$1,EbtFloat,EbpMedium);
$$.setMatrix(3, 4);
}
| HMATRIX4x4 {
SET_BASIC_TYPE($$,$1,EbtFloat,EbpMedium);
$$.setMatrix(4, 4);
}
| FMATRIX2x2 {
SET_BASIC_TYPE($$,$1,EbtFloat,EbpLow);
$$.setMatrix(2, 2);
}
| FMATRIX2x3 {
NONSQUARE_MATRIX_CHECK("fixed2x3", $1.line);
SET_BASIC_TYPE($$,$1,EbtFloat,EbpLow);
$$.setMatrix(3, 2);
}
| FMATRIX2x4 {
NONSQUARE_MATRIX_CHECK("fixed2x4", $1.line);
SET_BASIC_TYPE($$,$1,EbtFloat,EbpLow);
$$.setMatrix(4, 2);
}
| FMATRIX3x2 {
NONSQUARE_MATRIX_CHECK("fixed3x2", $1.line);
SET_BASIC_TYPE($$,$1,EbtFloat,EbpLow);
$$.setMatrix(2, 3);
}
| FMATRIX3x3 {
SET_BASIC_TYPE($$,$1,EbtFloat,EbpLow);
$$.setMatrix(3, 3);
}
| FMATRIX3x4 {
NONSQUARE_MATRIX_CHECK("fixed3x4", $1.line);
SET_BASIC_TYPE($$,$1,EbtFloat,EbpLow);
$$.setMatrix(4, 3);
}
| FMATRIX4x2 {
NONSQUARE_MATRIX_CHECK("fixed4x2", $1.line);
SET_BASIC_TYPE($$,$1,EbtFloat,EbpLow);
$$.setMatrix(2, 4);
}
| FMATRIX4x3 {
NONSQUARE_MATRIX_CHECK("fixed4x3", $1.line);
SET_BASIC_TYPE($$,$1,EbtFloat,EbpLow);
$$.setMatrix(3, 4);
}
| FMATRIX4x4 {
SET_BASIC_TYPE($$,$1,EbtFloat,EbpLow);
$$.setMatrix(4, 4);
}
| TEXTURE {
SET_BASIC_TYPE($$,$1,EbtTexture,EbpUndefined);
}
| SAMPLERGENERIC {
SET_BASIC_TYPE($$,$1,EbtSamplerGeneric,EbpUndefined);
}
| SAMPLER1D {
SET_BASIC_TYPE($$,$1,EbtSampler1D,EbpUndefined);
}
| SAMPLER2D {
SET_BASIC_TYPE($$,$1,EbtSampler2D,EbpUndefined);
}
| SAMPLER2D_HALF {
SET_BASIC_TYPE($$,$1,EbtSampler2D,EbpMedium);
}
| SAMPLER2D_FLOAT {
SET_BASIC_TYPE($$,$1,EbtSampler2D,EbpHigh);
}
| SAMPLER3D {
SET_BASIC_TYPE($$,$1,EbtSampler3D,EbpUndefined);
}
| SAMPLERCUBE {
SET_BASIC_TYPE($$,$1,EbtSamplerCube,EbpUndefined);
}
| SAMPLERCUBE_HALF {
SET_BASIC_TYPE($$,$1,EbtSamplerCube,EbpMedium);
}
| SAMPLERCUBE_FLOAT {
SET_BASIC_TYPE($$,$1,EbtSamplerCube,EbpHigh);
}
| SAMPLERRECT {
SET_BASIC_TYPE($$,$1,EbtSamplerRect,EbpUndefined);
}
| SAMPLERRECTSHADOW {
SET_BASIC_TYPE($$,$1,EbtSamplerRectShadow,EbpUndefined);
}
| SAMPLER1DSHADOW {
SET_BASIC_TYPE($$,$1,EbtSampler1DShadow,EbpUndefined);
}
| SAMPLER2DSHADOW {
SET_BASIC_TYPE($$,$1,EbtSampler2DShadow,EbpUndefined);
}
| struct_specifier {
$$ = $1;
$$.qualifier = parseContext.getDefaultQualifier();
}
| TYPE_NAME {
//
// This is for user defined type names. The lexical phase looked up the
// type.
//
TType& structure = static_cast<TVariable*>($1.symbol)->getType();
SET_BASIC_TYPE($$,$1,EbtStruct,EbpUndefined);
$$.userDef = &structure;
}
;
struct_specifier
: STRUCT IDENTIFIER LEFT_BRACE struct_declaration_list RIGHT_BRACE {
TType* structure = new TType($4, *$2.string, EbpUndefined, $2.line);
TVariable* userTypeDef = new TVariable($2.string, *structure, true);
if (! parseContext.symbolTable.insert(*userTypeDef)) {
parseContext.error($2.line, "redefinition", $2.string->c_str(), "struct");
parseContext.recover();
}
$$.setBasic(EbtStruct, EvqTemporary, $1.line);
$$.userDef = structure;
}
| STRUCT LEFT_BRACE struct_declaration_list RIGHT_BRACE {
TType* structure = new TType($3, TString(""), EbpUndefined, $1.line);
$$.setBasic(EbtStruct, EvqTemporary, $1.line);
$$.userDef = structure;
}
;
struct_declaration_list
: struct_declaration {
$$ = $1;
}
| struct_declaration_list struct_declaration {
$$ = $1;
for (unsigned int i = 0; i < $2->size(); ++i) {
for (unsigned int j = 0; j < $$->size(); ++j) {
if ((*$$)[j].type->getFieldName() == (*$2)[i].type->getFieldName()) {
parseContext.error((*$2)[i].line, "duplicate field name in structure:", "struct", (*$2)[i].type->getFieldName().c_str());
parseContext.recover();
}
}
$$->push_back((*$2)[i]);
}
}
;
struct_declaration
: type_specifier struct_declarator_list SEMICOLON {
$$ = $2;
if (parseContext.voidErrorCheck($1.line, (*$2)[0].type->getFieldName(), $1)) {
parseContext.recover();
}
for (unsigned int i = 0; i < $$->size(); ++i) {
//
// Careful not to replace already know aspects of type, like array-ness
//
TType* type = (*$$)[i].type;
type->setBasicType($1.type);
type->setPrecision($1.precision);
type->setColsCount($1.matcols);
type->setRowsCount($1.matrows);
type->setMatrix($1.matrix);
// don't allow arrays of arrays
if (type->isArray()) {
if (parseContext.arrayTypeErrorCheck($1.line, $1))
parseContext.recover();
}
if ($1.array)
type->setArraySize($1.arraySize);
if ($1.userDef) {
type->setStruct($1.userDef->getStruct());
type->setTypeName($1.userDef->getTypeName());
}
}
}
;
struct_declarator_list
: struct_declarator {
$$ = NewPoolTTypeList();
$$->push_back($1);
}
| struct_declarator_list COMMA struct_declarator {
$$->push_back($3);
}
;
struct_declarator
: IDENTIFIER {
$$.type = new TType(EbtVoid, EbpUndefined);
$$.line = $1.line;
$$.type->setFieldName(*$1.string);
}
| IDENTIFIER COLON IDENTIFIER {
$$.type = new TType(EbtVoid, EbpUndefined);
$$.line = $1.line;
$$.type->setFieldName(*$1.string);
$$.type->setSemantic(*$3.string);
}
| IDENTIFIER LEFT_BRACKET const_expression RIGHT_BRACKET {
$$.type = new TType(EbtVoid, EbpUndefined);
$$.line = $1.line;
$$.type->setFieldName(*$1.string);
int size;
if (parseContext.arraySizeErrorCheck($2.line, $3, size))
parseContext.recover();
$$.type->setArraySize(size);
}
| IDENTIFIER LEFT_BRACKET const_expression RIGHT_BRACKET COLON IDENTIFIER {
$$.type = new TType(EbtVoid, EbpUndefined);
$$.line = $1.line;
$$.type->setFieldName(*$1.string);
int size;
if (parseContext.arraySizeErrorCheck($2.line, $3, size))
parseContext.recover();
$$.type->setArraySize(size);
$$.type->setSemantic(*$6.string);
}
;
initializer
: assign_expression { $$ = $1; }
| initialization_list { $$ = $1; }
| sampler_initializer { $$ = $1; }
;
declaration_statement
: declaration { $$ = $1; }
;
statement
: compound_statement { $$ = $1; }
| simple_statement { $$ = $1; }
;
// Grammar Note: No labeled statements; 'goto' is not supported.
simple_statement
: declaration_statement { $$ = $1; }
| expression_statement { $$ = $1; }
| selection_statement { $$ = $1; }
| iteration_statement { $$ = $1; }
| jump_statement { $$ = $1; }
;
compound_statement
: LEFT_BRACE RIGHT_BRACE { $$ = 0; }
| LEFT_BRACE { parseContext.symbolTable.push(); } statement_list { parseContext.symbolTable.pop(); } RIGHT_BRACE {
if ($3 != 0)
$3->setOperator(EOpSequence);
$$ = $3;
}
;
statement_no_new_scope
: compound_statement_no_new_scope { $$ = $1; }
| simple_statement { $$ = $1; }
;
compound_statement_no_new_scope
// Statement that doesn't create a new scope, for selection_statement, iteration_statement
: LEFT_BRACE RIGHT_BRACE {
$$ = 0;
}
| LEFT_BRACE statement_list RIGHT_BRACE {
if ($2)
$2->setOperator(EOpSequence);
$$ = $2;
}
;
statement_list
: statement {
$$ = ir_make_aggregate($1, gNullSourceLoc);
}
| statement_list statement {
$$ = ir_grow_aggregate($1, $2, gNullSourceLoc);
}
;
expression_statement
: SEMICOLON { $$ = 0; }
| expression SEMICOLON { $$ = static_cast<TIntermNode*>($1); }
;
selection_statement
: IF LEFT_PAREN expression RIGHT_PAREN selection_rest_statement {
if (parseContext.boolErrorCheck($1.line, $3))
parseContext.recover();
$$ = ir_add_selection($3, $5, $1.line, parseContext.infoSink);
}
;
selection_rest_statement
: statement ELSE statement {
$$.node1 = $1;
$$.node2 = $3;
}
| statement {
$$.node1 = $1;
$$.node2 = 0;
}
;
// Grammar Note: No 'switch'. Switch statements not supported.
condition
// In 1996 c++ draft, conditions can include single declarations
: expression {
$$ = $1;
if (parseContext.boolErrorCheck($1->getLine(), $1))
parseContext.recover();
}
| fully_specified_type IDENTIFIER EQUAL initializer {
TIntermSymbol* symbol;
if (parseContext.structQualifierErrorCheck($2.line, $1))
parseContext.recover();
if (parseContext.boolErrorCheck($2.line, $1))
parseContext.recover();
if (!parseContext.executeInitializer($2.line, *$2.string, $1, $4, symbol)) {
$$ = ir_add_declaration(symbol, $4, $2.line, parseContext);
} else {
parseContext.recover();
$$ = 0;
}
}
;
iteration_statement
: WHILE LEFT_PAREN { parseContext.symbolTable.push(); ++parseContext.loopNestingLevel; } condition RIGHT_PAREN statement_no_new_scope {
parseContext.symbolTable.pop();
$$ = ir_add_loop(ELoopWhile, $4, 0, $6, $1.line);
--parseContext.loopNestingLevel;
}
| DO { ++parseContext.loopNestingLevel; } statement WHILE LEFT_PAREN expression RIGHT_PAREN SEMICOLON {
if (parseContext.boolErrorCheck($8.line, $6))
parseContext.recover();
$$ = ir_add_loop(ELoopDoWhile, $6, 0, $3, $4.line);
--parseContext.loopNestingLevel;
}
| FOR LEFT_PAREN { parseContext.symbolTable.push(); ++parseContext.loopNestingLevel; } for_init_statement for_rest_statement RIGHT_PAREN statement_no_new_scope {
parseContext.symbolTable.pop();
$$ = ir_make_aggregate($4, $2.line);
$$ = ir_grow_aggregate(
$$,
ir_add_loop(ELoopFor, reinterpret_cast<TIntermTyped*>($5.node1), reinterpret_cast<TIntermTyped*>($5.node2), $7, $1.line),
$1.line);
$$->getAsAggregate()->setOperator(EOpSequence);
--parseContext.loopNestingLevel;
}
;
for_init_statement
: expression_statement {
$$ = $1;
}
| declaration_statement {
$$ = $1;
}
;
conditionopt
: condition {
$$ = $1;
}
| /* May be null */ {
$$ = 0;
}
;
for_rest_statement
: conditionopt SEMICOLON {
$$.node1 = $1;
$$.node2 = 0;
}
| conditionopt SEMICOLON expression {
$$.node1 = $1;
$$.node2 = $3;
}
;
jump_statement
: CONTINUE SEMICOLON {
if (parseContext.loopNestingLevel <= 0) {
parseContext.error($1.line, "continue statement only allowed in loops", "", "");
parseContext.recover();
}
$$ = ir_add_branch(EOpContinue, $1.line);
}
| BREAK SEMICOLON {
if (parseContext.loopNestingLevel <= 0) {
parseContext.error($1.line, "break statement only allowed in loops", "", "");
parseContext.recover();
}
$$ = ir_add_branch(EOpBreak, $1.line);
}
| RETURN SEMICOLON {
$$ = ir_add_branch(EOpReturn, $1.line);
if (parseContext.currentFunctionType->getBasicType() != EbtVoid) {
parseContext.error($1.line, "non-void function must return a value", "return", "");
parseContext.recover();
}
}
| RETURN expression SEMICOLON {
TIntermTyped *temp = $2;
if (parseContext.currentFunctionType->getBasicType() == EbtVoid) {
parseContext.error($1.line, "void function cannot return a value", "return", "");
parseContext.recover();
} else if (*(parseContext.currentFunctionType) != $2->getType()) {
TOperator op = parseContext.getConstructorOp(*(parseContext.currentFunctionType));
if (op != EOpNull)
temp = parseContext.constructBuiltIn((parseContext.currentFunctionType), op, $2, $1.line, false);
else
temp = 0;
if (temp == 0) {
parseContext.error($1.line, "function return is not matching type:", "return", "");
parseContext.recover();
temp = $2;
}
}
$$ = ir_add_branch(EOpReturn, temp, $1.line);
parseContext.functionReturnsValue = true;
}
| DISCARD SEMICOLON {
// Jim: using discard when compiling vertex shaders should not be considered a syntactic error, instead,
// we should issue a semantic error only if the code path is actually executed. (Not yet implemented)
//FRAG_ONLY("discard", $1.line);
$$ = ir_add_branch(EOpKill, $1.line);
}
;
// Grammar Note: No 'goto'. Gotos are not supported.
translation_unit
: external_declaration {
$$ = $1;
parseContext.treeRoot = $$;
}
| translation_unit external_declaration {
$$ = ir_grow_aggregate($1, $2, gNullSourceLoc);
parseContext.treeRoot = $$;
}
;
external_declaration
: function_definition {
$$ = $1;
}
| declaration {
$$ = $1;
}
| SEMICOLON { $$ = 0; }
;
function_definition
: function_prototype {
TFunction& function = *($1.function);
TFunction* prevDec = static_cast<TFunction*>(parseContext.symbolTable.find(function.getMangledName()));
//
// Note: 'prevDec' could be 'function' if this is the first time we've seen function
// as it would have just been put in the symbol table. Otherwise, we're looking up
// an earlier occurance.
//
if (prevDec->isDefined()) {
//
// Then this function already has a body.
//
parseContext.error($1.line, "function already has a body", function.getName().c_str(), "");
parseContext.recover();
}
prevDec->setDefined();
//
// New symbol table scope for body of function plus its arguments
//
parseContext.symbolTable.push();
//
// Remember the return type for later checking for RETURN statements.
//
parseContext.currentFunctionType = &(prevDec->getReturnType());
parseContext.functionReturnsValue = false;
//
// Insert parameters into the symbol table.
// If the parameter has no name, it's not an error, just don't insert it
// (could be used for unused args).
//
// Also, accumulate the list of parameters into the HIL, so lower level code
// knows where to find parameters.
//
TIntermAggregate* paramNodes = new TIntermAggregate;
for (int i = 0; i < function.getParamCount(); i++) {
TParameter& param = function[i];
if (param.name != 0) {
TVariable *variable = new TVariable(param.name, param.info, *param.type);
//
// Insert the parameters with name in the symbol table.
//
if (! parseContext.symbolTable.insert(*variable)) {
parseContext.error($1.line, "redefinition", variable->getName().c_str(), "");
parseContext.recover();
delete variable;
}
//
// Transfer ownership of name pointer to symbol table.
//
param.name = 0;
//
// Add the parameter to the HIL
//
paramNodes = ir_grow_aggregate(
paramNodes,
ir_add_symbol(variable, $1.line),
$1.line);
} else {
paramNodes = ir_grow_aggregate(paramNodes, ir_add_symbol_internal(0, "", param.info, *param.type, $1.line), $1.line);
}
}
ir_set_aggregate_op(paramNodes, EOpParameters, $1.line);
$1.intermAggregate = paramNodes;
parseContext.loopNestingLevel = 0;
}
compound_statement_no_new_scope {
//?? Check that all paths return a value if return type != void ?
// May be best done as post process phase on intermediate code
if (parseContext.currentFunctionType->getBasicType() != EbtVoid && ! parseContext.functionReturnsValue) {
parseContext.error($1.line, "function does not return a value:", "", $1.function->getName().c_str());
parseContext.recover();
}
parseContext.symbolTable.pop();
$$ = ir_grow_aggregate($1.intermAggregate, $3, gNullSourceLoc);
ir_set_aggregate_op($$, EOpFunction, $1.line);
$$->getAsAggregate()->setName($1.function->getMangledName().c_str());
$$->getAsAggregate()->setPlainName($1.function->getName().c_str());
$$->getAsAggregate()->setType($1.function->getReturnType());
if ( $1.function->getInfo())
$$->getAsAggregate()->setSemantic($1.function->getInfo()->getSemantic());
}
;
initialization_list
: LEFT_BRACE initializer_list RIGHT_BRACE {
$$ = $2;
}
| LEFT_BRACE initializer_list COMMA RIGHT_BRACE {
$$ = $2;
}
;
initializer_list
: assign_expression {
//create a new aggNode
$$ = ir_make_aggregate( $1, $1->getLine());
}
| initialization_list {
//take the inherited aggNode and return it
$$ = $1->getAsAggregate();
}
| initializer_list COMMA assign_expression {
// append to the aggNode
$$ = ir_grow_aggregate( $1, $3, $3->getLine());
}
| initializer_list COMMA initialization_list {
// append all children or $3 to $1
$$ = parseContext.mergeAggregates( $1, $3->getAsAggregate());
}
;
annotation
: LEFT_ANGLE RIGHT_ANGLE {
//empty annotation
$$ = 0;
}
| LEFT_ANGLE annotation_list RIGHT_ANGLE {
$$ = $2;
}
;
annotation_list
: annotation_item {
$$ = new TAnnotation;
$$->addKey( *$1.string);
}
| annotation_list annotation_item {
$1->addKey( *$2.string);
$$ = $1;
}
;
annotation_item
: ann_type IDENTIFIER EQUAL ann_literal SEMICOLON {
$$.string = $2.string;
}
;
ann_type
: FLOAT_TYPE {}
| HALF_TYPE {}
| FIXED_TYPE {}
| INT_TYPE {}
| BOOL_TYPE {}
| STRING_TYPE {}
| BVEC2 {}
| BVEC3 {}
| BVEC4 {}
| IVEC2 {}
| IVEC3 {}
| IVEC4 {}
| VEC2 {}
| VEC3 {}
| VEC4 {}
| HVEC2 {}
| HVEC3 {}
| HVEC4 {}
| FVEC2 {}
| FVEC3 {}
| FVEC4 {}
;
ann_literal
: ann_numerical_constant {}
| STRINGCONSTANT {}
| ann_literal_constructor {}
| ann_literal_init_list {}
;
ann_numerical_constant
: INTCONSTANT {
$$.f = (float)$1.i;
}
| BOOLCONSTANT {
$$.f = ($1.b) ? 1.0f : 0.0f;
}
| FLOATCONSTANT {
$$.f = $1.f;
}
;
ann_literal_constructor
: ann_type LEFT_PAREN ann_value_list RIGHT_PAREN {}
;
ann_value_list
: ann_numerical_constant {}
| ann_value_list COMMA ann_numerical_constant {}
;
ann_literal_init_list
: LEFT_BRACE ann_value_list RIGHT_BRACE {}
;
register_specifier
: COLON REGISTER LEFT_PAREN IDENTIFIER RIGHT_PAREN {
// This is being thrown away
}
;
semantic
: COLON IDENTIFIER { $$.string = $2.string;}
;
type_info
: { $$ = 0;}
| semantic { $$ = new TTypeInfo( *$1.string, 0); }
| register_specifier { $$ = 0; }
| annotation { $$ = new TTypeInfo( "", $1); }
| semantic annotation { $$ = new TTypeInfo( *$1.string, $2); }
| semantic register_specifier { $$ = new TTypeInfo( *$1.string, 0); }
| register_specifier annotation { $$ = new TTypeInfo( "", $2); }
| semantic register_specifier annotation { $$ = new TTypeInfo( *$1.string, $3); }
;
sampler_initializer
: SAMPLERSTATE LEFT_BRACE sampler_init_list RIGHT_BRACE {
TIntermConstant* constant = ir_add_constant(TType(EbtFloat, EbpUndefined, EvqConst, 1), $1.line);
constant->setValue(0.f);
$$ = constant;
}
| SAMPLERSTATE LEFT_BRACE RIGHT_BRACE {
}
;
sampler_init_list
: sampler_init_item { }
| sampler_init_list sampler_init_item { }
;
sampler_init_item
: IDENTIFIER EQUAL IDENTIFIER SEMICOLON {}
| IDENTIFIER EQUAL LEFT_ANGLE IDENTIFIER RIGHT_ANGLE SEMICOLON {}
| IDENTIFIER EQUAL LEFT_PAREN IDENTIFIER RIGHT_PAREN SEMICOLON {}
| TEXTURE EQUAL IDENTIFIER SEMICOLON {}
| TEXTURE EQUAL LEFT_ANGLE IDENTIFIER RIGHT_ANGLE SEMICOLON {}
| TEXTURE EQUAL LEFT_PAREN IDENTIFIER RIGHT_PAREN SEMICOLON {}
;
%%