michael@0: /* michael@0: // michael@0: // Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: // michael@0: michael@0: This file contains the Yacc grammar for GLSL ES. michael@0: Based on ANSI C Yacc grammar: michael@0: http://www.lysator.liu.se/c/ANSI-C-grammar-y.html michael@0: michael@0: IF YOU MODIFY THIS FILE YOU ALSO NEED TO RUN generate_parser.sh, michael@0: WHICH GENERATES THE GLSL ES PARSER (glslang_tab.cpp AND glslang_tab.h). michael@0: */ michael@0: michael@0: %{ michael@0: // michael@0: // Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: // michael@0: michael@0: // This file is auto-generated by generate_parser.sh. DO NOT EDIT! michael@0: michael@0: // Ignore errors in auto-generated code. michael@0: #if defined(__GNUC__) michael@0: #pragma GCC diagnostic ignored "-Wunused-function" michael@0: #pragma GCC diagnostic ignored "-Wunused-variable" michael@0: #pragma GCC diagnostic ignored "-Wswitch-enum" michael@0: #elif defined(_MSC_VER) michael@0: #pragma warning(disable: 4065) michael@0: #pragma warning(disable: 4189) michael@0: #pragma warning(disable: 4505) michael@0: #pragma warning(disable: 4701) michael@0: #endif michael@0: michael@0: #include "compiler/SymbolTable.h" michael@0: #include "compiler/ParseHelper.h" michael@0: #include "GLSLANG/ShaderLang.h" michael@0: michael@0: #define YYENABLE_NLS 0 michael@0: michael@0: #define YYLEX_PARAM context->scanner michael@0: %} michael@0: michael@0: %expect 1 /* One shift reduce conflict because of if | else */ michael@0: %pure-parser michael@0: %parse-param {TParseContext* context} michael@0: %locations michael@0: michael@0: %code requires { michael@0: #define YYLTYPE TSourceLoc michael@0: #define YYLTYPE_IS_DECLARED 1 michael@0: } michael@0: michael@0: %union { michael@0: struct { michael@0: union { michael@0: TString *string; michael@0: float f; michael@0: int i; michael@0: bool b; michael@0: }; michael@0: TSymbol* symbol; michael@0: } lex; michael@0: struct { michael@0: TOperator op; michael@0: union { michael@0: TIntermNode* intermNode; michael@0: TIntermNodePair nodePair; michael@0: TIntermTyped* intermTypedNode; michael@0: TIntermAggregate* intermAggregate; michael@0: }; michael@0: union { michael@0: TPublicType type; michael@0: TPrecision precision; michael@0: TQualifier qualifier; michael@0: TFunction* function; michael@0: TParameter param; michael@0: TField* field; michael@0: TFieldList* fieldList; michael@0: }; michael@0: } interm; michael@0: } michael@0: michael@0: %{ michael@0: extern int yylex(YYSTYPE* yylval, YYLTYPE* yylloc, void* yyscanner); michael@0: static void yyerror(YYLTYPE* yylloc, TParseContext* context, const char* reason); michael@0: michael@0: #define YYLLOC_DEFAULT(Current, Rhs, N) \ michael@0: do { \ michael@0: if (YYID(N)) { \ michael@0: (Current).first_file = YYRHSLOC(Rhs, 1).first_file; \ michael@0: (Current).first_line = YYRHSLOC(Rhs, 1).first_line; \ michael@0: (Current).last_file = YYRHSLOC(Rhs, N).last_file; \ michael@0: (Current).last_line = YYRHSLOC(Rhs, N).last_line; \ michael@0: } \ michael@0: else { \ michael@0: (Current).first_file = YYRHSLOC(Rhs, 0).last_file; \ michael@0: (Current).first_line = YYRHSLOC(Rhs, 0).last_line; \ michael@0: (Current).last_file = YYRHSLOC(Rhs, 0).last_file; \ michael@0: (Current).last_line = YYRHSLOC(Rhs, 0).last_line; \ michael@0: } \ michael@0: } while (0) michael@0: michael@0: #define VERTEX_ONLY(S, L) { \ michael@0: if (context->shaderType != SH_VERTEX_SHADER) { \ michael@0: context->error(L, " supported in vertex shaders only ", S); \ michael@0: context->recover(); \ michael@0: } \ michael@0: } michael@0: michael@0: #define FRAG_ONLY(S, L) { \ michael@0: if (context->shaderType != SH_FRAGMENT_SHADER) { \ michael@0: context->error(L, " supported in fragment shaders only ", S); \ michael@0: context->recover(); \ michael@0: } \ michael@0: } michael@0: %} michael@0: michael@0: %token INVARIANT HIGH_PRECISION MEDIUM_PRECISION LOW_PRECISION PRECISION michael@0: %token ATTRIBUTE CONST_QUAL BOOL_TYPE FLOAT_TYPE INT_TYPE michael@0: %token BREAK CONTINUE DO ELSE FOR IF DISCARD RETURN michael@0: %token BVEC2 BVEC3 BVEC4 IVEC2 IVEC3 IVEC4 VEC2 VEC3 VEC4 michael@0: %token MATRIX2 MATRIX3 MATRIX4 IN_QUAL OUT_QUAL INOUT_QUAL UNIFORM VARYING michael@0: %token STRUCT VOID_TYPE WHILE michael@0: %token SAMPLER2D SAMPLERCUBE SAMPLER_EXTERNAL_OES SAMPLER2DRECT michael@0: michael@0: %token IDENTIFIER TYPE_NAME FLOATCONSTANT INTCONSTANT BOOLCONSTANT michael@0: %token LEFT_OP RIGHT_OP michael@0: %token INC_OP DEC_OP LE_OP GE_OP EQ_OP NE_OP michael@0: %token AND_OP OR_OP XOR_OP MUL_ASSIGN DIV_ASSIGN ADD_ASSIGN michael@0: %token MOD_ASSIGN LEFT_ASSIGN RIGHT_ASSIGN AND_ASSIGN XOR_ASSIGN OR_ASSIGN michael@0: %token SUB_ASSIGN michael@0: michael@0: %token LEFT_PAREN RIGHT_PAREN LEFT_BRACKET RIGHT_BRACKET LEFT_BRACE RIGHT_BRACE DOT michael@0: %token COMMA COLON EQUAL SEMICOLON BANG DASH TILDE PLUS STAR SLASH PERCENT michael@0: %token LEFT_ANGLE RIGHT_ANGLE VERTICAL_BAR CARET AMPERSAND QUESTION michael@0: michael@0: %type identifier michael@0: %type assignment_operator unary_operator michael@0: %type variable_identifier primary_expression postfix_expression michael@0: %type expression integer_expression assignment_expression michael@0: %type unary_expression multiplicative_expression additive_expression michael@0: %type relational_expression equality_expression michael@0: %type conditional_expression constant_expression michael@0: %type logical_or_expression logical_xor_expression logical_and_expression michael@0: %type shift_expression and_expression exclusive_or_expression inclusive_or_expression michael@0: %type function_call initializer condition conditionopt michael@0: michael@0: %type translation_unit function_definition michael@0: %type statement simple_statement michael@0: %type statement_list compound_statement michael@0: %type declaration_statement selection_statement expression_statement michael@0: %type declaration external_declaration michael@0: %type for_init_statement compound_statement_no_new_scope michael@0: %type selection_rest_statement for_rest_statement michael@0: %type iteration_statement jump_statement statement_no_new_scope statement_with_scope michael@0: %type single_declaration init_declarator_list michael@0: michael@0: %type parameter_declaration parameter_declarator parameter_type_specifier michael@0: %type parameter_qualifier michael@0: michael@0: %type precision_qualifier michael@0: %type type_qualifier fully_specified_type type_specifier michael@0: %type type_specifier_no_prec type_specifier_nonarray michael@0: %type struct_specifier michael@0: %type struct_declarator michael@0: %type struct_declarator_list struct_declaration struct_declaration_list michael@0: %type function_header function_declarator function_identifier michael@0: %type function_header_with_parameters function_call_header michael@0: %type function_call_header_with_parameters function_call_header_no_parameters function_call_generic function_prototype michael@0: %type function_call_or_method michael@0: michael@0: %start translation_unit michael@0: %% michael@0: michael@0: identifier michael@0: : IDENTIFIER michael@0: | TYPE_NAME michael@0: michael@0: variable_identifier michael@0: : IDENTIFIER { michael@0: // The symbol table search was done in the lexical phase michael@0: const TSymbol* symbol = $1.symbol; michael@0: const TVariable* variable; michael@0: if (symbol == 0) { michael@0: context->error(@1, "undeclared identifier", $1.string->c_str()); michael@0: context->recover(); michael@0: TType type(EbtFloat, EbpUndefined); michael@0: TVariable* fakeVariable = new TVariable($1.string, type); michael@0: context->symbolTable.insert(*fakeVariable); michael@0: variable = fakeVariable; michael@0: } else { michael@0: // This identifier can only be a variable type symbol michael@0: if (! symbol->isVariable()) { michael@0: context->error(@1, "variable expected", $1.string->c_str()); michael@0: context->recover(); michael@0: } michael@0: michael@0: variable = static_cast(symbol); michael@0: michael@0: if (context->symbolTable.findBuiltIn(variable->getName()) && michael@0: !variable->getExtension().empty() && michael@0: context->extensionErrorCheck(@1, variable->getExtension())) { michael@0: context->recover(); michael@0: } michael@0: } michael@0: michael@0: // don't delete $1.string, it's used by error recovery, and the pool michael@0: // pop will reclaim the memory michael@0: michael@0: if (variable->getType().getQualifier() == EvqConst ) { michael@0: ConstantUnion* constArray = variable->getConstPointer(); michael@0: TType t(variable->getType()); michael@0: $$ = context->intermediate.addConstantUnion(constArray, t, @1); michael@0: } else michael@0: $$ = context->intermediate.addSymbol(variable->getUniqueId(), michael@0: variable->getName(), michael@0: variable->getType(), michael@0: @1); michael@0: } michael@0: ; michael@0: michael@0: primary_expression michael@0: : variable_identifier { michael@0: $$ = $1; michael@0: } michael@0: | INTCONSTANT { michael@0: // michael@0: // INT_TYPE is only 16-bit plus sign bit for vertex/fragment shaders, michael@0: // check for overflow for constants michael@0: // michael@0: if (abs($1.i) >= (1 << 16)) { michael@0: context->error(@1, " integer constant overflow", ""); michael@0: context->recover(); michael@0: } michael@0: ConstantUnion *unionArray = new ConstantUnion[1]; michael@0: unionArray->setIConst($1.i); michael@0: $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtInt, EbpUndefined, EvqConst), @1); michael@0: } michael@0: | FLOATCONSTANT { michael@0: ConstantUnion *unionArray = new ConstantUnion[1]; michael@0: unionArray->setFConst($1.f); michael@0: $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtFloat, EbpUndefined, EvqConst), @1); michael@0: } michael@0: | BOOLCONSTANT { michael@0: ConstantUnion *unionArray = new ConstantUnion[1]; michael@0: unionArray->setBConst($1.b); michael@0: $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtBool, EbpUndefined, EvqConst), @1); michael@0: } michael@0: | LEFT_PAREN expression RIGHT_PAREN { michael@0: $$ = $2; michael@0: } michael@0: ; michael@0: michael@0: postfix_expression michael@0: : primary_expression { michael@0: $$ = $1; michael@0: } michael@0: | postfix_expression LEFT_BRACKET integer_expression RIGHT_BRACKET { michael@0: $$ = context->addIndexExpression($1, @2, $3); michael@0: } michael@0: | function_call { michael@0: $$ = $1; michael@0: } michael@0: | postfix_expression DOT identifier { michael@0: if ($1->isArray()) { michael@0: context->error(@3, "cannot apply dot operator to an array", "."); michael@0: context->recover(); michael@0: } michael@0: michael@0: if ($1->isVector()) { michael@0: TVectorFields fields; michael@0: if (! context->parseVectorFields(*$3.string, $1->getNominalSize(), fields, @3)) { michael@0: fields.num = 1; michael@0: fields.offsets[0] = 0; michael@0: context->recover(); michael@0: } michael@0: michael@0: if ($1->getType().getQualifier() == EvqConst) { // constant folding for vector fields michael@0: $$ = context->addConstVectorNode(fields, $1, @3); michael@0: if ($$ == 0) { michael@0: context->recover(); michael@0: $$ = $1; michael@0: } michael@0: else michael@0: $$->setType(TType($1->getBasicType(), $1->getPrecision(), EvqConst, (int) (*$3.string).size())); michael@0: } else { michael@0: TString vectorString = *$3.string; michael@0: TIntermTyped* index = context->intermediate.addSwizzle(fields, @3); michael@0: $$ = context->intermediate.addIndex(EOpVectorSwizzle, $1, index, @2); michael@0: $$->setType(TType($1->getBasicType(), $1->getPrecision(), EvqTemporary, (int) vectorString.size())); michael@0: } michael@0: } else if ($1->isMatrix()) { michael@0: TMatrixFields fields; michael@0: if (! context->parseMatrixFields(*$3.string, $1->getNominalSize(), fields, @3)) { michael@0: fields.wholeRow = false; michael@0: fields.wholeCol = false; michael@0: fields.row = 0; michael@0: fields.col = 0; michael@0: context->recover(); michael@0: } michael@0: michael@0: if (fields.wholeRow || fields.wholeCol) { michael@0: context->error(@2, " non-scalar fields not implemented yet", "."); michael@0: context->recover(); michael@0: ConstantUnion *unionArray = new ConstantUnion[1]; michael@0: unionArray->setIConst(0); michael@0: TIntermTyped* index = context->intermediate.addConstantUnion(unionArray, TType(EbtInt, EbpUndefined, EvqConst), @3); michael@0: $$ = context->intermediate.addIndex(EOpIndexDirect, $1, index, @2); michael@0: $$->setType(TType($1->getBasicType(), $1->getPrecision(),EvqTemporary, $1->getNominalSize())); michael@0: } else { michael@0: ConstantUnion *unionArray = new ConstantUnion[1]; michael@0: unionArray->setIConst(fields.col * $1->getNominalSize() + fields.row); michael@0: TIntermTyped* index = context->intermediate.addConstantUnion(unionArray, TType(EbtInt, EbpUndefined, EvqConst), @3); michael@0: $$ = context->intermediate.addIndex(EOpIndexDirect, $1, index, @2); michael@0: $$->setType(TType($1->getBasicType(), $1->getPrecision())); michael@0: } michael@0: } else if ($1->getBasicType() == EbtStruct) { michael@0: bool fieldFound = false; michael@0: const TFieldList& fields = $1->getType().getStruct()->fields(); michael@0: unsigned int i; michael@0: for (i = 0; i < fields.size(); ++i) { michael@0: if (fields[i]->name() == *$3.string) { michael@0: fieldFound = true; michael@0: break; michael@0: } michael@0: } michael@0: if (fieldFound) { michael@0: if ($1->getType().getQualifier() == EvqConst) { michael@0: $$ = context->addConstStruct(*$3.string, $1, @2); michael@0: if ($$ == 0) { michael@0: context->recover(); michael@0: $$ = $1; michael@0: } michael@0: else { michael@0: $$->setType(*fields[i]->type()); michael@0: // change the qualifier of the return type, not of the structure field michael@0: // as the structure definition is shared between various structures. michael@0: $$->getTypePointer()->setQualifier(EvqConst); michael@0: } michael@0: } else { michael@0: ConstantUnion *unionArray = new ConstantUnion[1]; michael@0: unionArray->setIConst(i); michael@0: TIntermTyped* index = context->intermediate.addConstantUnion(unionArray, *fields[i]->type(), @3); michael@0: $$ = context->intermediate.addIndex(EOpIndexDirectStruct, $1, index, @2); michael@0: $$->setType(*fields[i]->type()); michael@0: } michael@0: } else { michael@0: context->error(@2, " no such field in structure", $3.string->c_str()); michael@0: context->recover(); michael@0: $$ = $1; michael@0: } michael@0: } else { michael@0: context->error(@2, " field selection requires structure, vector, or matrix on left hand side", $3.string->c_str()); michael@0: context->recover(); michael@0: $$ = $1; michael@0: } michael@0: // don't delete $3.string, it's from the pool michael@0: } michael@0: | postfix_expression INC_OP { michael@0: if (context->lValueErrorCheck(@2, "++", $1)) michael@0: context->recover(); michael@0: $$ = context->intermediate.addUnaryMath(EOpPostIncrement, $1, @2, context->symbolTable); michael@0: if ($$ == 0) { michael@0: context->unaryOpError(@2, "++", $1->getCompleteString()); michael@0: context->recover(); michael@0: $$ = $1; michael@0: } michael@0: } michael@0: | postfix_expression DEC_OP { michael@0: if (context->lValueErrorCheck(@2, "--", $1)) michael@0: context->recover(); michael@0: $$ = context->intermediate.addUnaryMath(EOpPostDecrement, $1, @2, context->symbolTable); michael@0: if ($$ == 0) { michael@0: context->unaryOpError(@2, "--", $1->getCompleteString()); michael@0: context->recover(); michael@0: $$ = $1; michael@0: } michael@0: } michael@0: ; michael@0: michael@0: integer_expression michael@0: : expression { michael@0: if (context->integerErrorCheck($1, "[]")) michael@0: context->recover(); michael@0: $$ = $1; michael@0: } michael@0: ; michael@0: michael@0: function_call michael@0: : function_call_or_method { michael@0: TFunction* fnCall = $1.function; michael@0: TOperator op = fnCall->getBuiltInOp(); michael@0: michael@0: if (op != EOpNull) michael@0: { michael@0: // michael@0: // Then this should be a constructor. michael@0: // Don't go through the symbol table for constructors. michael@0: // Their parameters will be verified algorithmically. michael@0: // michael@0: TType type(EbtVoid, EbpUndefined); // use this to get the type back michael@0: if (context->constructorErrorCheck(@1, $1.intermNode, *fnCall, op, &type)) { michael@0: $$ = 0; michael@0: } else { michael@0: // michael@0: // It's a constructor, of type 'type'. michael@0: // michael@0: $$ = context->addConstructor($1.intermNode, &type, op, fnCall, @1); michael@0: } michael@0: michael@0: if ($$ == 0) { michael@0: context->recover(); michael@0: $$ = context->intermediate.setAggregateOperator(0, op, @1); michael@0: } michael@0: $$->setType(type); michael@0: } else { michael@0: // michael@0: // Not a constructor. Find it in the symbol table. michael@0: // michael@0: const TFunction* fnCandidate; michael@0: bool builtIn; michael@0: fnCandidate = context->findFunction(@1, fnCall, &builtIn); michael@0: if (fnCandidate) { michael@0: // michael@0: // A declared function. michael@0: // michael@0: if (builtIn && !fnCandidate->getExtension().empty() && michael@0: context->extensionErrorCheck(@1, fnCandidate->getExtension())) { michael@0: context->recover(); michael@0: } michael@0: op = fnCandidate->getBuiltInOp(); michael@0: if (builtIn && op != EOpNull) { michael@0: // michael@0: // A function call mapped to a built-in operation. michael@0: // michael@0: if (fnCandidate->getParamCount() == 1) { michael@0: // michael@0: // Treat it like a built-in unary operator. michael@0: // michael@0: $$ = context->intermediate.addUnaryMath(op, $1.intermNode, @1, context->symbolTable); michael@0: if ($$ == 0) { michael@0: std::stringstream extraInfoStream; michael@0: extraInfoStream << "built in unary operator function. Type: " << static_cast($1.intermNode)->getCompleteString(); michael@0: std::string extraInfo = extraInfoStream.str(); michael@0: context->error($1.intermNode->getLine(), " wrong operand type", "Internal Error", extraInfo.c_str()); michael@0: YYERROR; michael@0: } michael@0: } else { michael@0: $$ = context->intermediate.setAggregateOperator($1.intermAggregate, op, @1); michael@0: } michael@0: } else { michael@0: // This is a real function call michael@0: michael@0: $$ = context->intermediate.setAggregateOperator($1.intermAggregate, EOpFunctionCall, @1); michael@0: $$->setType(fnCandidate->getReturnType()); michael@0: michael@0: // this is how we know whether the given function is a builtIn function or a user defined function michael@0: // if builtIn == false, it's a userDefined -> could be an overloaded builtIn function also michael@0: // if builtIn == true, it's definitely a builtIn function with EOpNull michael@0: if (!builtIn) michael@0: $$->getAsAggregate()->setUserDefined(); michael@0: $$->getAsAggregate()->setName(fnCandidate->getMangledName()); michael@0: michael@0: TQualifier qual; michael@0: for (size_t i = 0; i < fnCandidate->getParamCount(); ++i) { michael@0: qual = fnCandidate->getParam(i).type->getQualifier(); michael@0: if (qual == EvqOut || qual == EvqInOut) { michael@0: if (context->lValueErrorCheck($$->getLine(), "assign", $$->getAsAggregate()->getSequence()[i]->getAsTyped())) { michael@0: context->error($1.intermNode->getLine(), "Constant value cannot be passed for 'out' or 'inout' parameters.", "Error"); michael@0: context->recover(); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: $$->setType(fnCandidate->getReturnType()); michael@0: } else { michael@0: // error message was put out by PaFindFunction() michael@0: // Put on a dummy node for error recovery michael@0: ConstantUnion *unionArray = new ConstantUnion[1]; michael@0: unionArray->setFConst(0.0f); michael@0: $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtFloat, EbpUndefined, EvqConst), @1); michael@0: context->recover(); michael@0: } michael@0: } michael@0: delete fnCall; michael@0: } michael@0: ; michael@0: michael@0: function_call_or_method michael@0: : function_call_generic { michael@0: $$ = $1; michael@0: } michael@0: | postfix_expression DOT function_call_generic { michael@0: context->error(@3, "methods are not supported", ""); michael@0: context->recover(); michael@0: $$ = $3; michael@0: } michael@0: ; michael@0: michael@0: function_call_generic michael@0: : function_call_header_with_parameters RIGHT_PAREN { michael@0: $$ = $1; michael@0: } michael@0: | function_call_header_no_parameters RIGHT_PAREN { michael@0: $$ = $1; michael@0: } michael@0: ; michael@0: michael@0: function_call_header_no_parameters michael@0: : function_call_header VOID_TYPE { michael@0: $$.function = $1; michael@0: $$.intermNode = 0; michael@0: } michael@0: | function_call_header { michael@0: $$.function = $1; michael@0: $$.intermNode = 0; michael@0: } michael@0: ; michael@0: michael@0: function_call_header_with_parameters michael@0: : function_call_header assignment_expression { michael@0: TParameter param = { 0, new TType($2->getType()) }; michael@0: $1->addParameter(param); michael@0: $$.function = $1; michael@0: $$.intermNode = $2; michael@0: } michael@0: | function_call_header_with_parameters COMMA assignment_expression { michael@0: TParameter param = { 0, new TType($3->getType()) }; michael@0: $1.function->addParameter(param); michael@0: $$.function = $1.function; michael@0: $$.intermNode = context->intermediate.growAggregate($1.intermNode, $3, @2); michael@0: } michael@0: ; michael@0: michael@0: function_call_header michael@0: : function_identifier LEFT_PAREN { michael@0: $$ = $1; michael@0: } michael@0: ; michael@0: michael@0: // Grammar Note: Constructors look like functions, but are recognized as types. michael@0: michael@0: function_identifier michael@0: : type_specifier_nonarray { michael@0: // michael@0: // Constructor michael@0: // michael@0: TOperator op = EOpNull; michael@0: if ($1.userDef) { michael@0: op = EOpConstructStruct; michael@0: } else { michael@0: switch ($1.type) { michael@0: case EbtFloat: michael@0: if ($1.matrix) { michael@0: switch($1.size) { michael@0: case 2: op = EOpConstructMat2; break; michael@0: case 3: op = EOpConstructMat3; break; michael@0: case 4: op = EOpConstructMat4; break; michael@0: } michael@0: } else { michael@0: switch($1.size) { michael@0: case 1: op = EOpConstructFloat; break; michael@0: case 2: op = EOpConstructVec2; break; michael@0: case 3: op = EOpConstructVec3; break; michael@0: case 4: op = EOpConstructVec4; break; michael@0: } michael@0: } michael@0: break; michael@0: case EbtInt: michael@0: switch($1.size) { michael@0: case 1: op = EOpConstructInt; break; michael@0: case 2: op = EOpConstructIVec2; break; michael@0: case 3: op = EOpConstructIVec3; break; michael@0: case 4: op = EOpConstructIVec4; break; michael@0: } michael@0: break; michael@0: case EbtBool: michael@0: switch($1.size) { michael@0: case 1: op = EOpConstructBool; break; michael@0: case 2: op = EOpConstructBVec2; break; michael@0: case 3: op = EOpConstructBVec3; break; michael@0: case 4: op = EOpConstructBVec4; break; michael@0: } michael@0: break; michael@0: default: break; michael@0: } michael@0: if (op == EOpNull) { michael@0: context->error(@1, "cannot construct this type", getBasicString($1.type)); michael@0: context->recover(); michael@0: $1.type = EbtFloat; michael@0: op = EOpConstructFloat; michael@0: } michael@0: } michael@0: TString tempString; michael@0: TType type($1); michael@0: TFunction *function = new TFunction(&tempString, type, op); michael@0: $$ = function; michael@0: } michael@0: | IDENTIFIER { michael@0: if (context->reservedErrorCheck(@1, *$1.string)) michael@0: context->recover(); michael@0: TType type(EbtVoid, EbpUndefined); michael@0: TFunction *function = new TFunction($1.string, type); michael@0: $$ = function; michael@0: } michael@0: ; michael@0: michael@0: unary_expression michael@0: : postfix_expression { michael@0: $$ = $1; michael@0: } michael@0: | INC_OP unary_expression { michael@0: if (context->lValueErrorCheck(@1, "++", $2)) michael@0: context->recover(); michael@0: $$ = context->intermediate.addUnaryMath(EOpPreIncrement, $2, @1, context->symbolTable); michael@0: if ($$ == 0) { michael@0: context->unaryOpError(@1, "++", $2->getCompleteString()); michael@0: context->recover(); michael@0: $$ = $2; michael@0: } michael@0: } michael@0: | DEC_OP unary_expression { michael@0: if (context->lValueErrorCheck(@1, "--", $2)) michael@0: context->recover(); michael@0: $$ = context->intermediate.addUnaryMath(EOpPreDecrement, $2, @1, context->symbolTable); michael@0: if ($$ == 0) { michael@0: context->unaryOpError(@1, "--", $2->getCompleteString()); michael@0: context->recover(); michael@0: $$ = $2; michael@0: } michael@0: } michael@0: | unary_operator unary_expression { michael@0: if ($1.op != EOpNull) { michael@0: $$ = context->intermediate.addUnaryMath($1.op, $2, @1, context->symbolTable); michael@0: if ($$ == 0) { michael@0: const char* errorOp = ""; michael@0: switch($1.op) { michael@0: case EOpNegative: errorOp = "-"; break; michael@0: case EOpLogicalNot: errorOp = "!"; break; michael@0: default: break; michael@0: } michael@0: context->unaryOpError(@1, errorOp, $2->getCompleteString()); michael@0: context->recover(); michael@0: $$ = $2; michael@0: } michael@0: } else michael@0: $$ = $2; michael@0: } michael@0: ; michael@0: // Grammar Note: No traditional style type casts. michael@0: michael@0: unary_operator michael@0: : PLUS { $$.op = EOpNull; } michael@0: | DASH { $$.op = EOpNegative; } michael@0: | BANG { $$.op = EOpLogicalNot; } michael@0: ; michael@0: // Grammar Note: No '*' or '&' unary ops. Pointers are not supported. michael@0: michael@0: multiplicative_expression michael@0: : unary_expression { $$ = $1; } michael@0: | multiplicative_expression STAR unary_expression { michael@0: $$ = context->intermediate.addBinaryMath(EOpMul, $1, $3, @2, context->symbolTable); michael@0: if ($$ == 0) { michael@0: context->binaryOpError(@2, "*", $1->getCompleteString(), $3->getCompleteString()); michael@0: context->recover(); michael@0: $$ = $1; michael@0: } michael@0: } michael@0: | multiplicative_expression SLASH unary_expression { michael@0: $$ = context->intermediate.addBinaryMath(EOpDiv, $1, $3, @2, context->symbolTable); michael@0: if ($$ == 0) { michael@0: context->binaryOpError(@2, "/", $1->getCompleteString(), $3->getCompleteString()); michael@0: context->recover(); michael@0: $$ = $1; michael@0: } michael@0: } michael@0: ; michael@0: michael@0: additive_expression michael@0: : multiplicative_expression { $$ = $1; } michael@0: | additive_expression PLUS multiplicative_expression { michael@0: $$ = context->intermediate.addBinaryMath(EOpAdd, $1, $3, @2, context->symbolTable); michael@0: if ($$ == 0) { michael@0: context->binaryOpError(@2, "+", $1->getCompleteString(), $3->getCompleteString()); michael@0: context->recover(); michael@0: $$ = $1; michael@0: } michael@0: } michael@0: | additive_expression DASH multiplicative_expression { michael@0: $$ = context->intermediate.addBinaryMath(EOpSub, $1, $3, @2, context->symbolTable); michael@0: if ($$ == 0) { michael@0: context->binaryOpError(@2, "-", $1->getCompleteString(), $3->getCompleteString()); michael@0: context->recover(); michael@0: $$ = $1; michael@0: } michael@0: } michael@0: ; michael@0: michael@0: shift_expression michael@0: : additive_expression { $$ = $1; } michael@0: ; michael@0: michael@0: relational_expression michael@0: : shift_expression { $$ = $1; } michael@0: | relational_expression LEFT_ANGLE shift_expression { michael@0: $$ = context->intermediate.addBinaryMath(EOpLessThan, $1, $3, @2, context->symbolTable); michael@0: if ($$ == 0) { michael@0: context->binaryOpError(@2, "<", $1->getCompleteString(), $3->getCompleteString()); michael@0: context->recover(); michael@0: ConstantUnion *unionArray = new ConstantUnion[1]; michael@0: unionArray->setBConst(false); michael@0: $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtBool, EbpUndefined, EvqConst), @2); michael@0: } michael@0: } michael@0: | relational_expression RIGHT_ANGLE shift_expression { michael@0: $$ = context->intermediate.addBinaryMath(EOpGreaterThan, $1, $3, @2, context->symbolTable); michael@0: if ($$ == 0) { michael@0: context->binaryOpError(@2, ">", $1->getCompleteString(), $3->getCompleteString()); michael@0: context->recover(); michael@0: ConstantUnion *unionArray = new ConstantUnion[1]; michael@0: unionArray->setBConst(false); michael@0: $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtBool, EbpUndefined, EvqConst), @2); michael@0: } michael@0: } michael@0: | relational_expression LE_OP shift_expression { michael@0: $$ = context->intermediate.addBinaryMath(EOpLessThanEqual, $1, $3, @2, context->symbolTable); michael@0: if ($$ == 0) { michael@0: context->binaryOpError(@2, "<=", $1->getCompleteString(), $3->getCompleteString()); michael@0: context->recover(); michael@0: ConstantUnion *unionArray = new ConstantUnion[1]; michael@0: unionArray->setBConst(false); michael@0: $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtBool, EbpUndefined, EvqConst), @2); michael@0: } michael@0: } michael@0: | relational_expression GE_OP shift_expression { michael@0: $$ = context->intermediate.addBinaryMath(EOpGreaterThanEqual, $1, $3, @2, context->symbolTable); michael@0: if ($$ == 0) { michael@0: context->binaryOpError(@2, ">=", $1->getCompleteString(), $3->getCompleteString()); michael@0: context->recover(); michael@0: ConstantUnion *unionArray = new ConstantUnion[1]; michael@0: unionArray->setBConst(false); michael@0: $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtBool, EbpUndefined, EvqConst), @2); michael@0: } michael@0: } michael@0: ; michael@0: michael@0: equality_expression michael@0: : relational_expression { $$ = $1; } michael@0: | equality_expression EQ_OP relational_expression { michael@0: $$ = context->intermediate.addBinaryMath(EOpEqual, $1, $3, @2, context->symbolTable); michael@0: if ($$ == 0) { michael@0: context->binaryOpError(@2, "==", $1->getCompleteString(), $3->getCompleteString()); michael@0: context->recover(); michael@0: ConstantUnion *unionArray = new ConstantUnion[1]; michael@0: unionArray->setBConst(false); michael@0: $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtBool, EbpUndefined, EvqConst), @2); michael@0: } michael@0: } michael@0: | equality_expression NE_OP relational_expression { michael@0: $$ = context->intermediate.addBinaryMath(EOpNotEqual, $1, $3, @2, context->symbolTable); michael@0: if ($$ == 0) { michael@0: context->binaryOpError(@2, "!=", $1->getCompleteString(), $3->getCompleteString()); michael@0: context->recover(); michael@0: ConstantUnion *unionArray = new ConstantUnion[1]; michael@0: unionArray->setBConst(false); michael@0: $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtBool, EbpUndefined, EvqConst), @2); michael@0: } michael@0: } michael@0: ; michael@0: michael@0: and_expression michael@0: : equality_expression { $$ = $1; } michael@0: ; michael@0: michael@0: exclusive_or_expression michael@0: : and_expression { $$ = $1; } michael@0: ; michael@0: michael@0: inclusive_or_expression michael@0: : exclusive_or_expression { $$ = $1; } michael@0: ; michael@0: michael@0: logical_and_expression michael@0: : inclusive_or_expression { $$ = $1; } michael@0: | logical_and_expression AND_OP inclusive_or_expression { michael@0: $$ = context->intermediate.addBinaryMath(EOpLogicalAnd, $1, $3, @2, context->symbolTable); michael@0: if ($$ == 0) { michael@0: context->binaryOpError(@2, "&&", $1->getCompleteString(), $3->getCompleteString()); michael@0: context->recover(); michael@0: ConstantUnion *unionArray = new ConstantUnion[1]; michael@0: unionArray->setBConst(false); michael@0: $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtBool, EbpUndefined, EvqConst), @2); michael@0: } michael@0: } michael@0: ; michael@0: michael@0: logical_xor_expression michael@0: : logical_and_expression { $$ = $1; } michael@0: | logical_xor_expression XOR_OP logical_and_expression { michael@0: $$ = context->intermediate.addBinaryMath(EOpLogicalXor, $1, $3, @2, context->symbolTable); michael@0: if ($$ == 0) { michael@0: context->binaryOpError(@2, "^^", $1->getCompleteString(), $3->getCompleteString()); michael@0: context->recover(); michael@0: ConstantUnion *unionArray = new ConstantUnion[1]; michael@0: unionArray->setBConst(false); michael@0: $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtBool, EbpUndefined, EvqConst), @2); michael@0: } michael@0: } michael@0: ; michael@0: michael@0: logical_or_expression michael@0: : logical_xor_expression { $$ = $1; } michael@0: | logical_or_expression OR_OP logical_xor_expression { michael@0: $$ = context->intermediate.addBinaryMath(EOpLogicalOr, $1, $3, @2, context->symbolTable); michael@0: if ($$ == 0) { michael@0: context->binaryOpError(@2, "||", $1->getCompleteString(), $3->getCompleteString()); michael@0: context->recover(); michael@0: ConstantUnion *unionArray = new ConstantUnion[1]; michael@0: unionArray->setBConst(false); michael@0: $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtBool, EbpUndefined, EvqConst), @2); michael@0: } michael@0: } michael@0: ; michael@0: michael@0: conditional_expression michael@0: : logical_or_expression { $$ = $1; } michael@0: | logical_or_expression QUESTION expression COLON assignment_expression { michael@0: if (context->boolErrorCheck(@2, $1)) michael@0: context->recover(); michael@0: michael@0: $$ = context->intermediate.addSelection($1, $3, $5, @2); michael@0: if ($3->getType() != $5->getType()) michael@0: $$ = 0; michael@0: michael@0: if ($$ == 0) { michael@0: context->binaryOpError(@2, ":", $3->getCompleteString(), $5->getCompleteString()); michael@0: context->recover(); michael@0: $$ = $5; michael@0: } michael@0: } michael@0: ; michael@0: michael@0: assignment_expression michael@0: : conditional_expression { $$ = $1; } michael@0: | unary_expression assignment_operator assignment_expression { michael@0: if (context->lValueErrorCheck(@2, "assign", $1)) michael@0: context->recover(); michael@0: $$ = context->intermediate.addAssign($2.op, $1, $3, @2); michael@0: if ($$ == 0) { michael@0: context->assignError(@2, "assign", $1->getCompleteString(), $3->getCompleteString()); michael@0: context->recover(); michael@0: $$ = $1; michael@0: } michael@0: } michael@0: ; michael@0: michael@0: assignment_operator michael@0: : EQUAL { $$.op = EOpAssign; } michael@0: | MUL_ASSIGN { $$.op = EOpMulAssign; } michael@0: | DIV_ASSIGN { $$.op = EOpDivAssign; } michael@0: | ADD_ASSIGN { $$.op = EOpAddAssign; } michael@0: | SUB_ASSIGN { $$.op = EOpSubAssign; } michael@0: ; michael@0: michael@0: expression michael@0: : assignment_expression { michael@0: $$ = $1; michael@0: } michael@0: | expression COMMA assignment_expression { michael@0: $$ = context->intermediate.addComma($1, $3, @2); michael@0: if ($$ == 0) { michael@0: context->binaryOpError(@2, ",", $1->getCompleteString(), $3->getCompleteString()); michael@0: context->recover(); michael@0: $$ = $3; michael@0: } michael@0: } michael@0: ; michael@0: michael@0: constant_expression michael@0: : conditional_expression { michael@0: if (context->constErrorCheck($1)) michael@0: context->recover(); michael@0: $$ = $1; michael@0: } michael@0: ; michael@0: michael@0: declaration michael@0: : function_prototype SEMICOLON { michael@0: TFunction &function = *($1.function); michael@0: michael@0: TIntermAggregate *prototype = new TIntermAggregate; michael@0: prototype->setType(function.getReturnType()); michael@0: prototype->setName(function.getName()); michael@0: michael@0: for (size_t i = 0; i < function.getParamCount(); i++) michael@0: { michael@0: const TParameter ¶m = function.getParam(i); michael@0: if (param.name != 0) michael@0: { michael@0: TVariable variable(param.name, *param.type); michael@0: michael@0: prototype = context->intermediate.growAggregate(prototype, context->intermediate.addSymbol(variable.getUniqueId(), variable.getName(), variable.getType(), @1), @1); michael@0: } michael@0: else michael@0: { michael@0: prototype = context->intermediate.growAggregate(prototype, context->intermediate.addSymbol(0, "", *param.type, @1), @1); michael@0: } michael@0: } michael@0: michael@0: prototype->setOp(EOpPrototype); michael@0: $$ = prototype; michael@0: michael@0: context->symbolTable.pop(); michael@0: } michael@0: | init_declarator_list SEMICOLON { michael@0: if ($1.intermAggregate) michael@0: $1.intermAggregate->setOp(EOpDeclaration); michael@0: $$ = $1.intermAggregate; michael@0: } michael@0: | PRECISION precision_qualifier type_specifier_no_prec SEMICOLON { michael@0: if (($2 == EbpHigh) && (context->shaderType == SH_FRAGMENT_SHADER) && !context->fragmentPrecisionHigh) { michael@0: context->error(@1, "precision is not supported in fragment shader", "highp"); michael@0: context->recover(); michael@0: } michael@0: if (!context->symbolTable.setDefaultPrecision( $3, $2 )) { michael@0: context->error(@1, "illegal type argument for default precision qualifier", getBasicString($3.type)); michael@0: context->recover(); michael@0: } michael@0: $$ = 0; michael@0: } michael@0: ; michael@0: michael@0: function_prototype michael@0: : function_declarator RIGHT_PAREN { michael@0: // michael@0: // Multiple declarations of the same function are allowed. michael@0: // michael@0: // If this is a definition, the definition production code will check for redefinitions michael@0: // (we don't know at this point if it's a definition or not). michael@0: // michael@0: // Redeclarations are allowed. But, return types and parameter qualifiers must match. michael@0: // michael@0: TFunction* prevDec = static_cast(context->symbolTable.find($1->getMangledName())); michael@0: if (prevDec) { michael@0: if (prevDec->getReturnType() != $1->getReturnType()) { michael@0: context->error(@2, "overloaded functions must have the same return type", $1->getReturnType().getBasicString()); michael@0: context->recover(); michael@0: } michael@0: for (size_t i = 0; i < prevDec->getParamCount(); ++i) { michael@0: if (prevDec->getParam(i).type->getQualifier() != $1->getParam(i).type->getQualifier()) { michael@0: context->error(@2, "overloaded functions must have the same parameter qualifiers", $1->getParam(i).type->getQualifierString()); michael@0: context->recover(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // michael@0: // Check for previously declared variables using the same name. michael@0: // michael@0: TSymbol *prevSym = context->symbolTable.find($1->getName()); michael@0: if (prevSym) michael@0: { michael@0: if (!prevSym->isFunction()) michael@0: { michael@0: context->error(@2, "redefinition", $1->getName().c_str(), "function"); michael@0: context->recover(); michael@0: } michael@0: } michael@0: else michael@0: { michael@0: // Insert the unmangled name to detect potential future redefinition as a variable. michael@0: context->symbolTable.getOuterLevel()->insert($1->getName(), *$1); michael@0: } michael@0: michael@0: // michael@0: // If this is a redeclaration, it could also be a definition, michael@0: // in which case, we want to use the variable names from this one, and not the one that's michael@0: // being redeclared. So, pass back up this declaration, not the one in the symbol table. michael@0: // michael@0: $$.function = $1; michael@0: michael@0: // We're at the inner scope level of the function's arguments and body statement. michael@0: // Add the function prototype to the surrounding scope instead. michael@0: context->symbolTable.getOuterLevel()->insert(*$$.function); michael@0: } michael@0: ; michael@0: michael@0: function_declarator michael@0: : function_header { michael@0: $$ = $1; michael@0: } michael@0: | function_header_with_parameters { michael@0: $$ = $1; michael@0: } michael@0: ; michael@0: michael@0: michael@0: function_header_with_parameters michael@0: : function_header parameter_declaration { michael@0: // Add the parameter michael@0: $$ = $1; michael@0: if ($2.param.type->getBasicType() != EbtVoid) michael@0: $1->addParameter($2.param); michael@0: else michael@0: delete $2.param.type; michael@0: } michael@0: | function_header_with_parameters COMMA parameter_declaration { michael@0: // michael@0: // Only first parameter of one-parameter functions can be void michael@0: // The check for named parameters not being void is done in parameter_declarator michael@0: // michael@0: if ($3.param.type->getBasicType() == EbtVoid) { michael@0: // michael@0: // This parameter > first is void michael@0: // michael@0: context->error(@2, "cannot be an argument type except for '(void)'", "void"); michael@0: context->recover(); michael@0: delete $3.param.type; michael@0: } else { michael@0: // Add the parameter michael@0: $$ = $1; michael@0: $1->addParameter($3.param); michael@0: } michael@0: } michael@0: ; michael@0: michael@0: function_header michael@0: : fully_specified_type IDENTIFIER LEFT_PAREN { michael@0: if ($1.qualifier != EvqGlobal && $1.qualifier != EvqTemporary) { michael@0: context->error(@2, "no qualifiers allowed for function return", getQualifierString($1.qualifier)); michael@0: context->recover(); michael@0: } michael@0: // make sure a sampler is not involved as well... michael@0: if (context->structQualifierErrorCheck(@2, $1)) michael@0: context->recover(); michael@0: michael@0: // Add the function as a prototype after parsing it (we do not support recursion) michael@0: TFunction *function; michael@0: TType type($1); michael@0: function = new TFunction($2.string, type); michael@0: $$ = function; michael@0: michael@0: context->symbolTable.push(); michael@0: } michael@0: ; michael@0: michael@0: parameter_declarator michael@0: // Type + name michael@0: : type_specifier identifier { michael@0: if ($1.type == EbtVoid) { michael@0: context->error(@2, "illegal use of type 'void'", $2.string->c_str()); michael@0: context->recover(); michael@0: } michael@0: if (context->reservedErrorCheck(@2, *$2.string)) michael@0: context->recover(); michael@0: TParameter param = {$2.string, new TType($1)}; michael@0: $$.param = param; michael@0: } michael@0: | type_specifier identifier LEFT_BRACKET constant_expression RIGHT_BRACKET { michael@0: // Check that we can make an array out of this type michael@0: if (context->arrayTypeErrorCheck(@3, $1)) michael@0: context->recover(); michael@0: michael@0: if (context->reservedErrorCheck(@2, *$2.string)) michael@0: context->recover(); michael@0: michael@0: int size; michael@0: if (context->arraySizeErrorCheck(@3, $4, size)) michael@0: context->recover(); michael@0: $1.setArray(true, size); michael@0: michael@0: TType* type = new TType($1); michael@0: TParameter param = { $2.string, type }; michael@0: $$.param = param; michael@0: } michael@0: ; michael@0: michael@0: parameter_declaration michael@0: // michael@0: // The only parameter qualifier a parameter can have are michael@0: // IN_QUAL, OUT_QUAL, INOUT_QUAL, or CONST. michael@0: // michael@0: michael@0: // michael@0: // Type + name michael@0: // michael@0: : type_qualifier parameter_qualifier parameter_declarator { michael@0: $$ = $3; michael@0: if (context->paramErrorCheck(@3, $1.qualifier, $2, $$.param.type)) michael@0: context->recover(); michael@0: } michael@0: | parameter_qualifier parameter_declarator { michael@0: $$ = $2; michael@0: if (context->parameterSamplerErrorCheck(@2, $1, *$2.param.type)) michael@0: context->recover(); michael@0: if (context->paramErrorCheck(@2, EvqTemporary, $1, $$.param.type)) michael@0: context->recover(); michael@0: } michael@0: // michael@0: // Only type michael@0: // michael@0: | type_qualifier parameter_qualifier parameter_type_specifier { michael@0: $$ = $3; michael@0: if (context->paramErrorCheck(@3, $1.qualifier, $2, $$.param.type)) michael@0: context->recover(); michael@0: } michael@0: | parameter_qualifier parameter_type_specifier { michael@0: $$ = $2; michael@0: if (context->parameterSamplerErrorCheck(@2, $1, *$2.param.type)) michael@0: context->recover(); michael@0: if (context->paramErrorCheck(@2, EvqTemporary, $1, $$.param.type)) michael@0: context->recover(); michael@0: } michael@0: ; michael@0: michael@0: parameter_qualifier michael@0: : /* empty */ { michael@0: $$ = EvqIn; michael@0: } michael@0: | IN_QUAL { michael@0: $$ = EvqIn; michael@0: } michael@0: | OUT_QUAL { michael@0: $$ = EvqOut; michael@0: } michael@0: | INOUT_QUAL { michael@0: $$ = EvqInOut; michael@0: } michael@0: ; michael@0: michael@0: parameter_type_specifier michael@0: : type_specifier { michael@0: TParameter param = { 0, new TType($1) }; michael@0: $$.param = param; michael@0: } michael@0: ; michael@0: michael@0: init_declarator_list michael@0: : single_declaration { michael@0: $$ = $1; michael@0: } michael@0: | init_declarator_list COMMA identifier { michael@0: if ($1.type.type == EbtInvariant && !$3.symbol) michael@0: { michael@0: context->error(@3, "undeclared identifier declared as invariant", $3.string->c_str()); michael@0: context->recover(); michael@0: } michael@0: michael@0: TIntermSymbol* symbol = context->intermediate.addSymbol(0, *$3.string, TType($1.type), @3); michael@0: $$.intermAggregate = context->intermediate.growAggregate($1.intermNode, symbol, @3); michael@0: michael@0: if (context->structQualifierErrorCheck(@3, $$.type)) michael@0: context->recover(); michael@0: michael@0: if (context->nonInitConstErrorCheck(@3, *$3.string, $$.type, false)) michael@0: context->recover(); michael@0: michael@0: TVariable* variable = 0; michael@0: if (context->nonInitErrorCheck(@3, *$3.string, $$.type, variable)) michael@0: context->recover(); michael@0: if (symbol && variable) michael@0: symbol->setId(variable->getUniqueId()); michael@0: } michael@0: | init_declarator_list COMMA identifier LEFT_BRACKET RIGHT_BRACKET { michael@0: if (context->structQualifierErrorCheck(@3, $1.type)) michael@0: context->recover(); michael@0: michael@0: if (context->nonInitConstErrorCheck(@3, *$3.string, $1.type, true)) michael@0: context->recover(); michael@0: michael@0: $$ = $1; michael@0: michael@0: if (context->arrayTypeErrorCheck(@4, $1.type) || context->arrayQualifierErrorCheck(@4, $1.type)) michael@0: context->recover(); michael@0: else { michael@0: $1.type.setArray(true); michael@0: TVariable* variable; michael@0: if (context->arrayErrorCheck(@4, *$3.string, $1.type, variable)) michael@0: context->recover(); michael@0: } michael@0: } michael@0: | init_declarator_list COMMA identifier LEFT_BRACKET constant_expression RIGHT_BRACKET { michael@0: if (context->structQualifierErrorCheck(@3, $1.type)) michael@0: context->recover(); michael@0: michael@0: if (context->nonInitConstErrorCheck(@3, *$3.string, $1.type, true)) michael@0: context->recover(); michael@0: michael@0: $$ = $1; michael@0: michael@0: if (context->arrayTypeErrorCheck(@4, $1.type) || context->arrayQualifierErrorCheck(@4, $1.type)) michael@0: context->recover(); michael@0: else { michael@0: int size; michael@0: if (context->arraySizeErrorCheck(@4, $5, size)) michael@0: context->recover(); michael@0: $1.type.setArray(true, size); michael@0: TVariable* variable = 0; michael@0: if (context->arrayErrorCheck(@4, *$3.string, $1.type, variable)) michael@0: context->recover(); michael@0: TType type = TType($1.type); michael@0: type.setArraySize(size); michael@0: $$.intermAggregate = context->intermediate.growAggregate($1.intermNode, context->intermediate.addSymbol(variable ? variable->getUniqueId() : 0, *$3.string, type, @3), @3); michael@0: } michael@0: } michael@0: | init_declarator_list COMMA identifier EQUAL initializer { michael@0: if (context->structQualifierErrorCheck(@3, $1.type)) michael@0: context->recover(); michael@0: michael@0: $$ = $1; michael@0: michael@0: TIntermNode* intermNode; michael@0: if (!context->executeInitializer(@3, *$3.string, $1.type, $5, intermNode)) { michael@0: // michael@0: // build the intermediate representation michael@0: // michael@0: if (intermNode) michael@0: $$.intermAggregate = context->intermediate.growAggregate($1.intermNode, intermNode, @4); michael@0: else michael@0: $$.intermAggregate = $1.intermAggregate; michael@0: } else { michael@0: context->recover(); michael@0: $$.intermAggregate = 0; michael@0: } michael@0: } michael@0: ; michael@0: michael@0: single_declaration michael@0: : fully_specified_type { michael@0: $$.type = $1; michael@0: $$.intermAggregate = context->intermediate.makeAggregate(context->intermediate.addSymbol(0, "", TType($1), @1), @1); michael@0: } michael@0: | fully_specified_type identifier { michael@0: TIntermSymbol* symbol = context->intermediate.addSymbol(0, *$2.string, TType($1), @2); michael@0: $$.intermAggregate = context->intermediate.makeAggregate(symbol, @2); michael@0: michael@0: if (context->structQualifierErrorCheck(@2, $$.type)) michael@0: context->recover(); michael@0: michael@0: if (context->nonInitConstErrorCheck(@2, *$2.string, $$.type, false)) michael@0: context->recover(); michael@0: michael@0: $$.type = $1; michael@0: michael@0: TVariable* variable = 0; michael@0: if (context->nonInitErrorCheck(@2, *$2.string, $$.type, variable)) michael@0: context->recover(); michael@0: if (variable && symbol) michael@0: symbol->setId(variable->getUniqueId()); michael@0: } michael@0: | fully_specified_type identifier LEFT_BRACKET RIGHT_BRACKET { michael@0: context->error(@2, "unsized array declarations not supported", $2.string->c_str()); michael@0: context->recover(); michael@0: michael@0: TIntermSymbol* symbol = context->intermediate.addSymbol(0, *$2.string, TType($1), @2); michael@0: $$.intermAggregate = context->intermediate.makeAggregate(symbol, @2); michael@0: $$.type = $1; michael@0: } michael@0: | fully_specified_type identifier LEFT_BRACKET constant_expression RIGHT_BRACKET { michael@0: TType type = TType($1); michael@0: int size; michael@0: if (context->arraySizeErrorCheck(@2, $4, size)) michael@0: context->recover(); michael@0: type.setArraySize(size); michael@0: TIntermSymbol* symbol = context->intermediate.addSymbol(0, *$2.string, type, @2); michael@0: $$.intermAggregate = context->intermediate.makeAggregate(symbol, @2); michael@0: michael@0: if (context->structQualifierErrorCheck(@2, $1)) michael@0: context->recover(); michael@0: michael@0: if (context->nonInitConstErrorCheck(@2, *$2.string, $1, true)) michael@0: context->recover(); michael@0: michael@0: $$.type = $1; michael@0: michael@0: if (context->arrayTypeErrorCheck(@3, $1) || context->arrayQualifierErrorCheck(@3, $1)) michael@0: context->recover(); michael@0: else { michael@0: int size; michael@0: if (context->arraySizeErrorCheck(@3, $4, size)) michael@0: context->recover(); michael@0: michael@0: $1.setArray(true, size); michael@0: TVariable* variable = 0; michael@0: if (context->arrayErrorCheck(@3, *$2.string, $1, variable)) michael@0: context->recover(); michael@0: if (variable && symbol) michael@0: symbol->setId(variable->getUniqueId()); michael@0: } michael@0: } michael@0: | fully_specified_type identifier EQUAL initializer { michael@0: if (context->structQualifierErrorCheck(@2, $1)) michael@0: context->recover(); michael@0: michael@0: $$.type = $1; michael@0: michael@0: TIntermNode* intermNode; michael@0: if (!context->executeInitializer(@2, *$2.string, $1, $4, intermNode)) { michael@0: // michael@0: // Build intermediate representation michael@0: // michael@0: if(intermNode) michael@0: $$.intermAggregate = context->intermediate.makeAggregate(intermNode, @3); michael@0: else michael@0: $$.intermAggregate = 0; michael@0: } else { michael@0: context->recover(); michael@0: $$.intermAggregate = 0; michael@0: } michael@0: } michael@0: | INVARIANT IDENTIFIER { michael@0: VERTEX_ONLY("invariant declaration", @1); michael@0: if (context->globalErrorCheck(@1, context->symbolTable.atGlobalLevel(), "invariant varying")) michael@0: context->recover(); michael@0: $$.type.setBasic(EbtInvariant, EvqInvariantVaryingOut, @2); michael@0: if (!$2.symbol) michael@0: { michael@0: context->error(@2, "undeclared identifier declared as invariant", $2.string->c_str()); michael@0: context->recover(); michael@0: michael@0: $$.intermAggregate = 0; michael@0: } michael@0: else michael@0: { michael@0: TIntermSymbol *symbol = context->intermediate.addSymbol(0, *$2.string, TType($$.type), @2); michael@0: $$.intermAggregate = context->intermediate.makeAggregate(symbol, @2); michael@0: } michael@0: } michael@0: ; michael@0: michael@0: fully_specified_type michael@0: : type_specifier { michael@0: $$ = $1; michael@0: michael@0: if ($1.array) { michael@0: context->error(@1, "not supported", "first-class array"); michael@0: context->recover(); michael@0: $1.setArray(false); michael@0: } michael@0: } michael@0: | type_qualifier type_specifier { michael@0: if ($2.array) { michael@0: context->error(@2, "not supported", "first-class array"); michael@0: context->recover(); michael@0: $2.setArray(false); michael@0: } michael@0: michael@0: if ($1.qualifier == EvqAttribute && michael@0: ($2.type == EbtBool || $2.type == EbtInt)) { michael@0: context->error(@2, "cannot be bool or int", getQualifierString($1.qualifier)); michael@0: context->recover(); michael@0: } michael@0: if (($1.qualifier == EvqVaryingIn || $1.qualifier == EvqVaryingOut) && michael@0: ($2.type == EbtBool || $2.type == EbtInt)) { michael@0: context->error(@2, "cannot be bool or int", getQualifierString($1.qualifier)); michael@0: context->recover(); michael@0: } michael@0: $$ = $2; michael@0: $$.qualifier = $1.qualifier; michael@0: } michael@0: ; michael@0: michael@0: type_qualifier michael@0: : CONST_QUAL { michael@0: $$.setBasic(EbtVoid, EvqConst, @1); michael@0: } michael@0: | ATTRIBUTE { michael@0: VERTEX_ONLY("attribute", @1); michael@0: if (context->globalErrorCheck(@1, context->symbolTable.atGlobalLevel(), "attribute")) michael@0: context->recover(); michael@0: $$.setBasic(EbtVoid, EvqAttribute, @1); michael@0: } michael@0: | VARYING { michael@0: if (context->globalErrorCheck(@1, context->symbolTable.atGlobalLevel(), "varying")) michael@0: context->recover(); michael@0: if (context->shaderType == SH_VERTEX_SHADER) michael@0: $$.setBasic(EbtVoid, EvqVaryingOut, @1); michael@0: else michael@0: $$.setBasic(EbtVoid, EvqVaryingIn, @1); michael@0: } michael@0: | INVARIANT VARYING { michael@0: if (context->globalErrorCheck(@1, context->symbolTable.atGlobalLevel(), "invariant varying")) michael@0: context->recover(); michael@0: if (context->shaderType == SH_VERTEX_SHADER) michael@0: $$.setBasic(EbtVoid, EvqInvariantVaryingOut, @1); michael@0: else michael@0: $$.setBasic(EbtVoid, EvqInvariantVaryingIn, @1); michael@0: } michael@0: | UNIFORM { michael@0: if (context->globalErrorCheck(@1, context->symbolTable.atGlobalLevel(), "uniform")) michael@0: context->recover(); michael@0: $$.setBasic(EbtVoid, EvqUniform, @1); michael@0: } michael@0: ; michael@0: michael@0: type_specifier michael@0: : type_specifier_no_prec { michael@0: $$ = $1; michael@0: michael@0: if ($$.precision == EbpUndefined) { michael@0: $$.precision = context->symbolTable.getDefaultPrecision($1.type); michael@0: if (context->precisionErrorCheck(@1, $$.precision, $1.type)) { michael@0: context->recover(); michael@0: } michael@0: } michael@0: } michael@0: | precision_qualifier type_specifier_no_prec { michael@0: $$ = $2; michael@0: $$.precision = $1; michael@0: } michael@0: ; michael@0: michael@0: precision_qualifier michael@0: : HIGH_PRECISION { michael@0: $$ = EbpHigh; michael@0: } michael@0: | MEDIUM_PRECISION { michael@0: $$ = EbpMedium; michael@0: } michael@0: | LOW_PRECISION { michael@0: $$ = EbpLow; michael@0: } michael@0: ; michael@0: michael@0: type_specifier_no_prec michael@0: : type_specifier_nonarray { michael@0: $$ = $1; michael@0: } michael@0: | type_specifier_nonarray LEFT_BRACKET constant_expression RIGHT_BRACKET { michael@0: $$ = $1; michael@0: michael@0: if (context->arrayTypeErrorCheck(@2, $1)) michael@0: context->recover(); michael@0: else { michael@0: int size; michael@0: if (context->arraySizeErrorCheck(@2, $3, size)) michael@0: context->recover(); michael@0: $$.setArray(true, size); michael@0: } michael@0: } michael@0: ; michael@0: michael@0: type_specifier_nonarray michael@0: : VOID_TYPE { michael@0: TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; michael@0: $$.setBasic(EbtVoid, qual, @1); michael@0: } michael@0: | FLOAT_TYPE { michael@0: TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; michael@0: $$.setBasic(EbtFloat, qual, @1); michael@0: } michael@0: | INT_TYPE { michael@0: TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; michael@0: $$.setBasic(EbtInt, qual, @1); michael@0: } michael@0: | BOOL_TYPE { michael@0: TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; michael@0: $$.setBasic(EbtBool, qual, @1); michael@0: } michael@0: | VEC2 { michael@0: TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; michael@0: $$.setBasic(EbtFloat, qual, @1); michael@0: $$.setAggregate(2); michael@0: } michael@0: | VEC3 { michael@0: TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; michael@0: $$.setBasic(EbtFloat, qual, @1); michael@0: $$.setAggregate(3); michael@0: } michael@0: | VEC4 { michael@0: TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; michael@0: $$.setBasic(EbtFloat, qual, @1); michael@0: $$.setAggregate(4); michael@0: } michael@0: | BVEC2 { michael@0: TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; michael@0: $$.setBasic(EbtBool, qual, @1); michael@0: $$.setAggregate(2); michael@0: } michael@0: | BVEC3 { michael@0: TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; michael@0: $$.setBasic(EbtBool, qual, @1); michael@0: $$.setAggregate(3); michael@0: } michael@0: | BVEC4 { michael@0: TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; michael@0: $$.setBasic(EbtBool, qual, @1); michael@0: $$.setAggregate(4); michael@0: } michael@0: | IVEC2 { michael@0: TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; michael@0: $$.setBasic(EbtInt, qual, @1); michael@0: $$.setAggregate(2); michael@0: } michael@0: | IVEC3 { michael@0: TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; michael@0: $$.setBasic(EbtInt, qual, @1); michael@0: $$.setAggregate(3); michael@0: } michael@0: | IVEC4 { michael@0: TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; michael@0: $$.setBasic(EbtInt, qual, @1); michael@0: $$.setAggregate(4); michael@0: } michael@0: | MATRIX2 { michael@0: TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; michael@0: $$.setBasic(EbtFloat, qual, @1); michael@0: $$.setAggregate(2, true); michael@0: } michael@0: | MATRIX3 { michael@0: TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; michael@0: $$.setBasic(EbtFloat, qual, @1); michael@0: $$.setAggregate(3, true); michael@0: } michael@0: | MATRIX4 { michael@0: TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; michael@0: $$.setBasic(EbtFloat, qual, @1); michael@0: $$.setAggregate(4, true); michael@0: } michael@0: | SAMPLER2D { michael@0: TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; michael@0: $$.setBasic(EbtSampler2D, qual, @1); michael@0: } michael@0: | SAMPLERCUBE { michael@0: TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; michael@0: $$.setBasic(EbtSamplerCube, qual, @1); michael@0: } michael@0: | SAMPLER_EXTERNAL_OES { michael@0: if (!context->supportsExtension("GL_OES_EGL_image_external")) { michael@0: context->error(@1, "unsupported type", "samplerExternalOES"); michael@0: context->recover(); michael@0: } michael@0: TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; michael@0: $$.setBasic(EbtSamplerExternalOES, qual, @1); michael@0: } michael@0: | SAMPLER2DRECT { michael@0: if (!context->supportsExtension("GL_ARB_texture_rectangle")) { michael@0: context->error(@1, "unsupported type", "sampler2DRect"); michael@0: context->recover(); michael@0: } michael@0: TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; michael@0: $$.setBasic(EbtSampler2DRect, qual, @1); michael@0: } michael@0: | struct_specifier { michael@0: $$ = $1; michael@0: $$.qualifier = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; michael@0: } michael@0: | TYPE_NAME { michael@0: // michael@0: // This is for user defined type names. The lexical phase looked up the michael@0: // type. michael@0: // michael@0: TType& structure = static_cast($1.symbol)->getType(); michael@0: TQualifier qual = context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary; michael@0: $$.setBasic(EbtStruct, qual, @1); michael@0: $$.userDef = &structure; michael@0: } michael@0: ; michael@0: michael@0: struct_specifier michael@0: : STRUCT identifier LEFT_BRACE { if (context->enterStructDeclaration(@2, *$2.string)) context->recover(); } struct_declaration_list RIGHT_BRACE { michael@0: if (context->reservedErrorCheck(@2, *$2.string)) michael@0: context->recover(); michael@0: michael@0: TType* structure = new TType(new TStructure($2.string, $5)); michael@0: TVariable* userTypeDef = new TVariable($2.string, *structure, true); michael@0: if (! context->symbolTable.insert(*userTypeDef)) { michael@0: context->error(@2, "redefinition", $2.string->c_str(), "struct"); michael@0: context->recover(); michael@0: } michael@0: $$.setBasic(EbtStruct, EvqTemporary, @1); michael@0: $$.userDef = structure; michael@0: context->exitStructDeclaration(); michael@0: } michael@0: | STRUCT LEFT_BRACE { if (context->enterStructDeclaration(@2, *$2.string)) context->recover(); } struct_declaration_list RIGHT_BRACE { michael@0: TType* structure = new TType(new TStructure(NewPoolTString(""), $4)); michael@0: $$.setBasic(EbtStruct, EvqTemporary, @1); michael@0: $$.userDef = structure; michael@0: context->exitStructDeclaration(); michael@0: } michael@0: ; michael@0: michael@0: struct_declaration_list michael@0: : struct_declaration { michael@0: $$ = $1; michael@0: } michael@0: | struct_declaration_list struct_declaration { michael@0: $$ = $1; michael@0: for (size_t i = 0; i < $2->size(); ++i) { michael@0: TField* field = (*$2)[i]; michael@0: for (size_t j = 0; j < $$->size(); ++j) { michael@0: if ((*$$)[j]->name() == field->name()) { michael@0: context->error(@2, "duplicate field name in structure:", "struct", field->name().c_str()); michael@0: context->recover(); michael@0: } michael@0: } michael@0: $$->push_back(field); michael@0: } michael@0: } michael@0: ; michael@0: michael@0: struct_declaration michael@0: : type_specifier struct_declarator_list SEMICOLON { michael@0: $$ = $2; michael@0: michael@0: if (context->voidErrorCheck(@1, (*$2)[0]->name(), $1)) { michael@0: context->recover(); michael@0: } michael@0: for (unsigned int i = 0; i < $$->size(); ++i) { michael@0: // michael@0: // Careful not to replace already known aspects of type, like array-ness michael@0: // michael@0: TType* type = (*$$)[i]->type(); michael@0: type->setBasicType($1.type); michael@0: type->setNominalSize($1.size); michael@0: type->setMatrix($1.matrix); michael@0: type->setPrecision($1.precision); michael@0: michael@0: // don't allow arrays of arrays michael@0: if (type->isArray()) { michael@0: if (context->arrayTypeErrorCheck(@1, $1)) michael@0: context->recover(); michael@0: } michael@0: if ($1.array) michael@0: type->setArraySize($1.arraySize); michael@0: if ($1.userDef) michael@0: type->setStruct($1.userDef->getStruct()); michael@0: michael@0: if (context->structNestingErrorCheck(@1, *(*$$)[i])) michael@0: context->recover(); michael@0: } michael@0: } michael@0: ; michael@0: michael@0: struct_declarator_list michael@0: : struct_declarator { michael@0: $$ = NewPoolTFieldList(); michael@0: $$->push_back($1); michael@0: } michael@0: | struct_declarator_list COMMA struct_declarator { michael@0: $$->push_back($3); michael@0: } michael@0: ; michael@0: michael@0: struct_declarator michael@0: : identifier { michael@0: if (context->reservedErrorCheck(@1, *$1.string)) michael@0: context->recover(); michael@0: michael@0: TType* type = new TType(EbtVoid, EbpUndefined); michael@0: $$ = new TField(type, $1.string); michael@0: } michael@0: | identifier LEFT_BRACKET constant_expression RIGHT_BRACKET { michael@0: if (context->reservedErrorCheck(@1, *$1.string)) michael@0: context->recover(); michael@0: michael@0: TType* type = new TType(EbtVoid, EbpUndefined); michael@0: int size = 0; michael@0: if (context->arraySizeErrorCheck(@3, $3, size)) michael@0: context->recover(); michael@0: type->setArraySize(size); michael@0: michael@0: $$ = new TField(type, $1.string); michael@0: } michael@0: ; michael@0: michael@0: initializer michael@0: : assignment_expression { $$ = $1; } michael@0: ; michael@0: michael@0: declaration_statement michael@0: : declaration { $$ = $1; } michael@0: ; michael@0: michael@0: statement michael@0: : compound_statement { $$ = $1; } michael@0: | simple_statement { $$ = $1; } michael@0: ; michael@0: michael@0: // Grammar Note: No labeled statements; 'goto' is not supported. michael@0: michael@0: simple_statement michael@0: : declaration_statement { $$ = $1; } michael@0: | expression_statement { $$ = $1; } michael@0: | selection_statement { $$ = $1; } michael@0: | iteration_statement { $$ = $1; } michael@0: | jump_statement { $$ = $1; } michael@0: ; michael@0: michael@0: compound_statement michael@0: : LEFT_BRACE RIGHT_BRACE { $$ = 0; } michael@0: | LEFT_BRACE { context->symbolTable.push(); } statement_list { context->symbolTable.pop(); } RIGHT_BRACE { michael@0: if ($3 != 0) { michael@0: $3->setOp(EOpSequence); michael@0: $3->setLine(@$); michael@0: } michael@0: $$ = $3; michael@0: } michael@0: ; michael@0: michael@0: statement_no_new_scope michael@0: : compound_statement_no_new_scope { $$ = $1; } michael@0: | simple_statement { $$ = $1; } michael@0: ; michael@0: michael@0: statement_with_scope michael@0: : { context->symbolTable.push(); } compound_statement_no_new_scope { context->symbolTable.pop(); $$ = $2; } michael@0: | { context->symbolTable.push(); } simple_statement { context->symbolTable.pop(); $$ = $2; } michael@0: ; michael@0: michael@0: compound_statement_no_new_scope michael@0: // Statement that doesn't create a new scope, for selection_statement, iteration_statement michael@0: : LEFT_BRACE RIGHT_BRACE { michael@0: $$ = 0; michael@0: } michael@0: | LEFT_BRACE statement_list RIGHT_BRACE { michael@0: if ($2) { michael@0: $2->setOp(EOpSequence); michael@0: $2->setLine(@$); michael@0: } michael@0: $$ = $2; michael@0: } michael@0: ; michael@0: michael@0: statement_list michael@0: : statement { michael@0: $$ = context->intermediate.makeAggregate($1, @$); michael@0: } michael@0: | statement_list statement { michael@0: $$ = context->intermediate.growAggregate($1, $2, @$); michael@0: } michael@0: ; michael@0: michael@0: expression_statement michael@0: : SEMICOLON { $$ = 0; } michael@0: | expression SEMICOLON { $$ = static_cast($1); } michael@0: ; michael@0: michael@0: selection_statement michael@0: : IF LEFT_PAREN expression RIGHT_PAREN selection_rest_statement { michael@0: if (context->boolErrorCheck(@1, $3)) michael@0: context->recover(); michael@0: $$ = context->intermediate.addSelection($3, $5, @1); michael@0: } michael@0: ; michael@0: michael@0: selection_rest_statement michael@0: : statement_with_scope ELSE statement_with_scope { michael@0: $$.node1 = $1; michael@0: $$.node2 = $3; michael@0: } michael@0: | statement_with_scope { michael@0: $$.node1 = $1; michael@0: $$.node2 = 0; michael@0: } michael@0: ; michael@0: michael@0: // Grammar Note: No 'switch'. Switch statements not supported. michael@0: michael@0: condition michael@0: // In 1996 c++ draft, conditions can include single declarations michael@0: : expression { michael@0: $$ = $1; michael@0: if (context->boolErrorCheck($1->getLine(), $1)) michael@0: context->recover(); michael@0: } michael@0: | fully_specified_type identifier EQUAL initializer { michael@0: TIntermNode* intermNode; michael@0: if (context->structQualifierErrorCheck(@2, $1)) michael@0: context->recover(); michael@0: if (context->boolErrorCheck(@2, $1)) michael@0: context->recover(); michael@0: michael@0: if (!context->executeInitializer(@2, *$2.string, $1, $4, intermNode)) michael@0: $$ = $4; michael@0: else { michael@0: context->recover(); michael@0: $$ = 0; michael@0: } michael@0: } michael@0: ; michael@0: michael@0: iteration_statement michael@0: : WHILE LEFT_PAREN { context->symbolTable.push(); ++context->loopNestingLevel; } condition RIGHT_PAREN statement_no_new_scope { michael@0: context->symbolTable.pop(); michael@0: $$ = context->intermediate.addLoop(ELoopWhile, 0, $4, 0, $6, @1); michael@0: --context->loopNestingLevel; michael@0: } michael@0: | DO { ++context->loopNestingLevel; } statement_with_scope WHILE LEFT_PAREN expression RIGHT_PAREN SEMICOLON { michael@0: if (context->boolErrorCheck(@8, $6)) michael@0: context->recover(); michael@0: michael@0: $$ = context->intermediate.addLoop(ELoopDoWhile, 0, $6, 0, $3, @4); michael@0: --context->loopNestingLevel; michael@0: } michael@0: | FOR LEFT_PAREN { context->symbolTable.push(); ++context->loopNestingLevel; } for_init_statement for_rest_statement RIGHT_PAREN statement_no_new_scope { michael@0: context->symbolTable.pop(); michael@0: $$ = context->intermediate.addLoop(ELoopFor, $4, reinterpret_cast($5.node1), reinterpret_cast($5.node2), $7, @1); michael@0: --context->loopNestingLevel; michael@0: } michael@0: ; michael@0: michael@0: for_init_statement michael@0: : expression_statement { michael@0: $$ = $1; michael@0: } michael@0: | declaration_statement { michael@0: $$ = $1; michael@0: } michael@0: ; michael@0: michael@0: conditionopt michael@0: : condition { michael@0: $$ = $1; michael@0: } michael@0: | /* May be null */ { michael@0: $$ = 0; michael@0: } michael@0: ; michael@0: michael@0: for_rest_statement michael@0: : conditionopt SEMICOLON { michael@0: $$.node1 = $1; michael@0: $$.node2 = 0; michael@0: } michael@0: | conditionopt SEMICOLON expression { michael@0: $$.node1 = $1; michael@0: $$.node2 = $3; michael@0: } michael@0: ; michael@0: michael@0: jump_statement michael@0: : CONTINUE SEMICOLON { michael@0: if (context->loopNestingLevel <= 0) { michael@0: context->error(@1, "continue statement only allowed in loops", ""); michael@0: context->recover(); michael@0: } michael@0: $$ = context->intermediate.addBranch(EOpContinue, @1); michael@0: } michael@0: | BREAK SEMICOLON { michael@0: if (context->loopNestingLevel <= 0) { michael@0: context->error(@1, "break statement only allowed in loops", ""); michael@0: context->recover(); michael@0: } michael@0: $$ = context->intermediate.addBranch(EOpBreak, @1); michael@0: } michael@0: | RETURN SEMICOLON { michael@0: $$ = context->intermediate.addBranch(EOpReturn, @1); michael@0: if (context->currentFunctionType->getBasicType() != EbtVoid) { michael@0: context->error(@1, "non-void function must return a value", "return"); michael@0: context->recover(); michael@0: } michael@0: } michael@0: | RETURN expression SEMICOLON { michael@0: $$ = context->intermediate.addBranch(EOpReturn, $2, @1); michael@0: context->functionReturnsValue = true; michael@0: if (context->currentFunctionType->getBasicType() == EbtVoid) { michael@0: context->error(@1, "void function cannot return a value", "return"); michael@0: context->recover(); michael@0: } else if (*(context->currentFunctionType) != $2->getType()) { michael@0: context->error(@1, "function return is not matching type:", "return"); michael@0: context->recover(); michael@0: } michael@0: } michael@0: | DISCARD SEMICOLON { michael@0: FRAG_ONLY("discard", @1); michael@0: $$ = context->intermediate.addBranch(EOpKill, @1); michael@0: } michael@0: ; michael@0: michael@0: // Grammar Note: No 'goto'. Gotos are not supported. michael@0: michael@0: translation_unit michael@0: : external_declaration { michael@0: $$ = $1; michael@0: context->treeRoot = $$; michael@0: } michael@0: | translation_unit external_declaration { michael@0: $$ = context->intermediate.growAggregate($1, $2, @$); michael@0: context->treeRoot = $$; michael@0: } michael@0: ; michael@0: michael@0: external_declaration michael@0: : function_definition { michael@0: $$ = $1; michael@0: } michael@0: | declaration { michael@0: $$ = $1; michael@0: } michael@0: ; michael@0: michael@0: function_definition michael@0: : function_prototype { michael@0: TFunction* function = $1.function; michael@0: michael@0: const TSymbol *builtIn = context->symbolTable.findBuiltIn(function->getMangledName()); michael@0: michael@0: if (builtIn) michael@0: { michael@0: context->error(@1, "built-in functions cannot be redefined", function->getName().c_str()); michael@0: context->recover(); michael@0: } michael@0: michael@0: TFunction* prevDec = static_cast(context->symbolTable.find(function->getMangledName())); michael@0: // michael@0: // Note: 'prevDec' could be 'function' if this is the first time we've seen function michael@0: // as it would have just been put in the symbol table. Otherwise, we're looking up michael@0: // an earlier occurance. michael@0: // michael@0: if (prevDec->isDefined()) { michael@0: // michael@0: // Then this function already has a body. michael@0: // michael@0: context->error(@1, "function already has a body", function->getName().c_str()); michael@0: context->recover(); michael@0: } michael@0: prevDec->setDefined(); michael@0: michael@0: // michael@0: // Raise error message if main function takes any parameters or return anything other than void michael@0: // michael@0: if (function->getName() == "main") { michael@0: if (function->getParamCount() > 0) { michael@0: context->error(@1, "function cannot take any parameter(s)", function->getName().c_str()); michael@0: context->recover(); michael@0: } michael@0: if (function->getReturnType().getBasicType() != EbtVoid) { michael@0: context->error(@1, "", function->getReturnType().getBasicString(), "main function cannot return a value"); michael@0: context->recover(); michael@0: } michael@0: } michael@0: michael@0: // michael@0: // Remember the return type for later checking for RETURN statements. michael@0: // michael@0: context->currentFunctionType = &(prevDec->getReturnType()); michael@0: context->functionReturnsValue = false; michael@0: michael@0: // michael@0: // Insert parameters into the symbol table. michael@0: // If the parameter has no name, it's not an error, just don't insert it michael@0: // (could be used for unused args). michael@0: // michael@0: // Also, accumulate the list of parameters into the HIL, so lower level code michael@0: // knows where to find parameters. michael@0: // michael@0: TIntermAggregate* paramNodes = new TIntermAggregate; michael@0: for (size_t i = 0; i < function->getParamCount(); i++) { michael@0: const TParameter& param = function->getParam(i); michael@0: if (param.name != 0) { michael@0: TVariable *variable = new TVariable(param.name, *param.type); michael@0: // michael@0: // Insert the parameters with name in the symbol table. michael@0: // michael@0: if (! context->symbolTable.insert(*variable)) { michael@0: context->error(@1, "redefinition", variable->getName().c_str()); michael@0: context->recover(); michael@0: delete variable; michael@0: } michael@0: michael@0: // michael@0: // Add the parameter to the HIL michael@0: // michael@0: paramNodes = context->intermediate.growAggregate( michael@0: paramNodes, michael@0: context->intermediate.addSymbol(variable->getUniqueId(), michael@0: variable->getName(), michael@0: variable->getType(), michael@0: @1), michael@0: @1); michael@0: } else { michael@0: paramNodes = context->intermediate.growAggregate(paramNodes, context->intermediate.addSymbol(0, "", *param.type, @1), @1); michael@0: } michael@0: } michael@0: context->intermediate.setAggregateOperator(paramNodes, EOpParameters, @1); michael@0: $1.intermAggregate = paramNodes; michael@0: context->loopNestingLevel = 0; michael@0: } michael@0: compound_statement_no_new_scope { michael@0: //?? Check that all paths return a value if return type != void ? michael@0: // May be best done as post process phase on intermediate code michael@0: if (context->currentFunctionType->getBasicType() != EbtVoid && ! context->functionReturnsValue) { michael@0: context->error(@1, "function does not return a value:", "", $1.function->getName().c_str()); michael@0: context->recover(); michael@0: } michael@0: michael@0: $$ = context->intermediate.growAggregate($1.intermAggregate, $3, @$); michael@0: context->intermediate.setAggregateOperator($$, EOpFunction, @1); michael@0: $$->getAsAggregate()->setName($1.function->getMangledName().c_str()); michael@0: $$->getAsAggregate()->setType($1.function->getReturnType()); michael@0: michael@0: // store the pragma information for debug and optimize and other vendor specific michael@0: // information. This information can be queried from the parse tree michael@0: $$->getAsAggregate()->setOptimize(context->pragma().optimize); michael@0: $$->getAsAggregate()->setDebug(context->pragma().debug); michael@0: michael@0: context->symbolTable.pop(); michael@0: } michael@0: ; michael@0: michael@0: %% michael@0: michael@0: void yyerror(YYLTYPE* yylloc, TParseContext* context, const char* reason) { michael@0: context->error(*yylloc, reason, ""); michael@0: context->recover(); michael@0: } michael@0: michael@0: int glslang_parse(TParseContext* context) { michael@0: return yyparse(context); michael@0: }