summaryrefslogtreecommitdiffstats
path: root/src/3rdparty/glslang/hlsl/hlslGrammar.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/3rdparty/glslang/hlsl/hlslGrammar.cpp')
-rw-r--r--src/3rdparty/glslang/hlsl/hlslGrammar.cpp4170
1 files changed, 4170 insertions, 0 deletions
diff --git a/src/3rdparty/glslang/hlsl/hlslGrammar.cpp b/src/3rdparty/glslang/hlsl/hlslGrammar.cpp
new file mode 100644
index 0000000..45cf5d5
--- /dev/null
+++ b/src/3rdparty/glslang/hlsl/hlslGrammar.cpp
@@ -0,0 +1,4170 @@
+//
+// 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