diff options
Diffstat (limited to 'gfx/angle/src/compiler/translator/glslang.y')
-rwxr-xr-x | gfx/angle/src/compiler/translator/glslang.y | 1533 |
1 files changed, 1533 insertions, 0 deletions
diff --git a/gfx/angle/src/compiler/translator/glslang.y b/gfx/angle/src/compiler/translator/glslang.y new file mode 100755 index 000000000..c15dd9d6e --- /dev/null +++ b/gfx/angle/src/compiler/translator/glslang.y @@ -0,0 +1,1533 @@ +/* +// +// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// + +This file contains the Yacc grammar for GLSL ES. +Based on ANSI C Yacc grammar: +http://www.lysator.liu.se/c/ANSI-C-grammar-y.html + +IF YOU MODIFY THIS FILE YOU ALSO NEED TO RUN generate_parser.sh, +WHICH GENERATES THE GLSL ES PARSER (glslang_tab.cpp AND glslang_tab.h). +*/ + +%{ +// +// Copyright (c) 2002-2014 The ANGLE Project Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// + +// This file is auto-generated by generate_parser.sh. DO NOT EDIT! + +// clang-format off + +// Ignore errors in auto-generated code. +#if defined(__GNUC__) +#pragma GCC diagnostic ignored "-Wunused-function" +#pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wswitch-enum" +#elif defined(_MSC_VER) +#pragma warning(disable: 4065) +#pragma warning(disable: 4189) +#pragma warning(disable: 4244) +#pragma warning(disable: 4505) +#pragma warning(disable: 4701) +#pragma warning(disable: 4702) +#endif + +#include "angle_gl.h" +#include "compiler/translator/Cache.h" +#include "compiler/translator/SymbolTable.h" +#include "compiler/translator/ParseContext.h" +#include "GLSLANG/ShaderLang.h" + +#define YYENABLE_NLS 0 + +using namespace sh; + +%} +%expect 1 /* One shift reduce conflict because of if | else */ +%parse-param {TParseContext* context} +%param {void *scanner} +%define api.pure full +%locations + +%code requires { +#define YYLTYPE TSourceLoc +#define YYLTYPE_IS_DECLARED 1 +} + +%union { + struct { + union { + TString *string; + float f; + int i; + unsigned int u; + bool b; + }; + TSymbol* symbol; + } lex; + struct { + TOperator op; + union { + TIntermNode *intermNode; + TIntermNodePair nodePair; + TIntermTyped *intermTypedNode; + TIntermAggregate *intermAggregate; + TIntermBlock *intermBlock; + TIntermDeclaration *intermDeclaration; + TIntermSwitch *intermSwitch; + TIntermCase *intermCase; + }; + union { + TTypeSpecifierNonArray typeSpecifierNonArray; + TPublicType type; + TPrecision precision; + TLayoutQualifier layoutQualifier; + TQualifier qualifier; + TFunction *function; + TParameter param; + TField *field; + TFieldList *fieldList; + TQualifierWrapperBase *qualifierWrapper; + TTypeQualifierBuilder *typeQualifierBuilder; + }; + } interm; +} + +%{ +extern int yylex(YYSTYPE* yylval, YYLTYPE* yylloc, void* yyscanner); +extern void yyerror(YYLTYPE* yylloc, TParseContext* context, void *scanner, const char* reason); + +#define YYLLOC_DEFAULT(Current, Rhs, N) \ + do { \ + if (N) { \ + (Current).first_file = YYRHSLOC(Rhs, 1).first_file; \ + (Current).first_line = YYRHSLOC(Rhs, 1).first_line; \ + (Current).last_file = YYRHSLOC(Rhs, N).last_file; \ + (Current).last_line = YYRHSLOC(Rhs, N).last_line; \ + } \ + else { \ + (Current).first_file = YYRHSLOC(Rhs, 0).last_file; \ + (Current).first_line = YYRHSLOC(Rhs, 0).last_line; \ + (Current).last_file = YYRHSLOC(Rhs, 0).last_file; \ + (Current).last_line = YYRHSLOC(Rhs, 0).last_line; \ + } \ + } while (0) + +#define VERTEX_ONLY(S, L) { \ + if (context->getShaderType() != GL_VERTEX_SHADER) { \ + context->error(L, " supported in vertex shaders only ", S); \ + } \ +} + +#define FRAG_ONLY(S, L) { \ + if (context->getShaderType() != GL_FRAGMENT_SHADER) { \ + context->error(L, " supported in fragment shaders only ", S); \ + } \ +} + +#define COMPUTE_ONLY(S, L) { \ + if (context->getShaderType() != GL_COMPUTE_SHADER) { \ + context->error(L, " supported in compute shaders only ", S); \ + } \ +} + +#define NON_COMPUTE_ONLY(S, L) { \ + if (context->getShaderType() != GL_VERTEX_SHADER && context->getShaderType() != GL_FRAGMENT_SHADER) { \ + context->error(L, " supported in vertex and fragment shaders only ", S); \ + } \ +} + +#define ES2_ONLY(S, L) { \ + if (context->getShaderVersion() != 100) { \ + context->error(L, " supported in GLSL ES 1.00 only ", S); \ + } \ +} + +#define ES3_OR_NEWER(TOKEN, LINE, REASON) { \ + if (context->getShaderVersion() < 300) { \ + context->error(LINE, REASON " supported in GLSL ES 3.00 and above only ", TOKEN); \ + } \ +} + +#define ES3_1_ONLY(TOKEN, LINE, REASON) { \ + if (context->getShaderVersion() != 310) { \ + context->error(LINE, REASON " supported in GLSL ES 3.10 only ", TOKEN); \ + } \ +} +%} + +%token <lex> INVARIANT HIGH_PRECISION MEDIUM_PRECISION LOW_PRECISION PRECISION +%token <lex> ATTRIBUTE CONST_QUAL BOOL_TYPE FLOAT_TYPE INT_TYPE UINT_TYPE +%token <lex> BREAK CONTINUE DO ELSE FOR IF DISCARD RETURN SWITCH CASE DEFAULT +%token <lex> BVEC2 BVEC3 BVEC4 IVEC2 IVEC3 IVEC4 VEC2 VEC3 VEC4 UVEC2 UVEC3 UVEC4 +%token <lex> MATRIX2 MATRIX3 MATRIX4 IN_QUAL OUT_QUAL INOUT_QUAL UNIFORM VARYING +%token <lex> MATRIX2x3 MATRIX3x2 MATRIX2x4 MATRIX4x2 MATRIX3x4 MATRIX4x3 +%token <lex> CENTROID FLAT SMOOTH +%token <lex> READONLY WRITEONLY COHERENT RESTRICT VOLATILE +%token <lex> STRUCT VOID_TYPE WHILE +%token <lex> SAMPLER2D SAMPLERCUBE SAMPLER_EXTERNAL_OES SAMPLER2DRECT SAMPLER2DARRAY +%token <lex> ISAMPLER2D ISAMPLER3D ISAMPLERCUBE ISAMPLER2DARRAY +%token <lex> USAMPLER2D USAMPLER3D USAMPLERCUBE USAMPLER2DARRAY +%token <lex> SAMPLER3D SAMPLER3DRECT SAMPLER2DSHADOW SAMPLERCUBESHADOW SAMPLER2DARRAYSHADOW +%token <lex> IMAGE2D IIMAGE2D UIMAGE2D IMAGE3D IIMAGE3D UIMAGE3D IMAGE2DARRAY IIMAGE2DARRAY UIMAGE2DARRAY +%token <lex> IMAGECUBE IIMAGECUBE UIMAGECUBE +%token <lex> LAYOUT + +%token <lex> IDENTIFIER TYPE_NAME FLOATCONSTANT INTCONSTANT UINTCONSTANT BOOLCONSTANT +%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 + +%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 <lex> identifier +%type <interm> assignment_operator unary_operator +%type <interm.intermTypedNode> variable_identifier primary_expression postfix_expression +%type <interm.intermTypedNode> expression integer_expression assignment_expression +%type <interm.intermTypedNode> unary_expression multiplicative_expression additive_expression +%type <interm.intermTypedNode> relational_expression equality_expression +%type <interm.intermTypedNode> conditional_expression constant_expression +%type <interm.intermTypedNode> logical_or_expression logical_xor_expression logical_and_expression +%type <interm.intermTypedNode> shift_expression and_expression exclusive_or_expression inclusive_or_expression +%type <interm.intermTypedNode> function_call initializer condition conditionopt + +%type <interm.intermBlock> translation_unit +%type <interm.intermNode> function_definition statement simple_statement +%type <interm.intermBlock> statement_list compound_statement compound_statement_no_new_scope +%type <interm.intermNode> declaration_statement selection_statement expression_statement +%type <interm.intermNode> declaration external_declaration +%type <interm.intermNode> for_init_statement +%type <interm.nodePair> selection_rest_statement for_rest_statement +%type <interm.intermSwitch> switch_statement +%type <interm.intermCase> case_label +%type <interm.intermNode> iteration_statement jump_statement statement_no_new_scope statement_with_scope +%type <interm> single_declaration init_declarator_list + +%type <interm> parameter_declaration parameter_declarator parameter_type_specifier +%type <interm.layoutQualifier> layout_qualifier_id_list layout_qualifier_id + +%type <interm.type> fully_specified_type type_specifier + +%type <interm.precision> precision_qualifier +%type <interm.layoutQualifier> layout_qualifier +%type <interm.qualifier> interpolation_qualifier +%type <interm.qualifierWrapper> storage_qualifier single_type_qualifier invariant_qualifier +%type <interm.typeQualifierBuilder> type_qualifier + +%type <interm.typeSpecifierNonArray> type_specifier_nonarray struct_specifier +%type <interm.type> type_specifier_no_prec +%type <interm.field> struct_declarator +%type <interm.fieldList> 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 <lex> enter_struct + +%start translation_unit +%% + +identifier + : IDENTIFIER + | TYPE_NAME + +variable_identifier + : IDENTIFIER { + // The symbol table search was done in the lexical phase + $$ = context->parseVariableIdentifier(@1, $1.string, $1.symbol); + + // don't delete $1.string, it's used by error recovery, and the pool + // pop will reclaim the memory + } + ; + +primary_expression + : variable_identifier { + $$ = $1; + } + | INTCONSTANT { + TConstantUnion *unionArray = new TConstantUnion[1]; + unionArray->setIConst($1.i); + $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtInt, EbpUndefined, EvqConst), @1); + } + | UINTCONSTANT { + TConstantUnion *unionArray = new TConstantUnion[1]; + unionArray->setUConst($1.u); + $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtUInt, EbpUndefined, EvqConst), @1); + } + | FLOATCONSTANT { + TConstantUnion *unionArray = new TConstantUnion[1]; + unionArray->setFConst($1.f); + $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtFloat, EbpUndefined, EvqConst), @1); + } + | BOOLCONSTANT { + TConstantUnion *unionArray = new TConstantUnion[1]; + unionArray->setBConst($1.b); + $$ = context->intermediate.addConstantUnion(unionArray, TType(EbtBool, EbpUndefined, EvqConst), @1); + } + | LEFT_PAREN expression RIGHT_PAREN { + $$ = $2; + } + ; + +postfix_expression + : primary_expression { + $$ = $1; + } + | postfix_expression LEFT_BRACKET integer_expression RIGHT_BRACKET { + $$ = context->addIndexExpression($1, @2, $3); + } + | function_call { + $$ = $1; + } + | postfix_expression DOT FIELD_SELECTION { + $$ = context->addFieldSelectionExpression($1, @2, *$3.string, @3); + } + | postfix_expression INC_OP { + $$ = context->addUnaryMathLValue(EOpPostIncrement, $1, @2); + } + | postfix_expression DEC_OP { + $$ = context->addUnaryMathLValue(EOpPostDecrement, $1, @2); + } + ; + +integer_expression + : expression { + context->checkIsScalarInteger($1, "[]"); + $$ = $1; + } + ; + +function_call + : function_call_or_method { + bool fatalError = false; + $$ = context->addFunctionCallOrMethod($1.function, $1.nodePair.node1, $1.nodePair.node2, @1, &fatalError); + if (fatalError) + { + YYERROR; + } + } + ; + +function_call_or_method + : function_call_generic { + $$ = $1; + $$.nodePair.node2 = nullptr; + } + | postfix_expression DOT function_call_generic { + ES3_OR_NEWER("", @3, "methods"); + $$ = $3; + $$.nodePair.node2 = $1; + } + ; + +function_call_generic + : function_call_header_with_parameters RIGHT_PAREN { + $$ = $1; + } + | function_call_header_no_parameters RIGHT_PAREN { + $$ = $1; + } + ; + +function_call_header_no_parameters + : function_call_header VOID_TYPE { + $$.function = $1; + $$.nodePair.node1 = nullptr; + } + | function_call_header { + $$.function = $1; + $$.nodePair.node1 = nullptr; + } + ; + +function_call_header_with_parameters + : function_call_header assignment_expression { + const TType *type = new TType($2->getType()); + $1->addParameter(TConstParameter(type)); + $$.function = $1; + $$.nodePair.node1 = TIntermediate::MakeAggregate($2, @2); + } + | function_call_header_with_parameters COMMA assignment_expression { + const TType *type = new TType($3->getType()); + $1.function->addParameter(TConstParameter(type)); + $$.function = $1.function; + $$.nodePair.node1 = context->intermediate.growAggregate($1.intermNode, $3, @2); + } + ; + +function_call_header + : function_identifier LEFT_PAREN { + $$ = $1; + } + ; + +// Grammar Note: Constructors look like functions, but are recognized as types. + +function_identifier + : type_specifier_no_prec { + if ($1.array) { + ES3_OR_NEWER("[]", @1, "array constructor"); + } + $$ = context->addConstructorFunc($1); + } + | IDENTIFIER { + context->checkIsNotReserved(@1, *$1.string); + const TType *type = TCache::getType(EbtVoid, EbpUndefined); + TFunction *function = new TFunction($1.string, type); + $$ = function; + } + | FIELD_SELECTION { + context->checkIsNotReserved(@1, *$1.string); + const TType *type = TCache::getType(EbtVoid, EbpUndefined); + TFunction *function = new TFunction($1.string, type); + $$ = function; + } + ; + +unary_expression + : postfix_expression { + $$ = $1; + } + | INC_OP unary_expression { + $$ = context->addUnaryMathLValue(EOpPreIncrement, $2, @1); + } + | DEC_OP unary_expression { + $$ = context->addUnaryMathLValue(EOpPreDecrement, $2, @1); + } + | unary_operator unary_expression { + if ($1.op != EOpNull) { + $$ = context->addUnaryMath($1.op, $2, @1); + } else + $$ = $2; + } + ; +// Grammar Note: No traditional style type casts. + +unary_operator + : PLUS { $$.op = EOpPositive; } + | DASH { $$.op = EOpNegative; } + | BANG { $$.op = EOpLogicalNot; } + | TILDE { + ES3_OR_NEWER("~", @$, "bit-wise operator"); + $$.op = EOpBitwiseNot; + } + ; +// Grammar Note: No '*' or '&' unary ops. Pointers are not supported. + +multiplicative_expression + : unary_expression { $$ = $1; } + | multiplicative_expression STAR unary_expression { + $$ = context->addBinaryMath(EOpMul, $1, $3, @2); + } + | multiplicative_expression SLASH unary_expression { + $$ = context->addBinaryMath(EOpDiv, $1, $3, @2); + } + | multiplicative_expression PERCENT unary_expression { + ES3_OR_NEWER("%", @2, "integer modulus operator"); + $$ = context->addBinaryMath(EOpIMod, $1, $3, @2); + } + ; + +additive_expression + : multiplicative_expression { $$ = $1; } + | additive_expression PLUS multiplicative_expression { + $$ = context->addBinaryMath(EOpAdd, $1, $3, @2); + } + | additive_expression DASH multiplicative_expression { + $$ = context->addBinaryMath(EOpSub, $1, $3, @2); + } + ; + +shift_expression + : additive_expression { $$ = $1; } + | shift_expression LEFT_OP additive_expression { + ES3_OR_NEWER("<<", @2, "bit-wise operator"); + $$ = context->addBinaryMath(EOpBitShiftLeft, $1, $3, @2); + } + | shift_expression RIGHT_OP additive_expression { + ES3_OR_NEWER(">>", @2, "bit-wise operator"); + $$ = context->addBinaryMath(EOpBitShiftRight, $1, $3, @2); + } + ; + +relational_expression + : shift_expression { $$ = $1; } + | relational_expression LEFT_ANGLE shift_expression { + $$ = context->addBinaryMathBooleanResult(EOpLessThan, $1, $3, @2); + } + | relational_expression RIGHT_ANGLE shift_expression { + $$ = context->addBinaryMathBooleanResult(EOpGreaterThan, $1, $3, @2); + } + | relational_expression LE_OP shift_expression { + $$ = context->addBinaryMathBooleanResult(EOpLessThanEqual, $1, $3, @2); + } + | relational_expression GE_OP shift_expression { + $$ = context->addBinaryMathBooleanResult(EOpGreaterThanEqual, $1, $3, @2); + } + ; + +equality_expression + : relational_expression { $$ = $1; } + | equality_expression EQ_OP relational_expression { + $$ = context->addBinaryMathBooleanResult(EOpEqual, $1, $3, @2); + } + | equality_expression NE_OP relational_expression { + $$ = context->addBinaryMathBooleanResult(EOpNotEqual, $1, $3, @2); + } + ; + +and_expression + : equality_expression { $$ = $1; } + | and_expression AMPERSAND equality_expression { + ES3_OR_NEWER("&", @2, "bit-wise operator"); + $$ = context->addBinaryMath(EOpBitwiseAnd, $1, $3, @2); + } + ; + +exclusive_or_expression + : and_expression { $$ = $1; } + | exclusive_or_expression CARET and_expression { + ES3_OR_NEWER("^", @2, "bit-wise operator"); + $$ = context->addBinaryMath(EOpBitwiseXor, $1, $3, @2); + } + ; + +inclusive_or_expression + : exclusive_or_expression { $$ = $1; } + | inclusive_or_expression VERTICAL_BAR exclusive_or_expression { + ES3_OR_NEWER("|", @2, "bit-wise operator"); + $$ = context->addBinaryMath(EOpBitwiseOr, $1, $3, @2); + } + ; + +logical_and_expression + : inclusive_or_expression { $$ = $1; } + | logical_and_expression AND_OP inclusive_or_expression { + $$ = context->addBinaryMathBooleanResult(EOpLogicalAnd, $1, $3, @2); + } + ; + +logical_xor_expression + : logical_and_expression { $$ = $1; } + | logical_xor_expression XOR_OP logical_and_expression { + $$ = context->addBinaryMathBooleanResult(EOpLogicalXor, $1, $3, @2); + } + ; + +logical_or_expression + : logical_xor_expression { $$ = $1; } + | logical_or_expression OR_OP logical_xor_expression { + $$ = context->addBinaryMathBooleanResult(EOpLogicalOr, $1, $3, @2); + } + ; + +conditional_expression + : logical_or_expression { $$ = $1; } + | logical_or_expression QUESTION expression COLON assignment_expression { + $$ = context->addTernarySelection($1, $3, $5, @2); + } + ; + +assignment_expression + : conditional_expression { $$ = $1; } + | unary_expression assignment_operator assignment_expression { + context->checkCanBeLValue(@2, "assign", $1); + $$ = context->addAssign($2.op, $1, $3, @2); + } + ; + +assignment_operator + : EQUAL { $$.op = EOpAssign; } + | MUL_ASSIGN { $$.op = EOpMulAssign; } + | DIV_ASSIGN { $$.op = EOpDivAssign; } + | MOD_ASSIGN { + ES3_OR_NEWER("%=", @$, "integer modulus operator"); + $$.op = EOpIModAssign; + } + | ADD_ASSIGN { $$.op = EOpAddAssign; } + | SUB_ASSIGN { $$.op = EOpSubAssign; } + | LEFT_ASSIGN { + ES3_OR_NEWER("<<=", @$, "bit-wise operator"); + $$.op = EOpBitShiftLeftAssign; + } + | RIGHT_ASSIGN { + ES3_OR_NEWER(">>=", @$, "bit-wise operator"); + $$.op = EOpBitShiftRightAssign; + } + | AND_ASSIGN { + ES3_OR_NEWER("&=", @$, "bit-wise operator"); + $$.op = EOpBitwiseAndAssign; + } + | XOR_ASSIGN { + ES3_OR_NEWER("^=", @$, "bit-wise operator"); + $$.op = EOpBitwiseXorAssign; + } + | OR_ASSIGN { + ES3_OR_NEWER("|=", @$, "bit-wise operator"); + $$.op = EOpBitwiseOrAssign; + } + ; + +expression + : assignment_expression { + $$ = $1; + } + | expression COMMA assignment_expression { + $$ = context->addComma($1, $3, @2); + } + ; + +constant_expression + : conditional_expression { + context->checkIsConst($1); + $$ = $1; + } + ; + +enter_struct + : IDENTIFIER LEFT_BRACE { + context->enterStructDeclaration(@1, *$1.string); + $$ = $1; + } + ; + +declaration + : function_prototype SEMICOLON { + $$ = context->addFunctionPrototypeDeclaration(*($1.function), @1); + } + | init_declarator_list SEMICOLON { + $$ = $1.intermDeclaration; + } + | PRECISION precision_qualifier type_specifier_no_prec SEMICOLON { + if (($2 == EbpHigh) && (context->getShaderType() == GL_FRAGMENT_SHADER) && !context->getFragmentPrecisionHigh()) { + context->error(@1, "precision is not supported in fragment shader", "highp"); + } + if (!context->symbolTable.setDefaultPrecision( $3, $2 )) { + context->error(@1, "illegal type argument for default precision qualifier", getBasicString($3.getBasicType())); + } + $$ = 0; + } + | type_qualifier enter_struct struct_declaration_list RIGHT_BRACE SEMICOLON { + ES3_OR_NEWER($2.string->c_str(), @1, "interface blocks"); + $$ = context->addInterfaceBlock(*$1, @2, *$2.string, $3, NULL, @$, NULL, @$); + } + | type_qualifier enter_struct struct_declaration_list RIGHT_BRACE IDENTIFIER SEMICOLON { + ES3_OR_NEWER($2.string->c_str(), @1, "interface blocks"); + $$ = context->addInterfaceBlock(*$1, @2, *$2.string, $3, $5.string, @5, NULL, @$); + } + | type_qualifier enter_struct struct_declaration_list RIGHT_BRACE IDENTIFIER LEFT_BRACKET constant_expression RIGHT_BRACKET SEMICOLON { + ES3_OR_NEWER($2.string->c_str(), @1, "interface blocks"); + $$ = context->addInterfaceBlock(*$1, @2, *$2.string, $3, $5.string, @5, $7, @6); + } + | type_qualifier SEMICOLON { + context->parseGlobalLayoutQualifier(*$1); + $$ = 0; + } + | type_qualifier IDENTIFIER SEMICOLON // e.g. to qualify an existing variable as invariant + { + $$ = context->parseInvariantDeclaration(*$1, @2, $2.string, $2.symbol); + } + ; + +function_prototype + : function_declarator RIGHT_PAREN { + $$.function = context->parseFunctionDeclarator(@2, $1); + context->exitFunctionDeclaration(); + } + ; + +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.turnToConst()); + 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 + // + context->error(@2, "cannot be an argument type except for '(void)'", "void"); + delete $3.param.type; + } else { + // Add the parameter + $$ = $1; + $1->addParameter($3.param.turnToConst()); + } + } + ; + +function_header + : fully_specified_type IDENTIFIER LEFT_PAREN { + $$ = context->parseFunctionHeader($1, $2.string, @2); + + context->symbolTable.push(); + context->enterFunctionDeclaration(); + } + ; + +parameter_declarator + // Type + name + : type_specifier identifier { + if ($1.getBasicType() == EbtVoid) { + context->error(@2, "illegal use of type 'void'", $2.string->c_str()); + } + context->checkIsNotReserved(@2, *$2.string); + TParameter param = {$2.string, new TType($1)}; + $$.param = param; + } + | type_specifier identifier LEFT_BRACKET constant_expression RIGHT_BRACKET { + // Check that we can make an array out of this type + context->checkIsValidTypeForArray(@3, $1); + + context->checkIsNotReserved(@2, *$2.string); + + unsigned int size = context->checkIsValidArraySize(@3, $4); + + $1.setArraySize(size); + + TType* type = new TType($1); + TParameter param = { $2.string, type }; + $$.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_declarator { + $$ = $2; + context->checkIsParameterQualifierValid(@2, *$1, $2.param.type); + } + | parameter_declarator { + $$ = $1; + $$.param.type->setQualifier(EvqIn); + } + | type_qualifier parameter_type_specifier { + $$ = $2; + context->checkIsParameterQualifierValid(@2, *$1, $2.param.type); + } + | parameter_type_specifier { + $$ = $1; + $$.param.type->setQualifier(EvqIn); + } + ; + +parameter_type_specifier + : type_specifier { + TParameter param = { 0, new TType($1) }; + $$.param = param; + } + ; + +init_declarator_list + : single_declaration { + $$ = $1; + } + | init_declarator_list COMMA identifier { + $$ = $1; + context->parseDeclarator($$.type, @3, *$3.string, $$.intermDeclaration); + } + | init_declarator_list COMMA identifier LEFT_BRACKET constant_expression RIGHT_BRACKET { + $$ = $1; + context->parseArrayDeclarator($$.type, @3, *$3.string, @4, $5, $$.intermDeclaration); + } + | init_declarator_list COMMA identifier LEFT_BRACKET RIGHT_BRACKET EQUAL initializer { + ES3_OR_NEWER("[]", @3, "implicitly sized array"); + $$ = $1; + context->parseArrayInitDeclarator($$.type, @3, *$3.string, @4, nullptr, @6, $7, $$.intermDeclaration); + } + | init_declarator_list COMMA identifier LEFT_BRACKET constant_expression RIGHT_BRACKET EQUAL initializer { + ES3_OR_NEWER("=", @7, "first-class arrays (array initializer)"); + $$ = $1; + context->parseArrayInitDeclarator($$.type, @3, *$3.string, @4, $5, @7, $8, $$.intermDeclaration); + } + | init_declarator_list COMMA identifier EQUAL initializer { + $$ = $1; + context->parseInitDeclarator($$.type, @3, *$3.string, @4, $5, $$.intermDeclaration); + } + ; + +single_declaration + : fully_specified_type { + $$.type = $1; + $$.intermDeclaration = context->parseSingleDeclaration($$.type, @1, ""); + } + | fully_specified_type identifier { + $$.type = $1; + $$.intermDeclaration = context->parseSingleDeclaration($$.type, @2, *$2.string); + } + | fully_specified_type identifier LEFT_BRACKET constant_expression RIGHT_BRACKET { + $$.type = $1; + $$.intermDeclaration = context->parseSingleArrayDeclaration($$.type, @2, *$2.string, @3, $4); + } + | fully_specified_type identifier LEFT_BRACKET RIGHT_BRACKET EQUAL initializer { + ES3_OR_NEWER("[]", @3, "implicitly sized array"); + $$.type = $1; + $$.intermDeclaration = context->parseSingleArrayInitDeclaration($$.type, @2, *$2.string, @3, nullptr, @5, $6); + } + | fully_specified_type identifier LEFT_BRACKET constant_expression RIGHT_BRACKET EQUAL initializer { + ES3_OR_NEWER("=", @6, "first-class arrays (array initializer)"); + $$.type = $1; + $$.intermDeclaration = context->parseSingleArrayInitDeclaration($$.type, @2, *$2.string, @3, $4, @6, $7); + } + | fully_specified_type identifier EQUAL initializer { + $$.type = $1; + $$.intermDeclaration = context->parseSingleInitDeclaration($$.type, @2, *$2.string, @3, $4); + } + ; + +fully_specified_type + : type_specifier { + context->addFullySpecifiedType(&$1); + $$ = $1; + } + | type_qualifier type_specifier { + $$ = context->addFullySpecifiedType(*$1, $2); + } + ; + +interpolation_qualifier + : SMOOTH { + $$ = EvqSmooth; + } + | FLAT { + $$ = EvqFlat; + } + ; + +type_qualifier + : single_type_qualifier { + $$ = context->createTypeQualifierBuilder(@1); + $$->appendQualifier($1); + } + | type_qualifier single_type_qualifier { + $$ = $1; + $$->appendQualifier($2); + } + ; + +invariant_qualifier + : INVARIANT { + // empty + } + ; + +single_type_qualifier + : storage_qualifier { + context->checkLocalVariableConstStorageQualifier(*$1); + $$ = $1; + } + | layout_qualifier { + context->checkIsAtGlobalLevel(@1, "layout"); + $$ = new TLayoutQualifierWrapper($1, @1); + } + | precision_qualifier { + $$ = new TPrecisionQualifierWrapper($1, @1); + } + | interpolation_qualifier { + $$ = new TInterpolationQualifierWrapper($1, @1); + } + | invariant_qualifier { + context->checkIsAtGlobalLevel(@1, "invariant"); + $$ = new TInvariantQualifierWrapper(@1); + } + ; + + +storage_qualifier + : + ATTRIBUTE { + VERTEX_ONLY("attribute", @1); + ES2_ONLY("attribute", @1); + context->checkIsAtGlobalLevel(@1, "attribute"); + $$ = new TStorageQualifierWrapper(EvqAttribute, @1); + } + | VARYING { + ES2_ONLY("varying", @1); + context->checkIsAtGlobalLevel(@1, "varying"); + if (context->getShaderType() == GL_VERTEX_SHADER) + $$ = new TStorageQualifierWrapper(EvqVaryingOut, @1); + else + $$ = new TStorageQualifierWrapper(EvqVaryingIn, @1); + } + | CONST_QUAL { + $$ = new TStorageQualifierWrapper(EvqConst, @1); + } + | IN_QUAL { + if (context->declaringFunction()) + { + $$ = new TStorageQualifierWrapper(EvqIn, @1); + } + else if (context->getShaderType() == GL_FRAGMENT_SHADER) + { + ES3_OR_NEWER("in", @1, "storage qualifier"); + $$ = new TStorageQualifierWrapper(EvqFragmentIn, @1); + } + else if (context->getShaderType() == GL_VERTEX_SHADER) + { + ES3_OR_NEWER("in", @1, "storage qualifier"); + $$ = new TStorageQualifierWrapper(EvqVertexIn, @1); + } + else + { + $$ = new TStorageQualifierWrapper(EvqComputeIn, @1); + } + } + | OUT_QUAL { + if (context->declaringFunction()) + { + $$ = new TStorageQualifierWrapper(EvqOut, @1); + } + else + { + ES3_OR_NEWER("out", @1, "storage qualifier"); + NON_COMPUTE_ONLY("out", @1); + if (context->getShaderType() == GL_FRAGMENT_SHADER) + { + $$ = new TStorageQualifierWrapper(EvqFragmentOut, @1); + } + else + { + $$ = new TStorageQualifierWrapper(EvqVertexOut, @1); + } + } + } + | INOUT_QUAL { + if (!context->declaringFunction()) + { + context->error(@1, "invalid inout qualifier", "'inout' can be only used with function parameters"); + } + $$ = new TStorageQualifierWrapper(EvqInOut, @1); + } + | CENTROID { + ES3_OR_NEWER("centroid", @1, "storage qualifier"); + $$ = new TStorageQualifierWrapper(EvqCentroid, @1); + } + | UNIFORM { + context->checkIsAtGlobalLevel(@1, "uniform"); + $$ = new TStorageQualifierWrapper(EvqUniform, @1); + } + | READONLY { + $$ = new TMemoryQualifierWrapper(EvqReadOnly, @1); + } + | WRITEONLY { + $$ = new TMemoryQualifierWrapper(EvqWriteOnly, @1); + } + | COHERENT { + $$ = new TMemoryQualifierWrapper(EvqCoherent, @1); + } + | RESTRICT { + $$ = new TMemoryQualifierWrapper(EvqRestrict, @1); + } + | VOLATILE { + $$ = new TMemoryQualifierWrapper(EvqVolatile, @1); + } + ; + +type_specifier + : type_specifier_no_prec { + $$ = $1; + $$.precision = context->symbolTable.getDefaultPrecision($1.getBasicType()); + } + ; + +precision_qualifier + : HIGH_PRECISION { + $$ = EbpHigh; + } + | MEDIUM_PRECISION { + $$ = EbpMedium; + } + | LOW_PRECISION { + $$ = EbpLow; + } + ; + +layout_qualifier + : LAYOUT LEFT_PAREN layout_qualifier_id_list RIGHT_PAREN { + ES3_OR_NEWER("layout", @1, "qualifier"); + $$ = $3; + } + ; + +layout_qualifier_id_list + : layout_qualifier_id { + $$ = $1; + } + | layout_qualifier_id_list COMMA layout_qualifier_id { + $$ = context->joinLayoutQualifiers($1, $3, @3); + } + ; + +layout_qualifier_id + : IDENTIFIER { + $$ = context->parseLayoutQualifier(*$1.string, @1); + } + | IDENTIFIER EQUAL INTCONSTANT { + $$ = context->parseLayoutQualifier(*$1.string, @1, $3.i, @3); + } + | IDENTIFIER EQUAL UINTCONSTANT { + $$ = context->parseLayoutQualifier(*$1.string, @1, $3.i, @3); + } + ; + +type_specifier_no_prec + : type_specifier_nonarray { + $$.initialize($1, (context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary)); + } + | type_specifier_nonarray LEFT_BRACKET RIGHT_BRACKET { + ES3_OR_NEWER("[]", @2, "implicitly sized array"); + $$.initialize($1, (context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary)); + $$.setArraySize(0); + } + | type_specifier_nonarray LEFT_BRACKET constant_expression RIGHT_BRACKET { + $$.initialize($1, (context->symbolTable.atGlobalLevel() ? EvqGlobal : EvqTemporary)); + if (context->checkIsValidTypeForArray(@2, $$)) + { + unsigned int size = context->checkIsValidArraySize(@2, $3); + $$.setArraySize(size); + } + } + ; + +type_specifier_nonarray + : VOID_TYPE { + $$.initialize(EbtVoid, @1); + } + | FLOAT_TYPE { + $$.initialize(EbtFloat, @1); + } + | INT_TYPE { + $$.initialize(EbtInt, @1); + } + | UINT_TYPE { + $$.initialize(EbtUInt, @1); + } + | BOOL_TYPE { + $$.initialize(EbtBool, @1); + } + | VEC2 { + $$.initialize(EbtFloat, @1); + $$.setAggregate(2); + } + | VEC3 { + $$.initialize(EbtFloat, @1); + $$.setAggregate(3); + } + | VEC4 { + $$.initialize(EbtFloat, @1); + $$.setAggregate(4); + } + | BVEC2 { + $$.initialize(EbtBool, @1); + $$.setAggregate(2); + } + | BVEC3 { + $$.initialize(EbtBool, @1); + $$.setAggregate(3); + } + | BVEC4 { + $$.initialize(EbtBool, @1); + $$.setAggregate(4); + } + | IVEC2 { + $$.initialize(EbtInt, @1); + $$.setAggregate(2); + } + | IVEC3 { + $$.initialize(EbtInt, @1); + $$.setAggregate(3); + } + | IVEC4 { + $$.initialize(EbtInt, @1); + $$.setAggregate(4); + } + | UVEC2 { + $$.initialize(EbtUInt, @1); + $$.setAggregate(2); + } + | UVEC3 { + $$.initialize(EbtUInt, @1); + $$.setAggregate(3); + } + | UVEC4 { + $$.initialize(EbtUInt, @1); + $$.setAggregate(4); + } + | MATRIX2 { + $$.initialize(EbtFloat, @1); + $$.setMatrix(2, 2); + } + | MATRIX3 { + $$.initialize(EbtFloat, @1); + $$.setMatrix(3, 3); + } + | MATRIX4 { + $$.initialize(EbtFloat, @1); + $$.setMatrix(4, 4); + } + | MATRIX2x3 { + $$.initialize(EbtFloat, @1); + $$.setMatrix(2, 3); + } + | MATRIX3x2 { + $$.initialize(EbtFloat, @1); + $$.setMatrix(3, 2); + } + | MATRIX2x4 { + $$.initialize(EbtFloat, @1); + $$.setMatrix(2, 4); + } + | MATRIX4x2 { + $$.initialize(EbtFloat, @1); + $$.setMatrix(4, 2); + } + | MATRIX3x4 { + $$.initialize(EbtFloat, @1); + $$.setMatrix(3, 4); + } + | MATRIX4x3 { + $$.initialize(EbtFloat, @1); + $$.setMatrix(4, 3); + } + | SAMPLER2D { + $$.initialize(EbtSampler2D, @1); + } + | SAMPLER3D { + $$.initialize(EbtSampler3D, @1); + } + | SAMPLERCUBE { + $$.initialize(EbtSamplerCube, @1); + } + | SAMPLER2DARRAY { + $$.initialize(EbtSampler2DArray, @1); + } + | ISAMPLER2D { + $$.initialize(EbtISampler2D, @1); + } + | ISAMPLER3D { + $$.initialize(EbtISampler3D, @1); + } + | ISAMPLERCUBE { + $$.initialize(EbtISamplerCube, @1); + } + | ISAMPLER2DARRAY { + $$.initialize(EbtISampler2DArray, @1); + } + | USAMPLER2D { + $$.initialize(EbtUSampler2D, @1); + } + | USAMPLER3D { + $$.initialize(EbtUSampler3D, @1); + } + | USAMPLERCUBE { + $$.initialize(EbtUSamplerCube, @1); + } + | USAMPLER2DARRAY { + $$.initialize(EbtUSampler2DArray, @1); + } + | SAMPLER2DSHADOW { + $$.initialize(EbtSampler2DShadow, @1); + } + | SAMPLERCUBESHADOW { + $$.initialize(EbtSamplerCubeShadow, @1); + } + | SAMPLER2DARRAYSHADOW { + $$.initialize(EbtSampler2DArrayShadow, @1); + } + | SAMPLER_EXTERNAL_OES { + if (!context->supportsExtension("GL_OES_EGL_image_external") && + !context->supportsExtension("GL_NV_EGL_stream_consumer_external")) { + context->error(@1, "unsupported type", "samplerExternalOES"); + } + $$.initialize(EbtSamplerExternalOES, @1); + } + | SAMPLER2DRECT { + if (!context->supportsExtension("GL_ARB_texture_rectangle")) { + context->error(@1, "unsupported type", "sampler2DRect"); + } + $$.initialize(EbtSampler2DRect, @1); + } + | struct_specifier { + $$ = $1; + } + | IMAGE2D { + $$.initialize(EbtImage2D, @1); + } + | IIMAGE2D { + $$.initialize(EbtIImage2D, @1); + } + | UIMAGE2D { + $$.initialize(EbtUImage2D, @1); + } + | IMAGE3D { + $$.initialize(EbtImage3D, @1); + } + | IIMAGE3D { + $$.initialize(EbtIImage3D, @1); + } + | UIMAGE3D { + $$.initialize(EbtUImage3D, @1); + } + | IMAGE2DARRAY { + $$.initialize(EbtImage2DArray, @1); + } + | IIMAGE2DARRAY { + $$.initialize(EbtIImage2DArray, @1); + } + | UIMAGE2DARRAY { + $$.initialize(EbtUImage2DArray, @1); + } + | IMAGECUBE { + $$.initialize(EbtImageCube, @1); + } + | IIMAGECUBE { + $$.initialize(EbtIImageCube, @1); + } + | UIMAGECUBE { + $$.initialize(EbtUImageCube, @1); + } + | TYPE_NAME { + // + // This is for user defined type names. The lexical phase looked up the + // type. + // + TType& structure = static_cast<TVariable*>($1.symbol)->getType(); + $$.initialize(EbtStruct, @1); + $$.userDef = &structure; + } + ; + +struct_specifier + : STRUCT identifier LEFT_BRACE { context->enterStructDeclaration(@2, *$2.string); } struct_declaration_list RIGHT_BRACE { + $$ = context->addStructure(@1, @2, $2.string, $5); + } + | STRUCT LEFT_BRACE { context->enterStructDeclaration(@2, *$2.string); } struct_declaration_list RIGHT_BRACE { + $$ = context->addStructure(@1, @$, NewPoolTString(""), $4); + } + ; + +struct_declaration_list + : struct_declaration { + $$ = $1; + } + | struct_declaration_list struct_declaration { + $$ = $1; + for (size_t i = 0; i < $2->size(); ++i) { + TField* field = (*$2)[i]; + for (size_t j = 0; j < $$->size(); ++j) { + if ((*$$)[j]->name() == field->name()) { + context->error(@2, "duplicate field name in structure:", "struct", field->name().c_str()); + } + } + $$->push_back(field); + } + } + ; + +struct_declaration + : type_specifier struct_declarator_list SEMICOLON { + $$ = context->addStructDeclaratorList($1, $2); + } + | type_qualifier type_specifier struct_declarator_list SEMICOLON { + // ES3 Only, but errors should be handled elsewhere + $$ = context->addStructDeclaratorListWithQualifiers(*$1, &$2, $3); + } + ; + +struct_declarator_list + : struct_declarator { + $$ = NewPoolTFieldList(); + $$->push_back($1); + } + | struct_declarator_list COMMA struct_declarator { + $$->push_back($3); + } + ; + +struct_declarator + : identifier { + context->checkIsNotReserved(@1, *$1.string); + + TType* type = new TType(EbtVoid, EbpUndefined); + $$ = new TField(type, $1.string, @1); + } + | identifier LEFT_BRACKET constant_expression RIGHT_BRACKET { + context->checkIsNotReserved(@1, *$1.string); + + TType* type = new TType(EbtVoid, EbpUndefined); + unsigned int size = context->checkIsValidArraySize(@3, $3); + type->setArraySize(size); + + $$ = new TField(type, $1.string, @1); + } + ; + +initializer + : assignment_expression { $$ = $1; } + ; + +declaration_statement + : declaration { $$ = $1; } + ; + +statement + : compound_statement { $$ = $1; } + | simple_statement { $$ = $1; } + ; + +// Grammar Note: Labeled statements for SWITCH only; 'goto' is not supported. + +simple_statement + : declaration_statement { $$ = $1; } + | expression_statement { $$ = $1; } + | selection_statement { $$ = $1; } + | switch_statement { $$ = $1; } + | case_label { $$ = $1; } + | iteration_statement { $$ = $1; } + | jump_statement { $$ = $1; } + ; + +compound_statement + : LEFT_BRACE RIGHT_BRACE { $$ = 0; } + | LEFT_BRACE { context->symbolTable.push(); } statement_list { context->symbolTable.pop(); } RIGHT_BRACE { + if ($3 != 0) { + $3->setLine(@$); + } + $$ = $3; + } + ; + +statement_no_new_scope + : compound_statement_no_new_scope { $$ = $1; } + | simple_statement { $$ = $1; } + ; + +statement_with_scope + : { context->symbolTable.push(); } compound_statement_no_new_scope { context->symbolTable.pop(); $$ = $2; } + | { context->symbolTable.push(); } simple_statement { context->symbolTable.pop(); $$ = $2; } + ; + +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->setLine(@$); + } + $$ = $2; + } + ; + +statement_list + : statement { + $$ = new TIntermBlock(); + $$->setLine(@$); + $$->appendStatement($1); + } + | statement_list statement { + $$ = $1; + $$->appendStatement($2); + } + ; + +expression_statement + : SEMICOLON { $$ = 0; } + | expression SEMICOLON { $$ = static_cast<TIntermNode*>($1); } + ; + +selection_statement + : IF LEFT_PAREN expression RIGHT_PAREN selection_rest_statement { + context->checkIsScalarBool(@1, $3); + $$ = context->intermediate.addIfElse($3, $5, @1); + } + ; + +selection_rest_statement + : statement_with_scope ELSE statement_with_scope { + $$.node1 = $1; + $$.node2 = $3; + } + | statement_with_scope { + $$.node1 = $1; + $$.node2 = 0; + } + ; + +switch_statement + : SWITCH LEFT_PAREN expression RIGHT_PAREN { context->incrSwitchNestingLevel(); } compound_statement { + $$ = context->addSwitch($3, $6, @1); + context->decrSwitchNestingLevel(); + } + ; + +case_label + : CASE constant_expression COLON { + $$ = context->addCase($2, @1); + } + | DEFAULT COLON { + $$ = context->addDefault(@1); + } + ; + +condition + // In 1996 c++ draft, conditions can include single declarations + : expression { + $$ = $1; + context->checkIsScalarBool($1->getLine(), $1); + } + | fully_specified_type identifier EQUAL initializer { + TIntermBinary *initNode = nullptr; + context->checkIsScalarBool(@2, $1); + + if (!context->executeInitializer(@2, *$2.string, $1, $4, &initNode)) + $$ = $4; + else { + $$ = 0; + } + } + ; + +iteration_statement + : WHILE LEFT_PAREN { context->symbolTable.push(); context->incrLoopNestingLevel(); } condition RIGHT_PAREN statement_no_new_scope { + context->symbolTable.pop(); + $$ = context->intermediate.addLoop(ELoopWhile, 0, $4, 0, $6, @1); + context->decrLoopNestingLevel(); + } + | DO { context->incrLoopNestingLevel(); } statement_with_scope WHILE LEFT_PAREN expression RIGHT_PAREN SEMICOLON { + context->checkIsScalarBool(@8, $6); + + $$ = context->intermediate.addLoop(ELoopDoWhile, 0, $6, 0, $3, @4); + context->decrLoopNestingLevel(); + } + | FOR LEFT_PAREN { context->symbolTable.push(); context->incrLoopNestingLevel(); } for_init_statement for_rest_statement RIGHT_PAREN statement_no_new_scope { + context->symbolTable.pop(); + $$ = context->intermediate.addLoop(ELoopFor, $4, reinterpret_cast<TIntermTyped*>($5.node1), reinterpret_cast<TIntermTyped*>($5.node2), $7, @1); + context->decrLoopNestingLevel(); + } + ; + +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 { + $$ = context->addBranch(EOpContinue, @1); + } + | BREAK SEMICOLON { + $$ = context->addBranch(EOpBreak, @1); + } + | RETURN SEMICOLON { + $$ = context->addBranch(EOpReturn, @1); + } + | RETURN expression SEMICOLON { + $$ = context->addBranch(EOpReturn, $2, @1); + } + | DISCARD SEMICOLON { + FRAG_ONLY("discard", @1); + $$ = context->addBranch(EOpKill, @1); + } + ; + +// Grammar Note: No 'goto'. Gotos are not supported. + +translation_unit + : external_declaration { + $$ = new TIntermBlock(); + $$->setLine(@$); + $$->appendStatement($1); + context->setTreeRoot($$); + } + | translation_unit external_declaration { + $$->appendStatement($2); + } + ; + +external_declaration + : function_definition { + $$ = $1; + } + | declaration { + $$ = $1; + } + ; + +function_definition + : function_prototype { + context->parseFunctionDefinitionHeader(@1, &($1.function), &$1.intermAggregate); + } + compound_statement_no_new_scope { + $$ = context->addFunctionDefinition(*($1.function), $1.intermAggregate, $3, @1); + } + ; + +%% + +int glslang_parse(TParseContext* context) { + return yyparse(context, context->getScanner()); +} |