diff options
Diffstat (limited to 'chromium/third_party/glslang/src/hlsl/hlslGrammar.cpp')
-rwxr-xr-x | chromium/third_party/glslang/src/hlsl/hlslGrammar.cpp | 1684 |
1 files changed, 1684 insertions, 0 deletions
diff --git a/chromium/third_party/glslang/src/hlsl/hlslGrammar.cpp b/chromium/third_party/glslang/src/hlsl/hlslGrammar.cpp new file mode 100755 index 00000000000..c0d4f285734 --- /dev/null +++ b/chromium/third_party/glslang/src/hlsl/hlslGrammar.cpp @@ -0,0 +1,1684 @@ +// +//Copyright (C) 2016 Google, Inc. +// +//All rights reserved. +// +//Redistribution and use in source and binary forms, with or without +//modification, are permitted provided that the following conditions +//are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of Google, Inc., nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +//"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +//LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +//FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +//COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +//INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +//BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +//CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +//ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +//POSSIBILITY OF SUCH DAMAGE. +// + +// +// This is a set of mutually recursive methods implementing the HLSL grammar. +// Generally, each returns +// - through an argument: a type specifically appropriate to which rule it +// recognized +// - through the return value: true/false to indicate whether or not it +// recognized its rule +// +// As much as possible, only grammar recognition should happen in this file, +// with all other work being farmed out to hlslParseHelper.cpp, which in turn +// will build the AST. +// +// The next token, yet to be "accepted" is always sitting in 'token'. +// When a method says it accepts a rule, that means all tokens involved +// in the rule will have been consumed, and none left in 'token'. +// + +#include "hlslTokens.h" +#include "hlslGrammar.h" + +namespace glslang { + +// Root entry point to this recursive decent parser. +// Return true if compilation unit was successfully accepted. +bool HlslGrammar::parse() +{ + advanceToken(); + return acceptCompilationUnit(); +} + +void HlslGrammar::expected(const char* syntax) +{ + parseContext.error(token.loc, "Expected", syntax, ""); +} + +// Only process the next token if it is an identifier. +// Return true if it was an identifier. +bool HlslGrammar::acceptIdentifier(HlslToken& idToken) +{ + if (peekTokenClass(EHTokIdentifier)) { + idToken = token; + advanceToken(); + return true; + } + + return false; +} + +// compilationUnit +// : list of externalDeclaration +// +bool HlslGrammar::acceptCompilationUnit() +{ + TIntermNode* unitNode = nullptr; + + while (! peekTokenClass(EHTokNone)) { + // externalDeclaration + TIntermNode* declarationNode; + if (! acceptDeclaration(declarationNode)) + return false; + + // hook it up + unitNode = intermediate.growAggregate(unitNode, declarationNode); + } + + // set root of AST + intermediate.setTreeRoot(unitNode); + + return true; +} + +// declaration +// : SEMICOLON +// : fully_specified_type SEMICOLON +// | fully_specified_type identifier post_decls SEMICOLON +// | fully_specified_type identifier post_decls = expression SEMICOLON +// | fully_specified_type identifier function_parameters post_decls SEMICOLON // function prototype +// | fully_specified_type identifier function_parameters post_decls compound_statement // function definition +// +// 'node' could get created if the declaration creates code, like an initializer +// or a function body. +// +bool HlslGrammar::acceptDeclaration(TIntermNode*& node) +{ + node = nullptr; + + // fully_specified_type + TType type; + if (! acceptFullySpecifiedType(type)) + return false; + + // identifier + HlslToken idToken; + if (acceptIdentifier(idToken)) { + acceptPostDecls(type); + // = expression + TIntermTyped* expressionNode = nullptr; + if (acceptTokenClass(EHTokAssign)) { + if (! acceptExpression(expressionNode)) { + expected("initializer"); + return false; + } + } + + // SEMICOLON + if (acceptTokenClass(EHTokSemicolon)) { + node = parseContext.declareVariable(idToken.loc, *idToken.string, type, 0, expressionNode); + return true; + } + + // function_parameters + TFunction* function = new TFunction(idToken.string, type); + if (acceptFunctionParameters(*function)) { + // post_decls + acceptPostDecls(type); + + // compound_statement + if (peekTokenClass(EHTokLeftBrace)) + return acceptFunctionDefinition(*function, node); + + // SEMICOLON + if (acceptTokenClass(EHTokSemicolon)) + return true; + + return false; + } + } + + // SEMICOLON + if (acceptTokenClass(EHTokSemicolon)) + return true; + + return true; +} + +// fully_specified_type +// : type_specifier +// | type_qualifier type_specifier +// +bool HlslGrammar::acceptFullySpecifiedType(TType& type) +{ + // type_qualifier + TQualifier qualifier; + qualifier.clear(); + acceptQualifier(qualifier); + + // type_specifier + if (! acceptType(type)) + return false; + type.getQualifier() = qualifier; + + return true; +} + +// type_qualifier +// : qualifier qualifier ... +// +// Zero or more of these, so this can't return false. +// +void HlslGrammar::acceptQualifier(TQualifier& qualifier) +{ + do { + switch (peek()) { + case EHTokStatic: + // normal glslang default + break; + case EHTokExtern: + // TODO: no meaning in glslang? + break; + case EHTokShared: + // TODO: hint + break; + case EHTokGroupShared: + qualifier.storage = EvqShared; + break; + case EHTokUniform: + qualifier.storage = EvqUniform; + break; + case EHTokConst: + qualifier.storage = EvqConst; + break; + case EHTokVolatile: + qualifier.volatil = true; + break; + case EHTokLinear: + qualifier.storage = EvqVaryingIn; + qualifier.smooth = true; + break; + case EHTokCentroid: + qualifier.centroid = true; + break; + case EHTokNointerpolation: + qualifier.flat = true; + break; + case EHTokNoperspective: + qualifier.nopersp = true; + break; + case EHTokSample: + qualifier.sample = true; + break; + case EHTokRowMajor: + qualifier.layoutMatrix = ElmRowMajor; + break; + case EHTokColumnMajor: + qualifier.layoutMatrix = ElmColumnMajor; + break; + case EHTokPrecise: + qualifier.noContraction = true; + break; + default: + return; + } + advanceToken(); + } while (true); +} + +// If token is for a type, update 'type' with the type information, +// and return true and advance. +// Otherwise, return false, and don't advance +bool HlslGrammar::acceptType(TType& type) +{ + switch (peek()) { + case EHTokStruct: + return acceptStruct(type); + break; + + case EHTokIdentifier: + // An identifier could be for a user-defined type. + // Note we cache the symbol table lookup, to save for a later rule + // when this is not a type. + token.symbol = parseContext.symbolTable.find(*token.string); + if (token.symbol && token.symbol->getAsVariable() && token.symbol->getAsVariable()->isUserType()) { + type.shallowCopy(token.symbol->getType()); + advanceToken(); + return true; + } else + return false; + + case EHTokVoid: + new(&type) TType(EbtVoid); + break; + + case EHTokFloat: + new(&type) TType(EbtFloat); + break; + case EHTokFloat1: + new(&type) TType(EbtFloat); + type.makeVector(); + break; + case EHTokFloat2: + new(&type) TType(EbtFloat, EvqTemporary, 2); + break; + case EHTokFloat3: + new(&type) TType(EbtFloat, EvqTemporary, 3); + break; + case EHTokFloat4: + new(&type) TType(EbtFloat, EvqTemporary, 4); + break; + + case EHTokDouble: + new(&type) TType(EbtDouble); + break; + case EHTokDouble1: + new(&type) TType(EbtDouble); + type.makeVector(); + break; + case EHTokDouble2: + new(&type) TType(EbtDouble, EvqTemporary, 2); + break; + case EHTokDouble3: + new(&type) TType(EbtDouble, EvqTemporary, 3); + break; + case EHTokDouble4: + new(&type) TType(EbtDouble, EvqTemporary, 4); + break; + + case EHTokInt: + case EHTokDword: + new(&type) TType(EbtInt); + break; + case EHTokInt1: + new(&type) TType(EbtInt); + type.makeVector(); + break; + case EHTokInt2: + new(&type) TType(EbtInt, EvqTemporary, 2); + break; + case EHTokInt3: + new(&type) TType(EbtInt, EvqTemporary, 3); + break; + case EHTokInt4: + new(&type) TType(EbtInt, EvqTemporary, 4); + break; + + case EHTokUint: + new(&type) TType(EbtUint); + break; + case EHTokUint1: + new(&type) TType(EbtUint); + type.makeVector(); + break; + case EHTokUint2: + new(&type) TType(EbtUint, EvqTemporary, 2); + break; + case EHTokUint3: + new(&type) TType(EbtUint, EvqTemporary, 3); + break; + case EHTokUint4: + new(&type) TType(EbtUint, EvqTemporary, 4); + break; + + case EHTokBool: + new(&type) TType(EbtBool); + break; + case EHTokBool1: + new(&type) TType(EbtBool); + type.makeVector(); + break; + case EHTokBool2: + new(&type) TType(EbtBool, EvqTemporary, 2); + break; + case EHTokBool3: + new(&type) TType(EbtBool, EvqTemporary, 3); + break; + case EHTokBool4: + new(&type) TType(EbtBool, EvqTemporary, 4); + break; + + case EHTokInt1x1: + new(&type) TType(EbtInt, EvqTemporary, 0, 1, 1); + break; + case EHTokInt1x2: + new(&type) TType(EbtInt, EvqTemporary, 0, 2, 1); + break; + case EHTokInt1x3: + new(&type) TType(EbtInt, EvqTemporary, 0, 3, 1); + break; + case EHTokInt1x4: + new(&type) TType(EbtInt, EvqTemporary, 0, 4, 1); + break; + case EHTokInt2x1: + new(&type) TType(EbtInt, EvqTemporary, 0, 1, 2); + break; + case EHTokInt2x2: + new(&type) TType(EbtInt, EvqTemporary, 0, 2, 2); + break; + case EHTokInt2x3: + new(&type) TType(EbtInt, EvqTemporary, 0, 3, 2); + break; + case EHTokInt2x4: + new(&type) TType(EbtInt, EvqTemporary, 0, 4, 2); + break; + case EHTokInt3x1: + new(&type) TType(EbtInt, EvqTemporary, 0, 1, 3); + break; + case EHTokInt3x2: + new(&type) TType(EbtInt, EvqTemporary, 0, 2, 3); + break; + case EHTokInt3x3: + new(&type) TType(EbtInt, EvqTemporary, 0, 3, 3); + break; + case EHTokInt3x4: + new(&type) TType(EbtInt, EvqTemporary, 0, 4, 3); + break; + case EHTokInt4x1: + new(&type) TType(EbtInt, EvqTemporary, 0, 1, 4); + break; + case EHTokInt4x2: + new(&type) TType(EbtInt, EvqTemporary, 0, 2, 4); + break; + case EHTokInt4x3: + new(&type) TType(EbtInt, EvqTemporary, 0, 3, 4); + break; + case EHTokInt4x4: + new(&type) TType(EbtInt, EvqTemporary, 0, 4, 4); + break; + + case EHTokUint1x1: + new(&type) TType(EbtUint, EvqTemporary, 0, 1, 1); + break; + case EHTokUint1x2: + new(&type) TType(EbtUint, EvqTemporary, 0, 2, 1); + break; + case EHTokUint1x3: + new(&type) TType(EbtUint, EvqTemporary, 0, 3, 1); + break; + case EHTokUint1x4: + new(&type) TType(EbtUint, EvqTemporary, 0, 4, 1); + break; + case EHTokUint2x1: + new(&type) TType(EbtUint, EvqTemporary, 0, 1, 2); + break; + case EHTokUint2x2: + new(&type) TType(EbtUint, EvqTemporary, 0, 2, 2); + break; + case EHTokUint2x3: + new(&type) TType(EbtUint, EvqTemporary, 0, 3, 2); + break; + case EHTokUint2x4: + new(&type) TType(EbtUint, EvqTemporary, 0, 4, 2); + break; + case EHTokUint3x1: + new(&type) TType(EbtUint, EvqTemporary, 0, 1, 3); + break; + case EHTokUint3x2: + new(&type) TType(EbtUint, EvqTemporary, 0, 2, 3); + break; + case EHTokUint3x3: + new(&type) TType(EbtUint, EvqTemporary, 0, 3, 3); + break; + case EHTokUint3x4: + new(&type) TType(EbtUint, EvqTemporary, 0, 4, 3); + break; + case EHTokUint4x1: + new(&type) TType(EbtUint, EvqTemporary, 0, 1, 4); + break; + case EHTokUint4x2: + new(&type) TType(EbtUint, EvqTemporary, 0, 2, 4); + break; + case EHTokUint4x3: + new(&type) TType(EbtUint, EvqTemporary, 0, 3, 4); + break; + case EHTokUint4x4: + new(&type) TType(EbtUint, EvqTemporary, 0, 4, 4); + break; + + case EHTokBool1x1: + new(&type) TType(EbtBool, EvqTemporary, 0, 1, 1); + break; + case EHTokBool1x2: + new(&type) TType(EbtBool, EvqTemporary, 0, 2, 1); + break; + case EHTokBool1x3: + new(&type) TType(EbtBool, EvqTemporary, 0, 3, 1); + break; + case EHTokBool1x4: + new(&type) TType(EbtBool, EvqTemporary, 0, 4, 1); + break; + case EHTokBool2x1: + new(&type) TType(EbtBool, EvqTemporary, 0, 1, 2); + break; + case EHTokBool2x2: + new(&type) TType(EbtBool, EvqTemporary, 0, 2, 2); + break; + case EHTokBool2x3: + new(&type) TType(EbtBool, EvqTemporary, 0, 3, 2); + break; + case EHTokBool2x4: + new(&type) TType(EbtBool, EvqTemporary, 0, 4, 2); + break; + case EHTokBool3x1: + new(&type) TType(EbtBool, EvqTemporary, 0, 1, 3); + break; + case EHTokBool3x2: + new(&type) TType(EbtBool, EvqTemporary, 0, 2, 3); + break; + case EHTokBool3x3: + new(&type) TType(EbtBool, EvqTemporary, 0, 3, 3); + break; + case EHTokBool3x4: + new(&type) TType(EbtBool, EvqTemporary, 0, 4, 3); + break; + case EHTokBool4x1: + new(&type) TType(EbtBool, EvqTemporary, 0, 1, 4); + break; + case EHTokBool4x2: + new(&type) TType(EbtBool, EvqTemporary, 0, 2, 4); + break; + case EHTokBool4x3: + new(&type) TType(EbtBool, EvqTemporary, 0, 3, 4); + break; + case EHTokBool4x4: + new(&type) TType(EbtBool, EvqTemporary, 0, 4, 4); + break; + + case EHTokFloat1x1: + new(&type) TType(EbtFloat, EvqTemporary, 0, 1, 1); + break; + case EHTokFloat1x2: + new(&type) TType(EbtFloat, EvqTemporary, 0, 2, 1); + break; + case EHTokFloat1x3: + new(&type) TType(EbtFloat, EvqTemporary, 0, 3, 1); + break; + case EHTokFloat1x4: + new(&type) TType(EbtFloat, EvqTemporary, 0, 4, 1); + break; + case EHTokFloat2x1: + new(&type) TType(EbtFloat, EvqTemporary, 0, 1, 2); + break; + case EHTokFloat2x2: + new(&type) TType(EbtFloat, EvqTemporary, 0, 2, 2); + break; + case EHTokFloat2x3: + new(&type) TType(EbtFloat, EvqTemporary, 0, 3, 2); + break; + case EHTokFloat2x4: + new(&type) TType(EbtFloat, EvqTemporary, 0, 4, 2); + break; + case EHTokFloat3x1: + new(&type) TType(EbtFloat, EvqTemporary, 0, 1, 3); + break; + case EHTokFloat3x2: + new(&type) TType(EbtFloat, EvqTemporary, 0, 2, 3); + break; + case EHTokFloat3x3: + new(&type) TType(EbtFloat, EvqTemporary, 0, 3, 3); + break; + case EHTokFloat3x4: + new(&type) TType(EbtFloat, EvqTemporary, 0, 4, 3); + break; + case EHTokFloat4x1: + new(&type) TType(EbtFloat, EvqTemporary, 0, 1, 4); + break; + case EHTokFloat4x2: + new(&type) TType(EbtFloat, EvqTemporary, 0, 2, 4); + break; + case EHTokFloat4x3: + new(&type) TType(EbtFloat, EvqTemporary, 0, 3, 4); + break; + case EHTokFloat4x4: + new(&type) TType(EbtFloat, EvqTemporary, 0, 4, 4); + break; + + case EHTokDouble1x1: + new(&type) TType(EbtDouble, EvqTemporary, 0, 1, 1); + break; + case EHTokDouble1x2: + new(&type) TType(EbtDouble, EvqTemporary, 0, 2, 1); + break; + case EHTokDouble1x3: + new(&type) TType(EbtDouble, EvqTemporary, 0, 3, 1); + break; + case EHTokDouble1x4: + new(&type) TType(EbtDouble, EvqTemporary, 0, 4, 1); + break; + case EHTokDouble2x1: + new(&type) TType(EbtDouble, EvqTemporary, 0, 1, 2); + break; + case EHTokDouble2x2: + new(&type) TType(EbtDouble, EvqTemporary, 0, 2, 2); + break; + case EHTokDouble2x3: + new(&type) TType(EbtDouble, EvqTemporary, 0, 3, 2); + break; + case EHTokDouble2x4: + new(&type) TType(EbtDouble, EvqTemporary, 0, 4, 2); + break; + case EHTokDouble3x1: + new(&type) TType(EbtDouble, EvqTemporary, 0, 1, 3); + break; + case EHTokDouble3x2: + new(&type) TType(EbtDouble, EvqTemporary, 0, 2, 3); + break; + case EHTokDouble3x3: + new(&type) TType(EbtDouble, EvqTemporary, 0, 3, 3); + break; + case EHTokDouble3x4: + new(&type) TType(EbtDouble, EvqTemporary, 0, 4, 3); + break; + case EHTokDouble4x1: + new(&type) TType(EbtDouble, EvqTemporary, 0, 1, 4); + break; + case EHTokDouble4x2: + new(&type) TType(EbtDouble, EvqTemporary, 0, 2, 4); + break; + case EHTokDouble4x3: + new(&type) TType(EbtDouble, EvqTemporary, 0, 3, 4); + break; + case EHTokDouble4x4: + new(&type) TType(EbtDouble, EvqTemporary, 0, 4, 4); + break; + + default: + return false; + } + + advanceToken(); + + return true; +} + +// struct +// : STRUCT IDENTIFIER LEFT_BRACE struct_declaration_list RIGHT_BRACE +// | STRUCT LEFT_BRACE struct_declaration_list RIGHT_BRACE +// +bool HlslGrammar::acceptStruct(TType& type) +{ + // STRUCT + if (! acceptTokenClass(EHTokStruct)) + return false; + + // IDENTIFIER + TString structName = ""; + if (peekTokenClass(EHTokIdentifier)) { + structName = *token.string; + advanceToken(); + } + + // LEFT_BRACE + if (! acceptTokenClass(EHTokLeftBrace)) { + expected("{"); + return false; + } + + // struct_declaration_list + TTypeList* typeList; + if (! acceptStructDeclarationList(typeList)) { + expected("struct member declarations"); + return false; + } + + // RIGHT_BRACE + if (! acceptTokenClass(EHTokRightBrace)) { + expected("}"); + return false; + } + + // create the user-defined type + new(&type) TType(typeList, structName); + + // If it was named, which means it can be reused later, add + // it to the symbol table. + if (structName.size() > 0) { + TVariable* userTypeDef = new TVariable(&structName, type, true); + if (! parseContext.symbolTable.insert(*userTypeDef)) + parseContext.error(token.loc, "redefinition", structName.c_str(), "struct"); + } + + return true; +} + +// struct_declaration_list +// : struct_declaration SEMI_COLON struct_declaration SEMI_COLON ... +// +// struct_declaration +// : fully_specified_type struct_declarator COMMA struct_declarator ... +// +// struct_declarator +// : IDENTIFIER post_decls +// | IDENTIFIER array_specifier post_decls +// +bool HlslGrammar::acceptStructDeclarationList(TTypeList*& typeList) +{ + typeList = new TTypeList(); + + do { + // success on seeing the RIGHT_BRACE coming up + if (peekTokenClass(EHTokRightBrace)) + return true; + + // struct_declaration + + // fully_specified_type + TType memberType; + if (! acceptFullySpecifiedType(memberType)) { + expected("member type"); + return false; + } + + // struct_declarator COMMA struct_declarator ... + do { + // peek IDENTIFIER + if (! peekTokenClass(EHTokIdentifier)) { + expected("member name"); + return false; + } + + // add it to the list of members + TTypeLoc member = { new TType(EbtVoid), token.loc }; + member.type->shallowCopy(memberType); + member.type->setFieldName(*token.string); + typeList->push_back(member); + + // accept IDENTIFIER + advanceToken(); + + // array_specifier + // TODO + + acceptPostDecls(*member.type); + + // success on seeing the SEMICOLON coming up + if (peekTokenClass(EHTokSemicolon)) + break; + + // COMMA + if (! acceptTokenClass(EHTokComma)) { + expected(","); + return false; + } + + } while (true); + + // SEMI_COLON + if (! acceptTokenClass(EHTokSemicolon)) { + expected(";"); + return false; + } + + } while (true); +} + +// function_parameters +// : LEFT_PAREN parameter_declaration COMMA parameter_declaration ... RIGHT_PAREN +// | LEFT_PAREN VOID RIGHT_PAREN +// +bool HlslGrammar::acceptFunctionParameters(TFunction& function) +{ + // LEFT_PAREN + if (! acceptTokenClass(EHTokLeftParen)) + return false; + + // VOID RIGHT_PAREN + if (! acceptTokenClass(EHTokVoid)) { + do { + // parameter_declaration + if (! acceptParameterDeclaration(function)) + break; + + // COMMA + if (! acceptTokenClass(EHTokComma)) + break; + } while (true); + } + + // RIGHT_PAREN + if (! acceptTokenClass(EHTokRightParen)) { + expected(")"); + return false; + } + + return true; +} + +// parameter_declaration +// : fully_specified_type +// | fully_specified_type identifier +// +bool HlslGrammar::acceptParameterDeclaration(TFunction& function) +{ + // fully_specified_type + TType* type = new TType; + if (! acceptFullySpecifiedType(*type)) + return false; + + // identifier + HlslToken idToken; + acceptIdentifier(idToken); + + TParameter param = { idToken.string, type }; + function.addParameter(param); + + return true; +} + +// Do the work to create the function definition in addition to +// parsing the body (compound_statement). +bool HlslGrammar::acceptFunctionDefinition(TFunction& function, TIntermNode*& node) +{ + TFunction* functionDeclarator = parseContext.handleFunctionDeclarator(token.loc, function, false /* not prototype */); + + // This does a pushScope() + node = parseContext.handleFunctionDefinition(token.loc, *functionDeclarator); + + // compound_statement + TIntermNode* functionBody = nullptr; + if (acceptCompoundStatement(functionBody)) { + node = intermediate.growAggregate(node, functionBody); + intermediate.setAggregateOperator(node, EOpFunction, functionDeclarator->getType(), token.loc); + node->getAsAggregate()->setName(functionDeclarator->getMangledName().c_str()); + parseContext.popScope(); + + return true; + } + + return false; +} + +// Accept an expression with parenthesis around it, where +// the parenthesis ARE NOT expression parenthesis, but the +// syntactically required ones like in "if ( expression )" +// +// Note this one is not set up to be speculative; as it gives +// errors if not found. +// +bool HlslGrammar::acceptParenExpression(TIntermTyped*& expression) +{ + // LEFT_PAREN + if (! acceptTokenClass(EHTokLeftParen)) + expected("("); + + if (! acceptExpression(expression)) { + expected("expression"); + return false; + } + + // RIGHT_PAREN + if (! acceptTokenClass(EHTokRightParen)) + expected(")"); + + return true; +} + +// The top-level full expression recognizer. +// +// expression +// : assignment_expression COMMA assignment_expression COMMA assignment_expression ... +// +bool HlslGrammar::acceptExpression(TIntermTyped*& node) +{ + node = nullptr; + + // assignment_expression + if (! acceptAssignmentExpression(node)) + return false; + + if (! peekTokenClass(EHTokComma)) + return true; + + do { + // ... COMMA + TSourceLoc loc = token.loc; + advanceToken(); + + // ... assignment_expression + TIntermTyped* rightNode = nullptr; + if (! acceptAssignmentExpression(rightNode)) { + expected("assignment expression"); + return false; + } + + node = intermediate.addComma(node, rightNode, loc); + + if (! peekTokenClass(EHTokComma)) + return true; + } while (true); +} + +// Accept an assignment expression, where assignment operations +// associate right-to-left. This is, it is implicit, for example +// +// a op (b op (c op d)) +// +// assigment_expression +// : binary_expression op binary_expression op binary_expression ... +// +bool HlslGrammar::acceptAssignmentExpression(TIntermTyped*& node) +{ + if (! acceptBinaryExpression(node, PlLogicalOr)) + return false; + + TOperator assignOp = HlslOpMap::assignment(peek()); + if (assignOp == EOpNull) + return true; + + // ... op + TSourceLoc loc = token.loc; + advanceToken(); + + // ... binary_expression + // But, done by recursing this function, which automatically + // gets the right-to-left associativity. + TIntermTyped* rightNode = nullptr; + if (! acceptAssignmentExpression(rightNode)) { + expected("assignment expression"); + return false; + } + + node = intermediate.addAssign(assignOp, node, rightNode, loc); + + if (! peekTokenClass(EHTokComma)) + return true; + + return true; +} + +// Accept a binary expression, for binary operations that +// associate left-to-right. This is, it is implicit, for example +// +// ((a op b) op c) op d +// +// binary_expression +// : expression op expression op expression ... +// +// where 'expression' is the next higher level in precedence. +// +bool HlslGrammar::acceptBinaryExpression(TIntermTyped*& node, PrecedenceLevel precedenceLevel) +{ + if (precedenceLevel > PlMul) + return acceptUnaryExpression(node); + + // assignment_expression + if (! acceptBinaryExpression(node, (PrecedenceLevel)(precedenceLevel + 1))) + return false; + + TOperator op = HlslOpMap::binary(peek()); + PrecedenceLevel tokenLevel = HlslOpMap::precedenceLevel(op); + if (tokenLevel < precedenceLevel) + return true; + + do { + // ... op + TSourceLoc loc = token.loc; + advanceToken(); + + // ... expression + TIntermTyped* rightNode = nullptr; + if (! acceptBinaryExpression(rightNode, (PrecedenceLevel)(precedenceLevel + 1))) { + expected("expression"); + return false; + } + + node = intermediate.addBinaryMath(op, node, rightNode, loc); + + if (! peekTokenClass(EHTokComma)) + return true; + } while (true); +} + +// unary_expression +// : (type) unary_expression +// | + unary_expression +// | - unary_expression +// | ! unary_expression +// | ~ unary_expression +// | ++ unary_expression +// | -- unary_expression +// | postfix_expression +// +bool HlslGrammar::acceptUnaryExpression(TIntermTyped*& node) +{ + // (type) unary_expression + // Have to look two steps ahead, because this could be, e.g., a + // postfix_expression instead, since that also starts with at "(". + if (acceptTokenClass(EHTokLeftParen)) { + TType castType; + if (acceptType(castType)) { + if (! acceptTokenClass(EHTokRightParen)) { + expected(")"); + return false; + } + + // We've matched "(type)" now, get the expression to cast + TSourceLoc loc = token.loc; + if (! acceptUnaryExpression(node)) + return false; + + // Hook it up like a constructor + TFunction* constructorFunction = parseContext.handleConstructorCall(loc, castType); + if (constructorFunction == nullptr) { + expected("type that can be constructed"); + return false; + } + TIntermTyped* arguments = nullptr; + parseContext.handleFunctionArgument(constructorFunction, arguments, node); + node = parseContext.handleFunctionCall(loc, constructorFunction, arguments); + + return true; + } else { + // This isn't a type cast, but it still started "(", so if it is a + // unary expression, it can only be a postfix_expression, so try that. + // Back it up first. + recedeToken(); + return acceptPostfixExpression(node); + } + } + + // peek for "op unary_expression" + TOperator unaryOp = HlslOpMap::preUnary(peek()); + + // postfix_expression (if no unary operator) + if (unaryOp == EOpNull) + return acceptPostfixExpression(node); + + // op unary_expression + TSourceLoc loc = token.loc; + advanceToken(); + if (! acceptUnaryExpression(node)) + return false; + + // + is a no-op + if (unaryOp == EOpAdd) + return true; + + node = intermediate.addUnaryMath(unaryOp, node, loc); + + return node != nullptr; +} + +// postfix_expression +// : LEFT_PAREN expression RIGHT_PAREN +// | literal +// | constructor +// | identifier +// | function_call +// | postfix_expression LEFT_BRACKET integer_expression RIGHT_BRACKET +// | postfix_expression DOT IDENTIFIER +// | postfix_expression INC_OP +// | postfix_expression DEC_OP +// +bool HlslGrammar::acceptPostfixExpression(TIntermTyped*& node) +{ + // Not implemented as self-recursive: + // The logical "right recursion" is done with an loop at the end + + // idToken will pick up either a variable or a function name in a function call + HlslToken idToken; + + // Find something before the postfix operations, as they can't operate + // on nothing. So, no "return true", they fall through, only "return false". + if (acceptTokenClass(EHTokLeftParen)) { + // LEFT_PAREN expression RIGHT_PAREN + if (! acceptExpression(node)) { + expected("expression"); + return false; + } + if (! acceptTokenClass(EHTokRightParen)) { + expected(")"); + return false; + } + } else if (acceptLiteral(node)) { + // literal (nothing else to do yet), go on to the + } else if (acceptConstructor(node)) { + // constructor (nothing else to do yet) + } else if (acceptIdentifier(idToken)) { + // identifier or function_call name + if (! peekTokenClass(EHTokLeftParen)) { + node = parseContext.handleVariable(idToken.loc, idToken.symbol, token.string); + } else if (acceptFunctionCall(idToken, node)) { + // function_call (nothing else to do yet) + } else { + expected("function call arguments"); + return false; + } + } else { + // nothing found, can't post operate + return false; + } + + // Something was found, chain as many postfix operations as exist. + do { + TSourceLoc loc = token.loc; + TOperator postOp = HlslOpMap::postUnary(peek()); + + // Consume only a valid post-unary operator, otherwise we are done. + switch (postOp) { + case EOpIndexDirectStruct: + case EOpIndexIndirect: + case EOpPostIncrement: + case EOpPostDecrement: + advanceToken(); + break; + default: + return true; + } + + // We have a valid post-unary operator, process it. + switch (postOp) { + case EOpIndexDirectStruct: + // todo + break; + case EOpIndexIndirect: + { + TIntermTyped* indexNode = nullptr; + if (! acceptExpression(indexNode) || + ! peekTokenClass(EHTokRightBracket)) { + expected("expression followed by ']'"); + return false; + } + // todo: node = intermediate.addBinaryMath( + } + case EOpPostIncrement: + case EOpPostDecrement: + node = intermediate.addUnaryMath(postOp, node, loc); + break; + default: + assert(0); + break; + } + } while (true); +} + +// constructor +// : type argument_list +// +bool HlslGrammar::acceptConstructor(TIntermTyped*& node) +{ + // type + TType type; + if (acceptType(type)) { + TFunction* constructorFunction = parseContext.handleConstructorCall(token.loc, type); + if (constructorFunction == nullptr) + return false; + + // arguments + TIntermTyped* arguments = nullptr; + if (! acceptArguments(constructorFunction, arguments)) { + expected("constructor arguments"); + return false; + } + + // hook it up + node = parseContext.handleFunctionCall(arguments->getLoc(), constructorFunction, arguments); + + return true; + } + + return false; +} + +// The function_call identifier was already recognized, and passed in as idToken. +// +// function_call +// : [idToken] arguments +// +bool HlslGrammar::acceptFunctionCall(HlslToken idToken, TIntermTyped*& node) +{ + // arguments + TFunction* function = new TFunction(idToken.string, TType(EbtVoid)); + TIntermTyped* arguments = nullptr; + if (! acceptArguments(function, arguments)) + return false; + + node = parseContext.handleFunctionCall(idToken.loc, function, arguments); + + return true; +} + +// arguments +// : LEFT_PAREN expression COMMA expression COMMA ... RIGHT_PAREN +// +// The arguments are pushed onto the 'function' argument list and +// onto the 'arguments' aggregate. +// +bool HlslGrammar::acceptArguments(TFunction* function, TIntermTyped*& arguments) +{ + // LEFT_PAREN + if (! acceptTokenClass(EHTokLeftParen)) + return false; + + do { + // expression + TIntermTyped* arg; + if (! acceptAssignmentExpression(arg)) + break; + + // hook it up + parseContext.handleFunctionArgument(function, arguments, arg); + + // COMMA + if (! acceptTokenClass(EHTokComma)) + break; + } while (true); + + // RIGHT_PAREN + if (! acceptTokenClass(EHTokRightParen)) { + expected(")"); + return false; + } + + return true; +} + +bool HlslGrammar::acceptLiteral(TIntermTyped*& node) +{ + switch (token.tokenClass) { + case EHTokIntConstant: + node = intermediate.addConstantUnion(token.i, token.loc, true); + break; + case EHTokFloatConstant: + node = intermediate.addConstantUnion(token.d, EbtFloat, token.loc, true); + break; + case EHTokDoubleConstant: + node = intermediate.addConstantUnion(token.d, EbtDouble, token.loc, true); + break; + case EHTokBoolConstant: + node = intermediate.addConstantUnion(token.b, token.loc, true); + break; + + default: + return false; + } + + advanceToken(); + + return true; +} + +// compound_statement +// : LEFT_CURLY statement statement ... RIGHT_CURLY +// +bool HlslGrammar::acceptCompoundStatement(TIntermNode*& retStatement) +{ + TIntermAggregate* compoundStatement = nullptr; + + // LEFT_CURLY + if (! acceptTokenClass(EHTokLeftBrace)) + return false; + + // statement statement ... + TIntermNode* statement = nullptr; + while (acceptStatement(statement)) { + // hook it up + compoundStatement = intermediate.growAggregate(compoundStatement, statement); + } + if (compoundStatement) + compoundStatement->setOperator(EOpSequence); + + retStatement = compoundStatement; + + // RIGHT_CURLY + return acceptTokenClass(EHTokRightBrace); +} + +bool HlslGrammar::acceptScopedStatement(TIntermNode*& statement) +{ + parseContext.pushScope(); + bool result = acceptStatement(statement); + parseContext.popScope(); + + return result; +} + +bool HlslGrammar::acceptScopedCompoundStatement(TIntermNode*& statement) +{ + parseContext.pushScope(); + bool result = acceptCompoundStatement(statement); + parseContext.popScope(); + + return result; +} + +// statement +// : attributes attributed_statement +// +// attributed_statement +// : compound_statement +// | SEMICOLON +// | expression SEMICOLON +// | declaration_statement +// | selection_statement +// | switch_statement +// | case_label +// | iteration_statement +// | jump_statement +// +bool HlslGrammar::acceptStatement(TIntermNode*& statement) +{ + statement = nullptr; + + // attributes + acceptAttributes(); + + // attributed_statement + switch (peek()) { + case EHTokLeftBrace: + return acceptScopedCompoundStatement(statement); + + case EHTokIf: + return acceptSelectionStatement(statement); + + case EHTokSwitch: + return acceptSwitchStatement(statement); + + case EHTokFor: + case EHTokDo: + case EHTokWhile: + return acceptIterationStatement(statement); + + case EHTokContinue: + case EHTokBreak: + case EHTokDiscard: + case EHTokReturn: + return acceptJumpStatement(statement); + + case EHTokCase: + return acceptCaseLabel(statement); + + case EHTokSemicolon: + return acceptTokenClass(EHTokSemicolon); + + case EHTokRightBrace: + // Performance: not strictly necessary, but stops a bunch of hunting early, + // and is how sequences of statements end. + return false; + + default: + { + // declaration + if (acceptDeclaration(statement)) + return true; + + // expression + TIntermTyped* node; + if (acceptExpression(node)) + statement = node; + else + return false; + + // SEMICOLON (following an expression) + if (! acceptTokenClass(EHTokSemicolon)) { + expected(";"); + return false; + } + } + } + + return true; +} + +// attributes +// : list of zero or more of: LEFT_BRACKET attribute RIGHT_BRACKET +// +// attribute: +// : UNROLL +// | UNROLL LEFT_PAREN literal RIGHT_PAREN +// | FASTOPT +// | ALLOW_UAV_CONDITION +// | BRANCH +// | FLATTEN +// | FORCECASE +// | CALL +// +void HlslGrammar::acceptAttributes() +{ + // For now, accept the [ XXX(X) ] syntax, but drop. + // TODO: subset to correct set? Pass on? + do { + // LEFT_BRACKET? + if (! acceptTokenClass(EHTokLeftBracket)) + return; + + // attribute + if (peekTokenClass(EHTokIdentifier)) { + // 'token.string' is the attribute + advanceToken(); + } else if (! peekTokenClass(EHTokRightBracket)) { + expected("identifier"); + advanceToken(); + } + + // (x) + if (acceptTokenClass(EHTokLeftParen)) { + TIntermTyped* node; + if (! acceptLiteral(node)) + expected("literal"); + // 'node' has the literal in it + if (! acceptTokenClass(EHTokRightParen)) + expected(")"); + } + + // RIGHT_BRACKET + if (acceptTokenClass(EHTokRightBracket)) + continue; + + expected("]"); + return; + + } while (true); +} + +// selection_statement +// : IF LEFT_PAREN expression RIGHT_PAREN statement +// : IF LEFT_PAREN expression RIGHT_PAREN statement ELSE statement +// +bool HlslGrammar::acceptSelectionStatement(TIntermNode*& statement) +{ + TSourceLoc loc = token.loc; + + // IF + if (! acceptTokenClass(EHTokIf)) + return false; + + // so that something declared in the condition is scoped to the lifetimes + // of the then-else statements + parseContext.pushScope(); + + // LEFT_PAREN expression RIGHT_PAREN + TIntermTyped* condition; + if (! acceptParenExpression(condition)) + return false; + + // create the child statements + TIntermNodePair thenElse = { nullptr, nullptr }; + + // then statement + if (! acceptScopedStatement(thenElse.node1)) { + expected("then statement"); + return false; + } + + // ELSE + if (acceptTokenClass(EHTokElse)) { + // else statement + if (! acceptScopedStatement(thenElse.node2)) { + expected("else statement"); + return false; + } + } + + // Put the pieces together + statement = intermediate.addSelection(condition, thenElse, loc); + parseContext.popScope(); + + return true; +} + +bool HlslGrammar::acceptSwitchStatement(TIntermNode*& statement) +{ + return false; +} + +// iteration_statement +// : WHILE LEFT_PAREN condition RIGHT_PAREN statement +// | DO LEFT_BRACE statement RIGHT_BRACE WHILE LEFT_PAREN expression RIGHT_PAREN SEMICOLON +// | FOR LEFT_PAREN for_init_statement for_rest_statement RIGHT_PAREN statement +// +// Non-speculative, only call if it needs to be found; WHILE or DO or FOR already seen. +bool HlslGrammar::acceptIterationStatement(TIntermNode*& statement) +{ + TSourceLoc loc = token.loc; + TIntermTyped* condition = nullptr; + + EHlslTokenClass loop = peek(); + assert(loop == EHTokDo || loop == EHTokFor || loop == EHTokWhile); + + // WHILE or DO or FOR + advanceToken(); + + switch (loop) { + case EHTokWhile: + // so that something declared in the condition is scoped to the lifetime + // of the while sub-statement + parseContext.pushScope(); + parseContext.nestLooping(); + + // LEFT_PAREN condition RIGHT_PAREN + if (! acceptParenExpression(condition)) + return false; + + // statement + if (! acceptScopedStatement(statement)) { + expected("while sub-statement"); + return false; + } + + parseContext.unnestLooping(); + parseContext.popScope(); + + statement = intermediate.addLoop(statement, condition, nullptr, true, loc); + + return true; + + case EHTokDo: + parseContext.nestLooping(); + + if (! acceptTokenClass(EHTokLeftBrace)) + expected("{"); + + // statement + if (! peekTokenClass(EHTokRightBrace) && ! acceptScopedStatement(statement)) { + expected("do sub-statement"); + return false; + } + + if (! acceptTokenClass(EHTokRightBrace)) + expected("}"); + + // WHILE + if (! acceptTokenClass(EHTokWhile)) { + expected("while"); + return false; + } + + // LEFT_PAREN condition RIGHT_PAREN + TIntermTyped* condition; + if (! acceptParenExpression(condition)) + return false; + + if (! acceptTokenClass(EHTokSemicolon)) + expected(";"); + + parseContext.unnestLooping(); + + statement = intermediate.addLoop(statement, condition, 0, false, loc); + + return true; + + case EHTokFor: + { + // LEFT_PAREN + if (! acceptTokenClass(EHTokLeftParen)) + expected("("); + + // so that something declared in the condition is scoped to the lifetime + // of the for sub-statement + parseContext.pushScope(); + + // initializer SEMI_COLON + TIntermTyped* initializer = nullptr; // TODO, "for (initializer" needs to support decl. statement + acceptExpression(initializer); + if (! acceptTokenClass(EHTokSemicolon)) + expected(";"); + + parseContext.nestLooping(); + + // condition SEMI_COLON + acceptExpression(condition); + if (! acceptTokenClass(EHTokSemicolon)) + expected(";"); + + // iterator SEMI_COLON + TIntermTyped* iterator = nullptr; + acceptExpression(iterator); + if (! acceptTokenClass(EHTokRightParen)) + expected(")"); + + // statement + if (! acceptScopedStatement(statement)) { + expected("for sub-statement"); + return false; + } + + statement = intermediate.addForLoop(statement, initializer, condition, iterator, true, loc); + + parseContext.popScope(); + parseContext.unnestLooping(); + + return true; + } + + default: + return false; + } +} + +// jump_statement +// : CONTINUE SEMICOLON +// | BREAK SEMICOLON +// | DISCARD SEMICOLON +// | RETURN SEMICOLON +// | RETURN expression SEMICOLON +// +bool HlslGrammar::acceptJumpStatement(TIntermNode*& statement) +{ + switch (peek()) { + case EHTokContinue: + case EHTokBreak: + case EHTokDiscard: + // TODO + return false; + + case EHTokReturn: + // return + if (acceptTokenClass(EHTokReturn)) { + // expression + TIntermTyped* node; + if (acceptExpression(node)) { + // hook it up + statement = intermediate.addBranch(EOpReturn, node, token.loc); + } else + statement = intermediate.addBranch(EOpReturn, token.loc); + + // SEMICOLON + if (! acceptTokenClass(EHTokSemicolon)) { + expected(";"); + return false; + } + + return true; + } + + default: + return false; + } +} + + +bool HlslGrammar::acceptCaseLabel(TIntermNode*& statement) +{ + return false; +} + +// post_decls +// : COLON semantic // optional +// COLON PACKOFFSET LEFT_PAREN ... RIGHT_PAREN // optional +// COLON REGISTER // optional +// annotations // optional +// +void HlslGrammar::acceptPostDecls(TType& type) +{ + do { + // COLON + if (acceptTokenClass(EHTokColon)) { + HlslToken idToken; + if (acceptTokenClass(EHTokPackOffset)) { + if (! acceptTokenClass(EHTokLeftParen)) { + expected("("); + return; + } + acceptTokenClass(EHTokIdentifier); + acceptTokenClass(EHTokDot); + acceptTokenClass(EHTokIdentifier); + if (! acceptTokenClass(EHTokRightParen)) { + expected(")"); + break; + } + // TODO: process the packoffset information + } else if (! acceptIdentifier(idToken)) { + expected("semantic or packoffset or register"); + return; + } else if (*idToken.string == "register") { + if (! acceptTokenClass(EHTokLeftParen)) { + expected("("); + return; + } + acceptTokenClass(EHTokIdentifier); + acceptTokenClass(EHTokComma); + acceptTokenClass(EHTokIdentifier); + acceptTokenClass(EHTokLeftBracket); + if (peekTokenClass(EHTokIntConstant)) + advanceToken(); + acceptTokenClass(EHTokRightBracket); + if (! acceptTokenClass(EHTokRightParen)) { + expected(")"); + break; + } + // TODO: process the register information + } else { + // semantic, in idToken.string + parseContext.handleSemantic(type, *idToken.string); + } + } else if (acceptTokenClass(EHTokLeftAngle)) { + // TODO: process annotations, just accepting them for now + do { + if (peekTokenClass(EHTokNone)) + return; + if (acceptTokenClass(EHTokRightAngle)) + break; + advanceToken(); + } while (true); + } else + break; + + } while (true); +} + +} // end namespace glslang |