// //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