4171 lines
		
	
	
		
			128 KiB
		
	
	
	
		
			C++
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			4171 lines
		
	
	
		
			128 KiB
		
	
	
	
		
			C++
		
	
	
		
			Executable File
		
	
	
	
	
| //
 | |
| // Copyright (C) 2016-2018 Google, Inc.
 | |
| // Copyright (C) 2016 LunarG, 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"
 | |
| #include "hlslAttributes.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, "");
 | |
| }
 | |
| 
 | |
| void HlslGrammar::unimplemented(const char* error)
 | |
| {
 | |
|     parseContext.error(token.loc, "Unimplemented", error, "");
 | |
| }
 | |
| 
 | |
| // IDENTIFIER
 | |
| // THIS
 | |
| // type that can be used as IDENTIFIER
 | |
| //
 | |
| // Only process the next token if it is an identifier.
 | |
| // Return true if it was an identifier.
 | |
| bool HlslGrammar::acceptIdentifier(HlslToken& idToken)
 | |
| {
 | |
|     // IDENTIFIER
 | |
|     if (peekTokenClass(EHTokIdentifier)) {
 | |
|         idToken = token;
 | |
|         advanceToken();
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     // THIS
 | |
|     // -> maps to the IDENTIFIER spelled with the internal special name for 'this'
 | |
|     if (peekTokenClass(EHTokThis)) {
 | |
|         idToken = token;
 | |
|         advanceToken();
 | |
|         idToken.tokenClass = EHTokIdentifier;
 | |
|         idToken.string = NewPoolTString(intermediate.implicitThisName);
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     // type that can be used as IDENTIFIER
 | |
| 
 | |
|     // Even though "sample", "bool", "float", etc keywords (for types, interpolation modifiers),
 | |
|     // they ARE still accepted as identifiers.  This is not a dense space: e.g, "void" is not a
 | |
|     // valid identifier, nor is "linear".  This code special cases the known instances of this, so
 | |
|     // e.g, "int sample;" or "float float;" is accepted.  Other cases can be added here if needed.
 | |
| 
 | |
|     const char* idString = getTypeString(peek());
 | |
|     if (idString == nullptr)
 | |
|         return false;
 | |
| 
 | |
|     token.string     = NewPoolTString(idString);
 | |
|     token.tokenClass = EHTokIdentifier;
 | |
|     idToken = token;
 | |
|     typeIdentifiers = true;
 | |
| 
 | |
|     advanceToken();
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| // compilationUnit
 | |
| //      : declaration_list EOF
 | |
| //
 | |
| bool HlslGrammar::acceptCompilationUnit()
 | |
| {
 | |
|     if (! acceptDeclarationList(unitNode))
 | |
|         return false;
 | |
| 
 | |
|     if (! peekTokenClass(EHTokNone))
 | |
|         return false;
 | |
| 
 | |
|     // set root of AST
 | |
|     if (unitNode && !unitNode->getAsAggregate())
 | |
|         unitNode = intermediate.growAggregate(nullptr, unitNode);
 | |
|     intermediate.setTreeRoot(unitNode);
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| // Recognize the following, but with the extra condition that it can be
 | |
| // successfully terminated by EOF or '}'.
 | |
| //
 | |
| // declaration_list
 | |
| //      : list of declaration_or_semicolon followed by EOF or RIGHT_BRACE
 | |
| //
 | |
| // declaration_or_semicolon
 | |
| //      : declaration
 | |
| //      : SEMICOLON
 | |
| //
 | |
| bool HlslGrammar::acceptDeclarationList(TIntermNode*& nodeList)
 | |
| {
 | |
|     do {
 | |
|         // HLSL allows extra semicolons between global declarations
 | |
|         do { } while (acceptTokenClass(EHTokSemicolon));
 | |
| 
 | |
|         // EOF or RIGHT_BRACE
 | |
|         if (peekTokenClass(EHTokNone) || peekTokenClass(EHTokRightBrace))
 | |
|             return true;
 | |
| 
 | |
|         // declaration
 | |
|         if (! acceptDeclaration(nodeList))
 | |
|             return false;
 | |
|     } while (true);
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| // sampler_state
 | |
| //      : LEFT_BRACE [sampler_state_assignment ... ] RIGHT_BRACE
 | |
| //
 | |
| // sampler_state_assignment
 | |
| //     : sampler_state_identifier EQUAL value SEMICOLON
 | |
| //
 | |
| // sampler_state_identifier
 | |
| //     : ADDRESSU
 | |
| //     | ADDRESSV
 | |
| //     | ADDRESSW
 | |
| //     | BORDERCOLOR
 | |
| //     | FILTER
 | |
| //     | MAXANISOTROPY
 | |
| //     | MAXLOD
 | |
| //     | MINLOD
 | |
| //     | MIPLODBIAS
 | |
| //
 | |
| bool HlslGrammar::acceptSamplerState()
 | |
| {
 | |
|     // TODO: this should be genericized to accept a list of valid tokens and
 | |
|     // return token/value pairs.  Presently it is specific to texture values.
 | |
| 
 | |
|     if (! acceptTokenClass(EHTokLeftBrace))
 | |
|         return true;
 | |
| 
 | |
|     parseContext.warn(token.loc, "unimplemented", "immediate sampler state", "");
 | |
| 
 | |
|     do {
 | |
|         // read state name
 | |
|         HlslToken state;
 | |
|         if (! acceptIdentifier(state))
 | |
|             break;  // end of list
 | |
| 
 | |
|         // FXC accepts any case
 | |
|         TString stateName = *state.string;
 | |
|         std::transform(stateName.begin(), stateName.end(), stateName.begin(), ::tolower);
 | |
| 
 | |
|         if (! acceptTokenClass(EHTokAssign)) {
 | |
|             expected("assign");
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         if (stateName == "minlod" || stateName == "maxlod") {
 | |
|             if (! peekTokenClass(EHTokIntConstant)) {
 | |
|                 expected("integer");
 | |
|                 return false;
 | |
|             }
 | |
| 
 | |
|             TIntermTyped* lod = nullptr;
 | |
|             if (! acceptLiteral(lod))  // should never fail, since we just looked for an integer
 | |
|                 return false;
 | |
|         } else if (stateName == "maxanisotropy") {
 | |
|             if (! peekTokenClass(EHTokIntConstant)) {
 | |
|                 expected("integer");
 | |
|                 return false;
 | |
|             }
 | |
| 
 | |
|             TIntermTyped* maxAnisotropy = nullptr;
 | |
|             if (! acceptLiteral(maxAnisotropy))  // should never fail, since we just looked for an integer
 | |
|                 return false;
 | |
|         } else if (stateName == "filter") {
 | |
|             HlslToken filterMode;
 | |
|             if (! acceptIdentifier(filterMode)) {
 | |
|                 expected("filter mode");
 | |
|                 return false;
 | |
|             }
 | |
|         } else if (stateName == "addressu" || stateName == "addressv" || stateName == "addressw") {
 | |
|             HlslToken addrMode;
 | |
|             if (! acceptIdentifier(addrMode)) {
 | |
|                 expected("texture address mode");
 | |
|                 return false;
 | |
|             }
 | |
|         } else if (stateName == "miplodbias") {
 | |
|             TIntermTyped* lodBias = nullptr;
 | |
|             if (! acceptLiteral(lodBias)) {
 | |
|                 expected("lod bias");
 | |
|                 return false;
 | |
|             }
 | |
|         } else if (stateName == "bordercolor") {
 | |
|             return false;
 | |
|         } else {
 | |
|             expected("texture state");
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         // SEMICOLON
 | |
|         if (! acceptTokenClass(EHTokSemicolon)) {
 | |
|             expected("semicolon");
 | |
|             return false;
 | |
|         }
 | |
|     } while (true);
 | |
| 
 | |
|     if (! acceptTokenClass(EHTokRightBrace))
 | |
|         return false;
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| // sampler_declaration_dx9
 | |
| //    : SAMPLER identifier EQUAL sampler_type sampler_state
 | |
| //
 | |
| bool HlslGrammar::acceptSamplerDeclarationDX9(TType& /*type*/)
 | |
| {
 | |
|     if (! acceptTokenClass(EHTokSampler))
 | |
|         return false;
 | |
| 
 | |
|     // TODO: remove this when DX9 style declarations are implemented.
 | |
|     unimplemented("Direct3D 9 sampler declaration");
 | |
| 
 | |
|     // read sampler name
 | |
|     HlslToken name;
 | |
|     if (! acceptIdentifier(name)) {
 | |
|         expected("sampler name");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     if (! acceptTokenClass(EHTokAssign)) {
 | |
|         expected("=");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| // declaration
 | |
| //      : attributes attributed_declaration
 | |
| //      | NAMESPACE IDENTIFIER LEFT_BRACE declaration_list RIGHT_BRACE
 | |
| //
 | |
| // attributed_declaration
 | |
| //      : sampler_declaration_dx9 post_decls SEMICOLON
 | |
| //      | fully_specified_type                           // for cbuffer/tbuffer
 | |
| //      | fully_specified_type declarator_list SEMICOLON // for non cbuffer/tbuffer
 | |
| //      | fully_specified_type identifier function_parameters post_decls compound_statement  // function definition
 | |
| //      | fully_specified_type identifier sampler_state post_decls compound_statement        // sampler definition
 | |
| //      | typedef declaration
 | |
| //
 | |
| // declarator_list
 | |
| //      : declarator COMMA declarator COMMA declarator...  // zero or more declarators
 | |
| //
 | |
| // declarator
 | |
| //      : identifier array_specifier post_decls
 | |
| //      | identifier array_specifier post_decls EQUAL assignment_expression
 | |
| //      | identifier function_parameters post_decls                                          // function prototype
 | |
| //
 | |
| // Parsing has to go pretty far in to know whether it's a variable, prototype, or
 | |
| // function definition, so the implementation below doesn't perfectly divide up the grammar
 | |
| // as above.  (The 'identifier' in the first item in init_declarator list is the
 | |
| // same as 'identifier' for function declarations.)
 | |
| //
 | |
| // This can generate more than one subtree, one per initializer or a function body.
 | |
| // All initializer subtrees are put in their own aggregate node, making one top-level
 | |
| // node for all the initializers. Each function created is a top-level node to grow
 | |
| // into the passed-in nodeList.
 | |
| //
 | |
| // If 'nodeList' is passed in as non-null, it must be an aggregate to extend for
 | |
| // each top-level node the declaration creates. Otherwise, if only one top-level
 | |
| // node in generated here, that is want is returned in nodeList.
 | |
| //
 | |
| bool HlslGrammar::acceptDeclaration(TIntermNode*& nodeList)
 | |
| {
 | |
|     // NAMESPACE IDENTIFIER LEFT_BRACE declaration_list RIGHT_BRACE
 | |
|     if (acceptTokenClass(EHTokNamespace)) {
 | |
|         HlslToken namespaceToken;
 | |
|         if (!acceptIdentifier(namespaceToken)) {
 | |
|             expected("namespace name");
 | |
|             return false;
 | |
|         }
 | |
|         parseContext.pushNamespace(*namespaceToken.string);
 | |
|         if (!acceptTokenClass(EHTokLeftBrace)) {
 | |
|             expected("{");
 | |
|             return false;
 | |
|         }
 | |
|         if (!acceptDeclarationList(nodeList)) {
 | |
|             expected("declaration list");
 | |
|             return false;
 | |
|         }
 | |
|         if (!acceptTokenClass(EHTokRightBrace)) {
 | |
|             expected("}");
 | |
|             return false;
 | |
|         }
 | |
|         parseContext.popNamespace();
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     bool declarator_list = false; // true when processing comma separation
 | |
| 
 | |
|     // attributes
 | |
|     TFunctionDeclarator declarator;
 | |
|     acceptAttributes(declarator.attributes);
 | |
| 
 | |
|     // typedef
 | |
|     bool typedefDecl = acceptTokenClass(EHTokTypedef);
 | |
| 
 | |
|     TType declaredType;
 | |
| 
 | |
|     // DX9 sampler declaration use a different syntax
 | |
|     // DX9 shaders need to run through HLSL compiler (fxc) via a back compat mode, it isn't going to
 | |
|     // be possible to simultaneously compile D3D10+ style shaders and DX9 shaders. If we want to compile DX9
 | |
|     // HLSL shaders, this will have to be a master level switch
 | |
|     // As such, the sampler keyword in D3D10+ turns into an automatic sampler type, and is commonly used
 | |
|     // For that reason, this line is commented out
 | |
|     // if (acceptSamplerDeclarationDX9(declaredType))
 | |
|     //     return true;
 | |
| 
 | |
|     bool forbidDeclarators = (peekTokenClass(EHTokCBuffer) || peekTokenClass(EHTokTBuffer));
 | |
|     // fully_specified_type
 | |
|     if (! acceptFullySpecifiedType(declaredType, nodeList, declarator.attributes, forbidDeclarators))
 | |
|         return false;
 | |
| 
 | |
|     // cbuffer and tbuffer end with the closing '}'.
 | |
|     // No semicolon is included.
 | |
|     if (forbidDeclarators)
 | |
|         return true;
 | |
| 
 | |
|     // declarator_list
 | |
|     //    : declarator
 | |
|     //         : identifier
 | |
|     HlslToken idToken;
 | |
|     TIntermAggregate* initializers = nullptr;
 | |
|     while (acceptIdentifier(idToken)) {
 | |
|         TString *fullName = idToken.string;
 | |
|         if (parseContext.symbolTable.atGlobalLevel())
 | |
|             parseContext.getFullNamespaceName(fullName);
 | |
|         if (peekTokenClass(EHTokLeftParen)) {
 | |
|             // looks like function parameters
 | |
| 
 | |
|             // merge in the attributes into the return type
 | |
|             parseContext.transferTypeAttributes(token.loc, declarator.attributes, declaredType, true);
 | |
| 
 | |
|             // Potentially rename shader entry point function.  No-op most of the time.
 | |
|             parseContext.renameShaderFunction(fullName);
 | |
| 
 | |
|             // function_parameters
 | |
|             declarator.function = new TFunction(fullName, declaredType);
 | |
|             if (!acceptFunctionParameters(*declarator.function)) {
 | |
|                 expected("function parameter list");
 | |
|                 return false;
 | |
|             }
 | |
| 
 | |
|             // post_decls
 | |
|             acceptPostDecls(declarator.function->getWritableType().getQualifier());
 | |
| 
 | |
|             // compound_statement (function body definition) or just a prototype?
 | |
|             declarator.loc = token.loc;
 | |
|             if (peekTokenClass(EHTokLeftBrace)) {
 | |
|                 if (declarator_list)
 | |
|                     parseContext.error(idToken.loc, "function body can't be in a declarator list", "{", "");
 | |
|                 if (typedefDecl)
 | |
|                     parseContext.error(idToken.loc, "function body can't be in a typedef", "{", "");
 | |
|                 return acceptFunctionDefinition(declarator, nodeList, nullptr);
 | |
|             } else {
 | |
|                 if (typedefDecl)
 | |
|                     parseContext.error(idToken.loc, "function typedefs not implemented", "{", "");
 | |
|                 parseContext.handleFunctionDeclarator(declarator.loc, *declarator.function, true);
 | |
|             }
 | |
|         } else {
 | |
|             // A variable declaration.
 | |
| 
 | |
|             // merge in the attributes, the first time around, into the shared type
 | |
|             if (! declarator_list)
 | |
|                 parseContext.transferTypeAttributes(token.loc, declarator.attributes, declaredType);
 | |
| 
 | |
|             // Fix the storage qualifier if it's a global.
 | |
|             if (declaredType.getQualifier().storage == EvqTemporary && parseContext.symbolTable.atGlobalLevel())
 | |
|                 declaredType.getQualifier().storage = EvqUniform;
 | |
| 
 | |
|             // recognize array_specifier
 | |
|             TArraySizes* arraySizes = nullptr;
 | |
|             acceptArraySpecifier(arraySizes);
 | |
| 
 | |
|             // We can handle multiple variables per type declaration, so
 | |
|             // the number of types can expand when arrayness is different.
 | |
|             TType variableType;
 | |
|             variableType.shallowCopy(declaredType);
 | |
| 
 | |
|             // In the most general case, arrayness is potentially coming both from the
 | |
|             // declared type and from the variable: "int[] a[];" or just one or the other.
 | |
|             // Merge it all to the variableType, so all arrayness is part of the variableType.
 | |
|             variableType.transferArraySizes(arraySizes);
 | |
|             variableType.copyArrayInnerSizes(declaredType.getArraySizes());
 | |
| 
 | |
|             // samplers accept immediate sampler state
 | |
|             if (variableType.getBasicType() == EbtSampler) {
 | |
|                 if (! acceptSamplerState())
 | |
|                     return false;
 | |
|             }
 | |
| 
 | |
|             // post_decls
 | |
|             acceptPostDecls(variableType.getQualifier());
 | |
| 
 | |
|             // EQUAL assignment_expression
 | |
|             TIntermTyped* expressionNode = nullptr;
 | |
|             if (acceptTokenClass(EHTokAssign)) {
 | |
|                 if (typedefDecl)
 | |
|                     parseContext.error(idToken.loc, "can't have an initializer", "typedef", "");
 | |
|                 if (! acceptAssignmentExpression(expressionNode)) {
 | |
|                     expected("initializer");
 | |
|                     return false;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             // TODO: things scoped within an annotation need their own name space;
 | |
|             // TODO: strings are not yet handled.
 | |
|             if (variableType.getBasicType() != EbtString && parseContext.getAnnotationNestingLevel() == 0) {
 | |
|                 if (typedefDecl)
 | |
|                     parseContext.declareTypedef(idToken.loc, *fullName, variableType);
 | |
|                 else if (variableType.getBasicType() == EbtBlock) {
 | |
|                     if (expressionNode)
 | |
|                         parseContext.error(idToken.loc, "buffer aliasing not yet supported", "block initializer", "");
 | |
|                     parseContext.declareBlock(idToken.loc, variableType, fullName);
 | |
|                     parseContext.declareStructBufferCounter(idToken.loc, variableType, *fullName);
 | |
|                 } else {
 | |
|                     if (variableType.getQualifier().storage == EvqUniform && ! variableType.containsOpaque()) {
 | |
|                         // this isn't really an individual variable, but a member of the $Global buffer
 | |
|                         parseContext.growGlobalUniformBlock(idToken.loc, variableType, *fullName);
 | |
|                     } else {
 | |
|                         // Declare the variable and add any initializer code to the AST.
 | |
|                         // The top-level node is always made into an aggregate, as that's
 | |
|                         // historically how the AST has been.
 | |
|                         initializers = intermediate.growAggregate(initializers, 
 | |
|                             parseContext.declareVariable(idToken.loc, *fullName, variableType, expressionNode),
 | |
|                             idToken.loc);
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // COMMA
 | |
|         if (acceptTokenClass(EHTokComma))
 | |
|             declarator_list = true;
 | |
|     }
 | |
| 
 | |
|     // The top-level initializer node is a sequence.
 | |
|     if (initializers != nullptr)
 | |
|         initializers->setOperator(EOpSequence);
 | |
| 
 | |
|     // if we have a locally scoped static, it needs a globally scoped initializer
 | |
|     if (declaredType.getQualifier().storage == EvqGlobal && !parseContext.symbolTable.atGlobalLevel()) {
 | |
|         unitNode = intermediate.growAggregate(unitNode, initializers, idToken.loc);
 | |
|     } else {
 | |
|         // Add the initializers' aggregate to the nodeList we were handed.
 | |
|         if (nodeList)
 | |
|             nodeList = intermediate.growAggregate(nodeList, initializers);
 | |
|         else
 | |
|             nodeList = initializers;
 | |
|     }
 | |
| 
 | |
|     // SEMICOLON
 | |
|     if (! acceptTokenClass(EHTokSemicolon)) {
 | |
|         // This may have been a false detection of what appeared to be a declaration, but
 | |
|         // was actually an assignment such as "float = 4", where "float" is an identifier.
 | |
|         // We put the token back to let further parsing happen for cases where that may
 | |
|         // happen.  This errors on the side of caution, and mostly triggers the error.
 | |
|         if (peek() == EHTokAssign || peek() == EHTokLeftBracket || peek() == EHTokDot || peek() == EHTokComma)
 | |
|             recedeToken();
 | |
|         else
 | |
|             expected(";");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| // control_declaration
 | |
| //      : fully_specified_type identifier EQUAL expression
 | |
| //
 | |
| bool HlslGrammar::acceptControlDeclaration(TIntermNode*& node)
 | |
| {
 | |
|     node = nullptr;
 | |
|     TAttributes attributes;
 | |
| 
 | |
|     // fully_specified_type
 | |
|     TType type;
 | |
|     if (! acceptFullySpecifiedType(type, attributes))
 | |
|         return false;
 | |
| 
 | |
|     if (attributes.size() > 0)
 | |
|         parseContext.warn(token.loc, "attributes don't apply to control declaration", "", "");
 | |
| 
 | |
|     // filter out type casts
 | |
|     if (peekTokenClass(EHTokLeftParen)) {
 | |
|         recedeToken();
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     // identifier
 | |
|     HlslToken idToken;
 | |
|     if (! acceptIdentifier(idToken)) {
 | |
|         expected("identifier");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     // EQUAL
 | |
|     TIntermTyped* expressionNode = nullptr;
 | |
|     if (! acceptTokenClass(EHTokAssign)) {
 | |
|         expected("=");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     // expression
 | |
|     if (! acceptExpression(expressionNode)) {
 | |
|         expected("initializer");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     node = parseContext.declareVariable(idToken.loc, *idToken.string, type, expressionNode);
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| // fully_specified_type
 | |
| //      : type_specifier
 | |
| //      | type_qualifier type_specifier
 | |
| //
 | |
| bool HlslGrammar::acceptFullySpecifiedType(TType& type, const TAttributes& attributes)
 | |
| {
 | |
|     TIntermNode* nodeList = nullptr;
 | |
|     return acceptFullySpecifiedType(type, nodeList, attributes);
 | |
| }
 | |
| bool HlslGrammar::acceptFullySpecifiedType(TType& type, TIntermNode*& nodeList, const TAttributes& attributes, bool forbidDeclarators)
 | |
| {
 | |
|     // type_qualifier
 | |
|     TQualifier qualifier;
 | |
|     qualifier.clear();
 | |
|     if (! acceptQualifier(qualifier))
 | |
|         return false;
 | |
|     TSourceLoc loc = token.loc;
 | |
| 
 | |
|     // type_specifier
 | |
|     if (! acceptType(type, nodeList)) {
 | |
|         // If this is not a type, we may have inadvertently gone down a wrong path
 | |
|         // by parsing "sample", which can be treated like either an identifier or a
 | |
|         // qualifier.  Back it out, if we did.
 | |
|         if (qualifier.sample)
 | |
|             recedeToken();
 | |
| 
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     if (type.getBasicType() == EbtBlock) {
 | |
|         // the type was a block, which set some parts of the qualifier
 | |
|         parseContext.mergeQualifiers(type.getQualifier(), qualifier);
 | |
|     
 | |
|         // merge in the attributes
 | |
|         parseContext.transferTypeAttributes(token.loc, attributes, type);
 | |
| 
 | |
|         // further, it can create an anonymous instance of the block
 | |
|         // (cbuffer and tbuffer don't consume the next identifier, and
 | |
|         // should set forbidDeclarators)
 | |
|         if (forbidDeclarators || peek() != EHTokIdentifier)
 | |
|             parseContext.declareBlock(loc, type);
 | |
|     } else {
 | |
|         // Some qualifiers are set when parsing the type.  Merge those with
 | |
|         // whatever comes from acceptQualifier.
 | |
|         assert(qualifier.layoutFormat == ElfNone);
 | |
| 
 | |
|         qualifier.layoutFormat = type.getQualifier().layoutFormat;
 | |
|         qualifier.precision    = type.getQualifier().precision;
 | |
| 
 | |
|         if (type.getQualifier().storage == EvqOut ||
 | |
|             type.getQualifier().storage == EvqBuffer) {
 | |
|             qualifier.storage      = type.getQualifier().storage;
 | |
|             qualifier.readonly     = type.getQualifier().readonly;
 | |
|         }
 | |
| 
 | |
|         if (type.isBuiltIn())
 | |
|             qualifier.builtIn = type.getQualifier().builtIn;
 | |
| 
 | |
|         type.getQualifier() = qualifier;
 | |
|     }
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| // type_qualifier
 | |
| //      : qualifier qualifier ...
 | |
| //
 | |
| // Zero or more of these, so this can't return false.
 | |
| //
 | |
| bool HlslGrammar::acceptQualifier(TQualifier& qualifier)
 | |
| {
 | |
|     do {
 | |
|         switch (peek()) {
 | |
|         case EHTokStatic:
 | |
|             qualifier.storage = EvqGlobal;
 | |
|             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.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 = ElmColumnMajor;
 | |
|             break;
 | |
|         case EHTokColumnMajor:
 | |
|             qualifier.layoutMatrix = ElmRowMajor;
 | |
|             break;
 | |
|         case EHTokPrecise:
 | |
|             qualifier.noContraction = true;
 | |
|             break;
 | |
|         case EHTokIn:
 | |
|             qualifier.storage = (qualifier.storage == EvqOut) ? EvqInOut : EvqIn;
 | |
|             break;
 | |
|         case EHTokOut:
 | |
|             qualifier.storage = (qualifier.storage == EvqIn) ? EvqInOut : EvqOut;
 | |
|             break;
 | |
|         case EHTokInOut:
 | |
|             qualifier.storage = EvqInOut;
 | |
|             break;
 | |
|         case EHTokLayout:
 | |
|             if (! acceptLayoutQualifierList(qualifier))
 | |
|                 return false;
 | |
|             continue;
 | |
|         case EHTokGloballyCoherent:
 | |
|             qualifier.coherent = true;
 | |
|             break;
 | |
|         case EHTokInline:
 | |
|             // TODO: map this to SPIR-V function control
 | |
|             break;
 | |
| 
 | |
|         // GS geometries: these are specified on stage input variables, and are an error (not verified here)
 | |
|         // for output variables.
 | |
|         case EHTokPoint:
 | |
|             qualifier.storage = EvqIn;
 | |
|             if (!parseContext.handleInputGeometry(token.loc, ElgPoints))
 | |
|                 return false;
 | |
|             break;
 | |
|         case EHTokLine:
 | |
|             qualifier.storage = EvqIn;
 | |
|             if (!parseContext.handleInputGeometry(token.loc, ElgLines))
 | |
|                 return false;
 | |
|             break;
 | |
|         case EHTokTriangle:
 | |
|             qualifier.storage = EvqIn;
 | |
|             if (!parseContext.handleInputGeometry(token.loc, ElgTriangles))
 | |
|                 return false;
 | |
|             break;
 | |
|         case EHTokLineAdj:
 | |
|             qualifier.storage = EvqIn;
 | |
|             if (!parseContext.handleInputGeometry(token.loc, ElgLinesAdjacency))
 | |
|                 return false;
 | |
|             break;
 | |
|         case EHTokTriangleAdj:
 | |
|             qualifier.storage = EvqIn;
 | |
|             if (!parseContext.handleInputGeometry(token.loc, ElgTrianglesAdjacency))
 | |
|                 return false;
 | |
|             break;
 | |
| 
 | |
|         default:
 | |
|             return true;
 | |
|         }
 | |
|         advanceToken();
 | |
|     } while (true);
 | |
| }
 | |
| 
 | |
| // layout_qualifier_list
 | |
| //      : LAYOUT LEFT_PAREN layout_qualifier COMMA layout_qualifier ... RIGHT_PAREN
 | |
| //
 | |
| // layout_qualifier
 | |
| //      : identifier
 | |
| //      | identifier EQUAL expression
 | |
| //
 | |
| // Zero or more of these, so this can't return false.
 | |
| //
 | |
| bool HlslGrammar::acceptLayoutQualifierList(TQualifier& qualifier)
 | |
| {
 | |
|     if (! acceptTokenClass(EHTokLayout))
 | |
|         return false;
 | |
| 
 | |
|     // LEFT_PAREN
 | |
|     if (! acceptTokenClass(EHTokLeftParen))
 | |
|         return false;
 | |
| 
 | |
|     do {
 | |
|         // identifier
 | |
|         HlslToken idToken;
 | |
|         if (! acceptIdentifier(idToken))
 | |
|             break;
 | |
| 
 | |
|         // EQUAL expression
 | |
|         if (acceptTokenClass(EHTokAssign)) {
 | |
|             TIntermTyped* expr;
 | |
|             if (! acceptConditionalExpression(expr)) {
 | |
|                 expected("expression");
 | |
|                 return false;
 | |
|             }
 | |
|             parseContext.setLayoutQualifier(idToken.loc, qualifier, *idToken.string, expr);
 | |
|         } else
 | |
|             parseContext.setLayoutQualifier(idToken.loc, qualifier, *idToken.string);
 | |
| 
 | |
|         // COMMA
 | |
|         if (! acceptTokenClass(EHTokComma))
 | |
|             break;
 | |
|     } while (true);
 | |
| 
 | |
|     // RIGHT_PAREN
 | |
|     if (! acceptTokenClass(EHTokRightParen)) {
 | |
|         expected(")");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| // template_type
 | |
| //      : FLOAT
 | |
| //      | DOUBLE
 | |
| //      | INT
 | |
| //      | DWORD
 | |
| //      | UINT
 | |
| //      | BOOL
 | |
| //
 | |
| bool HlslGrammar::acceptTemplateVecMatBasicType(TBasicType& basicType)
 | |
| {
 | |
|     switch (peek()) {
 | |
|     case EHTokFloat:
 | |
|         basicType = EbtFloat;
 | |
|         break;
 | |
|     case EHTokDouble:
 | |
|         basicType = EbtDouble;
 | |
|         break;
 | |
|     case EHTokInt:
 | |
|     case EHTokDword:
 | |
|         basicType = EbtInt;
 | |
|         break;
 | |
|     case EHTokUint:
 | |
|         basicType = EbtUint;
 | |
|         break;
 | |
|     case EHTokBool:
 | |
|         basicType = EbtBool;
 | |
|         break;
 | |
|     default:
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     advanceToken();
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| // vector_template_type
 | |
| //      : VECTOR
 | |
| //      | VECTOR LEFT_ANGLE template_type COMMA integer_literal RIGHT_ANGLE
 | |
| //
 | |
| bool HlslGrammar::acceptVectorTemplateType(TType& type)
 | |
| {
 | |
|     if (! acceptTokenClass(EHTokVector))
 | |
|         return false;
 | |
| 
 | |
|     if (! acceptTokenClass(EHTokLeftAngle)) {
 | |
|         // in HLSL, 'vector' alone means float4.
 | |
|         new(&type) TType(EbtFloat, EvqTemporary, 4);
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     TBasicType basicType;
 | |
|     if (! acceptTemplateVecMatBasicType(basicType)) {
 | |
|         expected("scalar type");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     // COMMA
 | |
|     if (! acceptTokenClass(EHTokComma)) {
 | |
|         expected(",");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     // integer
 | |
|     if (! peekTokenClass(EHTokIntConstant)) {
 | |
|         expected("literal integer");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     TIntermTyped* vecSize;
 | |
|     if (! acceptLiteral(vecSize))
 | |
|         return false;
 | |
| 
 | |
|     const int vecSizeI = vecSize->getAsConstantUnion()->getConstArray()[0].getIConst();
 | |
| 
 | |
|     new(&type) TType(basicType, EvqTemporary, vecSizeI);
 | |
| 
 | |
|     if (vecSizeI == 1)
 | |
|         type.makeVector();
 | |
| 
 | |
|     if (!acceptTokenClass(EHTokRightAngle)) {
 | |
|         expected("right angle bracket");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| // matrix_template_type
 | |
| //      : MATRIX
 | |
| //      | MATRIX LEFT_ANGLE template_type COMMA integer_literal COMMA integer_literal RIGHT_ANGLE
 | |
| //
 | |
| bool HlslGrammar::acceptMatrixTemplateType(TType& type)
 | |
| {
 | |
|     if (! acceptTokenClass(EHTokMatrix))
 | |
|         return false;
 | |
| 
 | |
|     if (! acceptTokenClass(EHTokLeftAngle)) {
 | |
|         // in HLSL, 'matrix' alone means float4x4.
 | |
|         new(&type) TType(EbtFloat, EvqTemporary, 0, 4, 4);
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     TBasicType basicType;
 | |
|     if (! acceptTemplateVecMatBasicType(basicType)) {
 | |
|         expected("scalar type");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     // COMMA
 | |
|     if (! acceptTokenClass(EHTokComma)) {
 | |
|         expected(",");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     // integer rows
 | |
|     if (! peekTokenClass(EHTokIntConstant)) {
 | |
|         expected("literal integer");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     TIntermTyped* rows;
 | |
|     if (! acceptLiteral(rows))
 | |
|         return false;
 | |
| 
 | |
|     // COMMA
 | |
|     if (! acceptTokenClass(EHTokComma)) {
 | |
|         expected(",");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     // integer cols
 | |
|     if (! peekTokenClass(EHTokIntConstant)) {
 | |
|         expected("literal integer");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     TIntermTyped* cols;
 | |
|     if (! acceptLiteral(cols))
 | |
|         return false;
 | |
| 
 | |
|     new(&type) TType(basicType, EvqTemporary, 0,
 | |
|                      rows->getAsConstantUnion()->getConstArray()[0].getIConst(),
 | |
|                      cols->getAsConstantUnion()->getConstArray()[0].getIConst());
 | |
| 
 | |
|     if (!acceptTokenClass(EHTokRightAngle)) {
 | |
|         expected("right angle bracket");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| // layout_geometry
 | |
| //      : LINESTREAM
 | |
| //      | POINTSTREAM
 | |
| //      | TRIANGLESTREAM
 | |
| //
 | |
| bool HlslGrammar::acceptOutputPrimitiveGeometry(TLayoutGeometry& geometry)
 | |
| {
 | |
|     // read geometry type
 | |
|     const EHlslTokenClass geometryType = peek();
 | |
| 
 | |
|     switch (geometryType) {
 | |
|     case EHTokPointStream:    geometry = ElgPoints;        break;
 | |
|     case EHTokLineStream:     geometry = ElgLineStrip;     break;
 | |
|     case EHTokTriangleStream: geometry = ElgTriangleStrip; break;
 | |
|     default:
 | |
|         return false;  // not a layout geometry
 | |
|     }
 | |
| 
 | |
|     advanceToken();  // consume the layout keyword
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| // tessellation_decl_type
 | |
| //      : INPUTPATCH
 | |
| //      | OUTPUTPATCH
 | |
| //
 | |
| bool HlslGrammar::acceptTessellationDeclType(TBuiltInVariable& patchType)
 | |
| {
 | |
|     // read geometry type
 | |
|     const EHlslTokenClass tessType = peek();
 | |
| 
 | |
|     switch (tessType) {
 | |
|     case EHTokInputPatch:    patchType = EbvInputPatch;  break;
 | |
|     case EHTokOutputPatch:   patchType = EbvOutputPatch; break;
 | |
|     default:
 | |
|         return false;  // not a tessellation decl
 | |
|     }
 | |
| 
 | |
|     advanceToken();  // consume the keyword
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| // tessellation_patch_template_type
 | |
| //      : tessellation_decl_type LEFT_ANGLE type comma integer_literal RIGHT_ANGLE
 | |
| //
 | |
| bool HlslGrammar::acceptTessellationPatchTemplateType(TType& type)
 | |
| {
 | |
|     TBuiltInVariable patchType;
 | |
| 
 | |
|     if (! acceptTessellationDeclType(patchType))
 | |
|         return false;
 | |
|     
 | |
|     if (! acceptTokenClass(EHTokLeftAngle))
 | |
|         return false;
 | |
| 
 | |
|     if (! acceptType(type)) {
 | |
|         expected("tessellation patch type");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     if (! acceptTokenClass(EHTokComma))
 | |
|         return false;
 | |
| 
 | |
|     // integer size
 | |
|     if (! peekTokenClass(EHTokIntConstant)) {
 | |
|         expected("literal integer");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     TIntermTyped* size;
 | |
|     if (! acceptLiteral(size))
 | |
|         return false;
 | |
| 
 | |
|     TArraySizes* arraySizes = new TArraySizes;
 | |
|     arraySizes->addInnerSize(size->getAsConstantUnion()->getConstArray()[0].getIConst());
 | |
|     type.transferArraySizes(arraySizes);
 | |
|     type.getQualifier().builtIn = patchType;
 | |
| 
 | |
|     if (! acceptTokenClass(EHTokRightAngle)) {
 | |
|         expected("right angle bracket");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     return true;
 | |
| }
 | |
|     
 | |
| // stream_out_template_type
 | |
| //      : output_primitive_geometry_type LEFT_ANGLE type RIGHT_ANGLE
 | |
| //
 | |
| bool HlslGrammar::acceptStreamOutTemplateType(TType& type, TLayoutGeometry& geometry)
 | |
| {
 | |
|     geometry = ElgNone;
 | |
| 
 | |
|     if (! acceptOutputPrimitiveGeometry(geometry))
 | |
|         return false;
 | |
| 
 | |
|     if (! acceptTokenClass(EHTokLeftAngle))
 | |
|         return false;
 | |
| 
 | |
|     if (! acceptType(type)) {
 | |
|         expected("stream output type");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     type.getQualifier().storage = EvqOut;
 | |
|     type.getQualifier().builtIn = EbvGsOutputStream;
 | |
| 
 | |
|     if (! acceptTokenClass(EHTokRightAngle)) {
 | |
|         expected("right angle bracket");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| // annotations
 | |
| //      : LEFT_ANGLE declaration SEMI_COLON ... declaration SEMICOLON RIGHT_ANGLE
 | |
| //
 | |
| bool HlslGrammar::acceptAnnotations(TQualifier&)
 | |
| {
 | |
|     if (! acceptTokenClass(EHTokLeftAngle))
 | |
|         return false;
 | |
| 
 | |
|     // note that we are nesting a name space
 | |
|     parseContext.nestAnnotations();
 | |
| 
 | |
|     // declaration SEMI_COLON ... declaration SEMICOLON RIGHT_ANGLE
 | |
|     do {
 | |
|         // eat any extra SEMI_COLON; don't know if the grammar calls for this or not
 | |
|         while (acceptTokenClass(EHTokSemicolon))
 | |
|             ;
 | |
| 
 | |
|         if (acceptTokenClass(EHTokRightAngle))
 | |
|             break;
 | |
| 
 | |
|         // declaration
 | |
|         TIntermNode* node = nullptr;
 | |
|         if (! acceptDeclaration(node)) {
 | |
|             expected("declaration in annotation");
 | |
|             return false;
 | |
|         }
 | |
|     } while (true);
 | |
| 
 | |
|     parseContext.unnestAnnotations();
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| // subpass input type
 | |
| //      : SUBPASSINPUT
 | |
| //      | SUBPASSINPUT VECTOR LEFT_ANGLE template_type RIGHT_ANGLE
 | |
| //      | SUBPASSINPUTMS
 | |
| //      | SUBPASSINPUTMS VECTOR LEFT_ANGLE template_type RIGHT_ANGLE
 | |
| bool HlslGrammar::acceptSubpassInputType(TType& type)
 | |
| {
 | |
|     // read subpass type
 | |
|     const EHlslTokenClass subpassInputType = peek();
 | |
| 
 | |
|     bool multisample;
 | |
| 
 | |
|     switch (subpassInputType) {
 | |
|     case EHTokSubpassInput:   multisample = false; break;
 | |
|     case EHTokSubpassInputMS: multisample = true;  break;
 | |
|     default:
 | |
|         return false;  // not a subpass input declaration
 | |
|     }
 | |
| 
 | |
|     advanceToken();  // consume the sampler type keyword
 | |
| 
 | |
|     TType subpassType(EbtFloat, EvqUniform, 4); // default type is float4
 | |
| 
 | |
|     if (acceptTokenClass(EHTokLeftAngle)) {
 | |
|         if (! acceptType(subpassType)) {
 | |
|             expected("scalar or vector type");
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         const TBasicType basicRetType = subpassType.getBasicType() ;
 | |
| 
 | |
|         switch (basicRetType) {
 | |
|         case EbtFloat:
 | |
|         case EbtUint:
 | |
|         case EbtInt:
 | |
|         case EbtStruct:
 | |
|             break;
 | |
|         default:
 | |
|             unimplemented("basic type in subpass input");
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         if (! acceptTokenClass(EHTokRightAngle)) {
 | |
|             expected("right angle bracket");
 | |
|             return false;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     const TBasicType subpassBasicType = subpassType.isStruct() ? (*subpassType.getStruct())[0].type->getBasicType()
 | |
|         : subpassType.getBasicType();
 | |
| 
 | |
|     TSampler sampler;
 | |
|     sampler.setSubpass(subpassBasicType, multisample);
 | |
| 
 | |
|     // Remember the declared return type.  Function returns false on error.
 | |
|     if (!parseContext.setTextureReturnType(sampler, subpassType, token.loc))
 | |
|         return false;
 | |
| 
 | |
|     type.shallowCopy(TType(sampler, EvqUniform));
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| // sampler_type for DX9 compatibility 
 | |
| //      : SAMPLER
 | |
| //      | SAMPLER1D
 | |
| //      | SAMPLER2D
 | |
| //      | SAMPLER3D
 | |
| //      | SAMPLERCUBE
 | |
| bool HlslGrammar::acceptSamplerTypeDX9(TType &type)
 | |
| {
 | |
|     // read sampler type
 | |
|     const EHlslTokenClass samplerType = peek();
 | |
| 
 | |
|     TSamplerDim dim = EsdNone;
 | |
|     TType txType(EbtFloat, EvqUniform, 4); // default type is float4
 | |
| 
 | |
|     bool isShadow = false;
 | |
| 
 | |
|     switch (samplerType)
 | |
|     {
 | |
|     case EHTokSampler:		dim = Esd2D;	break;
 | |
|     case EHTokSampler1d:	dim = Esd1D;	break;
 | |
|     case EHTokSampler2d:	dim = Esd2D;	break;
 | |
|     case EHTokSampler3d:	dim = Esd3D;	break;
 | |
|     case EHTokSamplerCube:	dim = EsdCube;	break;
 | |
|     default:
 | |
|         return false; // not a dx9 sampler declaration
 | |
|     }
 | |
| 
 | |
|     advanceToken(); // consume the sampler type keyword
 | |
| 
 | |
|     TArraySizes *arraySizes = nullptr; // TODO: array
 | |
| 
 | |
|     TSampler sampler;
 | |
|     sampler.set(txType.getBasicType(), dim, false, isShadow, false);
 | |
| 
 | |
| 	if (!parseContext.setTextureReturnType(sampler, txType, token.loc))
 | |
|         return false;
 | |
| 
 | |
|     type.shallowCopy(TType(sampler, EvqUniform, arraySizes));
 | |
|     type.getQualifier().layoutFormat = ElfNone;
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| // sampler_type
 | |
| //      : SAMPLER
 | |
| //      | SAMPLER1D
 | |
| //      | SAMPLER2D
 | |
| //      | SAMPLER3D
 | |
| //      | SAMPLERCUBE
 | |
| //      | SAMPLERSTATE
 | |
| //      | SAMPLERCOMPARISONSTATE
 | |
| bool HlslGrammar::acceptSamplerType(TType& type)
 | |
| {
 | |
|     // read sampler type
 | |
|     const EHlslTokenClass samplerType = peek();
 | |
| 
 | |
|     // TODO: for DX9
 | |
|     // TSamplerDim dim = EsdNone;
 | |
| 
 | |
|     bool isShadow = false;
 | |
| 
 | |
|     switch (samplerType) {
 | |
|     case EHTokSampler:      break;
 | |
|     case EHTokSampler1d:    /*dim = Esd1D*/; break;
 | |
|     case EHTokSampler2d:    /*dim = Esd2D*/; break;
 | |
|     case EHTokSampler3d:    /*dim = Esd3D*/; break;
 | |
|     case EHTokSamplerCube:  /*dim = EsdCube*/; break;
 | |
|     case EHTokSamplerState: break;
 | |
|     case EHTokSamplerComparisonState: isShadow = true; break;
 | |
|     default:
 | |
|         return false;  // not a sampler declaration
 | |
|     }
 | |
| 
 | |
|     advanceToken();  // consume the sampler type keyword
 | |
| 
 | |
|     TArraySizes* arraySizes = nullptr; // TODO: array
 | |
| 
 | |
|     TSampler sampler;
 | |
|     sampler.setPureSampler(isShadow);
 | |
| 
 | |
|     type.shallowCopy(TType(sampler, EvqUniform, arraySizes));
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| // texture_type
 | |
| //      | BUFFER
 | |
| //      | TEXTURE1D
 | |
| //      | TEXTURE1DARRAY
 | |
| //      | TEXTURE2D
 | |
| //      | TEXTURE2DARRAY
 | |
| //      | TEXTURE3D
 | |
| //      | TEXTURECUBE
 | |
| //      | TEXTURECUBEARRAY
 | |
| //      | TEXTURE2DMS
 | |
| //      | TEXTURE2DMSARRAY
 | |
| //      | RWBUFFER
 | |
| //      | RWTEXTURE1D
 | |
| //      | RWTEXTURE1DARRAY
 | |
| //      | RWTEXTURE2D
 | |
| //      | RWTEXTURE2DARRAY
 | |
| //      | RWTEXTURE3D
 | |
| 
 | |
| bool HlslGrammar::acceptTextureType(TType& type)
 | |
| {
 | |
|     const EHlslTokenClass textureType = peek();
 | |
| 
 | |
|     TSamplerDim dim = EsdNone;
 | |
|     bool array = false;
 | |
|     bool ms    = false;
 | |
|     bool image = false;
 | |
|     bool combined = true;
 | |
| 
 | |
|     switch (textureType) {
 | |
|     case EHTokBuffer:            dim = EsdBuffer; combined = false;    break;
 | |
|     case EHTokTexture1d:         dim = Esd1D;                          break;
 | |
|     case EHTokTexture1darray:    dim = Esd1D; array = true;            break;
 | |
|     case EHTokTexture2d:         dim = Esd2D;                          break;
 | |
|     case EHTokTexture2darray:    dim = Esd2D; array = true;            break;
 | |
|     case EHTokTexture3d:         dim = Esd3D;                          break;
 | |
|     case EHTokTextureCube:       dim = EsdCube;                        break;
 | |
|     case EHTokTextureCubearray:  dim = EsdCube; array = true;          break;
 | |
|     case EHTokTexture2DMS:       dim = Esd2D; ms = true;               break;
 | |
|     case EHTokTexture2DMSarray:  dim = Esd2D; array = true; ms = true; break;
 | |
|     case EHTokRWBuffer:          dim = EsdBuffer; image=true;          break;
 | |
|     case EHTokRWTexture1d:       dim = Esd1D; array=false; image=true; break;
 | |
|     case EHTokRWTexture1darray:  dim = Esd1D; array=true;  image=true; break;
 | |
|     case EHTokRWTexture2d:       dim = Esd2D; array=false; image=true; break;
 | |
|     case EHTokRWTexture2darray:  dim = Esd2D; array=true;  image=true; break;
 | |
|     case EHTokRWTexture3d:       dim = Esd3D; array=false; image=true; break;
 | |
|     default:
 | |
|         return false;  // not a texture declaration
 | |
|     }
 | |
| 
 | |
|     advanceToken();  // consume the texture object keyword
 | |
| 
 | |
|     TType txType(EbtFloat, EvqUniform, 4); // default type is float4
 | |
| 
 | |
|     TIntermTyped* msCount = nullptr;
 | |
| 
 | |
|     // texture type: required for multisample types and RWBuffer/RWTextures!
 | |
|     if (acceptTokenClass(EHTokLeftAngle)) {
 | |
|         if (! acceptType(txType)) {
 | |
|             expected("scalar or vector type");
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         const TBasicType basicRetType = txType.getBasicType() ;
 | |
| 
 | |
|         switch (basicRetType) {
 | |
|         case EbtFloat:
 | |
|         case EbtUint:
 | |
|         case EbtInt:
 | |
|         case EbtStruct:
 | |
|             break;
 | |
|         default:
 | |
|             unimplemented("basic type in texture");
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         // Buffers can handle small mats if they fit in 4 components
 | |
|         if (dim == EsdBuffer && txType.isMatrix()) {
 | |
|             if ((txType.getMatrixCols() * txType.getMatrixRows()) > 4) {
 | |
|                 expected("components < 4 in matrix buffer type");
 | |
|                 return false;
 | |
|             }
 | |
| 
 | |
|             // TODO: except we don't handle it yet...
 | |
|             unimplemented("matrix type in buffer");
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         if (!txType.isScalar() && !txType.isVector() && !txType.isStruct()) {
 | |
|             expected("scalar, vector, or struct type");
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         if (ms && acceptTokenClass(EHTokComma)) {
 | |
|             // read sample count for multisample types, if given
 | |
|             if (! peekTokenClass(EHTokIntConstant)) {
 | |
|                 expected("multisample count");
 | |
|                 return false;
 | |
|             }
 | |
| 
 | |
|             if (! acceptLiteral(msCount))  // should never fail, since we just found an integer
 | |
|                 return false;
 | |
|         }
 | |
| 
 | |
|         if (! acceptTokenClass(EHTokRightAngle)) {
 | |
|             expected("right angle bracket");
 | |
|             return false;
 | |
|         }
 | |
|     } else if (ms) {
 | |
|         expected("texture type for multisample");
 | |
|         return false;
 | |
|     } else if (image) {
 | |
|         expected("type for RWTexture/RWBuffer");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     TArraySizes* arraySizes = nullptr;
 | |
|     const bool shadow = false; // declared on the sampler
 | |
| 
 | |
|     TSampler sampler;
 | |
|     TLayoutFormat format = ElfNone;
 | |
| 
 | |
|     // Buffer, RWBuffer and RWTexture (images) require a TLayoutFormat.  We handle only a limit set.
 | |
|     if (image || dim == EsdBuffer)
 | |
|         format = parseContext.getLayoutFromTxType(token.loc, txType);
 | |
| 
 | |
|     const TBasicType txBasicType = txType.isStruct() ? (*txType.getStruct())[0].type->getBasicType()
 | |
|         : txType.getBasicType();
 | |
| 
 | |
|     // Non-image Buffers are combined
 | |
|     if (dim == EsdBuffer && !image) {
 | |
|         sampler.set(txType.getBasicType(), dim, array);
 | |
|     } else {
 | |
|         // DX10 textures are separated.  TODO: DX9.
 | |
|         if (image) {
 | |
|             sampler.setImage(txBasicType, dim, array, shadow, ms);
 | |
|         } else {
 | |
|             sampler.setTexture(txBasicType, dim, array, shadow, ms);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // Remember the declared return type.  Function returns false on error.
 | |
|     if (!parseContext.setTextureReturnType(sampler, txType, token.loc))
 | |
|         return false;
 | |
| 
 | |
|     // Force uncombined, if necessary
 | |
|     if (!combined)
 | |
|         sampler.combined = false;
 | |
| 
 | |
|     type.shallowCopy(TType(sampler, EvqUniform, arraySizes));
 | |
|     type.getQualifier().layoutFormat = format;
 | |
| 
 | |
|     return 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)
 | |
| {
 | |
|     TIntermNode* nodeList = nullptr;
 | |
|     return acceptType(type, nodeList);
 | |
| }
 | |
| bool HlslGrammar::acceptType(TType& type, TIntermNode*& nodeList)
 | |
| {
 | |
|     // Basic types for min* types, use native halfs if the option allows them.
 | |
|     bool enable16BitTypes = parseContext.hlslEnable16BitTypes();
 | |
| 
 | |
|     const TBasicType min16float_bt = enable16BitTypes ? EbtFloat16 : EbtFloat;
 | |
|     const TBasicType min10float_bt = enable16BitTypes ? EbtFloat16 : EbtFloat;
 | |
|     const TBasicType half_bt       = enable16BitTypes ? EbtFloat16 : EbtFloat;
 | |
|     const TBasicType min16int_bt   = enable16BitTypes ? EbtInt16   : EbtInt;
 | |
|     const TBasicType min12int_bt   = enable16BitTypes ? EbtInt16   : EbtInt;
 | |
|     const TBasicType min16uint_bt  = enable16BitTypes ? EbtUint16  : EbtUint;
 | |
| 
 | |
|     // Some types might have turned into identifiers. Take the hit for checking
 | |
|     // when this has happened.
 | |
|     if (typeIdentifiers) {
 | |
|         const char* identifierString = getTypeString(peek());
 | |
|         if (identifierString != nullptr) {
 | |
|             TString name = identifierString;
 | |
|             // if it's an identifier, it's not a type
 | |
|             if (parseContext.symbolTable.find(name) != nullptr)
 | |
|                 return false;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     bool isUnorm = false;
 | |
|     bool isSnorm = false;
 | |
| 
 | |
|     // Accept snorm and unorm.  Presently, this is ignored, save for an error check below.
 | |
|     switch (peek()) {
 | |
|     case EHTokUnorm:
 | |
|         isUnorm = true;
 | |
|         advanceToken();  // eat the token
 | |
|         break;
 | |
|     case EHTokSNorm:
 | |
|         isSnorm = true;
 | |
|         advanceToken();  // eat the token
 | |
|         break;
 | |
|     default:
 | |
|         break;
 | |
|     }
 | |
| 
 | |
|     switch (peek()) {
 | |
|     case EHTokVector:
 | |
|         return acceptVectorTemplateType(type);
 | |
|         break;
 | |
| 
 | |
|     case EHTokMatrix:
 | |
|         return acceptMatrixTemplateType(type);
 | |
|         break;
 | |
| 
 | |
|     case EHTokPointStream:            // fall through
 | |
|     case EHTokLineStream:             // ...
 | |
|     case EHTokTriangleStream:         // ...
 | |
|         {
 | |
|             TLayoutGeometry geometry;
 | |
|             if (! acceptStreamOutTemplateType(type, geometry))
 | |
|                 return false;
 | |
| 
 | |
|             if (! parseContext.handleOutputGeometry(token.loc, geometry))
 | |
|                 return false;
 | |
| 
 | |
|             return true;
 | |
|         }
 | |
| 
 | |
|     case EHTokInputPatch:             // fall through
 | |
|     case EHTokOutputPatch:            // ...
 | |
|         {
 | |
|             if (! acceptTessellationPatchTemplateType(type))
 | |
|                 return false;
 | |
| 
 | |
|             return true;
 | |
|         }
 | |
| 
 | |
|     case EHTokSampler:                // fall through
 | |
|     case EHTokSampler1d:              // ...
 | |
|     case EHTokSampler2d:              // ...
 | |
|     case EHTokSampler3d:              // ...
 | |
|     case EHTokSamplerCube:            // ...
 | |
|         if (parseContext.hlslDX9Compatible())
 | |
| 			return acceptSamplerTypeDX9(type);
 | |
|         else
 | |
|             return acceptSamplerType(type);
 | |
|         break;
 | |
| 
 | |
|     case EHTokSamplerState:           // fall through
 | |
|     case EHTokSamplerComparisonState: // ...
 | |
|         return acceptSamplerType(type);
 | |
|         break;
 | |
| 
 | |
|     case EHTokSubpassInput:           // fall through
 | |
|     case EHTokSubpassInputMS:         // ...
 | |
|         return acceptSubpassInputType(type);
 | |
|         break;
 | |
| 
 | |
|     case EHTokBuffer:                 // fall through
 | |
|     case EHTokTexture1d:              // ...
 | |
|     case EHTokTexture1darray:         // ...
 | |
|     case EHTokTexture2d:              // ...
 | |
|     case EHTokTexture2darray:         // ...
 | |
|     case EHTokTexture3d:              // ...
 | |
|     case EHTokTextureCube:            // ...
 | |
|     case EHTokTextureCubearray:       // ...
 | |
|     case EHTokTexture2DMS:            // ...
 | |
|     case EHTokTexture2DMSarray:       // ...
 | |
|     case EHTokRWTexture1d:            // ...
 | |
|     case EHTokRWTexture1darray:       // ...
 | |
|     case EHTokRWTexture2d:            // ...
 | |
|     case EHTokRWTexture2darray:       // ...
 | |
|     case EHTokRWTexture3d:            // ...
 | |
|     case EHTokRWBuffer:               // ...
 | |
|         return acceptTextureType(type);
 | |
|         break;
 | |
| 
 | |
|     case EHTokAppendStructuredBuffer:
 | |
|     case EHTokByteAddressBuffer:
 | |
|     case EHTokConsumeStructuredBuffer:
 | |
|     case EHTokRWByteAddressBuffer:
 | |
|     case EHTokRWStructuredBuffer:
 | |
|     case EHTokStructuredBuffer:
 | |
|         return acceptStructBufferType(type);
 | |
|         break;
 | |
| 
 | |
|     case EHTokTextureBuffer:
 | |
|         return acceptTextureBufferType(type);
 | |
|         break;
 | |
| 
 | |
|     case EHTokConstantBuffer:
 | |
|         return acceptConstantBufferType(type);
 | |
| 
 | |
|     case EHTokClass:
 | |
|     case EHTokStruct:
 | |
|     case EHTokCBuffer:
 | |
|     case EHTokTBuffer:
 | |
|         return acceptStruct(type, nodeList);
 | |
| 
 | |
|     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.
 | |
|         if (parseContext.lookupUserType(*token.string, type) != nullptr) {
 | |
|             advanceToken();
 | |
|             return true;
 | |
|         } else
 | |
|             return false;
 | |
| 
 | |
|     case EHTokVoid:
 | |
|         new(&type) TType(EbtVoid);
 | |
|         break;
 | |
| 
 | |
|     case EHTokString:
 | |
|         new(&type) TType(EbtString);
 | |
|         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 EHTokUint64:
 | |
|         new(&type) TType(EbtUint64);
 | |
|         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 EHTokHalf:
 | |
|         new(&type) TType(half_bt, EvqTemporary);
 | |
|         break;
 | |
|     case EHTokHalf1:
 | |
|         new(&type) TType(half_bt, EvqTemporary);
 | |
|         type.makeVector();
 | |
|         break;
 | |
|     case EHTokHalf2:
 | |
|         new(&type) TType(half_bt, EvqTemporary, 2);
 | |
|         break;
 | |
|     case EHTokHalf3:
 | |
|         new(&type) TType(half_bt, EvqTemporary, 3);
 | |
|         break;
 | |
|     case EHTokHalf4:
 | |
|         new(&type) TType(half_bt, EvqTemporary, 4);
 | |
|         break;
 | |
| 
 | |
|     case EHTokMin16float:
 | |
|         new(&type) TType(min16float_bt, EvqTemporary, EpqMedium);
 | |
|         break;
 | |
|     case EHTokMin16float1:
 | |
|         new(&type) TType(min16float_bt, EvqTemporary, EpqMedium);
 | |
|         type.makeVector();
 | |
|         break;
 | |
|     case EHTokMin16float2:
 | |
|         new(&type) TType(min16float_bt, EvqTemporary, EpqMedium, 2);
 | |
|         break;
 | |
|     case EHTokMin16float3:
 | |
|         new(&type) TType(min16float_bt, EvqTemporary, EpqMedium, 3);
 | |
|         break;
 | |
|     case EHTokMin16float4:
 | |
|         new(&type) TType(min16float_bt, EvqTemporary, EpqMedium, 4);
 | |
|         break;
 | |
| 
 | |
|     case EHTokMin10float:
 | |
|         new(&type) TType(min10float_bt, EvqTemporary, EpqMedium);
 | |
|         break;
 | |
|     case EHTokMin10float1:
 | |
|         new(&type) TType(min10float_bt, EvqTemporary, EpqMedium);
 | |
|         type.makeVector();
 | |
|         break;
 | |
|     case EHTokMin10float2:
 | |
|         new(&type) TType(min10float_bt, EvqTemporary, EpqMedium, 2);
 | |
|         break;
 | |
|     case EHTokMin10float3:
 | |
|         new(&type) TType(min10float_bt, EvqTemporary, EpqMedium, 3);
 | |
|         break;
 | |
|     case EHTokMin10float4:
 | |
|         new(&type) TType(min10float_bt, EvqTemporary, EpqMedium, 4);
 | |
|         break;
 | |
| 
 | |
|     case EHTokMin16int:
 | |
|         new(&type) TType(min16int_bt, EvqTemporary, EpqMedium);
 | |
|         break;
 | |
|     case EHTokMin16int1:
 | |
|         new(&type) TType(min16int_bt, EvqTemporary, EpqMedium);
 | |
|         type.makeVector();
 | |
|         break;
 | |
|     case EHTokMin16int2:
 | |
|         new(&type) TType(min16int_bt, EvqTemporary, EpqMedium, 2);
 | |
|         break;
 | |
|     case EHTokMin16int3:
 | |
|         new(&type) TType(min16int_bt, EvqTemporary, EpqMedium, 3);
 | |
|         break;
 | |
|     case EHTokMin16int4:
 | |
|         new(&type) TType(min16int_bt, EvqTemporary, EpqMedium, 4);
 | |
|         break;
 | |
| 
 | |
|     case EHTokMin12int:
 | |
|         new(&type) TType(min12int_bt, EvqTemporary, EpqMedium);
 | |
|         break;
 | |
|     case EHTokMin12int1:
 | |
|         new(&type) TType(min12int_bt, EvqTemporary, EpqMedium);
 | |
|         type.makeVector();
 | |
|         break;
 | |
|     case EHTokMin12int2:
 | |
|         new(&type) TType(min12int_bt, EvqTemporary, EpqMedium, 2);
 | |
|         break;
 | |
|     case EHTokMin12int3:
 | |
|         new(&type) TType(min12int_bt, EvqTemporary, EpqMedium, 3);
 | |
|         break;
 | |
|     case EHTokMin12int4:
 | |
|         new(&type) TType(min12int_bt, EvqTemporary, EpqMedium, 4);
 | |
|         break;
 | |
| 
 | |
|     case EHTokMin16uint:
 | |
|         new(&type) TType(min16uint_bt, EvqTemporary, EpqMedium);
 | |
|         break;
 | |
|     case EHTokMin16uint1:
 | |
|         new(&type) TType(min16uint_bt, EvqTemporary, EpqMedium);
 | |
|         type.makeVector();
 | |
|         break;
 | |
|     case EHTokMin16uint2:
 | |
|         new(&type) TType(min16uint_bt, EvqTemporary, EpqMedium, 2);
 | |
|         break;
 | |
|     case EHTokMin16uint3:
 | |
|         new(&type) TType(min16uint_bt, EvqTemporary, EpqMedium, 3);
 | |
|         break;
 | |
|     case EHTokMin16uint4:
 | |
|         new(&type) TType(min16uint_bt, EvqTemporary, EpqMedium, 4);
 | |
|         break;
 | |
| 
 | |
|     case EHTokInt1x1:
 | |
|         new(&type) TType(EbtInt, EvqTemporary, 0, 1, 1);
 | |
|         break;
 | |
|     case EHTokInt1x2:
 | |
|         new(&type) TType(EbtInt, EvqTemporary, 0, 1, 2);
 | |
|         break;
 | |
|     case EHTokInt1x3:
 | |
|         new(&type) TType(EbtInt, EvqTemporary, 0, 1, 3);
 | |
|         break;
 | |
|     case EHTokInt1x4:
 | |
|         new(&type) TType(EbtInt, EvqTemporary, 0, 1, 4);
 | |
|         break;
 | |
|     case EHTokInt2x1:
 | |
|         new(&type) TType(EbtInt, EvqTemporary, 0, 2, 1);
 | |
|         break;
 | |
|     case EHTokInt2x2:
 | |
|         new(&type) TType(EbtInt, EvqTemporary, 0, 2, 2);
 | |
|         break;
 | |
|     case EHTokInt2x3:
 | |
|         new(&type) TType(EbtInt, EvqTemporary, 0, 2, 3);
 | |
|         break;
 | |
|     case EHTokInt2x4:
 | |
|         new(&type) TType(EbtInt, EvqTemporary, 0, 2, 4);
 | |
|         break;
 | |
|     case EHTokInt3x1:
 | |
|         new(&type) TType(EbtInt, EvqTemporary, 0, 3, 1);
 | |
|         break;
 | |
|     case EHTokInt3x2:
 | |
|         new(&type) TType(EbtInt, EvqTemporary, 0, 3, 2);
 | |
|         break;
 | |
|     case EHTokInt3x3:
 | |
|         new(&type) TType(EbtInt, EvqTemporary, 0, 3, 3);
 | |
|         break;
 | |
|     case EHTokInt3x4:
 | |
|         new(&type) TType(EbtInt, EvqTemporary, 0, 3, 4);
 | |
|         break;
 | |
|     case EHTokInt4x1:
 | |
|         new(&type) TType(EbtInt, EvqTemporary, 0, 4, 1);
 | |
|         break;
 | |
|     case EHTokInt4x2:
 | |
|         new(&type) TType(EbtInt, EvqTemporary, 0, 4, 2);
 | |
|         break;
 | |
|     case EHTokInt4x3:
 | |
|         new(&type) TType(EbtInt, EvqTemporary, 0, 4, 3);
 | |
|         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, 1, 2);
 | |
|         break;
 | |
|     case EHTokUint1x3:
 | |
|         new(&type) TType(EbtUint, EvqTemporary, 0, 1, 3);
 | |
|         break;
 | |
|     case EHTokUint1x4:
 | |
|         new(&type) TType(EbtUint, EvqTemporary, 0, 1, 4);
 | |
|         break;
 | |
|     case EHTokUint2x1:
 | |
|         new(&type) TType(EbtUint, EvqTemporary, 0, 2, 1);
 | |
|         break;
 | |
|     case EHTokUint2x2:
 | |
|         new(&type) TType(EbtUint, EvqTemporary, 0, 2, 2);
 | |
|         break;
 | |
|     case EHTokUint2x3:
 | |
|         new(&type) TType(EbtUint, EvqTemporary, 0, 2, 3);
 | |
|         break;
 | |
|     case EHTokUint2x4:
 | |
|         new(&type) TType(EbtUint, EvqTemporary, 0, 2, 4);
 | |
|         break;
 | |
|     case EHTokUint3x1:
 | |
|         new(&type) TType(EbtUint, EvqTemporary, 0, 3, 1);
 | |
|         break;
 | |
|     case EHTokUint3x2:
 | |
|         new(&type) TType(EbtUint, EvqTemporary, 0, 3, 2);
 | |
|         break;
 | |
|     case EHTokUint3x3:
 | |
|         new(&type) TType(EbtUint, EvqTemporary, 0, 3, 3);
 | |
|         break;
 | |
|     case EHTokUint3x4:
 | |
|         new(&type) TType(EbtUint, EvqTemporary, 0, 3, 4);
 | |
|         break;
 | |
|     case EHTokUint4x1:
 | |
|         new(&type) TType(EbtUint, EvqTemporary, 0, 4, 1);
 | |
|         break;
 | |
|     case EHTokUint4x2:
 | |
|         new(&type) TType(EbtUint, EvqTemporary, 0, 4, 2);
 | |
|         break;
 | |
|     case EHTokUint4x3:
 | |
|         new(&type) TType(EbtUint, EvqTemporary, 0, 4, 3);
 | |
|         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, 1, 2);
 | |
|         break;
 | |
|     case EHTokBool1x3:
 | |
|         new(&type) TType(EbtBool, EvqTemporary, 0, 1, 3);
 | |
|         break;
 | |
|     case EHTokBool1x4:
 | |
|         new(&type) TType(EbtBool, EvqTemporary, 0, 1, 4);
 | |
|         break;
 | |
|     case EHTokBool2x1:
 | |
|         new(&type) TType(EbtBool, EvqTemporary, 0, 2, 1);
 | |
|         break;
 | |
|     case EHTokBool2x2:
 | |
|         new(&type) TType(EbtBool, EvqTemporary, 0, 2, 2);
 | |
|         break;
 | |
|     case EHTokBool2x3:
 | |
|         new(&type) TType(EbtBool, EvqTemporary, 0, 2, 3);
 | |
|         break;
 | |
|     case EHTokBool2x4:
 | |
|         new(&type) TType(EbtBool, EvqTemporary, 0, 2, 4);
 | |
|         break;
 | |
|     case EHTokBool3x1:
 | |
|         new(&type) TType(EbtBool, EvqTemporary, 0, 3, 1);
 | |
|         break;
 | |
|     case EHTokBool3x2:
 | |
|         new(&type) TType(EbtBool, EvqTemporary, 0, 3, 2);
 | |
|         break;
 | |
|     case EHTokBool3x3:
 | |
|         new(&type) TType(EbtBool, EvqTemporary, 0, 3, 3);
 | |
|         break;
 | |
|     case EHTokBool3x4:
 | |
|         new(&type) TType(EbtBool, EvqTemporary, 0, 3, 4);
 | |
|         break;
 | |
|     case EHTokBool4x1:
 | |
|         new(&type) TType(EbtBool, EvqTemporary, 0, 4, 1);
 | |
|         break;
 | |
|     case EHTokBool4x2:
 | |
|         new(&type) TType(EbtBool, EvqTemporary, 0, 4, 2);
 | |
|         break;
 | |
|     case EHTokBool4x3:
 | |
|         new(&type) TType(EbtBool, EvqTemporary, 0, 4, 3);
 | |
|         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, 1, 2);
 | |
|         break;
 | |
|     case EHTokFloat1x3:
 | |
|         new(&type) TType(EbtFloat, EvqTemporary, 0, 1, 3);
 | |
|         break;
 | |
|     case EHTokFloat1x4:
 | |
|         new(&type) TType(EbtFloat, EvqTemporary, 0, 1, 4);
 | |
|         break;
 | |
|     case EHTokFloat2x1:
 | |
|         new(&type) TType(EbtFloat, EvqTemporary, 0, 2, 1);
 | |
|         break;
 | |
|     case EHTokFloat2x2:
 | |
|         new(&type) TType(EbtFloat, EvqTemporary, 0, 2, 2);
 | |
|         break;
 | |
|     case EHTokFloat2x3:
 | |
|         new(&type) TType(EbtFloat, EvqTemporary, 0, 2, 3);
 | |
|         break;
 | |
|     case EHTokFloat2x4:
 | |
|         new(&type) TType(EbtFloat, EvqTemporary, 0, 2, 4);
 | |
|         break;
 | |
|     case EHTokFloat3x1:
 | |
|         new(&type) TType(EbtFloat, EvqTemporary, 0, 3, 1);
 | |
|         break;
 | |
|     case EHTokFloat3x2:
 | |
|         new(&type) TType(EbtFloat, EvqTemporary, 0, 3, 2);
 | |
|         break;
 | |
|     case EHTokFloat3x3:
 | |
|         new(&type) TType(EbtFloat, EvqTemporary, 0, 3, 3);
 | |
|         break;
 | |
|     case EHTokFloat3x4:
 | |
|         new(&type) TType(EbtFloat, EvqTemporary, 0, 3, 4);
 | |
|         break;
 | |
|     case EHTokFloat4x1:
 | |
|         new(&type) TType(EbtFloat, EvqTemporary, 0, 4, 1);
 | |
|         break;
 | |
|     case EHTokFloat4x2:
 | |
|         new(&type) TType(EbtFloat, EvqTemporary, 0, 4, 2);
 | |
|         break;
 | |
|     case EHTokFloat4x3:
 | |
|         new(&type) TType(EbtFloat, EvqTemporary, 0, 4, 3);
 | |
|         break;
 | |
|     case EHTokFloat4x4:
 | |
|         new(&type) TType(EbtFloat, EvqTemporary, 0, 4, 4);
 | |
|         break;
 | |
| 
 | |
|     case EHTokHalf1x1:
 | |
|         new(&type) TType(half_bt, EvqTemporary, 0, 1, 1);
 | |
|         break;
 | |
|     case EHTokHalf1x2:
 | |
|         new(&type) TType(half_bt, EvqTemporary, 0, 1, 2);
 | |
|         break;
 | |
|     case EHTokHalf1x3:
 | |
|         new(&type) TType(half_bt, EvqTemporary, 0, 1, 3);
 | |
|         break;
 | |
|     case EHTokHalf1x4:
 | |
|         new(&type) TType(half_bt, EvqTemporary, 0, 1, 4);
 | |
|         break;
 | |
|     case EHTokHalf2x1:
 | |
|         new(&type) TType(half_bt, EvqTemporary, 0, 2, 1);
 | |
|         break;
 | |
|     case EHTokHalf2x2:
 | |
|         new(&type) TType(half_bt, EvqTemporary, 0, 2, 2);
 | |
|         break;
 | |
|     case EHTokHalf2x3:
 | |
|         new(&type) TType(half_bt, EvqTemporary, 0, 2, 3);
 | |
|         break;
 | |
|     case EHTokHalf2x4:
 | |
|         new(&type) TType(half_bt, EvqTemporary, 0, 2, 4);
 | |
|         break;
 | |
|     case EHTokHalf3x1:
 | |
|         new(&type) TType(half_bt, EvqTemporary, 0, 3, 1);
 | |
|         break;
 | |
|     case EHTokHalf3x2:
 | |
|         new(&type) TType(half_bt, EvqTemporary, 0, 3, 2);
 | |
|         break;
 | |
|     case EHTokHalf3x3:
 | |
|         new(&type) TType(half_bt, EvqTemporary, 0, 3, 3);
 | |
|         break;
 | |
|     case EHTokHalf3x4:
 | |
|         new(&type) TType(half_bt, EvqTemporary, 0, 3, 4);
 | |
|         break;
 | |
|     case EHTokHalf4x1:
 | |
|         new(&type) TType(half_bt, EvqTemporary, 0, 4, 1);
 | |
|         break;
 | |
|     case EHTokHalf4x2:
 | |
|         new(&type) TType(half_bt, EvqTemporary, 0, 4, 2);
 | |
|         break;
 | |
|     case EHTokHalf4x3:
 | |
|         new(&type) TType(half_bt, EvqTemporary, 0, 4, 3);
 | |
|         break;
 | |
|     case EHTokHalf4x4:
 | |
|         new(&type) TType(half_bt, EvqTemporary, 0, 4, 4);
 | |
|         break;
 | |
| 
 | |
|     case EHTokDouble1x1:
 | |
|         new(&type) TType(EbtDouble, EvqTemporary, 0, 1, 1);
 | |
|         break;
 | |
|     case EHTokDouble1x2:
 | |
|         new(&type) TType(EbtDouble, EvqTemporary, 0, 1, 2);
 | |
|         break;
 | |
|     case EHTokDouble1x3:
 | |
|         new(&type) TType(EbtDouble, EvqTemporary, 0, 1, 3);
 | |
|         break;
 | |
|     case EHTokDouble1x4:
 | |
|         new(&type) TType(EbtDouble, EvqTemporary, 0, 1, 4);
 | |
|         break;
 | |
|     case EHTokDouble2x1:
 | |
|         new(&type) TType(EbtDouble, EvqTemporary, 0, 2, 1);
 | |
|         break;
 | |
|     case EHTokDouble2x2:
 | |
|         new(&type) TType(EbtDouble, EvqTemporary, 0, 2, 2);
 | |
|         break;
 | |
|     case EHTokDouble2x3:
 | |
|         new(&type) TType(EbtDouble, EvqTemporary, 0, 2, 3);
 | |
|         break;
 | |
|     case EHTokDouble2x4:
 | |
|         new(&type) TType(EbtDouble, EvqTemporary, 0, 2, 4);
 | |
|         break;
 | |
|     case EHTokDouble3x1:
 | |
|         new(&type) TType(EbtDouble, EvqTemporary, 0, 3, 1);
 | |
|         break;
 | |
|     case EHTokDouble3x2:
 | |
|         new(&type) TType(EbtDouble, EvqTemporary, 0, 3, 2);
 | |
|         break;
 | |
|     case EHTokDouble3x3:
 | |
|         new(&type) TType(EbtDouble, EvqTemporary, 0, 3, 3);
 | |
|         break;
 | |
|     case EHTokDouble3x4:
 | |
|         new(&type) TType(EbtDouble, EvqTemporary, 0, 3, 4);
 | |
|         break;
 | |
|     case EHTokDouble4x1:
 | |
|         new(&type) TType(EbtDouble, EvqTemporary, 0, 4, 1);
 | |
|         break;
 | |
|     case EHTokDouble4x2:
 | |
|         new(&type) TType(EbtDouble, EvqTemporary, 0, 4, 2);
 | |
|         break;
 | |
|     case EHTokDouble4x3:
 | |
|         new(&type) TType(EbtDouble, EvqTemporary, 0, 4, 3);
 | |
|         break;
 | |
|     case EHTokDouble4x4:
 | |
|         new(&type) TType(EbtDouble, EvqTemporary, 0, 4, 4);
 | |
|         break;
 | |
| 
 | |
|     default:
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     advanceToken();
 | |
| 
 | |
|     if ((isUnorm || isSnorm) && !type.isFloatingDomain()) {
 | |
|         parseContext.error(token.loc, "unorm and snorm only valid in floating point domain", "", "");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| // struct
 | |
| //      : struct_type IDENTIFIER post_decls LEFT_BRACE struct_declaration_list RIGHT_BRACE
 | |
| //      | struct_type            post_decls LEFT_BRACE struct_declaration_list RIGHT_BRACE
 | |
| //      | struct_type IDENTIFIER // use of previously declared struct type
 | |
| //
 | |
| // struct_type
 | |
| //      : STRUCT
 | |
| //      | CLASS
 | |
| //      | CBUFFER
 | |
| //      | TBUFFER
 | |
| //
 | |
| bool HlslGrammar::acceptStruct(TType& type, TIntermNode*& nodeList)
 | |
| {
 | |
|     // This storage qualifier will tell us whether it's an AST
 | |
|     // block type or just a generic structure type.
 | |
|     TStorageQualifier storageQualifier = EvqTemporary;
 | |
|     bool readonly = false;
 | |
| 
 | |
|     if (acceptTokenClass(EHTokCBuffer)) {
 | |
|         // CBUFFER
 | |
|         storageQualifier = EvqUniform;
 | |
|     } else if (acceptTokenClass(EHTokTBuffer)) {
 | |
|         // TBUFFER
 | |
|         storageQualifier = EvqBuffer;
 | |
|         readonly = true;
 | |
|     } else if (! acceptTokenClass(EHTokClass) && ! acceptTokenClass(EHTokStruct)) {
 | |
|         // Neither CLASS nor STRUCT
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     // Now known to be one of CBUFFER, TBUFFER, CLASS, or STRUCT
 | |
| 
 | |
| 
 | |
|     // IDENTIFIER.  It might also be a keyword which can double as an identifier.
 | |
|     // For example:  'cbuffer ConstantBuffer' or 'struct ConstantBuffer' is legal.
 | |
|     // 'cbuffer int' is also legal, and 'struct int' appears rejected only because
 | |
|     // it attempts to redefine the 'int' type.
 | |
|     const char* idString = getTypeString(peek());
 | |
|     TString structName = "";
 | |
|     if (peekTokenClass(EHTokIdentifier) || idString != nullptr) {
 | |
|         if (idString != nullptr)
 | |
|             structName = *idString;
 | |
|         else
 | |
|             structName = *token.string;
 | |
|         advanceToken();
 | |
|     }
 | |
| 
 | |
|     // post_decls
 | |
|     TQualifier postDeclQualifier;
 | |
|     postDeclQualifier.clear();
 | |
|     bool postDeclsFound = acceptPostDecls(postDeclQualifier);
 | |
| 
 | |
|     // LEFT_BRACE, or
 | |
|     // struct_type IDENTIFIER
 | |
|     if (! acceptTokenClass(EHTokLeftBrace)) {
 | |
|         if (structName.size() > 0 && !postDeclsFound && parseContext.lookupUserType(structName, type) != nullptr) {
 | |
|             // struct_type IDENTIFIER
 | |
|             return true;
 | |
|         } else {
 | |
|             expected("{");
 | |
|             return false;
 | |
|         }
 | |
|     }
 | |
| 
 | |
| 
 | |
|     // struct_declaration_list
 | |
|     TTypeList* typeList;
 | |
|     // Save each member function so they can be processed after we have a fully formed 'this'.
 | |
|     TVector<TFunctionDeclarator> functionDeclarators;
 | |
| 
 | |
|     parseContext.pushNamespace(structName);
 | |
|     bool acceptedList = acceptStructDeclarationList(typeList, nodeList, functionDeclarators);
 | |
|     parseContext.popNamespace();
 | |
| 
 | |
|     if (! acceptedList) {
 | |
|         expected("struct member declarations");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     // RIGHT_BRACE
 | |
|     if (! acceptTokenClass(EHTokRightBrace)) {
 | |
|         expected("}");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     // create the user-defined type
 | |
|     if (storageQualifier == EvqTemporary)
 | |
|         new(&type) TType(typeList, structName);
 | |
|     else {
 | |
|         postDeclQualifier.storage = storageQualifier;
 | |
|         postDeclQualifier.readonly = readonly;
 | |
|         new(&type) TType(typeList, structName, postDeclQualifier); // sets EbtBlock
 | |
|     }
 | |
| 
 | |
|     parseContext.declareStruct(token.loc, structName, type);
 | |
| 
 | |
|     // For member functions: now that we know the type of 'this', go back and
 | |
|     // - add their implicit argument with 'this' (not to the mangling, just the argument list)
 | |
|     // - parse the functions, their tokens were saved for deferred parsing (now)
 | |
|     for (int b = 0; b < (int)functionDeclarators.size(); ++b) {
 | |
|         // update signature
 | |
|         if (functionDeclarators[b].function->hasImplicitThis())
 | |
|             functionDeclarators[b].function->addThisParameter(type, intermediate.implicitThisName);
 | |
|     }
 | |
| 
 | |
|     // All member functions get parsed inside the class/struct namespace and with the
 | |
|     // class/struct members in a symbol-table level.
 | |
|     parseContext.pushNamespace(structName);
 | |
|     parseContext.pushThisScope(type, functionDeclarators);
 | |
|     bool deferredSuccess = true;
 | |
|     for (int b = 0; b < (int)functionDeclarators.size() && deferredSuccess; ++b) {
 | |
|         // parse body
 | |
|         pushTokenStream(functionDeclarators[b].body);
 | |
|         if (! acceptFunctionBody(functionDeclarators[b], nodeList))
 | |
|             deferredSuccess = false;
 | |
|         popTokenStream();
 | |
|     }
 | |
|     parseContext.popThisScope();
 | |
|     parseContext.popNamespace();
 | |
| 
 | |
|     return deferredSuccess;
 | |
| }
 | |
| 
 | |
| // constantbuffer
 | |
| //    : CONSTANTBUFFER LEFT_ANGLE type RIGHT_ANGLE
 | |
| bool HlslGrammar::acceptConstantBufferType(TType& type)
 | |
| {
 | |
|     if (! acceptTokenClass(EHTokConstantBuffer))
 | |
|         return false;
 | |
| 
 | |
|     if (! acceptTokenClass(EHTokLeftAngle)) {
 | |
|         expected("left angle bracket");
 | |
|         return false;
 | |
|     }
 | |
|     
 | |
|     TType templateType;
 | |
|     if (! acceptType(templateType)) {
 | |
|         expected("type");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     if (! acceptTokenClass(EHTokRightAngle)) {
 | |
|         expected("right angle bracket");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     TQualifier postDeclQualifier;
 | |
|     postDeclQualifier.clear();
 | |
|     postDeclQualifier.storage = EvqUniform;
 | |
| 
 | |
|     if (templateType.isStruct()) {
 | |
|         // Make a block from the type parsed as the template argument
 | |
|         TTypeList* typeList = templateType.getWritableStruct();
 | |
|         new(&type) TType(typeList, "", postDeclQualifier); // sets EbtBlock
 | |
| 
 | |
|         type.getQualifier().storage = EvqUniform;
 | |
| 
 | |
|         return true;
 | |
|     } else {
 | |
|         parseContext.error(token.loc, "non-structure type in ConstantBuffer", "", "");
 | |
|         return false;
 | |
|     }
 | |
| }
 | |
| 
 | |
| // texture_buffer
 | |
| //    : TEXTUREBUFFER LEFT_ANGLE type RIGHT_ANGLE
 | |
| bool HlslGrammar::acceptTextureBufferType(TType& type)
 | |
| {
 | |
|     if (! acceptTokenClass(EHTokTextureBuffer))
 | |
|         return false;
 | |
| 
 | |
|     if (! acceptTokenClass(EHTokLeftAngle)) {
 | |
|         expected("left angle bracket");
 | |
|         return false;
 | |
|     }
 | |
|     
 | |
|     TType templateType;
 | |
|     if (! acceptType(templateType)) {
 | |
|         expected("type");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     if (! acceptTokenClass(EHTokRightAngle)) {
 | |
|         expected("right angle bracket");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     templateType.getQualifier().storage = EvqBuffer;
 | |
|     templateType.getQualifier().readonly = true;
 | |
| 
 | |
|     TType blockType(templateType.getWritableStruct(), "", templateType.getQualifier());
 | |
| 
 | |
|     blockType.getQualifier().storage = EvqBuffer;
 | |
|     blockType.getQualifier().readonly = true;
 | |
| 
 | |
|     type.shallowCopy(blockType);
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| 
 | |
| // struct_buffer
 | |
| //    : APPENDSTRUCTUREDBUFFER
 | |
| //    | BYTEADDRESSBUFFER
 | |
| //    | CONSUMESTRUCTUREDBUFFER
 | |
| //    | RWBYTEADDRESSBUFFER
 | |
| //    | RWSTRUCTUREDBUFFER
 | |
| //    | STRUCTUREDBUFFER
 | |
| bool HlslGrammar::acceptStructBufferType(TType& type)
 | |
| {
 | |
|     const EHlslTokenClass structBuffType = peek();
 | |
| 
 | |
|     // TODO: globallycoherent
 | |
|     bool hasTemplateType = true;
 | |
|     bool readonly = false;
 | |
| 
 | |
|     TStorageQualifier storage = EvqBuffer;
 | |
|     TBuiltInVariable  builtinType = EbvNone;
 | |
| 
 | |
|     switch (structBuffType) {
 | |
|     case EHTokAppendStructuredBuffer:
 | |
|         builtinType = EbvAppendConsume;
 | |
|         break;
 | |
|     case EHTokByteAddressBuffer:
 | |
|         hasTemplateType = false;
 | |
|         readonly = true;
 | |
|         builtinType = EbvByteAddressBuffer;
 | |
|         break;
 | |
|     case EHTokConsumeStructuredBuffer:
 | |
|         builtinType = EbvAppendConsume;
 | |
|         break;
 | |
|     case EHTokRWByteAddressBuffer:
 | |
|         hasTemplateType = false;
 | |
|         builtinType = EbvRWByteAddressBuffer;
 | |
|         break;
 | |
|     case EHTokRWStructuredBuffer:
 | |
|         builtinType = EbvRWStructuredBuffer;
 | |
|         break;
 | |
|     case EHTokStructuredBuffer:
 | |
|         builtinType = EbvStructuredBuffer;
 | |
|         readonly = true;
 | |
|         break;
 | |
|     default:
 | |
|         return false;  // not a structure buffer type
 | |
|     }
 | |
| 
 | |
|     advanceToken();  // consume the structure keyword
 | |
| 
 | |
|     // type on which this StructedBuffer is templatized.  E.g, StructedBuffer<MyStruct> ==> MyStruct
 | |
|     TType* templateType = new TType;
 | |
| 
 | |
|     if (hasTemplateType) {
 | |
|         if (! acceptTokenClass(EHTokLeftAngle)) {
 | |
|             expected("left angle bracket");
 | |
|             return false;
 | |
|         }
 | |
|     
 | |
|         if (! acceptType(*templateType)) {
 | |
|             expected("type");
 | |
|             return false;
 | |
|         }
 | |
|         if (! acceptTokenClass(EHTokRightAngle)) {
 | |
|             expected("right angle bracket");
 | |
|             return false;
 | |
|         }
 | |
|     } else {
 | |
|         // byte address buffers have no explicit type.
 | |
|         TType uintType(EbtUint, storage);
 | |
|         templateType->shallowCopy(uintType);
 | |
|     }
 | |
| 
 | |
|     // Create an unsized array out of that type.
 | |
|     // TODO: does this work if it's already an array type?
 | |
|     TArraySizes* unsizedArray = new TArraySizes;
 | |
|     unsizedArray->addInnerSize(UnsizedArraySize);
 | |
|     templateType->transferArraySizes(unsizedArray);
 | |
|     templateType->getQualifier().storage = storage;
 | |
| 
 | |
|     // field name is canonical for all structbuffers
 | |
|     templateType->setFieldName("@data");
 | |
| 
 | |
|     TTypeList* blockStruct = new TTypeList;
 | |
|     TTypeLoc  member = { templateType, token.loc };
 | |
|     blockStruct->push_back(member);
 | |
| 
 | |
|     // This is the type of the buffer block (SSBO)
 | |
|     TType blockType(blockStruct, "", templateType->getQualifier());
 | |
| 
 | |
|     blockType.getQualifier().storage = storage;
 | |
|     blockType.getQualifier().readonly = readonly;
 | |
|     blockType.getQualifier().builtIn = builtinType;
 | |
| 
 | |
|     // We may have created an equivalent type before, in which case we should use its
 | |
|     // deep structure.
 | |
|     parseContext.shareStructBufferType(blockType);
 | |
| 
 | |
|     type.shallowCopy(blockType);
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| // struct_declaration_list
 | |
| //      : struct_declaration SEMI_COLON struct_declaration SEMI_COLON ...
 | |
| //
 | |
| // struct_declaration
 | |
| //      : attributes fully_specified_type struct_declarator COMMA struct_declarator ...
 | |
| //      | attributes fully_specified_type IDENTIFIER function_parameters post_decls compound_statement // member-function definition
 | |
| //
 | |
| // struct_declarator
 | |
| //      : IDENTIFIER post_decls
 | |
| //      | IDENTIFIER array_specifier post_decls
 | |
| //      | IDENTIFIER function_parameters post_decls                                         // member-function prototype
 | |
| //
 | |
| bool HlslGrammar::acceptStructDeclarationList(TTypeList*& typeList, TIntermNode*& nodeList,
 | |
|                                               TVector<TFunctionDeclarator>& declarators)
 | |
| {
 | |
|     typeList = new TTypeList();
 | |
|     HlslToken idToken;
 | |
| 
 | |
|     do {
 | |
|         // success on seeing the RIGHT_BRACE coming up
 | |
|         if (peekTokenClass(EHTokRightBrace))
 | |
|             break;
 | |
| 
 | |
|         // struct_declaration
 | |
| 
 | |
|         // attributes
 | |
|         TAttributes attributes;
 | |
|         acceptAttributes(attributes);
 | |
| 
 | |
|         bool declarator_list = false;
 | |
| 
 | |
|         // fully_specified_type
 | |
|         TType memberType;
 | |
|         if (! acceptFullySpecifiedType(memberType, nodeList, attributes)) {
 | |
|             expected("member type");
 | |
|             return false;
 | |
|         }
 | |
|         
 | |
|         // merge in the attributes
 | |
|         parseContext.transferTypeAttributes(token.loc, attributes, memberType);
 | |
| 
 | |
|         // struct_declarator COMMA struct_declarator ...
 | |
|         bool functionDefinitionAccepted = false;
 | |
|         do {
 | |
|             if (! acceptIdentifier(idToken)) {
 | |
|                 expected("member name");
 | |
|                 return false;
 | |
|             }
 | |
| 
 | |
|             if (peekTokenClass(EHTokLeftParen)) {
 | |
|                 // function_parameters
 | |
|                 if (!declarator_list) {
 | |
|                     declarators.resize(declarators.size() + 1);
 | |
|                     // request a token stream for deferred processing
 | |
|                     functionDefinitionAccepted = acceptMemberFunctionDefinition(nodeList, memberType, *idToken.string,
 | |
|                                                                                 declarators.back());
 | |
|                     if (functionDefinitionAccepted)
 | |
|                         break;
 | |
|                 }
 | |
|                 expected("member-function definition");
 | |
|                 return false;
 | |
|             } else {
 | |
|                 // add it to the list of members
 | |
|                 TTypeLoc member = { new TType(EbtVoid), token.loc };
 | |
|                 member.type->shallowCopy(memberType);
 | |
|                 member.type->setFieldName(*idToken.string);
 | |
|                 typeList->push_back(member);
 | |
| 
 | |
|                 // array_specifier
 | |
|                 TArraySizes* arraySizes = nullptr;
 | |
|                 acceptArraySpecifier(arraySizes);
 | |
|                 if (arraySizes)
 | |
|                     typeList->back().type->transferArraySizes(arraySizes);
 | |
| 
 | |
|                 acceptPostDecls(member.type->getQualifier());
 | |
| 
 | |
|                 // EQUAL assignment_expression
 | |
|                 if (acceptTokenClass(EHTokAssign)) {
 | |
|                     parseContext.warn(idToken.loc, "struct-member initializers ignored", "typedef", "");
 | |
|                     TIntermTyped* expressionNode = nullptr;
 | |
|                     if (! acceptAssignmentExpression(expressionNode)) {
 | |
|                         expected("initializer");
 | |
|                         return false;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             // success on seeing the SEMICOLON coming up
 | |
|             if (peekTokenClass(EHTokSemicolon))
 | |
|                 break;
 | |
| 
 | |
|             // COMMA
 | |
|             if (acceptTokenClass(EHTokComma))
 | |
|                 declarator_list = true;
 | |
|             else {
 | |
|                 expected(",");
 | |
|                 return false;
 | |
|             }
 | |
| 
 | |
|         } while (true);
 | |
| 
 | |
|         // SEMI_COLON
 | |
|         if (! functionDefinitionAccepted && ! acceptTokenClass(EHTokSemicolon)) {
 | |
|             expected(";");
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|     } while (true);
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| // member_function_definition
 | |
| //    | function_parameters post_decls compound_statement
 | |
| //
 | |
| // Expects type to have EvqGlobal for a static member and
 | |
| // EvqTemporary for non-static member.
 | |
| bool HlslGrammar::acceptMemberFunctionDefinition(TIntermNode*& nodeList, const TType& type, TString& memberName,
 | |
|                                                  TFunctionDeclarator& declarator)
 | |
| {
 | |
|     bool accepted = false;
 | |
| 
 | |
|     TString* functionName = &memberName;
 | |
|     parseContext.getFullNamespaceName(functionName);
 | |
|     declarator.function = new TFunction(functionName, type);
 | |
|     if (type.getQualifier().storage == EvqTemporary)
 | |
|         declarator.function->setImplicitThis();
 | |
|     else
 | |
|         declarator.function->setIllegalImplicitThis();
 | |
| 
 | |
|     // function_parameters
 | |
|     if (acceptFunctionParameters(*declarator.function)) {
 | |
|         // post_decls
 | |
|         acceptPostDecls(declarator.function->getWritableType().getQualifier());
 | |
| 
 | |
|         // compound_statement (function body definition)
 | |
|         if (peekTokenClass(EHTokLeftBrace)) {
 | |
|             declarator.loc = token.loc;
 | |
|             declarator.body = new TVector<HlslToken>;
 | |
|             accepted = acceptFunctionDefinition(declarator, nodeList, declarator.body);
 | |
|         }
 | |
|     } else
 | |
|         expected("function parameter list");
 | |
| 
 | |
|     return accepted;
 | |
| }
 | |
| 
 | |
| // 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;
 | |
| }
 | |
| 
 | |
| // default_parameter_declaration
 | |
| //      : EQUAL conditional_expression
 | |
| //      : EQUAL initializer
 | |
| bool HlslGrammar::acceptDefaultParameterDeclaration(const TType& type, TIntermTyped*& node)
 | |
| {
 | |
|     node = nullptr;
 | |
| 
 | |
|     // Valid not to have a default_parameter_declaration
 | |
|     if (!acceptTokenClass(EHTokAssign))
 | |
|         return true;
 | |
| 
 | |
|     if (!acceptConditionalExpression(node)) {
 | |
|         if (!acceptInitializer(node))
 | |
|             return false;
 | |
| 
 | |
|         // For initializer lists, we have to const-fold into a constructor for the type, so build
 | |
|         // that.
 | |
|         TFunction* constructor = parseContext.makeConstructorCall(token.loc, type);
 | |
|         if (constructor == nullptr)  // cannot construct
 | |
|             return false;
 | |
| 
 | |
|         TIntermTyped* arguments = nullptr;
 | |
|         for (int i = 0; i < int(node->getAsAggregate()->getSequence().size()); i++)
 | |
|             parseContext.handleFunctionArgument(constructor, arguments, node->getAsAggregate()->getSequence()[i]->getAsTyped());
 | |
| 
 | |
|         node = parseContext.handleFunctionCall(token.loc, constructor, node);
 | |
|     }
 | |
| 
 | |
|     if (node == nullptr)
 | |
|         return false;
 | |
| 
 | |
|     // If this is simply a constant, we can use it directly.
 | |
|     if (node->getAsConstantUnion())
 | |
|         return true;
 | |
| 
 | |
|     // Otherwise, it has to be const-foldable.
 | |
|     TIntermTyped* origNode = node;
 | |
| 
 | |
|     node = intermediate.fold(node->getAsAggregate());
 | |
| 
 | |
|     if (node != nullptr && origNode != node)
 | |
|         return true;
 | |
| 
 | |
|     parseContext.error(token.loc, "invalid default parameter value", "", "");
 | |
| 
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| // parameter_declaration
 | |
| //      : attributes attributed_declaration
 | |
| //
 | |
| // attributed_declaration
 | |
| //      : fully_specified_type post_decls [ = default_parameter_declaration ]
 | |
| //      | fully_specified_type identifier array_specifier post_decls [ = default_parameter_declaration ]
 | |
| //
 | |
| bool HlslGrammar::acceptParameterDeclaration(TFunction& function)
 | |
| {
 | |
|     // attributes
 | |
|     TAttributes attributes;
 | |
|     acceptAttributes(attributes);
 | |
| 
 | |
|     // fully_specified_type
 | |
|     TType* type = new TType;
 | |
|     if (! acceptFullySpecifiedType(*type, attributes))
 | |
|         return false;
 | |
| 
 | |
|     // merge in the attributes
 | |
|     parseContext.transferTypeAttributes(token.loc, attributes, *type);
 | |
| 
 | |
|     // identifier
 | |
|     HlslToken idToken;
 | |
|     acceptIdentifier(idToken);
 | |
| 
 | |
|     // array_specifier
 | |
|     TArraySizes* arraySizes = nullptr;
 | |
|     acceptArraySpecifier(arraySizes);
 | |
|     if (arraySizes) {
 | |
|         if (arraySizes->hasUnsized()) {
 | |
|             parseContext.error(token.loc, "function parameter requires array size", "[]", "");
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         type->transferArraySizes(arraySizes);
 | |
|     }
 | |
| 
 | |
|     // post_decls
 | |
|     acceptPostDecls(type->getQualifier());
 | |
| 
 | |
|     TIntermTyped* defaultValue;
 | |
|     if (!acceptDefaultParameterDeclaration(*type, defaultValue))
 | |
|         return false;
 | |
| 
 | |
|     parseContext.paramFix(*type);
 | |
| 
 | |
|     // If any prior parameters have default values, all the parameters after that must as well.
 | |
|     if (defaultValue == nullptr && function.getDefaultParamCount() > 0) {
 | |
|         parseContext.error(idToken.loc, "invalid parameter after default value parameters", idToken.string->c_str(), "");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     TParameter param = { idToken.string, type, defaultValue };
 | |
|     function.addParameter(param);
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| // Do the work to create the function definition in addition to
 | |
| // parsing the body (compound_statement).
 | |
| //
 | |
| // If 'deferredTokens' are passed in, just get the token stream,
 | |
| // don't process.
 | |
| //
 | |
| bool HlslGrammar::acceptFunctionDefinition(TFunctionDeclarator& declarator, TIntermNode*& nodeList,
 | |
|                                            TVector<HlslToken>* deferredTokens)
 | |
| {
 | |
|     parseContext.handleFunctionDeclarator(declarator.loc, *declarator.function, false /* not prototype */);
 | |
| 
 | |
|     if (deferredTokens)
 | |
|         return captureBlockTokens(*deferredTokens);
 | |
|     else
 | |
|         return acceptFunctionBody(declarator, nodeList);
 | |
| }
 | |
| 
 | |
| bool HlslGrammar::acceptFunctionBody(TFunctionDeclarator& declarator, TIntermNode*& nodeList)
 | |
| {
 | |
|     // we might get back an entry-point
 | |
|     TIntermNode* entryPointNode = nullptr;
 | |
| 
 | |
|     // This does a pushScope()
 | |
|     TIntermNode* functionNode = parseContext.handleFunctionDefinition(declarator.loc, *declarator.function,
 | |
|                                                                       declarator.attributes, entryPointNode);
 | |
| 
 | |
|     // compound_statement
 | |
|     TIntermNode* functionBody = nullptr;
 | |
|     if (! acceptCompoundStatement(functionBody))
 | |
|         return false;
 | |
| 
 | |
|     // this does a popScope()
 | |
|     parseContext.handleFunctionBody(declarator.loc, *declarator.function, functionBody, functionNode);
 | |
| 
 | |
|     // Hook up the 1 or 2 function definitions.
 | |
|     nodeList = intermediate.growAggregate(nodeList, functionNode);
 | |
|     nodeList = intermediate.growAggregate(nodeList, entryPointNode);
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| // Accept an expression with parenthesis around it, where
 | |
| // the parenthesis ARE NOT expression parenthesis, but the
 | |
| // syntactically required ones like in "if ( expression )".
 | |
| //
 | |
| // Also accepts a declaration expression; "if (int a = expression)".
 | |
| //
 | |
| // Note this one is not set up to be speculative; as it gives
 | |
| // errors if not found.
 | |
| //
 | |
| bool HlslGrammar::acceptParenExpression(TIntermTyped*& expression)
 | |
| {
 | |
|     expression = nullptr;
 | |
| 
 | |
|     // LEFT_PAREN
 | |
|     if (! acceptTokenClass(EHTokLeftParen))
 | |
|         expected("(");
 | |
| 
 | |
|     bool decl = false;
 | |
|     TIntermNode* declNode = nullptr;
 | |
|     decl = acceptControlDeclaration(declNode);
 | |
|     if (decl) {
 | |
|         if (declNode == nullptr || declNode->getAsTyped() == nullptr) {
 | |
|             expected("initialized declaration");
 | |
|             return false;
 | |
|         } else
 | |
|             expression = declNode->getAsTyped();
 | |
|     } else {
 | |
|         // no declaration
 | |
|         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);
 | |
| }
 | |
| 
 | |
| // initializer
 | |
| //      : LEFT_BRACE RIGHT_BRACE
 | |
| //      | LEFT_BRACE initializer_list RIGHT_BRACE
 | |
| //
 | |
| // initializer_list
 | |
| //      : assignment_expression COMMA assignment_expression COMMA ...
 | |
| //
 | |
| bool HlslGrammar::acceptInitializer(TIntermTyped*& node)
 | |
| {
 | |
|     // LEFT_BRACE
 | |
|     if (! acceptTokenClass(EHTokLeftBrace))
 | |
|         return false;
 | |
| 
 | |
|     // RIGHT_BRACE
 | |
|     TSourceLoc loc = token.loc;
 | |
|     if (acceptTokenClass(EHTokRightBrace)) {
 | |
|         // a zero-length initializer list
 | |
|         node = intermediate.makeAggregate(loc);
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     // initializer_list
 | |
|     node = nullptr;
 | |
|     do {
 | |
|         // assignment_expression
 | |
|         TIntermTyped* expr;
 | |
|         if (! acceptAssignmentExpression(expr)) {
 | |
|             expected("assignment expression in initializer list");
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         const bool firstNode = (node == nullptr);
 | |
| 
 | |
|         node = intermediate.growAggregate(node, expr, loc);
 | |
| 
 | |
|         // If every sub-node in the list has qualifier EvqConst, the returned node becomes
 | |
|         // EvqConst.  Otherwise, it becomes EvqTemporary. That doesn't happen with e.g.
 | |
|         // EvqIn or EvqPosition, since the collection isn't EvqPosition if all the members are.
 | |
|         if (firstNode && expr->getQualifier().storage == EvqConst)
 | |
|             node->getQualifier().storage = EvqConst;
 | |
|         else if (expr->getQualifier().storage != EvqConst)
 | |
|             node->getQualifier().storage = EvqTemporary;
 | |
| 
 | |
|         // COMMA
 | |
|         if (acceptTokenClass(EHTokComma)) {
 | |
|             if (acceptTokenClass(EHTokRightBrace))  // allow trailing comma
 | |
|                 return true;
 | |
|             continue;
 | |
|         }
 | |
| 
 | |
|         // RIGHT_BRACE
 | |
|         if (acceptTokenClass(EHTokRightBrace))
 | |
|             return true;
 | |
| 
 | |
|         expected(", or }");
 | |
|         return false;
 | |
|     } while (true);
 | |
| }
 | |
| 
 | |
| // Accept an assignment expression, where assignment operations
 | |
| // associate right-to-left.  That is, it is implicit, for example
 | |
| //
 | |
| //    a op (b op (c op d))
 | |
| //
 | |
| // assigment_expression
 | |
| //      : initializer
 | |
| //      | conditional_expression
 | |
| //      | conditional_expression assign_op conditional_expression assign_op conditional_expression ...
 | |
| //
 | |
| bool HlslGrammar::acceptAssignmentExpression(TIntermTyped*& node)
 | |
| {
 | |
|     // initializer
 | |
|     if (peekTokenClass(EHTokLeftBrace)) {
 | |
|         if (acceptInitializer(node))
 | |
|             return true;
 | |
| 
 | |
|         expected("initializer");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     // conditional_expression
 | |
|     if (! acceptConditionalExpression(node))
 | |
|         return false;
 | |
| 
 | |
|     // assignment operation?
 | |
|     TOperator assignOp = HlslOpMap::assignment(peek());
 | |
|     if (assignOp == EOpNull)
 | |
|         return true;
 | |
| 
 | |
|     // assign_op
 | |
|     TSourceLoc loc = token.loc;
 | |
|     advanceToken();
 | |
| 
 | |
|     // conditional_expression assign_op conditional_expression ...
 | |
|     // 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 = parseContext.handleAssign(loc, assignOp, node, rightNode);
 | |
|     node = parseContext.handleLvalue(loc, "assign", node);
 | |
| 
 | |
|     if (node == nullptr) {
 | |
|         parseContext.error(loc, "could not create assignment", "", "");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     if (! peekTokenClass(EHTokComma))
 | |
|         return true;
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| // Accept a conditional expression, which associates right-to-left,
 | |
| // accomplished by the "true" expression calling down to lower
 | |
| // precedence levels than this level.
 | |
| //
 | |
| // conditional_expression
 | |
| //      : binary_expression
 | |
| //      | binary_expression QUESTION expression COLON assignment_expression
 | |
| //
 | |
| bool HlslGrammar::acceptConditionalExpression(TIntermTyped*& node)
 | |
| {
 | |
|     // binary_expression
 | |
|     if (! acceptBinaryExpression(node, PlLogicalOr))
 | |
|         return false;
 | |
| 
 | |
|     if (! acceptTokenClass(EHTokQuestion))
 | |
|         return true;
 | |
| 
 | |
|     node = parseContext.convertConditionalExpression(token.loc, node, false);
 | |
|     if (node == nullptr)
 | |
|         return false;
 | |
| 
 | |
|     ++parseContext.controlFlowNestingLevel;  // this only needs to work right if no errors
 | |
| 
 | |
|     TIntermTyped* trueNode = nullptr;
 | |
|     if (! acceptExpression(trueNode)) {
 | |
|         expected("expression after ?");
 | |
|         return false;
 | |
|     }
 | |
|     TSourceLoc loc = token.loc;
 | |
| 
 | |
|     if (! acceptTokenClass(EHTokColon)) {
 | |
|         expected(":");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     TIntermTyped* falseNode = nullptr;
 | |
|     if (! acceptAssignmentExpression(falseNode)) {
 | |
|         expected("expression after :");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     --parseContext.controlFlowNestingLevel;
 | |
| 
 | |
|     node = intermediate.addSelection(node, trueNode, falseNode, loc);
 | |
| 
 | |
|     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;
 | |
| 
 | |
|     do {
 | |
|         TOperator op = HlslOpMap::binary(peek());
 | |
|         PrecedenceLevel tokenLevel = HlslOpMap::precedenceLevel(op);
 | |
|         if (tokenLevel < precedenceLevel)
 | |
|             return true;
 | |
| 
 | |
|         // ... 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 (node == nullptr) {
 | |
|             parseContext.error(loc, "Could not perform requested binary operation", "", "");
 | |
|             return false;
 | |
|         }
 | |
|     } 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)) {
 | |
|             // recognize any array_specifier as part of the type
 | |
|             TArraySizes* arraySizes = nullptr;
 | |
|             acceptArraySpecifier(arraySizes);
 | |
|             if (arraySizes != nullptr)
 | |
|                 castType.transferArraySizes(arraySizes);
 | |
|             TSourceLoc loc = token.loc;
 | |
|             if (acceptTokenClass(EHTokRightParen)) {
 | |
|                 // We've matched "(type)" now, get the expression to cast
 | |
|                 if (! acceptUnaryExpression(node))
 | |
|                     return false;
 | |
| 
 | |
|                 // Hook it up like a constructor
 | |
|                 TFunction* constructorFunction = parseContext.makeConstructorCall(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 node != nullptr;
 | |
|             } else {
 | |
|                 // This could be a parenthesized constructor, ala (int(3)), and we just accepted
 | |
|                 // the '(int' part.  We must back up twice.
 | |
|                 recedeToken();
 | |
|                 recedeToken();
 | |
| 
 | |
|                 // Note, there are no array constructors like
 | |
|                 //   (float[2](...))
 | |
|                 if (arraySizes != nullptr)
 | |
|                     parseContext.error(loc, "parenthesized array constructor not allowed", "([]())", "", "");
 | |
|             }
 | |
|         } 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);
 | |
| 
 | |
|     // These unary ops require lvalues
 | |
|     if (unaryOp == EOpPreIncrement || unaryOp == EOpPreDecrement)
 | |
|         node = parseContext.handleLvalue(loc, "unary operator", node);
 | |
| 
 | |
|     return node != nullptr;
 | |
| }
 | |
| 
 | |
| // postfix_expression
 | |
| //      : LEFT_PAREN expression RIGHT_PAREN
 | |
| //      | literal
 | |
| //      | constructor
 | |
| //      | IDENTIFIER [ COLONCOLON IDENTIFIER [ COLONCOLON IDENTIFIER ... ] ]
 | |
| //      | function_call
 | |
| //      | postfix_expression LEFT_BRACKET integer_expression RIGHT_BRACKET
 | |
| //      | postfix_expression DOT IDENTIFIER
 | |
| //      | postfix_expression DOT IDENTIFIER arguments
 | |
| //      | postfix_expression arguments
 | |
| //      | 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 a 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)
 | |
|     } else if (acceptConstructor(node)) {
 | |
|         // constructor (nothing else to do yet)
 | |
|     } else if (acceptIdentifier(idToken)) {
 | |
|         // user-type, namespace name, variable, or function name
 | |
|         TString* fullName = idToken.string;
 | |
|         while (acceptTokenClass(EHTokColonColon)) {
 | |
|             // user-type or namespace name
 | |
|             fullName = NewPoolTString(fullName->c_str());
 | |
|             fullName->append(parseContext.scopeMangler);
 | |
|             if (acceptIdentifier(idToken))
 | |
|                 fullName->append(*idToken.string);
 | |
|             else {
 | |
|                 expected("identifier after ::");
 | |
|                 return false;
 | |
|             }
 | |
|         }
 | |
|         if (! peekTokenClass(EHTokLeftParen)) {
 | |
|             node = parseContext.handleVariable(idToken.loc, fullName);
 | |
|             if (node == nullptr)
 | |
|                 return false;
 | |
|         } else if (acceptFunctionCall(idToken.loc, *fullName, node, nullptr)) {
 | |
|             // 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:
 | |
|         case EOpScoping:
 | |
|             advanceToken();
 | |
|             break;
 | |
|         default:
 | |
|             return true;
 | |
|         }
 | |
| 
 | |
|         // We have a valid post-unary operator, process it.
 | |
|         switch (postOp) {
 | |
|         case EOpScoping:
 | |
|         case EOpIndexDirectStruct:
 | |
|         {
 | |
|             // DOT IDENTIFIER
 | |
|             // includes swizzles, member variables, and member functions
 | |
|             HlslToken field;
 | |
|             if (! acceptIdentifier(field)) {
 | |
|                 expected("swizzle or member");
 | |
|                 return false;
 | |
|             }
 | |
| 
 | |
|             if (peekTokenClass(EHTokLeftParen)) {
 | |
|                 // member function
 | |
|                 TIntermTyped* thisNode = node;
 | |
| 
 | |
|                 // arguments
 | |
|                 if (! acceptFunctionCall(field.loc, *field.string, node, thisNode)) {
 | |
|                     expected("function parameters");
 | |
|                     return false;
 | |
|                 }
 | |
|             } else
 | |
|                 node = parseContext.handleDotDereference(field.loc, node, *field.string);
 | |
| 
 | |
|             break;
 | |
|         }
 | |
|         case EOpIndexIndirect:
 | |
|         {
 | |
|             // LEFT_BRACKET integer_expression RIGHT_BRACKET
 | |
|             TIntermTyped* indexNode = nullptr;
 | |
|             if (! acceptExpression(indexNode) ||
 | |
|                 ! peekTokenClass(EHTokRightBracket)) {
 | |
|                 expected("expression followed by ']'");
 | |
|                 return false;
 | |
|             }
 | |
|             advanceToken();
 | |
|             node = parseContext.handleBracketDereference(indexNode->getLoc(), node, indexNode);
 | |
|             if (node == nullptr)
 | |
|                 return false;
 | |
|             break;
 | |
|         }
 | |
|         case EOpPostIncrement:
 | |
|             // INC_OP
 | |
|             // fall through
 | |
|         case EOpPostDecrement:
 | |
|             // DEC_OP
 | |
|             node = intermediate.addUnaryMath(postOp, node, loc);
 | |
|             node = parseContext.handleLvalue(loc, "unary operator", node);
 | |
|             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.makeConstructorCall(token.loc, type);
 | |
|         if (constructorFunction == nullptr)
 | |
|             return false;
 | |
| 
 | |
|         // arguments
 | |
|         TIntermTyped* arguments = nullptr;
 | |
|         if (! acceptArguments(constructorFunction, arguments)) {
 | |
|             // It's possible this is a type keyword used as an identifier.  Put the token back
 | |
|             // for later use.
 | |
|             recedeToken();
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         // hook it up
 | |
|         node = parseContext.handleFunctionCall(arguments->getLoc(), constructorFunction, arguments);
 | |
| 
 | |
|         return node != nullptr;
 | |
|     }
 | |
| 
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| // The function_call identifier was already recognized, and passed in as idToken.
 | |
| //
 | |
| // function_call
 | |
| //      : [idToken] arguments
 | |
| //
 | |
| bool HlslGrammar::acceptFunctionCall(const TSourceLoc& loc, TString& name, TIntermTyped*& node, TIntermTyped* baseObject)
 | |
| {
 | |
|     // name
 | |
|     TString* functionName = nullptr;
 | |
|     if (baseObject == nullptr) {
 | |
|         functionName = &name;
 | |
|     } else if (parseContext.isBuiltInMethod(loc, baseObject, name)) {
 | |
|         // Built-in methods are not in the symbol table as methods, but as global functions
 | |
|         // taking an explicit 'this' as the first argument.
 | |
|         functionName = NewPoolTString(BUILTIN_PREFIX);
 | |
|         functionName->append(name);
 | |
|     } else {
 | |
|         if (! baseObject->getType().isStruct()) {
 | |
|             expected("structure");
 | |
|             return false;
 | |
|         }
 | |
|         functionName = NewPoolTString("");
 | |
|         functionName->append(baseObject->getType().getTypeName());
 | |
|         parseContext.addScopeMangler(*functionName);
 | |
|         functionName->append(name);
 | |
|     }
 | |
| 
 | |
|     // function
 | |
|     TFunction* function = new TFunction(functionName, TType(EbtVoid));
 | |
| 
 | |
|     // arguments
 | |
|     TIntermTyped* arguments = nullptr;
 | |
|     if (baseObject != nullptr) {
 | |
|         // Non-static member functions have an implicit first argument of the base object.
 | |
|         parseContext.handleFunctionArgument(function, arguments, baseObject);
 | |
|     }
 | |
|     if (! acceptArguments(function, arguments))
 | |
|         return false;
 | |
| 
 | |
|     // call
 | |
|     node = parseContext.handleFunctionCall(loc, function, arguments);
 | |
| 
 | |
|     return node != nullptr;
 | |
| }
 | |
| 
 | |
| // 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;
 | |
| 
 | |
|     // RIGHT_PAREN
 | |
|     if (acceptTokenClass(EHTokRightParen))
 | |
|         return true;
 | |
| 
 | |
|     // must now be at least one expression...
 | |
|     do {
 | |
|         // expression
 | |
|         TIntermTyped* arg;
 | |
|         if (! acceptAssignmentExpression(arg))
 | |
|             return false;
 | |
| 
 | |
|         // 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 EHTokUintConstant:
 | |
|         node = intermediate.addConstantUnion(token.u, token.loc, true);
 | |
|         break;
 | |
|     case EHTokFloat16Constant:
 | |
|         node = intermediate.addConstantUnion(token.d, EbtFloat16, 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;
 | |
|     case EHTokStringConstant:
 | |
|         node = intermediate.addConstantUnion(token.string, token.loc, true);
 | |
|         break;
 | |
| 
 | |
|     default:
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     advanceToken();
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| // simple_statement
 | |
| //      : SEMICOLON
 | |
| //      | declaration_statement
 | |
| //      | expression SEMICOLON
 | |
| //
 | |
| bool HlslGrammar::acceptSimpleStatement(TIntermNode*& statement)
 | |
| {
 | |
|     // SEMICOLON
 | |
|     if (acceptTokenClass(EHTokSemicolon))
 | |
|         return true;
 | |
| 
 | |
|     // declaration
 | |
|     if (acceptDeclaration(statement))
 | |
|         return true;
 | |
| 
 | |
|     // expression
 | |
|     TIntermTyped* node;
 | |
|     if (acceptExpression(node))
 | |
|         statement = node;
 | |
|     else
 | |
|         return false;
 | |
| 
 | |
|     // SEMICOLON (following an expression)
 | |
|     if (acceptTokenClass(EHTokSemicolon))
 | |
|         return true;
 | |
|     else {
 | |
|         expected(";");
 | |
|         return false;
 | |
|     }
 | |
| }
 | |
| 
 | |
| // 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)) {
 | |
|         TIntermBranch* branch = statement ? statement->getAsBranchNode() : nullptr;
 | |
|         if (branch != nullptr && (branch->getFlowOp() == EOpCase ||
 | |
|                                   branch->getFlowOp() == EOpDefault)) {
 | |
|             // hook up individual subsequences within a switch statement
 | |
|             parseContext.wrapupSwitchSubsequence(compoundStatement, statement);
 | |
|             compoundStatement = nullptr;
 | |
|         } else {
 | |
|             // hook it up to the growing compound statement
 | |
|             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
 | |
| //      | simple_statement
 | |
| //      | selection_statement
 | |
| //      | switch_statement
 | |
| //      | case_label
 | |
| //      | default_label
 | |
| //      | iteration_statement
 | |
| //      | jump_statement
 | |
| //
 | |
| bool HlslGrammar::acceptStatement(TIntermNode*& statement)
 | |
| {
 | |
|     statement = nullptr;
 | |
| 
 | |
|     // attributes
 | |
|     TAttributes attributes;
 | |
|     acceptAttributes(attributes);
 | |
| 
 | |
|     // attributed_statement
 | |
|     switch (peek()) {
 | |
|     case EHTokLeftBrace:
 | |
|         return acceptScopedCompoundStatement(statement);
 | |
| 
 | |
|     case EHTokIf:
 | |
|         return acceptSelectionStatement(statement, attributes);
 | |
| 
 | |
|     case EHTokSwitch:
 | |
|         return acceptSwitchStatement(statement, attributes);
 | |
| 
 | |
|     case EHTokFor:
 | |
|     case EHTokDo:
 | |
|     case EHTokWhile:
 | |
|         return acceptIterationStatement(statement, attributes);
 | |
| 
 | |
|     case EHTokContinue:
 | |
|     case EHTokBreak:
 | |
|     case EHTokDiscard:
 | |
|     case EHTokReturn:
 | |
|         return acceptJumpStatement(statement);
 | |
| 
 | |
|     case EHTokCase:
 | |
|         return acceptCaseLabel(statement);
 | |
|     case EHTokDefault:
 | |
|         return acceptDefaultLabel(statement);
 | |
| 
 | |
|     case EHTokRightBrace:
 | |
|         // Performance: not strictly necessary, but stops a bunch of hunting early,
 | |
|         // and is how sequences of statements end.
 | |
|         return false;
 | |
| 
 | |
|     default:
 | |
|         return acceptSimpleStatement(statement);
 | |
|     }
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| // attributes
 | |
| //      : [zero or more:] bracketed-attribute
 | |
| //
 | |
| // bracketed-attribute:
 | |
| //      : LEFT_BRACKET scoped-attribute RIGHT_BRACKET
 | |
| //      : LEFT_BRACKET LEFT_BRACKET scoped-attribute RIGHT_BRACKET RIGHT_BRACKET
 | |
| //
 | |
| // scoped-attribute:
 | |
| //      : attribute
 | |
| //      | namespace COLON COLON attribute
 | |
| //
 | |
| // attribute:
 | |
| //      : UNROLL
 | |
| //      | UNROLL LEFT_PAREN literal RIGHT_PAREN
 | |
| //      | FASTOPT
 | |
| //      | ALLOW_UAV_CONDITION
 | |
| //      | BRANCH
 | |
| //      | FLATTEN
 | |
| //      | FORCECASE
 | |
| //      | CALL
 | |
| //      | DOMAIN
 | |
| //      | EARLYDEPTHSTENCIL
 | |
| //      | INSTANCE
 | |
| //      | MAXTESSFACTOR
 | |
| //      | OUTPUTCONTROLPOINTS
 | |
| //      | OUTPUTTOPOLOGY
 | |
| //      | PARTITIONING
 | |
| //      | PATCHCONSTANTFUNC
 | |
| //      | NUMTHREADS LEFT_PAREN x_size, y_size,z z_size RIGHT_PAREN
 | |
| //
 | |
| void HlslGrammar::acceptAttributes(TAttributes& attributes)
 | |
| {
 | |
|     // For now, accept the [ XXX(X) ] syntax, but drop all but
 | |
|     // numthreads, which is used to set the CS local size.
 | |
|     // TODO: subset to correct set?  Pass on?
 | |
|     do {
 | |
|         HlslToken attributeToken;
 | |
| 
 | |
|         // LEFT_BRACKET?
 | |
|         if (! acceptTokenClass(EHTokLeftBracket))
 | |
|             return;
 | |
|         // another LEFT_BRACKET?
 | |
|         bool doubleBrackets = false;
 | |
|         if (acceptTokenClass(EHTokLeftBracket))
 | |
|             doubleBrackets = true;
 | |
| 
 | |
|         // attribute? (could be namespace; will adjust later)
 | |
|         if (!acceptIdentifier(attributeToken)) {
 | |
|             if (!peekTokenClass(EHTokRightBracket)) {
 | |
|                 expected("namespace or attribute identifier");
 | |
|                 advanceToken();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         TString nameSpace;
 | |
|         if (acceptTokenClass(EHTokColonColon)) {
 | |
|             // namespace COLON COLON
 | |
|             nameSpace = *attributeToken.string;
 | |
|             // attribute
 | |
|             if (!acceptIdentifier(attributeToken)) {
 | |
|                 expected("attribute identifier");
 | |
|                 return;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         TIntermAggregate* expressions = nullptr;
 | |
| 
 | |
|         // (x, ...)
 | |
|         if (acceptTokenClass(EHTokLeftParen)) {
 | |
|             expressions = new TIntermAggregate;
 | |
| 
 | |
|             TIntermTyped* node;
 | |
|             bool expectingExpression = false;
 | |
| 
 | |
|             while (acceptAssignmentExpression(node)) {
 | |
|                 expectingExpression = false;
 | |
|                 expressions->getSequence().push_back(node);
 | |
|                 if (acceptTokenClass(EHTokComma))
 | |
|                     expectingExpression = true;
 | |
|             }
 | |
| 
 | |
|             // 'expressions' is an aggregate with the expressions in it
 | |
|             if (! acceptTokenClass(EHTokRightParen))
 | |
|                 expected(")");
 | |
| 
 | |
|             // Error for partial or missing expression
 | |
|             if (expectingExpression || expressions->getSequence().empty())
 | |
|                 expected("expression");
 | |
|         }
 | |
| 
 | |
|         // RIGHT_BRACKET
 | |
|         if (!acceptTokenClass(EHTokRightBracket)) {
 | |
|             expected("]");
 | |
|             return;
 | |
|         }
 | |
|         // another RIGHT_BRACKET?
 | |
|         if (doubleBrackets && !acceptTokenClass(EHTokRightBracket)) {
 | |
|             expected("]]");
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         // Add any values we found into the attribute map.
 | |
|         if (attributeToken.string != nullptr) {
 | |
|             TAttributeType attributeType = parseContext.attributeFromName(nameSpace, *attributeToken.string);
 | |
|             if (attributeType == EatNone)
 | |
|                 parseContext.warn(attributeToken.loc, "unrecognized attribute", attributeToken.string->c_str(), "");
 | |
|             else {
 | |
|                 TAttributeArgs attributeArgs = { attributeType, expressions };
 | |
|                 attributes.push_back(attributeArgs);
 | |
|             }
 | |
|         }
 | |
|     } 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, const TAttributes& attributes)
 | |
| {
 | |
|     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;
 | |
|     condition = parseContext.convertConditionalExpression(loc, condition);
 | |
|     if (condition == nullptr)
 | |
|         return false;
 | |
| 
 | |
|     // create the child statements
 | |
|     TIntermNodePair thenElse = { nullptr, nullptr };
 | |
| 
 | |
|     ++parseContext.controlFlowNestingLevel;  // this only needs to work right if no errors
 | |
| 
 | |
|     // 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.handleSelectionAttributes(loc, statement->getAsSelectionNode(), attributes);
 | |
| 
 | |
|     parseContext.popScope();
 | |
|     --parseContext.controlFlowNestingLevel;
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| // switch_statement
 | |
| //      : SWITCH LEFT_PAREN expression RIGHT_PAREN compound_statement
 | |
| //
 | |
| bool HlslGrammar::acceptSwitchStatement(TIntermNode*& statement, const TAttributes& attributes)
 | |
| {
 | |
|     // SWITCH
 | |
|     TSourceLoc loc = token.loc;
 | |
| 
 | |
|     if (! acceptTokenClass(EHTokSwitch))
 | |
|         return false;
 | |
| 
 | |
|     // LEFT_PAREN expression RIGHT_PAREN
 | |
|     parseContext.pushScope();
 | |
|     TIntermTyped* switchExpression;
 | |
|     if (! acceptParenExpression(switchExpression)) {
 | |
|         parseContext.popScope();
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     // compound_statement
 | |
|     parseContext.pushSwitchSequence(new TIntermSequence);
 | |
| 
 | |
|     ++parseContext.controlFlowNestingLevel;
 | |
|     bool statementOkay = acceptCompoundStatement(statement);
 | |
|     --parseContext.controlFlowNestingLevel;
 | |
| 
 | |
|     if (statementOkay)
 | |
|         statement = parseContext.addSwitch(loc, switchExpression, statement ? statement->getAsAggregate() : nullptr,
 | |
|                                            attributes);
 | |
| 
 | |
|     parseContext.popSwitchSequence();
 | |
|     parseContext.popScope();
 | |
| 
 | |
|     return statementOkay;
 | |
| }
 | |
| 
 | |
| // 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, const TAttributes& attributes)
 | |
| {
 | |
|     TSourceLoc loc = token.loc;
 | |
|     TIntermTyped* condition = nullptr;
 | |
| 
 | |
|     EHlslTokenClass loop = peek();
 | |
|     assert(loop == EHTokDo || loop == EHTokFor || loop == EHTokWhile);
 | |
| 
 | |
|     //  WHILE or DO or FOR
 | |
|     advanceToken();
 | |
| 
 | |
|     TIntermLoop* loopNode = nullptr;
 | |
|     switch (loop) {
 | |
|     case EHTokWhile:
 | |
|         // so that something declared in the condition is scoped to the lifetime
 | |
|         // of the while sub-statement
 | |
|         parseContext.pushScope();  // this only needs to work right if no errors
 | |
|         parseContext.nestLooping();
 | |
|         ++parseContext.controlFlowNestingLevel;
 | |
| 
 | |
|         // LEFT_PAREN condition RIGHT_PAREN
 | |
|         if (! acceptParenExpression(condition))
 | |
|             return false;
 | |
|         condition = parseContext.convertConditionalExpression(loc, condition);
 | |
|         if (condition == nullptr)
 | |
|             return false;
 | |
| 
 | |
|         // statement
 | |
|         if (! acceptScopedStatement(statement)) {
 | |
|             expected("while sub-statement");
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         parseContext.unnestLooping();
 | |
|         parseContext.popScope();
 | |
|         --parseContext.controlFlowNestingLevel;
 | |
| 
 | |
|         loopNode = intermediate.addLoop(statement, condition, nullptr, true, loc);
 | |
|         statement = loopNode;
 | |
|         break;
 | |
| 
 | |
|     case EHTokDo:
 | |
|         parseContext.nestLooping();  // this only needs to work right if no errors
 | |
|         ++parseContext.controlFlowNestingLevel;
 | |
| 
 | |
|         // statement
 | |
|         if (! acceptScopedStatement(statement)) {
 | |
|             expected("do sub-statement");
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         // WHILE
 | |
|         if (! acceptTokenClass(EHTokWhile)) {
 | |
|             expected("while");
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         // LEFT_PAREN condition RIGHT_PAREN
 | |
|         if (! acceptParenExpression(condition))
 | |
|             return false;
 | |
|         condition = parseContext.convertConditionalExpression(loc, condition);
 | |
|         if (condition == nullptr)
 | |
|             return false;
 | |
| 
 | |
|         if (! acceptTokenClass(EHTokSemicolon))
 | |
|             expected(";");
 | |
| 
 | |
|         parseContext.unnestLooping();
 | |
|         --parseContext.controlFlowNestingLevel;
 | |
| 
 | |
|         loopNode = intermediate.addLoop(statement, condition, 0, false, loc);
 | |
|         statement = loopNode;
 | |
|         break;
 | |
| 
 | |
|     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
 | |
|         TIntermNode* initNode = nullptr;
 | |
|         if (! acceptSimpleStatement(initNode))
 | |
|             expected("for-loop initializer statement");
 | |
| 
 | |
|         parseContext.nestLooping();  // this only needs to work right if no errors
 | |
|         ++parseContext.controlFlowNestingLevel;
 | |
| 
 | |
|         // condition SEMI_COLON
 | |
|         acceptExpression(condition);
 | |
|         if (! acceptTokenClass(EHTokSemicolon))
 | |
|             expected(";");
 | |
|         if (condition != nullptr) {
 | |
|             condition = parseContext.convertConditionalExpression(loc, condition);
 | |
|             if (condition == nullptr)
 | |
|                 return false;
 | |
|         }
 | |
| 
 | |
|         // 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, initNode, condition, iterator, true, loc, loopNode);
 | |
| 
 | |
|         parseContext.popScope();
 | |
|         parseContext.unnestLooping();
 | |
|         --parseContext.controlFlowNestingLevel;
 | |
| 
 | |
|         break;
 | |
|     }
 | |
| 
 | |
|     default:
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     parseContext.handleLoopAttributes(loc, loopNode, attributes);
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| // jump_statement
 | |
| //      : CONTINUE SEMICOLON
 | |
| //      | BREAK SEMICOLON
 | |
| //      | DISCARD SEMICOLON
 | |
| //      | RETURN SEMICOLON
 | |
| //      | RETURN expression SEMICOLON
 | |
| //
 | |
| bool HlslGrammar::acceptJumpStatement(TIntermNode*& statement)
 | |
| {
 | |
|     EHlslTokenClass jump = peek();
 | |
|     switch (jump) {
 | |
|     case EHTokContinue:
 | |
|     case EHTokBreak:
 | |
|     case EHTokDiscard:
 | |
|     case EHTokReturn:
 | |
|         advanceToken();
 | |
|         break;
 | |
|     default:
 | |
|         // not something we handle in this function
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     switch (jump) {
 | |
|     case EHTokContinue:
 | |
|         statement = intermediate.addBranch(EOpContinue, token.loc);
 | |
|         if (parseContext.loopNestingLevel == 0) {
 | |
|             expected("loop");
 | |
|             return false;
 | |
|         }
 | |
|         break;
 | |
|     case EHTokBreak:
 | |
|         statement = intermediate.addBranch(EOpBreak, token.loc);
 | |
|         if (parseContext.loopNestingLevel == 0 && parseContext.switchSequenceStack.size() == 0) {
 | |
|             expected("loop or switch");
 | |
|             return false;
 | |
|         }
 | |
|         break;
 | |
|     case EHTokDiscard:
 | |
|         statement = intermediate.addBranch(EOpKill, token.loc);
 | |
|         break;
 | |
| 
 | |
|     case EHTokReturn:
 | |
|     {
 | |
|         // expression
 | |
|         TIntermTyped* node;
 | |
|         if (acceptExpression(node)) {
 | |
|             // hook it up
 | |
|             statement = parseContext.handleReturnValue(token.loc, node);
 | |
|         } else
 | |
|             statement = intermediate.addBranch(EOpReturn, token.loc);
 | |
|         break;
 | |
|     }
 | |
| 
 | |
|     default:
 | |
|         assert(0);
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     // SEMICOLON
 | |
|     if (! acceptTokenClass(EHTokSemicolon))
 | |
|         expected(";");
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| // case_label
 | |
| //      : CASE expression COLON
 | |
| //
 | |
| bool HlslGrammar::acceptCaseLabel(TIntermNode*& statement)
 | |
| {
 | |
|     TSourceLoc loc = token.loc;
 | |
|     if (! acceptTokenClass(EHTokCase))
 | |
|         return false;
 | |
| 
 | |
|     TIntermTyped* expression;
 | |
|     if (! acceptExpression(expression)) {
 | |
|         expected("case expression");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     if (! acceptTokenClass(EHTokColon)) {
 | |
|         expected(":");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     statement = parseContext.intermediate.addBranch(EOpCase, expression, loc);
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| // default_label
 | |
| //      : DEFAULT COLON
 | |
| //
 | |
| bool HlslGrammar::acceptDefaultLabel(TIntermNode*& statement)
 | |
| {
 | |
|     TSourceLoc loc = token.loc;
 | |
|     if (! acceptTokenClass(EHTokDefault))
 | |
|         return false;
 | |
| 
 | |
|     if (! acceptTokenClass(EHTokColon)) {
 | |
|         expected(":");
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     statement = parseContext.intermediate.addBranch(EOpDefault, loc);
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| // array_specifier
 | |
| //      : LEFT_BRACKET integer_expression RGHT_BRACKET ... // optional
 | |
| //      : LEFT_BRACKET RGHT_BRACKET // optional
 | |
| //
 | |
| void HlslGrammar::acceptArraySpecifier(TArraySizes*& arraySizes)
 | |
| {
 | |
|     arraySizes = nullptr;
 | |
| 
 | |
|     // Early-out if there aren't any array dimensions
 | |
|     if (!peekTokenClass(EHTokLeftBracket))
 | |
|         return;
 | |
| 
 | |
|     // If we get here, we have at least one array dimension.  This will track the sizes we find.
 | |
|     arraySizes = new TArraySizes;
 | |
| 
 | |
|     // Collect each array dimension.
 | |
|     while (acceptTokenClass(EHTokLeftBracket)) {
 | |
|         TSourceLoc loc = token.loc;
 | |
|         TIntermTyped* sizeExpr = nullptr;
 | |
| 
 | |
|         // Array sizing expression is optional.  If omitted, array will be later sized by initializer list.
 | |
|         const bool hasArraySize = acceptAssignmentExpression(sizeExpr);
 | |
| 
 | |
|         if (! acceptTokenClass(EHTokRightBracket)) {
 | |
|             expected("]");
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         if (hasArraySize) {
 | |
|             TArraySize arraySize;
 | |
|             parseContext.arraySizeCheck(loc, sizeExpr, arraySize);
 | |
|             arraySizes->addInnerSize(arraySize);
 | |
|         } else {
 | |
|             arraySizes->addInnerSize(0);  // sized by initializers.
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| // post_decls
 | |
| //      : COLON semantic // optional
 | |
| //        COLON PACKOFFSET LEFT_PAREN c[Subcomponent][.component] RIGHT_PAREN // optional
 | |
| //        COLON REGISTER LEFT_PAREN [shader_profile,] Type#[subcomp]opt (COMMA SPACEN)opt RIGHT_PAREN // optional
 | |
| //        COLON LAYOUT layout_qualifier_list
 | |
| //        annotations // optional
 | |
| //
 | |
| // Return true if any tokens were accepted. That is,
 | |
| // false can be returned on successfully recognizing nothing,
 | |
| // not necessarily meaning bad syntax.
 | |
| //
 | |
| bool HlslGrammar::acceptPostDecls(TQualifier& qualifier)
 | |
| {
 | |
|     bool found = false;
 | |
| 
 | |
|     do {
 | |
|         // COLON
 | |
|         if (acceptTokenClass(EHTokColon)) {
 | |
|             found = true;
 | |
|             HlslToken idToken;
 | |
|             if (peekTokenClass(EHTokLayout))
 | |
|                 acceptLayoutQualifierList(qualifier);
 | |
|             else if (acceptTokenClass(EHTokPackOffset)) {
 | |
|                 // PACKOFFSET LEFT_PAREN c[Subcomponent][.component] RIGHT_PAREN
 | |
|                 if (! acceptTokenClass(EHTokLeftParen)) {
 | |
|                     expected("(");
 | |
|                     return false;
 | |
|                 }
 | |
|                 HlslToken locationToken;
 | |
|                 if (! acceptIdentifier(locationToken)) {
 | |
|                     expected("c[subcomponent][.component]");
 | |
|                     return false;
 | |
|                 }
 | |
|                 HlslToken componentToken;
 | |
|                 if (acceptTokenClass(EHTokDot)) {
 | |
|                     if (! acceptIdentifier(componentToken)) {
 | |
|                         expected("component");
 | |
|                         return false;
 | |
|                     }
 | |
|                 }
 | |
|                 if (! acceptTokenClass(EHTokRightParen)) {
 | |
|                     expected(")");
 | |
|                     break;
 | |
|                 }
 | |
|                 parseContext.handlePackOffset(locationToken.loc, qualifier, *locationToken.string, componentToken.string);
 | |
|             } else if (! acceptIdentifier(idToken)) {
 | |
|                 expected("layout, semantic, packoffset, or register");
 | |
|                 return false;
 | |
|             } else if (*idToken.string == "register") {
 | |
|                 // REGISTER LEFT_PAREN [shader_profile,] Type#[subcomp]opt (COMMA SPACEN)opt RIGHT_PAREN
 | |
|                 // LEFT_PAREN
 | |
|                 if (! acceptTokenClass(EHTokLeftParen)) {
 | |
|                     expected("(");
 | |
|                     return false;
 | |
|                 }
 | |
|                 HlslToken registerDesc;  // for Type#
 | |
|                 HlslToken profile;
 | |
|                 if (! acceptIdentifier(registerDesc)) {
 | |
|                     expected("register number description");
 | |
|                     return false;
 | |
|                 }
 | |
|                 if (registerDesc.string->size() > 1 && !isdigit((*registerDesc.string)[1]) &&
 | |
|                                                        acceptTokenClass(EHTokComma)) {
 | |
|                     // Then we didn't really see the registerDesc yet, it was
 | |
|                     // actually the profile.  Adjust...
 | |
|                     profile = registerDesc;
 | |
|                     if (! acceptIdentifier(registerDesc)) {
 | |
|                         expected("register number description");
 | |
|                         return false;
 | |
|                     }
 | |
|                 }
 | |
|                 int subComponent = 0;
 | |
|                 if (acceptTokenClass(EHTokLeftBracket)) {
 | |
|                     // LEFT_BRACKET subcomponent RIGHT_BRACKET
 | |
|                     if (! peekTokenClass(EHTokIntConstant)) {
 | |
|                         expected("literal integer");
 | |
|                         return false;
 | |
|                     }
 | |
|                     subComponent = token.i;
 | |
|                     advanceToken();
 | |
|                     if (! acceptTokenClass(EHTokRightBracket)) {
 | |
|                         expected("]");
 | |
|                         break;
 | |
|                     }
 | |
|                 }
 | |
|                 // (COMMA SPACEN)opt
 | |
|                 HlslToken spaceDesc;
 | |
|                 if (acceptTokenClass(EHTokComma)) {
 | |
|                     if (! acceptIdentifier(spaceDesc)) {
 | |
|                         expected ("space identifier");
 | |
|                         return false;
 | |
|                     }
 | |
|                 }
 | |
|                 // RIGHT_PAREN
 | |
|                 if (! acceptTokenClass(EHTokRightParen)) {
 | |
|                     expected(")");
 | |
|                     break;
 | |
|                 }
 | |
|                 parseContext.handleRegister(registerDesc.loc, qualifier, profile.string, *registerDesc.string, subComponent, spaceDesc.string);
 | |
|             } else {
 | |
|                 // semantic, in idToken.string
 | |
|                 TString semanticUpperCase = *idToken.string;
 | |
|                 std::transform(semanticUpperCase.begin(), semanticUpperCase.end(), semanticUpperCase.begin(), ::toupper);
 | |
|                 parseContext.handleSemantic(idToken.loc, qualifier, mapSemantic(semanticUpperCase.c_str()), semanticUpperCase);
 | |
|             }
 | |
|         } else if (peekTokenClass(EHTokLeftAngle)) {
 | |
|             found = true;
 | |
|             acceptAnnotations(qualifier);
 | |
|         } else
 | |
|             break;
 | |
| 
 | |
|     } while (true);
 | |
| 
 | |
|     return found;
 | |
| }
 | |
| 
 | |
| //
 | |
| // Get the stream of tokens from the scanner, but skip all syntactic/semantic
 | |
| // processing.
 | |
| //
 | |
| bool HlslGrammar::captureBlockTokens(TVector<HlslToken>& tokens)
 | |
| {
 | |
|     if (! peekTokenClass(EHTokLeftBrace))
 | |
|         return false;
 | |
| 
 | |
|     int braceCount = 0;
 | |
| 
 | |
|     do {
 | |
|         switch (peek()) {
 | |
|         case EHTokLeftBrace:
 | |
|             ++braceCount;
 | |
|             break;
 | |
|         case EHTokRightBrace:
 | |
|             --braceCount;
 | |
|             break;
 | |
|         case EHTokNone:
 | |
|             // End of input before balance { } is bad...
 | |
|             return false;
 | |
|         default:
 | |
|             break;
 | |
|         }
 | |
| 
 | |
|         tokens.push_back(token);
 | |
|         advanceToken();
 | |
|     } while (braceCount > 0);
 | |
| 
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| // Return a string for just the types that can also be declared as an identifier.
 | |
| const char* HlslGrammar::getTypeString(EHlslTokenClass tokenClass) const
 | |
| {
 | |
|     switch (tokenClass) {
 | |
|     case EHTokSample:     return "sample";
 | |
|     case EHTokHalf:       return "half";
 | |
|     case EHTokHalf1x1:    return "half1x1";
 | |
|     case EHTokHalf1x2:    return "half1x2";
 | |
|     case EHTokHalf1x3:    return "half1x3";
 | |
|     case EHTokHalf1x4:    return "half1x4";
 | |
|     case EHTokHalf2x1:    return "half2x1";
 | |
|     case EHTokHalf2x2:    return "half2x2";
 | |
|     case EHTokHalf2x3:    return "half2x3";
 | |
|     case EHTokHalf2x4:    return "half2x4";
 | |
|     case EHTokHalf3x1:    return "half3x1";
 | |
|     case EHTokHalf3x2:    return "half3x2";
 | |
|     case EHTokHalf3x3:    return "half3x3";
 | |
|     case EHTokHalf3x4:    return "half3x4";
 | |
|     case EHTokHalf4x1:    return "half4x1";
 | |
|     case EHTokHalf4x2:    return "half4x2";
 | |
|     case EHTokHalf4x3:    return "half4x3";
 | |
|     case EHTokHalf4x4:    return "half4x4";
 | |
|     case EHTokBool:       return "bool";
 | |
|     case EHTokFloat:      return "float";
 | |
|     case EHTokDouble:     return "double";
 | |
|     case EHTokInt:        return "int";
 | |
|     case EHTokUint:       return "uint";
 | |
|     case EHTokMin16float: return "min16float";
 | |
|     case EHTokMin10float: return "min10float";
 | |
|     case EHTokMin16int:   return "min16int";
 | |
|     case EHTokMin12int:   return "min12int";
 | |
|     case EHTokConstantBuffer: return "ConstantBuffer";
 | |
|     case EHTokLayout:     return "layout";
 | |
|     default:
 | |
|         return nullptr;
 | |
|     }
 | |
| }
 | |
| 
 | |
| } // end namespace glslang
 | 
