2016-03-13 03:11:22 +00:00
|
|
|
//
|
|
|
|
//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.
|
|
|
|
//
|
|
|
|
|
2016-03-13 17:24:20 +00:00
|
|
|
//
|
|
|
|
// 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 it 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'.
|
|
|
|
//
|
|
|
|
|
2016-03-13 03:11:22 +00:00
|
|
|
#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, "");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Load 'token' with the next token in the stream of tokens.
|
|
|
|
void HlslGrammar::advanceToken()
|
|
|
|
{
|
|
|
|
scanContext.tokenize(token);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return true and advance to the next token if the current token is the
|
|
|
|
// expected (passed in) token class.
|
|
|
|
bool HlslGrammar::acceptTokenClass(EHlslTokenClass tokenClass)
|
|
|
|
{
|
|
|
|
if (token.tokenClass == tokenClass) {
|
|
|
|
advanceToken();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// compilationUnit
|
|
|
|
// : list of externalDeclaration
|
|
|
|
//
|
|
|
|
bool HlslGrammar::acceptCompilationUnit()
|
|
|
|
{
|
2016-03-13 17:24:20 +00:00
|
|
|
TIntermNode* unitNode = nullptr;
|
|
|
|
|
2016-03-13 03:11:22 +00:00
|
|
|
while (token.tokenClass != EHTokNone) {
|
2016-03-13 17:24:20 +00:00
|
|
|
// externalDeclaration
|
|
|
|
TIntermNode* declarationNode;
|
|
|
|
if (! acceptDeclaration(declarationNode))
|
2016-03-13 03:11:22 +00:00
|
|
|
return false;
|
2016-03-13 17:24:20 +00:00
|
|
|
|
|
|
|
// hook it up
|
|
|
|
unitNode = parseContext.intermediate.growAggregate(unitNode, declarationNode);
|
2016-03-13 03:11:22 +00:00
|
|
|
}
|
|
|
|
|
2016-03-13 17:24:20 +00:00
|
|
|
// set root of AST
|
|
|
|
parseContext.intermediate.setTreeRoot(unitNode);
|
|
|
|
|
2016-03-13 03:11:22 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// declaration
|
2016-03-13 03:24:24 +00:00
|
|
|
// : SEMICOLON
|
|
|
|
// : fully_specified_type ;
|
|
|
|
// | fully_specified_type identifier ;
|
|
|
|
// | fully_specified_type identifier = expression ;
|
|
|
|
// | fully_specified_type identifier function_parameters ; // function prototype
|
|
|
|
// | fully_specified_type function_parameters compound_statement // function definition
|
|
|
|
//
|
2016-03-13 17:24:20 +00:00
|
|
|
// 'node' could get created if the declaration creates code, like an initializer
|
|
|
|
// or a function body.
|
|
|
|
//
|
|
|
|
bool HlslGrammar::acceptDeclaration(TIntermNode*& node)
|
2016-03-13 03:11:22 +00:00
|
|
|
{
|
2016-03-13 17:24:20 +00:00
|
|
|
node = nullptr;
|
|
|
|
|
2016-03-13 03:24:24 +00:00
|
|
|
// fully_specified_type
|
|
|
|
TType type;
|
|
|
|
if (! acceptFullySpecifiedType(type))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// identifier
|
|
|
|
if (token.tokenClass == EHTokIdentifier) {
|
|
|
|
TSourceLoc declLoc = token.loc;
|
|
|
|
TString* declName = token.string;
|
|
|
|
advanceToken();
|
|
|
|
|
|
|
|
// = expression
|
|
|
|
TIntermTyped* expressionNode = nullptr;
|
|
|
|
if (acceptTokenClass(EHTokEqual)) {
|
|
|
|
if (! acceptExpression(expressionNode)) {
|
|
|
|
expected("initializer");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ;
|
|
|
|
if (acceptTokenClass(EHTokSemicolon)) {
|
2016-03-13 17:24:20 +00:00
|
|
|
node = parseContext.declareVariable(declLoc, *declName, type, 0, expressionNode);
|
2016-03-13 03:24:24 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// no identifier, just ;
|
|
|
|
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 (token.tokenClass) {
|
|
|
|
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 (token.tokenClass) {
|
|
|
|
case EHTokInt:
|
|
|
|
case EHTokInt1:
|
|
|
|
case EHTokDword:
|
|
|
|
new(&type) TType(EbtInt);
|
|
|
|
break;
|
|
|
|
case EHTokFloat:
|
|
|
|
case EHTokFloat1:
|
|
|
|
new(&type) TType(EbtFloat);
|
|
|
|
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 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 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 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;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
advanceToken();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// expression
|
|
|
|
// : identifier
|
|
|
|
// | ( expression )
|
2016-03-13 17:24:20 +00:00
|
|
|
// | type(...) // constructor
|
2016-03-13 03:24:24 +00:00
|
|
|
// | literal
|
2016-03-13 17:24:20 +00:00
|
|
|
// | identifier operator identifier // to be generalized to all expressions
|
2016-03-13 03:24:24 +00:00
|
|
|
//
|
|
|
|
bool HlslGrammar::acceptExpression(TIntermTyped*& node)
|
|
|
|
{
|
|
|
|
// identifier
|
|
|
|
if (token.tokenClass == EHTokIdentifier) {
|
|
|
|
node = parseContext.handleVariable(token.loc, token.symbol, token.string);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ( expression )
|
|
|
|
if (acceptTokenClass(EHTokLeftParen)) {
|
|
|
|
if (! acceptExpression(node)) {
|
|
|
|
expected("expression");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (! acceptTokenClass(EHTokRightParen)) {
|
|
|
|
expected("right parenthesis");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// literal
|
|
|
|
if (acceptLiteral(node))
|
|
|
|
return true;
|
|
|
|
|
2016-03-13 17:24:20 +00:00
|
|
|
// type(...) // constructor
|
|
|
|
if (acceptConstructor(node))
|
2016-03-13 03:24:24 +00:00
|
|
|
return true;
|
|
|
|
|
2016-03-13 17:24:20 +00:00
|
|
|
// identifier operator identifier
|
2016-03-13 03:24:24 +00:00
|
|
|
if (token.tokenClass == EHTokIdentifier) {
|
|
|
|
TIntermTyped* left = parseContext.handleVariable(token.loc, token.symbol, token.string);
|
|
|
|
advanceToken();
|
|
|
|
|
|
|
|
// operator
|
|
|
|
TOperator op;
|
|
|
|
if (! acceptOperator(op))
|
|
|
|
return false;
|
|
|
|
TSourceLoc loc = token.loc;
|
|
|
|
|
|
|
|
// right
|
|
|
|
if (token.tokenClass == EHTokIdentifier) {
|
|
|
|
TIntermTyped* right = parseContext.handleVariable(token.loc, token.symbol, token.string);
|
|
|
|
advanceToken();
|
|
|
|
node = parseContext.intermediate.addBinaryMath(op, left, right, loc);
|
|
|
|
} else
|
|
|
|
return false;
|
|
|
|
} else
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-03-13 17:24:20 +00:00
|
|
|
// constructor
|
|
|
|
// : type arguments
|
|
|
|
//
|
|
|
|
bool HlslGrammar::acceptConstructor(TIntermTyped*& node)
|
|
|
|
{
|
|
|
|
// type
|
|
|
|
TType type;
|
|
|
|
if (acceptType(type)) {
|
|
|
|
TFunction* constructorFunction = parseContext.handleConstructorCall(token.loc, type);
|
|
|
|
if (constructorFunction == nullptr)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// arguments
|
|
|
|
TIntermAggregate* 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;
|
|
|
|
}
|
|
|
|
|
2016-03-13 03:24:24 +00:00
|
|
|
// arguments
|
|
|
|
// : ( expression , expression, ... )
|
|
|
|
//
|
2016-03-13 17:24:20 +00:00
|
|
|
// The arguments are pushed onto the 'function' argument list and
|
|
|
|
// onto the 'arguments' aggregate.
|
|
|
|
//
|
|
|
|
bool HlslGrammar::acceptArguments(TFunction* function, TIntermAggregate*& arguments)
|
2016-03-13 03:24:24 +00:00
|
|
|
{
|
2016-03-13 17:24:20 +00:00
|
|
|
// (
|
2016-03-13 03:24:24 +00:00
|
|
|
if (! acceptTokenClass(EHTokLeftParen))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
do {
|
2016-03-13 17:24:20 +00:00
|
|
|
// expression
|
2016-03-13 03:24:24 +00:00
|
|
|
TIntermTyped* arg;
|
|
|
|
if (! acceptExpression(arg))
|
|
|
|
break;
|
2016-03-13 17:24:20 +00:00
|
|
|
|
|
|
|
// hook it up
|
|
|
|
parseContext.handleFunctionArgument(function, arguments, arg);
|
|
|
|
|
|
|
|
// ,
|
2016-03-13 03:24:24 +00:00
|
|
|
if (! acceptTokenClass(EHTokComma))
|
|
|
|
break;
|
|
|
|
} while (true);
|
|
|
|
|
2016-03-13 17:24:20 +00:00
|
|
|
// )
|
2016-03-13 03:24:24 +00:00
|
|
|
if (! acceptTokenClass(EHTokRightParen)) {
|
|
|
|
expected("right parenthesis");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HlslGrammar::acceptLiteral(TIntermTyped*& node)
|
|
|
|
{
|
|
|
|
switch (token.tokenClass) {
|
|
|
|
case EHTokIntConstant:
|
|
|
|
node = parseContext.intermediate.addConstantUnion(token.i, token.loc, true);
|
|
|
|
break;
|
|
|
|
case EHTokFloatConstant:
|
|
|
|
node = parseContext.intermediate.addConstantUnion(token.d, EbtFloat, token.loc, true);
|
|
|
|
break;
|
|
|
|
case EHTokDoubleConstant:
|
|
|
|
node = parseContext.intermediate.addConstantUnion(token.d, EbtDouble, token.loc, true);
|
|
|
|
break;
|
|
|
|
case EHTokBoolConstant:
|
|
|
|
node = parseContext.intermediate.addConstantUnion(token.b, token.loc, true);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-03-13 03:11:22 +00:00
|
|
|
advanceToken();
|
2016-03-13 03:24:24 +00:00
|
|
|
|
2016-03-13 03:11:22 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-03-13 03:24:24 +00:00
|
|
|
bool HlslGrammar::acceptOperator(TOperator& op)
|
|
|
|
{
|
|
|
|
switch (token.tokenClass) {
|
|
|
|
case EHTokPlus:
|
|
|
|
op = EOpAdd;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
advanceToken();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HlslGrammar::acceptCompoundStatement()
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-03-13 03:11:22 +00:00
|
|
|
} // end namespace glslang
|