910 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			C++
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			910 lines
		
	
	
		
			25 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 SEMICOLON
 | 
						|
//      | fully_specified_type identifier = expression SEMICOLON
 | 
						|
//      | fully_specified_type identifier function_parameters SEMICOLON                          // function prototype
 | 
						|
//      | fully_specified_type identifier function_parameters COLON semantic 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)) {
 | 
						|
        // = 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)) {
 | 
						|
            // COLON semantic
 | 
						|
            acceptSemantic();
 | 
						|
 | 
						|
            // 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;
 | 
						|
}
 | 
						|
 | 
						|
// If token is a qualifier, return its token class and advance to the next
 | 
						|
// qualifier.  Otherwise, return false, and don't advance.
 | 
						|
void HlslGrammar::acceptQualifier(TQualifier& qualifier)
 | 
						|
{
 | 
						|
    switch (peek()) {
 | 
						|
    case EHTokUniform:
 | 
						|
        qualifier.storage = EvqUniform;
 | 
						|
        break;
 | 
						|
    case EHTokConst:
 | 
						|
        qualifier.storage = EvqConst;
 | 
						|
        break;
 | 
						|
    default:
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    advanceToken();
 | 
						|
}
 | 
						|
 | 
						|
// 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)
 | 
						|
{
 | 
						|
    if (! token.isType)
 | 
						|
        return false;
 | 
						|
 | 
						|
    switch (peek()) {
 | 
						|
    case EHTokInt:
 | 
						|
    case EHTokInt1:
 | 
						|
    case EHTokDword:
 | 
						|
        new(&type) TType(EbtInt);
 | 
						|
        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 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 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 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;
 | 
						|
}
 | 
						|
 | 
						|
// function_parameters
 | 
						|
//      : LEFT_PAREN parameter_declaration COMMA parameter_declaration ... RIGHT_PAREN
 | 
						|
//
 | 
						|
bool HlslGrammar::acceptFunctionParameters(TFunction& function)
 | 
						|
{
 | 
						|
    // LEFT_PAREN
 | 
						|
    if (! acceptTokenClass(EHTokLeftParen))
 | 
						|
        return false;
 | 
						|
 | 
						|
    do {
 | 
						|
        // parameter_declaration
 | 
						|
        if (! acceptParameterDeclaration(function))
 | 
						|
            break;
 | 
						|
 | 
						|
        // COMMA
 | 
						|
        if (! acceptTokenClass(EHTokComma))
 | 
						|
            break;
 | 
						|
    } while (true);
 | 
						|
 | 
						|
    // RIGHT_PAREN
 | 
						|
    if (! acceptTokenClass(EHTokRightParen)) {
 | 
						|
        expected("right parenthesis");
 | 
						|
        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 symbol table push
 | 
						|
    node = parseContext.handleFunctionDefinition(token.loc, *functionDeclarator);
 | 
						|
 | 
						|
    // compound_statement
 | 
						|
    TIntermAggregate* 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.symbolTable.pop(nullptr);
 | 
						|
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    return false;
 | 
						|
}
 | 
						|
 | 
						|
// The top-level full expression recognizer.
 | 
						|
//
 | 
						|
// expression
 | 
						|
//      : assignment_expression COMMA assignment_expression COMMA assignment_expression ...
 | 
						|
//
 | 
						|
bool HlslGrammar::acceptExpression(TIntermTyped*& node)
 | 
						|
{
 | 
						|
    // 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
 | 
						|
//      : + unary_expression
 | 
						|
//      | - unary_expression
 | 
						|
//      | ! unary_expression
 | 
						|
//      | ~ unary_expression
 | 
						|
//      | ++ unary_expression
 | 
						|
//      | -- unary_expression
 | 
						|
//      | postfix_expression
 | 
						|
//
 | 
						|
bool HlslGrammar::acceptUnaryExpression(TIntermTyped*& node)
 | 
						|
{
 | 
						|
    TOperator unaryOp = HlslOpMap::preUnary(peek());
 | 
						|
    
 | 
						|
    // postfix_expression
 | 
						|
    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;
 | 
						|
 | 
						|
    // LEFT_PAREN expression RIGHT_PAREN
 | 
						|
    if (acceptTokenClass(EHTokLeftParen)) {
 | 
						|
        if (! acceptExpression(node)) {
 | 
						|
            expected("expression");
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
        if (! acceptTokenClass(EHTokRightParen)) {
 | 
						|
            expected("right parenthesis");
 | 
						|
            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;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    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("right parenthesis");
 | 
						|
        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(TIntermAggregate*& compoundStatement)
 | 
						|
{
 | 
						|
    // 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);
 | 
						|
 | 
						|
    // RIGHT_CURLY
 | 
						|
    return acceptTokenClass(EHTokRightBrace);
 | 
						|
}
 | 
						|
 | 
						|
// statement
 | 
						|
//      : compound_statement
 | 
						|
//      | return SEMICOLON
 | 
						|
//      | return expression SEMICOLON
 | 
						|
//      | expression SEMICOLON
 | 
						|
//
 | 
						|
bool HlslGrammar::acceptStatement(TIntermNode*& statement)
 | 
						|
{
 | 
						|
    // compound_statement
 | 
						|
    TIntermAggregate* compoundStatement = nullptr;
 | 
						|
    if (acceptCompoundStatement(compoundStatement)) {
 | 
						|
        statement = compoundStatement;
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    // 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))
 | 
						|
            return false;
 | 
						|
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    // expression
 | 
						|
    TIntermTyped* node;
 | 
						|
    if (acceptExpression(node))
 | 
						|
        statement = node;
 | 
						|
 | 
						|
    // SEMICOLON
 | 
						|
    if (! acceptTokenClass(EHTokSemicolon))
 | 
						|
        return false;
 | 
						|
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
// COLON semantic
 | 
						|
bool HlslGrammar::acceptSemantic()
 | 
						|
{
 | 
						|
    // COLON
 | 
						|
    if (acceptTokenClass(EHTokColon)) {
 | 
						|
        // semantic
 | 
						|
        HlslToken idToken;
 | 
						|
        if (! acceptIdentifier(idToken)) {
 | 
						|
            expected("semantic");
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
} // end namespace glslang
 |