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