You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
1164 lines
30 KiB
C++
1164 lines
30 KiB
C++
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================
|
|
HlslParser.cpp - Implementation for parsing hlsl.
|
|
=============================================================================*/
|
|
|
|
#include "ShaderCompilerCommon.h"
|
|
#include "HlslParser.h"
|
|
#include "HlslExpressionParser.inl"
|
|
|
|
namespace CrossCompiler
|
|
{
|
|
typedef EParseResult(*TTryRule)(class FHlslParser& Scanner);
|
|
struct FRulePair
|
|
{
|
|
EHlslToken Token;
|
|
TTryRule OptionalPrefix;
|
|
TTryRule TryRule;
|
|
|
|
FRulePair(EHlslToken InToken, TTryRule InTryRule, TTryRule InOptionalPrefix = nullptr) : Token(InToken), OptionalPrefix(InOptionalPrefix), TryRule(InTryRule) {}
|
|
};
|
|
typedef TArray<FRulePair> TRulesArray;
|
|
|
|
class FHlslParser
|
|
{
|
|
public:
|
|
FHlslParser();
|
|
FHlslScanner Scanner;
|
|
FSymbolScope GlobalScope;
|
|
FSymbolScope* CurrentScope;
|
|
};
|
|
|
|
TRulesArray RulesTranslationUnit;
|
|
TRulesArray RulesStatements;
|
|
|
|
EParseResult TryRules(const TRulesArray& Rules, FHlslParser& Parser, const FString& RuleNames, bool bErrorIfNoMatch)
|
|
{
|
|
for (const auto& Rule : Rules)
|
|
{
|
|
auto CurrentTokenIndex = Parser.Scanner.GetCurrentTokenIndex();
|
|
if (Rule.OptionalPrefix)
|
|
{
|
|
if ((*Rule.OptionalPrefix)(Parser) == EParseResult::Error)
|
|
{
|
|
return EParseResult::Error;
|
|
}
|
|
}
|
|
|
|
if (Parser.Scanner.MatchToken(Rule.Token) || Rule.Token == EHlslToken::Invalid)
|
|
{
|
|
EParseResult Result = (*Rule.TryRule)(Parser);
|
|
if (Result == EParseResult::Error || Result == EParseResult::Matched)
|
|
{
|
|
return Result;
|
|
}
|
|
}
|
|
|
|
Parser.Scanner.SetCurrentTokenIndex(CurrentTokenIndex);
|
|
}
|
|
|
|
if (bErrorIfNoMatch)
|
|
{
|
|
Parser.Scanner.SourceError(FString::Printf(TEXT("No matching %s rules found!"), *RuleNames));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
return EParseResult::NotMatched;
|
|
}
|
|
|
|
EParseResult ParseArrayBracketsAndIndex(FHlslParser& Parser, bool bNeedsDimension)
|
|
{
|
|
if (Parser.Scanner.MatchToken(EHlslToken::LeftSquareBracket))
|
|
{
|
|
if (bNeedsDimension)
|
|
{
|
|
if (ParseExpression(Parser.Scanner, Parser.CurrentScope) != EParseResult::Matched)
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Expected expression!"));
|
|
return EParseResult::Error;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ParseExpression(Parser.Scanner, Parser.CurrentScope) == EParseResult::Error)
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Expected expression!"));
|
|
return EParseResult::Error;
|
|
}
|
|
}
|
|
|
|
if (!Parser.Scanner.MatchToken(EHlslToken::RightSquareBracket))
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Expected ']'!"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
return EParseResult::Matched;
|
|
}
|
|
|
|
return EParseResult::NotMatched;
|
|
}
|
|
|
|
EParseResult ParseMultiArrayBracketsAndIndex(FHlslParser& Parser, bool bNeedsDimension)
|
|
{
|
|
bool bFoundOne = false;
|
|
do
|
|
{
|
|
auto Result = ParseArrayBracketsAndIndex(Parser, bNeedsDimension);
|
|
if (Result == EParseResult::Error)
|
|
{
|
|
return EParseResult::Error;
|
|
}
|
|
else if (Result == EParseResult::NotMatched)
|
|
{
|
|
break;
|
|
}
|
|
|
|
bFoundOne = true;
|
|
}
|
|
while (Parser.Scanner.HasMoreTokens());
|
|
|
|
return bFoundOne ? EParseResult::Matched : EParseResult::NotMatched;
|
|
}
|
|
|
|
EParseResult ParseTextureOrBufferSimpleDeclaration(FHlslParser& Parser, bool bMultiple)
|
|
{
|
|
auto OriginalToken = Parser.Scanner.GetCurrentTokenIndex();
|
|
if (ParseGeneralType(Parser.Scanner, ETF_SAMPLER_TEXTURE_BUFFER) != EParseResult::Matched)
|
|
{
|
|
goto Unmatched;
|
|
}
|
|
|
|
if (Parser.Scanner.MatchToken(EHlslToken::Lower))
|
|
{
|
|
auto Result = ParseGeneralType(Parser.Scanner, ETF_BUILTIN_NUMERIC | ETF_USER_TYPES, Parser.CurrentScope);
|
|
if (Result != EParseResult::Matched)
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Expected type!"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
if (Parser.Scanner.MatchToken(EHlslToken::Comma))
|
|
{
|
|
if (!Parser.Scanner.MatchToken(EHlslToken::UnsignedIntegerConstant))
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Expected constant!"));
|
|
return EParseResult::Error;
|
|
}
|
|
}
|
|
|
|
if (!Parser.Scanner.MatchToken(EHlslToken::Greater))
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Expected '>'!"));
|
|
return EParseResult::Error;
|
|
}
|
|
}
|
|
|
|
{
|
|
NextParameter:
|
|
// Handle 'Sampler2D Sampler'
|
|
if (ParseGeneralType(Parser.Scanner, ETF_SAMPLER_TEXTURE_BUFFER) == EParseResult::Matched)
|
|
{
|
|
//...
|
|
}
|
|
else if (!Parser.Scanner.MatchToken(EHlslToken::Identifier))
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Expected Identifier!"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
if (ParseMultiArrayBracketsAndIndex(Parser, true) == EParseResult::Error)
|
|
{
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
if (bMultiple && Parser.Scanner.MatchToken(EHlslToken::Comma))
|
|
{
|
|
goto NextParameter;
|
|
}
|
|
|
|
return EParseResult::Matched;
|
|
}
|
|
|
|
Unmatched:
|
|
Parser.Scanner.SetCurrentTokenIndex(OriginalToken);
|
|
return EParseResult::NotMatched;
|
|
}
|
|
|
|
// Multi declaration parser flags
|
|
enum EDeclarationFlags
|
|
{
|
|
//EDF_ROLLBACK_IF_NO_MATCH = 0x0001,
|
|
EDF_CONST_ROW_MAJOR = 0x0002,
|
|
EDF_STATIC = 0x0004,
|
|
EDF_TEXTURE_SAMPLER_OR_BUFFER = 0x0008,
|
|
EDF_INITIALIZER = 0x0010,
|
|
EDF_INITIALIZER_LIST = 0x0020 | EDF_INITIALIZER,
|
|
EDF_SEMANTIC = 0x0040,
|
|
EDF_SEMICOLON = 0x0080,
|
|
EDF_IN_OUT = 0x0100,
|
|
EDF_MULTIPLE = 0x0200,
|
|
EDF_PRIMITIVE_DATA_TYPE = 0x0400,
|
|
EDF_SHARED = 0x0800,
|
|
EDF_NOINTERPOLATION = 0x1000,
|
|
};
|
|
|
|
EParseResult ParseInitializer(FHlslParser& Parser, bool bAllowLists)
|
|
{
|
|
if (bAllowLists && Parser.Scanner.MatchToken(EHlslToken::LeftBrace))
|
|
{
|
|
bool bFoundExpr = false;
|
|
while (Parser.Scanner.HasMoreTokens())
|
|
{
|
|
if (Parser.Scanner.MatchToken(EHlslToken::RightBrace))
|
|
{
|
|
return EParseResult::Matched;
|
|
}
|
|
auto Result = ParseExpression(Parser.Scanner, Parser.CurrentScope);
|
|
if (Result == EParseResult::Error)
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Invalid initielizer expression list\n"));
|
|
return EParseResult::Error;
|
|
}
|
|
else if (Result == EParseResult::NotMatched)
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Expected initializer expression\n"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
bFoundExpr = true;
|
|
if (Parser.Scanner.MatchToken(EHlslToken::Comma))
|
|
{
|
|
continue;
|
|
}
|
|
else if (Parser.Scanner.MatchToken(EHlslToken::RightBrace))
|
|
{
|
|
return EParseResult::Matched;
|
|
}
|
|
|
|
Parser.Scanner.SourceError(TEXT("Expected ','\n"));
|
|
break;
|
|
}
|
|
|
|
Parser.Scanner.SourceError(TEXT("Expected '}'\n"));
|
|
}
|
|
else
|
|
{
|
|
auto Result = ParseExpression(Parser.Scanner, Parser.CurrentScope);
|
|
if (Result == EParseResult::Error)
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Invalid initializer expression\n"));
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
return EParseResult::NotMatched;
|
|
}
|
|
|
|
|
|
EParseResult ParseGeneralDeclaration(FHlslParser& Parser, int32 Flags)
|
|
{
|
|
auto OriginalToken = Parser.Scanner.GetCurrentTokenIndex();
|
|
|
|
int32 StaticFound = 0;
|
|
int32 SharedFound = 0;
|
|
int32 ConstFound = 0;
|
|
int32 RowMajorFound = 0;
|
|
int32 InFound = 0;
|
|
int32 OutFound = 0;
|
|
int32 InOutFound = 0;
|
|
int32 PrimitiveFound = 0;
|
|
|
|
if (Flags & EDF_PRIMITIVE_DATA_TYPE)
|
|
{
|
|
const auto* Token = Parser.Scanner.GetCurrentToken();
|
|
if (Token && Token->Token == EHlslToken::Identifier)
|
|
{
|
|
if (Token->String == TEXT("point") ||
|
|
Token->String == TEXT("line") ||
|
|
Token->String == TEXT("triangle") ||
|
|
Token->String == TEXT("lineadj") ||
|
|
Token->String == TEXT("triangleadj"))
|
|
{
|
|
Parser.Scanner.Advance();
|
|
++PrimitiveFound;
|
|
}
|
|
}
|
|
}
|
|
|
|
while (Parser.Scanner.HasMoreTokens())
|
|
{
|
|
bool bFound = false;
|
|
if ((Flags & EDF_STATIC) && Parser.Scanner.MatchToken(EHlslToken::Static))
|
|
{
|
|
++StaticFound;
|
|
if (StaticFound > 1)
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("'static' found more than once!\n"));
|
|
}
|
|
}
|
|
else if ((Flags & EDF_NOINTERPOLATION) && Parser.Scanner.MatchToken(EHlslToken::NoInterpolation))
|
|
{
|
|
++SharedFound;
|
|
if (SharedFound > 1)
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("'groupshared' found more than once!\n"));
|
|
}
|
|
}
|
|
else if ((Flags & EDF_SHARED) && Parser.Scanner.MatchToken(EHlslToken::GroupShared))
|
|
{
|
|
++SharedFound;
|
|
if (SharedFound > 1)
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("'groupshared' found more than once!\n"));
|
|
}
|
|
}
|
|
else if ((Flags & EDF_CONST_ROW_MAJOR) && Parser.Scanner.MatchToken(EHlslToken::Const))
|
|
{
|
|
++ConstFound;
|
|
if (ConstFound > 1)
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("'const' found more than once!\n"));
|
|
}
|
|
}
|
|
else if ((Flags & EDF_CONST_ROW_MAJOR) && Parser.Scanner.MatchToken(EHlslToken::RowMajor))
|
|
{
|
|
++RowMajorFound;
|
|
if (RowMajorFound > 1)
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("'row_major' found more than once!\n"));
|
|
}
|
|
}
|
|
else if ((Flags & EDF_IN_OUT) && Parser.Scanner.MatchToken(EHlslToken::In))
|
|
{
|
|
++InFound;
|
|
if (InFound > 1)
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("'in' found more than once!\n"));
|
|
}
|
|
}
|
|
else if ((Flags & EDF_IN_OUT) && Parser.Scanner.MatchToken(EHlslToken::Out))
|
|
{
|
|
++OutFound;
|
|
if (OutFound > 1)
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("'out' found more than once!\n"));
|
|
}
|
|
}
|
|
else if ((Flags & EDF_IN_OUT) && Parser.Scanner.MatchToken(EHlslToken::InOut))
|
|
{
|
|
++InOutFound;
|
|
if (InOutFound > 1)
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("'inout' found more than once!\n"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool bCanBeUnmatched = (ConstFound + RowMajorFound + InFound + OutFound + InOutFound + StaticFound + SharedFound + PrimitiveFound) == 0;
|
|
|
|
if (PrimitiveFound == 0 && (Flags & EDF_PRIMITIVE_DATA_TYPE))
|
|
{
|
|
const auto* Token = Parser.Scanner.GetCurrentToken();
|
|
if (Token && Token->Token == EHlslToken::Identifier)
|
|
{
|
|
if (Token->String == TEXT("PointStream") ||
|
|
Token->String == TEXT("LineStream") ||
|
|
Token->String == TEXT("TriangleStream"))
|
|
{
|
|
Parser.Scanner.Advance();
|
|
bCanBeUnmatched = false;
|
|
|
|
if (!Parser.Scanner.MatchToken(EHlslToken::Lower))
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Expected '<'!"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
if (ParseGeneralType(Parser.Scanner, ETF_BUILTIN_NUMERIC | ETF_USER_TYPES, Parser.CurrentScope) != EParseResult::Matched)
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Expected type!"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
if (!Parser.Scanner.MatchToken(EHlslToken::Greater))
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Expected '>'!"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
if (!Parser.Scanner.MatchToken(EHlslToken::Identifier))
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Expected identifier!"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
return EParseResult::Matched;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool bTryBasicType = true;
|
|
if (Flags & EDF_TEXTURE_SAMPLER_OR_BUFFER)
|
|
{
|
|
auto Result = ParseTextureOrBufferSimpleDeclaration(Parser, (Flags & EDF_MULTIPLE) == EDF_MULTIPLE);
|
|
if (Result == EParseResult::Error)
|
|
{
|
|
return EParseResult::Error;
|
|
}
|
|
else if (Result == EParseResult::Matched)
|
|
{
|
|
bTryBasicType = false;
|
|
}
|
|
}
|
|
|
|
if (bTryBasicType)
|
|
{
|
|
auto Result = ParseGeneralType(Parser.Scanner, ETF_BUILTIN_NUMERIC | ETF_USER_TYPES, Parser.CurrentScope);
|
|
if (Result == EParseResult::Matched)
|
|
{
|
|
bool bMatched = false;
|
|
do
|
|
{
|
|
if (!Parser.Scanner.MatchToken(EHlslToken::Identifier))
|
|
{
|
|
Parser.Scanner.SetCurrentTokenIndex(OriginalToken);
|
|
return EParseResult::NotMatched;
|
|
}
|
|
|
|
if (ParseMultiArrayBracketsAndIndex(Parser, false) == EParseResult::Error)
|
|
{
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
bool bSemanticFound = false;
|
|
if (Flags & EDF_SEMANTIC)
|
|
{
|
|
if (Parser.Scanner.MatchToken(EHlslToken::Colon))
|
|
{
|
|
if (!Parser.Scanner.MatchToken(EHlslToken::Identifier))
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Expected identifier for semantic!"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
bSemanticFound = true;
|
|
}
|
|
}
|
|
|
|
if ((Flags & EDF_INITIALIZER) && !bSemanticFound)
|
|
{
|
|
if (Parser.Scanner.MatchToken(EHlslToken::Equal))
|
|
{
|
|
if (ParseInitializer(Parser, (Flags & EDF_INITIALIZER_LIST) == EDF_INITIALIZER_LIST) != EParseResult::Matched)
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Invalid initializer\n"));
|
|
return EParseResult::Error;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
while ((Flags & EDF_MULTIPLE) == EDF_MULTIPLE && Parser.Scanner.MatchToken(EHlslToken::Comma));
|
|
}
|
|
else if (bCanBeUnmatched && Result == EParseResult::NotMatched)
|
|
{
|
|
Parser.Scanner.SetCurrentTokenIndex(OriginalToken);
|
|
return EParseResult::NotMatched;
|
|
}
|
|
}
|
|
|
|
if (Flags & EDF_SEMICOLON)
|
|
{
|
|
if (!Parser.Scanner.MatchToken(EHlslToken::Semicolon))
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("';' expected!\n"));
|
|
return EParseResult::Error;
|
|
}
|
|
}
|
|
|
|
return EParseResult::Matched;
|
|
}
|
|
|
|
EParseResult ParseCBuffer(FHlslParser& Parser)
|
|
{
|
|
//FInfo Info;
|
|
Parser.Scanner.MatchToken(EHlslToken::Identifier);
|
|
bool bFoundRightBrace = false;
|
|
if (Parser.Scanner.MatchToken(EHlslToken::LeftBrace))
|
|
{
|
|
bool bFoundRightBrace = false;
|
|
while (Parser.Scanner.HasMoreTokens())
|
|
{
|
|
if (Parser.Scanner.MatchToken(EHlslToken::RightBrace))
|
|
{
|
|
bFoundRightBrace = true;
|
|
break;
|
|
}
|
|
|
|
auto Result = ParseGeneralDeclaration(Parser, EDF_CONST_ROW_MAJOR | EDF_SEMICOLON | EDF_TEXTURE_SAMPLER_OR_BUFFER);
|
|
if (Result == EParseResult::Error)
|
|
{
|
|
return EParseResult::Error;
|
|
}
|
|
else if (Result == EParseResult::NotMatched)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
check(bFoundRightBrace);
|
|
return EParseResult::Matched;
|
|
}
|
|
|
|
Parser.Scanner.SourceError(TEXT("Expected '}'!"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
EParseResult ParseStruct(FHlslParser& Parser)
|
|
{
|
|
//FInfo Info;
|
|
//@todo-rco: Test me!
|
|
const auto* Name = Parser.Scanner.GetCurrentToken();
|
|
if (Name && Parser.Scanner.MatchToken(EHlslToken::Identifier))
|
|
{
|
|
Parser.CurrentScope->Add(Name->String);
|
|
}
|
|
|
|
if (Parser.Scanner.MatchToken(EHlslToken::Colon))
|
|
{
|
|
if (!Parser.Scanner.MatchToken(EHlslToken::Identifier))
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Identifier expected!\n"));
|
|
return EParseResult::Error;
|
|
}
|
|
}
|
|
|
|
if (!Parser.Scanner.MatchToken(EHlslToken::LeftBrace))
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Expected '{'!"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
bool bFoundRightBrace = false;
|
|
while (Parser.Scanner.HasMoreTokens())
|
|
{
|
|
if (Parser.Scanner.MatchToken(EHlslToken::RightBrace))
|
|
{
|
|
bFoundRightBrace = true;
|
|
break;
|
|
}
|
|
|
|
auto Result = ParseGeneralDeclaration(Parser, EDF_CONST_ROW_MAJOR | EDF_SEMICOLON | EDF_SEMANTIC | EDF_TEXTURE_SAMPLER_OR_BUFFER | EDF_NOINTERPOLATION);
|
|
if (Result == EParseResult::Error)
|
|
{
|
|
return EParseResult::Error;
|
|
}
|
|
else if (Result == EParseResult::NotMatched)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!bFoundRightBrace)
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Expected '}'!"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
if (!Parser.Scanner.MatchToken(EHlslToken::Semicolon))
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Expected ';'!"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
return EParseResult::Matched;
|
|
}
|
|
|
|
EParseResult ParseFunctionParameterDeclaration(FHlslParser& Parser)
|
|
{
|
|
bool bStrictCheck = false;
|
|
|
|
while (Parser.Scanner.HasMoreTokens())
|
|
{
|
|
auto Result = ParseGeneralDeclaration(Parser, EDF_CONST_ROW_MAJOR | EDF_IN_OUT | EDF_TEXTURE_SAMPLER_OR_BUFFER | EDF_INITIALIZER | EDF_SEMANTIC | EDF_PRIMITIVE_DATA_TYPE | EDF_NOINTERPOLATION);
|
|
if (Result == EParseResult::Error)
|
|
{
|
|
return EParseResult::Error;
|
|
}
|
|
else if (!Parser.Scanner.MatchToken(EHlslToken::Comma))
|
|
{
|
|
break;
|
|
}
|
|
else if (Result == EParseResult::NotMatched)
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Internal error on function parameter!\n"));
|
|
return EParseResult::Error;
|
|
}
|
|
}
|
|
|
|
return EParseResult::Matched;
|
|
}
|
|
|
|
EParseResult ParseFunctionDeclarator(FHlslParser& Parser)
|
|
{
|
|
auto OriginalToken = Parser.Scanner.GetCurrentTokenIndex();
|
|
auto Result = ParseGeneralType(Parser.Scanner, ETF_BUILTIN_NUMERIC | ETF_SAMPLER_TEXTURE_BUFFER | ETF_USER_TYPES | ETF_VOID, Parser.CurrentScope);
|
|
if (Result == EParseResult::NotMatched)
|
|
{
|
|
Parser.Scanner.SetCurrentTokenIndex(OriginalToken);
|
|
return EParseResult::NotMatched;
|
|
}
|
|
check(Result == EParseResult::Matched);
|
|
|
|
if (!Parser.Scanner.MatchToken(EHlslToken::Identifier))
|
|
{
|
|
// This could be an error... But we should allow testing for a global variable before any rash decisions
|
|
Parser.Scanner.SetCurrentTokenIndex(OriginalToken);
|
|
return EParseResult::NotMatched;
|
|
}
|
|
|
|
if (!Parser.Scanner.MatchToken(EHlslToken::LeftParenthesis))
|
|
{
|
|
// This could be an error... But we should allow testing for a global variable before any rash decisions
|
|
Parser.Scanner.SetCurrentTokenIndex(OriginalToken);
|
|
return EParseResult::NotMatched;
|
|
}
|
|
|
|
if (Parser.Scanner.MatchToken(EHlslToken::Void))
|
|
{
|
|
//...
|
|
}
|
|
else if (Parser.Scanner.MatchToken(EHlslToken::RightParenthesis))
|
|
{
|
|
return EParseResult::Matched;
|
|
}
|
|
else
|
|
{
|
|
Result = ParseFunctionParameterDeclaration(Parser);
|
|
if (Result == EParseResult::Error)
|
|
{
|
|
return EParseResult::Error;
|
|
}
|
|
}
|
|
|
|
if (!Parser.Scanner.MatchToken(EHlslToken::RightParenthesis))
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("')' expected"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
return EParseResult::Matched;
|
|
}
|
|
|
|
EParseResult ParseStatement(FHlslParser& Parser)
|
|
{
|
|
const auto* Token = Parser.Scanner.PeekToken();
|
|
if (Token && Token->Token == EHlslToken::RightBrace)
|
|
{
|
|
return EParseResult::NotMatched;
|
|
}
|
|
|
|
static FString Statement(TEXT("Statement"));
|
|
return TryRules(RulesStatements, Parser, Statement, false);
|
|
}
|
|
|
|
EParseResult ParseStatementBlock(FHlslParser& Parser)
|
|
{
|
|
while (Parser.Scanner.HasMoreTokens())
|
|
{
|
|
auto Result = ParseStatement(Parser);
|
|
if (Result == EParseResult::NotMatched)
|
|
{
|
|
if (Parser.Scanner.MatchToken(EHlslToken::RightBrace))
|
|
{
|
|
return EParseResult::Matched;
|
|
}
|
|
else
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Statement expected!"));
|
|
break;
|
|
}
|
|
}
|
|
else if (Result == EParseResult::Error)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
Parser.Scanner.SourceError(TEXT("'}' expected!"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
EParseResult ParseFunctionDeclaration(FHlslParser& Parser)
|
|
{
|
|
EParseResult Result = ParseFunctionDeclarator(Parser);
|
|
if (Result == EParseResult::NotMatched || Result == EParseResult::Error)
|
|
{
|
|
return Result;
|
|
}
|
|
|
|
if (Parser.Scanner.MatchToken(EHlslToken::Semicolon))
|
|
{
|
|
// Forward declare
|
|
return EParseResult::Matched;
|
|
}
|
|
else
|
|
{
|
|
// Optional semantic
|
|
if (Parser.Scanner.MatchToken(EHlslToken::Colon))
|
|
{
|
|
if (!Parser.Scanner.MatchToken(EHlslToken::Identifier))
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Identifier for semantic expected"));
|
|
return EParseResult::Error;
|
|
}
|
|
}
|
|
|
|
if (!Parser.Scanner.MatchToken(EHlslToken::LeftBrace))
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("'{' expected"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
return ParseStatementBlock(Parser);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
EParseResult ParseLocalDeclaration(FHlslParser& Parser)
|
|
{
|
|
return ParseGeneralDeclaration(Parser, EDF_CONST_ROW_MAJOR | EDF_INITIALIZER | EDF_INITIALIZER_LIST | EDF_SEMICOLON | EDF_MULTIPLE);
|
|
}
|
|
|
|
EParseResult ParseGlobalVariableDeclaration(FHlslParser& Parser)
|
|
{
|
|
return ParseGeneralDeclaration(Parser, EDF_CONST_ROW_MAJOR | EDF_STATIC | EDF_SHARED | EDF_TEXTURE_SAMPLER_OR_BUFFER | EDF_INITIALIZER | EDF_INITIALIZER_LIST | EDF_SEMICOLON | EDF_MULTIPLE);
|
|
}
|
|
|
|
EParseResult ParseReturnStatement(FHlslParser& Parser)
|
|
{
|
|
if (Parser.Scanner.MatchToken(EHlslToken::Semicolon))
|
|
{
|
|
return EParseResult::Matched;
|
|
}
|
|
|
|
if (ParseExpression(Parser.Scanner, Parser.CurrentScope) != EParseResult::Matched)
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Expression expected"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
if (!Parser.Scanner.MatchToken(EHlslToken::Semicolon))
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("';' expected"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
return EParseResult::Matched;
|
|
}
|
|
|
|
EParseResult ParseDoStatement(FHlslParser& Parser)
|
|
{
|
|
if (ParseStatement(Parser) != EParseResult::Matched)
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Statement expected"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
if (!Parser.Scanner.MatchToken(EHlslToken::LeftParenthesis))
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("'(' expected"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
if (ParseExpression(Parser.Scanner, Parser.CurrentScope) != EParseResult::Matched)
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Expression expected"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
if (!Parser.Scanner.MatchToken(EHlslToken::RightParenthesis))
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("')' expected"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
if (ParseStatement(Parser) != EParseResult::Matched)
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Statement expected"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
return EParseResult::Matched;
|
|
}
|
|
|
|
EParseResult ParseWhileStatement(FHlslParser& Parser)
|
|
{
|
|
if (!Parser.Scanner.MatchToken(EHlslToken::LeftParenthesis))
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("'(' expected"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
if (ParseExpression(Parser.Scanner, Parser.CurrentScope) != EParseResult::Matched)
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Expression expected"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
if (!Parser.Scanner.MatchToken(EHlslToken::RightParenthesis))
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("')' expected"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
if (ParseStatement(Parser) != EParseResult::Matched)
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Statement expected"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
return EParseResult::Matched;
|
|
}
|
|
|
|
EParseResult ParseForStatement(FHlslParser& Parser)
|
|
{
|
|
if (!Parser.Scanner.MatchToken(EHlslToken::LeftParenthesis))
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Expected '('!\n"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
auto Result = ParseGeneralDeclaration(Parser, EDF_CONST_ROW_MAJOR | EDF_INITIALIZER);
|
|
if (Result == EParseResult::Error)
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Expected expression or declaration!\n"));
|
|
return EParseResult::Error;
|
|
}
|
|
else if (Result == EParseResult::NotMatched)
|
|
{
|
|
Result = ParseExpression(Parser.Scanner, Parser.CurrentScope);
|
|
if (Result == EParseResult::Error)
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Expected expression or declaration!\n"));
|
|
return EParseResult::Error;
|
|
}
|
|
}
|
|
|
|
if (!Parser.Scanner.MatchToken(EHlslToken::Semicolon))
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Expected ';'!\n"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
Result = ParseExpression(Parser.Scanner, Parser.CurrentScope);
|
|
if (Result == EParseResult::Error)
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Expected expression or declaration!\n"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
if (!Parser.Scanner.MatchToken(EHlslToken::Semicolon))
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Expected ';'!\n"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
Result = ParseExpression(Parser.Scanner, Parser.CurrentScope);
|
|
if (Result == EParseResult::Error)
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Expected expression or declaration!\n"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
if (!Parser.Scanner.MatchToken(EHlslToken::RightParenthesis))
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Expected ')'!\n"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
return ParseStatement(Parser);
|
|
}
|
|
|
|
EParseResult ParseIfStatement(FHlslParser& Parser)
|
|
{
|
|
if (!Parser.Scanner.MatchToken(EHlslToken::LeftParenthesis))
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("'(' expected"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
if (ParseExpression(Parser.Scanner, Parser.CurrentScope) != EParseResult::Matched)
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Expression expected"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
if (!Parser.Scanner.MatchToken(EHlslToken::RightParenthesis))
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("')' expected"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
if (ParseStatement(Parser) != EParseResult::Matched)
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Statement expected"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
if (Parser.Scanner.MatchToken(EHlslToken::Else))
|
|
{
|
|
if (ParseStatement(Parser) != EParseResult::Matched)
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Statement expected"));
|
|
return EParseResult::Error;
|
|
}
|
|
}
|
|
|
|
return EParseResult::Matched;
|
|
}
|
|
|
|
EParseResult TryParseAttribute(FHlslParser& Parser)
|
|
{
|
|
if (Parser.Scanner.MatchToken(EHlslToken::LeftSquareBracket))
|
|
{
|
|
if (!Parser.Scanner.MatchToken(EHlslToken::Identifier))
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Incorrect attribute\n"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
if (Parser.Scanner.MatchToken(EHlslToken::LeftParenthesis))
|
|
{
|
|
do
|
|
{
|
|
if (ParseExpression(Parser.Scanner, Parser.CurrentScope) != EParseResult::Matched)
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Incorrect attribute! Expected expression.\n"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
if (!Parser.Scanner.MatchToken(EHlslToken::Comma))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
while (Parser.Scanner.HasMoreTokens());
|
|
|
|
if (!Parser.Scanner.MatchToken(EHlslToken::RightParenthesis))
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Incorrect attribute! Expected ')'.\n"));
|
|
return EParseResult::Error;
|
|
}
|
|
}
|
|
|
|
if (!Parser.Scanner.MatchToken(EHlslToken::RightSquareBracket))
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Incorrect attribute\n"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
return EParseResult::Matched;
|
|
}
|
|
|
|
return EParseResult::NotMatched;
|
|
}
|
|
|
|
EParseResult ParseSwitchStatement(FHlslParser& Parser)
|
|
{
|
|
if (!Parser.Scanner.MatchToken(EHlslToken::LeftParenthesis))
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("'(' expected"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
if (ParseExpression(Parser.Scanner, Parser.CurrentScope) != EParseResult::Matched)
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Expression expected"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
if (!Parser.Scanner.MatchToken(EHlslToken::RightParenthesis))
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("')' expected"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
if (!Parser.Scanner.MatchToken(EHlslToken::LeftBrace))
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("'{' expected"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
bool bDefaultFound = false;
|
|
while (Parser.Scanner.HasMoreTokens())
|
|
{
|
|
if (Parser.Scanner.MatchToken(EHlslToken::Default))
|
|
{
|
|
if (bDefaultFound)
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("'default' found twice on switch() statement!"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
if (!Parser.Scanner.MatchToken(EHlslToken::Colon))
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("':' expected"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
bDefaultFound = true;
|
|
}
|
|
else if (Parser.Scanner.MatchToken(EHlslToken::Case))
|
|
{
|
|
if (ParseExpression(Parser.Scanner, Parser.CurrentScope) != EParseResult::Matched)
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("Expression expected on case label!"));
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
if (!Parser.Scanner.MatchToken(EHlslToken::Colon))
|
|
{
|
|
Parser.Scanner.SourceError(TEXT("':' expected"));
|
|
return EParseResult::Error;
|
|
}
|
|
}
|
|
|
|
auto* Peek = Parser.Scanner.PeekToken();
|
|
if (!Peek)
|
|
{
|
|
break;
|
|
}
|
|
else if (Parser.Scanner.MatchToken(EHlslToken::RightBrace))
|
|
{
|
|
return EParseResult::Matched;
|
|
}
|
|
else if (Peek->Token == EHlslToken::Case || Peek->Token == EHlslToken::Default)
|
|
{
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
auto Result = ParseStatement(Parser);
|
|
if (Result == EParseResult::Error)
|
|
{
|
|
return EParseResult::Error;
|
|
}
|
|
}
|
|
/*
|
|
// Process statements until hitting break, case, default or '}'
|
|
while (Parser.Scanner.HasMoreTokens())
|
|
{
|
|
if (Parser.Scanner.MatchToken(EHlslToken::RightBrace))
|
|
{
|
|
return EParseResult::Matched;
|
|
}
|
|
|
|
if (Parser.Scanner.MatchToken(EHlslToken::Break))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
check(0);
|
|
*/
|
|
}
|
|
|
|
return EParseResult::Error;
|
|
}
|
|
|
|
EParseResult ParseExpressionStatement(FHlslParser& Parser)
|
|
{
|
|
auto OriginalToken = Parser.Scanner.GetCurrentTokenIndex();
|
|
if (ParseExpression(Parser.Scanner, Parser.CurrentScope) == EParseResult::Matched)
|
|
{
|
|
if (Parser.Scanner.MatchToken(EHlslToken::Semicolon))
|
|
{
|
|
return EParseResult::Matched;
|
|
}
|
|
}
|
|
|
|
Parser.Scanner.SetCurrentTokenIndex(OriginalToken);
|
|
return EParseResult::NotMatched;
|
|
}
|
|
|
|
EParseResult ParseEmptyStatement(FHlslParser& Parser)
|
|
{
|
|
// Nothing to do here...
|
|
return EParseResult::Matched;
|
|
}
|
|
|
|
void InitRules()
|
|
{
|
|
static bool bInit = false;
|
|
if (!bInit)
|
|
{
|
|
bInit = true;
|
|
|
|
RulesTranslationUnit.Add(FRulePair(EHlslToken::Struct, ParseStruct));
|
|
RulesTranslationUnit.Add(FRulePair(EHlslToken::CBuffer, ParseCBuffer));
|
|
RulesTranslationUnit.Add(FRulePair(EHlslToken::Invalid, ParseFunctionDeclaration, TryParseAttribute));
|
|
RulesTranslationUnit.Add(FRulePair(EHlslToken::Invalid, ParseGlobalVariableDeclaration));
|
|
|
|
RulesStatements.Add(FRulePair(EHlslToken::LeftBrace, ParseStatementBlock));
|
|
RulesStatements.Add(FRulePair(EHlslToken::Return, ParseReturnStatement));
|
|
RulesStatements.Add(FRulePair(EHlslToken::Do, ParseDoStatement));
|
|
RulesStatements.Add(FRulePair(EHlslToken::While, ParseWhileStatement, TryParseAttribute));
|
|
RulesStatements.Add(FRulePair(EHlslToken::For, ParseForStatement, TryParseAttribute));
|
|
RulesStatements.Add(FRulePair(EHlslToken::If, ParseIfStatement, TryParseAttribute));
|
|
RulesStatements.Add(FRulePair(EHlslToken::Switch, ParseSwitchStatement, TryParseAttribute));
|
|
RulesStatements.Add(FRulePair(EHlslToken::Semicolon, ParseEmptyStatement));
|
|
RulesStatements.Add(FRulePair(EHlslToken::Break, ParseEmptyStatement));
|
|
RulesStatements.Add(FRulePair(EHlslToken::Invalid, ParseExpressionStatement));
|
|
RulesStatements.Add(FRulePair(EHlslToken::Invalid, ParseLocalDeclaration));
|
|
}
|
|
}
|
|
|
|
FHlslParser::FHlslParser() :
|
|
GlobalScope(nullptr)
|
|
{
|
|
CurrentScope = &GlobalScope;
|
|
InitRules();
|
|
}
|
|
|
|
namespace Parser
|
|
{
|
|
bool Parse(const FString& Input, const FString& Filename)
|
|
{
|
|
FHlslParser Parser;
|
|
if (!Parser.Scanner.Lex(Input, Filename))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool bSuccess = true;
|
|
while (Parser.Scanner.HasMoreTokens())
|
|
{
|
|
auto LastIndex = Parser.Scanner.GetCurrentTokenIndex();
|
|
|
|
static FString GlobalDeclOrDefinition(TEXT("Global declaration or definition"));
|
|
auto Result = TryRules(RulesTranslationUnit, Parser, GlobalDeclOrDefinition, true);
|
|
if (Result == EParseResult::Error)
|
|
{
|
|
bSuccess = false;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
check(Result == EParseResult::Matched);
|
|
}
|
|
|
|
check(LastIndex != Parser.Scanner.GetCurrentTokenIndex());
|
|
}
|
|
|
|
return bSuccess;
|
|
}
|
|
}
|
|
}
|