Files
UnrealEngine/Engine/Source/Developer/ShaderCompilerCommon/Private/HlslExpressionParser.inl
2025-05-18 13:04:45 +08:00

1519 lines
42 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
HlslExpressionParser.inl - Implementation for parsing hlsl expressions.
=============================================================================*/
#pragma once
#include "CoreTypes.h"
#include "Misc/AssertionMacros.h"
#include "Containers/UnrealString.h"
#include "Containers/Set.h"
#include "Developer/ShaderCompilerCommon/Private/HlslParser.h"
#include "Developer/ShaderCompilerCommon/Private/HlslAST.h"
#include "Hash/xxhash.h"
class Error;
namespace CrossCompiler
{
EParseResult ParseResultError();
struct FSymbolScope;
//struct FInfo;
EParseResult ComputeExpr(FHlslScanner& Scanner, int32 MinPrec, /*FInfo& Info,*/ FSymbolScope* SymbolScope, int32 ExpressionFlags, FLinearAllocator* Allocator, AST::FExpression** OutExpression, AST::FExpression** OutTernaryExpression);
EParseResult ParseExpressionList(EHlslToken EndListToken, FHlslScanner& Scanner, FSymbolScope* SymbolScope, EHlslToken NewStartListToken, FLinearAllocator* Allocator, AST::FExpression* OutExpression);
struct FSymbolScope
{
enum class EType
{
Unknown,
Global,
Namespace,
Statement,
Do,
While,
For,
If,
ScopedDeclaration,
Function,
};
// Type tag is for debugging
const EType ScopeType;
FLinearAllocator* Allocator;
FSymbolScope* Parent;
const TCHAR* Name;
TSet/*TLinearSet*/<FString> Symbols;
TLinearArray<FSymbolScope> Children;
struct FIdentifierKeyFuncs : public DefaultKeyFuncs<FStringView>
{
static FStringView GetSetKey(FStringView K) { return K; }
template <typename T>
static FStringView GetSetKey(const TPair<FStringView, T>& P) { return P.Key; }
static bool Matches(FStringView A, FStringView B) { return A.Equals(B, ESearchCase::CaseSensitive); }
static uint32 GetKeyHash(FStringView Key)
{
return FXxHash64::HashBuffer(Key.GetData(), Key.Len() * sizeof(*Key.GetData())).Hash;
}
};
TMap<FStringView, CrossCompiler::AST::FIdentifier*, FDefaultSetAllocator, FIdentifierKeyFuncs> Identifiers;
FSymbolScope(FLinearAllocator* InAllocator, FSymbolScope* InParent, EType InScopeType)
: ScopeType(InScopeType)
, Allocator(InAllocator)
, Parent(InParent)
, Name(nullptr)
, Children(InAllocator)
{
}
~FSymbolScope()
{
}
FORCENOINLINE void Add(const FString& Type)
{
Symbols.Emplace(Type);
}
FORCENOINLINE void Add(FStringView Type)
{
Symbols.Emplace(Type);
}
FORCENOINLINE void Add(const TCHAR* Type)
{
Symbols.Emplace(Type);
}
CrossCompiler::AST::FIdentifier* FindExistingIdentifier(uint32 IdentifierHash, FStringView Identifier, bool bSearchUpwards)
{
if (auto ExistingEntry = Identifiers.FindByHash(IdentifierHash, Identifier))
{
return *ExistingEntry;
}
else if (bSearchUpwards && Parent)
{
return Parent->FindExistingIdentifier(IdentifierHash, Identifier, bSearchUpwards);
}
else
{
return nullptr;
}
}
CrossCompiler::AST::FIdentifier* FindOrAddIdentifier(FStringView IdentifierName)
{
const uint32 IdentifierHash = FIdentifierKeyFuncs::GetKeyHash(IdentifierName);
const bool bSearchUpwards = true;
if (CrossCompiler::AST::FIdentifier* ExistingEntry = FindExistingIdentifier(IdentifierHash, IdentifierName, bSearchUpwards))
{
return ExistingEntry;
}
else
{
AST::FIdentifier* NewIdentifier = AST::FIdentifier::New(Allocator, IdentifierName);
Identifiers.AddByHash(IdentifierHash, IdentifierName, NewIdentifier);
return NewIdentifier;
}
}
static bool FindType(const FSymbolScope* Scope, const FString& Type, bool bSearchUpwards = true)
{
while (Scope)
{
if (Scope->Symbols.Contains(Type))
{
return true;
}
if (!bSearchUpwards)
{
return false;
}
Scope = Scope->Parent;
}
return false;
}
const FSymbolScope* FindNamespace(const TCHAR* Namespace) const
{
for (auto& Child : Children)
{
if (Child.Name && FCString::Strcmp(Child.Name, Namespace) == 0)
{
return &Child;
}
}
return nullptr;
}
static const FSymbolScope* FindGlobalNamespace(const TCHAR* Namespace, const FSymbolScope* Scope)
{
return Scope->GetGlobalScope()->FindNamespace(Namespace);
}
const FSymbolScope* GetGlobalScope() const
{
const FSymbolScope* Scope = this;
while (Scope->Parent)
{
Scope = Scope->Parent;
}
return Scope;
}
};
inline AST::FIdentifier* FindOrAddIdentifier(FLinearAllocator* Allocator, FSymbolScope* SymbolScope, FStringView IdentifierName)
{
if (SymbolScope)
{
return SymbolScope->FindOrAddIdentifier(IdentifierName);
}
else
{
return AST::FIdentifier::New(Allocator, IdentifierName);
}
}
struct FCreateSymbolScope
{
UE_NONCOPYABLE(FCreateSymbolScope)
FSymbolScope* Original;
FSymbolScope** Current;
FCreateSymbolScope(FLinearAllocator* InAllocator, FSymbolScope** InCurrent, FSymbolScope::EType ScopeType) :
Current(InCurrent)
{
Original = *InCurrent;
auto& NewScope = Original->Children.Emplace_GetRef(InAllocator, Original, ScopeType);
*Current = &NewScope;
}
~FCreateSymbolScope()
{
*Current = Original;
}
};
/*
struct FInfo
{
int32 Indent;
bool bEnabled;
FInfo(bool bInEnabled = false) : Indent(0), bEnabled(bInEnabled) {}
void Print(const FString& Message)
{
if (bEnabled)
{
FPlatformMisc::LocalPrint(*Message);
}
}
void PrintTabs()
{
if (bEnabled)
{
for (int32 Index = 0; Index < Indent; ++Index)
{
FPlatformMisc::LocalPrint(TEXT("\t"));
}
}
}
void PrintWithTabs(const FString& Message)
{
PrintTabs();
Print(Message);
}
};
struct FInfoIndentScope
{
FInfoIndentScope(FInfo& InInfo) : Info(InInfo)
{
++Info.Indent;
}
~FInfoIndentScope()
{
--Info.Indent;
}
FInfo& Info;
};*/
enum ETypeFlags
{
ETF_VOID = 1 << 0,
ETF_BUILTIN_NUMERIC = 1 << 1,
ETF_SAMPLER_TEXTURE_BUFFER = 1 << 2,
ETF_USER_TYPES = 1 << 3,
ETF_ERROR_IF_NOT_USER_TYPE = 1 << 4,
ETF_UNORM = 1 << 5,
};
enum EExtraQualifiers
{
EEQ_PRECISE = 1 << 0,
EEQ_UNORM = 1 << 1,
EEQ_SNORM = 1 << 2,
};
enum EExpressionFlags
{
EEF_ALLOW_ASSIGNMENT = 1 << 0,
};
EParseResult ParseGeneralTypeToken(const FHlslToken* Token, int32 TypeFlags, int32 ExtraQualifierFlags, FLinearAllocator* Allocator, AST::FTypeSpecifier** OutSpecifier)
{
if (!Token)
{
return ParseResultError();
}
bool bMatched = false;
const TCHAR* InnerType = nullptr;
switch (Token->Token)
{
case EHlslToken::Void:
if (TypeFlags & ETF_VOID)
{
bMatched = true;
}
break;
case EHlslToken::Bool:
case EHlslToken::Bool1:
case EHlslToken::Bool2:
case EHlslToken::Bool3:
case EHlslToken::Bool4:
case EHlslToken::Bool1x1:
case EHlslToken::Bool1x2:
case EHlslToken::Bool1x3:
case EHlslToken::Bool1x4:
case EHlslToken::Bool2x1:
case EHlslToken::Bool2x2:
case EHlslToken::Bool2x3:
case EHlslToken::Bool2x4:
case EHlslToken::Bool3x1:
case EHlslToken::Bool3x2:
case EHlslToken::Bool3x3:
case EHlslToken::Bool3x4:
case EHlslToken::Bool4x1:
case EHlslToken::Bool4x2:
case EHlslToken::Bool4x3:
case EHlslToken::Bool4x4:
case EHlslToken::Int:
case EHlslToken::Int1:
case EHlslToken::Int2:
case EHlslToken::Int3:
case EHlslToken::Int4:
case EHlslToken::Int1x1:
case EHlslToken::Int1x2:
case EHlslToken::Int1x3:
case EHlslToken::Int1x4:
case EHlslToken::Int2x1:
case EHlslToken::Int2x2:
case EHlslToken::Int2x3:
case EHlslToken::Int2x4:
case EHlslToken::Int3x1:
case EHlslToken::Int3x2:
case EHlslToken::Int3x3:
case EHlslToken::Int3x4:
case EHlslToken::Int4x1:
case EHlslToken::Int4x2:
case EHlslToken::Int4x3:
case EHlslToken::Int4x4:
case EHlslToken::Uint:
case EHlslToken::Uint1:
case EHlslToken::Uint2:
case EHlslToken::Uint3:
case EHlslToken::Uint4:
case EHlslToken::Uint1x1:
case EHlslToken::Uint1x2:
case EHlslToken::Uint1x3:
case EHlslToken::Uint1x4:
case EHlslToken::Uint2x1:
case EHlslToken::Uint2x2:
case EHlslToken::Uint2x3:
case EHlslToken::Uint2x4:
case EHlslToken::Uint3x1:
case EHlslToken::Uint3x2:
case EHlslToken::Uint3x3:
case EHlslToken::Uint3x4:
case EHlslToken::Uint4x1:
case EHlslToken::Uint4x2:
case EHlslToken::Uint4x3:
case EHlslToken::Uint4x4:
case EHlslToken::Uint64_t:
case EHlslToken::Uint64_t1:
case EHlslToken::Uint64_t2:
case EHlslToken::Uint64_t3:
case EHlslToken::Uint64_t4:
case EHlslToken::Uint64_t1x1:
case EHlslToken::Uint64_t1x2:
case EHlslToken::Uint64_t1x3:
case EHlslToken::Uint64_t1x4:
case EHlslToken::Uint64_t2x1:
case EHlslToken::Uint64_t2x2:
case EHlslToken::Uint64_t2x3:
case EHlslToken::Uint64_t2x4:
case EHlslToken::Uint64_t3x1:
case EHlslToken::Uint64_t3x2:
case EHlslToken::Uint64_t3x3:
case EHlslToken::Uint64_t3x4:
case EHlslToken::Uint64_t4x1:
case EHlslToken::Uint64_t4x2:
case EHlslToken::Uint64_t4x3:
case EHlslToken::Uint64_t4x4:
case EHlslToken::Half:
case EHlslToken::Half1:
case EHlslToken::Half2:
case EHlslToken::Half3:
case EHlslToken::Half4:
case EHlslToken::Half1x1:
case EHlslToken::Half1x2:
case EHlslToken::Half1x3:
case EHlslToken::Half1x4:
case EHlslToken::Half2x1:
case EHlslToken::Half2x2:
case EHlslToken::Half2x3:
case EHlslToken::Half2x4:
case EHlslToken::Half3x1:
case EHlslToken::Half3x2:
case EHlslToken::Half3x3:
case EHlslToken::Half3x4:
case EHlslToken::Half4x1:
case EHlslToken::Half4x2:
case EHlslToken::Half4x3:
case EHlslToken::Half4x4:
case EHlslToken::Min16Float:
case EHlslToken::Min16Float1:
case EHlslToken::Min16Float2:
case EHlslToken::Min16Float3:
case EHlslToken::Min16Float4:
case EHlslToken::Min16Float1x1:
case EHlslToken::Min16Float1x2:
case EHlslToken::Min16Float1x3:
case EHlslToken::Min16Float1x4:
case EHlslToken::Min16Float2x1:
case EHlslToken::Min16Float2x2:
case EHlslToken::Min16Float2x3:
case EHlslToken::Min16Float2x4:
case EHlslToken::Min16Float3x1:
case EHlslToken::Min16Float3x2:
case EHlslToken::Min16Float3x3:
case EHlslToken::Min16Float3x4:
case EHlslToken::Min16Float4x1:
case EHlslToken::Min16Float4x2:
case EHlslToken::Min16Float4x3:
case EHlslToken::Min16Float4x4:
case EHlslToken::Float:
case EHlslToken::Float1:
case EHlslToken::Float2:
case EHlslToken::Float3:
case EHlslToken::Float4:
case EHlslToken::Float1x1:
case EHlslToken::Float1x2:
case EHlslToken::Float1x3:
case EHlslToken::Float1x4:
case EHlslToken::Float2x1:
case EHlslToken::Float2x2:
case EHlslToken::Float2x3:
case EHlslToken::Float2x4:
case EHlslToken::Float3x1:
case EHlslToken::Float3x2:
case EHlslToken::Float3x3:
case EHlslToken::Float3x4:
case EHlslToken::Float4x1:
case EHlslToken::Float4x2:
case EHlslToken::Float4x3:
case EHlslToken::Float4x4:
if (TypeFlags & ETF_BUILTIN_NUMERIC)
{
bMatched = true;
}
break;
case EHlslToken::Texture:
case EHlslToken::Texture1D:
case EHlslToken::Texture1DArray:
case EHlslToken::Texture2D:
case EHlslToken::Texture2DArray:
case EHlslToken::Texture2DMS:
case EHlslToken::Texture2DMSArray:
case EHlslToken::Texture3D:
case EHlslToken::TextureCube:
case EHlslToken::TextureCubeArray:
case EHlslToken::Buffer:
case EHlslToken::AppendStructuredBuffer:
case EHlslToken::ConsumeStructuredBuffer:
case EHlslToken::RWBuffer:
case EHlslToken::RWStructuredBuffer:
case EHlslToken::RWTexture1D:
case EHlslToken::RWTexture1DArray:
case EHlslToken::RWTexture2D:
case EHlslToken::RWTexture2DArray:
case EHlslToken::RasterizerOrderedTexture2D:
case EHlslToken::RWTexture3D:
case EHlslToken::ConstantBuffer:
case EHlslToken::StructuredBuffer:
if (TypeFlags & ETF_SAMPLER_TEXTURE_BUFFER)
{
bMatched = true;
InnerType = TEXT("float4");
}
break;
case EHlslToken::RaytracingAccelerationStructure:
if (TypeFlags & ETF_SAMPLER_TEXTURE_BUFFER)
{
bMatched = true;
}
break;
case EHlslToken::Sampler:
case EHlslToken::Sampler1D:
case EHlslToken::Sampler2D:
case EHlslToken::Sampler3D:
case EHlslToken::SamplerCube:
case EHlslToken::SamplerState:
case EHlslToken::SamplerComparisonState:
case EHlslToken::ByteAddressBuffer:
case EHlslToken::RWByteAddressBuffer:
if (TypeFlags & ETF_SAMPLER_TEXTURE_BUFFER)
{
bMatched = true;
}
break;
}
if (bMatched)
{
//#todo-rco: Don't re-allocate types
TStringBuilder<64> TypeName;
TypeName += (ExtraQualifierFlags & EEQ_SNORM) == EEQ_SNORM
? TEXTVIEW("snorm ")
: ((ExtraQualifierFlags & EEQ_UNORM) == EEQ_UNORM
? TEXTVIEW("unorm ")
: TEXTVIEW(""));
TypeName += Token->String;
auto* Type = new(Allocator) AST::FTypeSpecifier(Allocator, Token->SourceInfo);
Type->TypeName = Allocator->Strdup(TypeName.ToView());
Type->InnerType = InnerType;
Type->bPrecise = (ExtraQualifierFlags & EEQ_PRECISE) == EEQ_PRECISE;
*OutSpecifier = Type;
return EParseResult::Matched;
}
return EParseResult::NotMatched;
}
EParseResult ParseGeneralTypeFromToken(const FHlslToken* Token, int32 TypeFlags, int32 ExtraQualifierFlags, FSymbolScope* SymbolScope, FLinearAllocator* Allocator, AST::FTypeSpecifier** OutSpecifier)
{
if (Token)
{
if (ParseGeneralTypeToken(Token, TypeFlags, ExtraQualifierFlags, Allocator, OutSpecifier) == EParseResult::Matched)
{
return EParseResult::Matched;
}
if (TypeFlags & ETF_USER_TYPES)
{
check(SymbolScope);
if (Token->Token == EHlslToken::Identifier)
{
if (FSymbolScope::FindType(SymbolScope, Token->String))
{
auto* Type = new(Allocator) AST::FTypeSpecifier(Allocator, Token->SourceInfo);
Type->TypeName = Allocator->Strdup(Token->String);
*OutSpecifier = Type;
return EParseResult::Matched;
}
else if (TypeFlags & ETF_ERROR_IF_NOT_USER_TYPE)
{
return ParseResultError();
}
}
}
return EParseResult::NotMatched;
}
return ParseResultError();
}
EParseResult ParseGeneralType(FHlslScanner& Scanner, int32 TypeFlags, FSymbolScope* SymbolScope, FLinearAllocator* Allocator, AST::FTypeSpecifier** OutSpecifier)
{
auto* Token = Scanner.PeekToken();
// Handle types with namespaces
{
auto* Token1 = Scanner.PeekToken(1);
auto* Token2 = Scanner.PeekToken(2);
TStringBuilder<256> TypeString;
if (Token && Token->Token == EHlslToken::Identifier && Token1 && Token1->Token == EHlslToken::ColonColon && Token2 && Token2->Token == EHlslToken::Identifier && SymbolScope)
{
auto* Namespace = SymbolScope->GetGlobalScope();
do
{
Token = Scanner.PeekToken();
check(Scanner.MatchToken(EHlslToken::Identifier));
auto* OuterNamespace = Token;
check(Scanner.MatchToken(EHlslToken::ColonColon));
auto* InnerOrType = Scanner.PeekToken();
if (!InnerOrType)
{
Scanner.SourceError(*FString::Printf(TEXT("Expecting identifier for type '%s'!"), InnerOrType ? *InnerOrType->String : TEXT("null")));
return ParseResultError();
}
Namespace = Namespace->FindNamespace(*OuterNamespace->String);
if (!Namespace)
{
Scanner.SourceError(*FString::Printf(TEXT("Unknown namespace '%s'!"), *TypeString));
return ParseResultError();
}
TypeString += OuterNamespace->String;
TypeString += TEXT("::");
auto* Token3 = Scanner.PeekToken(1);
if (Token3 && Token3->Token == EHlslToken::ColonColon)
{
continue;
}
else
{
if (InnerOrType && FChar::IsAlpha(InnerOrType->String[0]))
{
Scanner.Advance();
if (FSymbolScope::FindType(Namespace, InnerOrType->String, false))
{
auto* Type = new(Allocator) AST::FTypeSpecifier(Allocator, Token->SourceInfo);
Type->TypeName = Allocator->Strdup(TypeString.ToView());
*OutSpecifier = Type;
return EParseResult::Matched;
}
else
{
Scanner.SourceError(*FString::Printf(TEXT("Unknown type '%s'!"), *TypeString));
return ParseResultError();
}
}
break;
}
}
while (Token);
}
}
int32 ExtraQualifiers = 0;
bool bPrecise = false;
if (Scanner.MatchToken(EHlslToken::Precise))
{
Token = Scanner.GetCurrentToken();
ExtraQualifiers |= EEQ_PRECISE;
}
if ((TypeFlags & ETF_UNORM) == ETF_UNORM && Token && Token->String.Len() == 5)
{
if (!FCString::Strcmp(*Token->String, TEXT("unorm")))
{
ExtraQualifiers |= EEQ_UNORM;
Scanner.Advance();
Token = Scanner.GetCurrentToken();
}
else if (!FCString::Strcmp(*Token->String, TEXT("snorm")))
{
ExtraQualifiers |= EEQ_SNORM;
Scanner.Advance();
Token = Scanner.GetCurrentToken();
}
}
auto Result = ParseGeneralTypeFromToken(Token, TypeFlags, ExtraQualifiers, SymbolScope, Allocator, OutSpecifier);
if (Result == EParseResult::Matched)
{
Scanner.Advance();
return EParseResult::Matched;
}
if (Result == EParseResult::Error && (TypeFlags & ETF_ERROR_IF_NOT_USER_TYPE))
{
Scanner.SourceError(*FString::Printf(TEXT("Unknown type '%s'!"), Token ? *Token->String : TEXT("<null>")));
return Result;
}
return EParseResult::NotMatched;
}
EParseResult ParseFullType(FHlslScanner& Scanner, int32 TypeFlags, int32 TemplateTypeFlags, FSymbolScope* SymbolScope, FLinearAllocator* Allocator, AST::FFullySpecifiedType** OutFullySpecifiedType)
{
AST::FTypeSpecifier* TypeSpecifier = nullptr;
EParseResult Result = ParseGeneralType(Scanner, TypeFlags, SymbolScope, Allocator, &TypeSpecifier);
if (Result != EParseResult::Matched)
{
return Result;
}
// OutFullySpecifiedType could already be initialized
AST::FFullySpecifiedType* FullType = *OutFullySpecifiedType ? *OutFullySpecifiedType : new(Allocator) AST::FFullySpecifiedType(Allocator, TypeSpecifier->SourceInfo);
FullType->Specifier = TypeSpecifier;
if (Scanner.MatchToken(EHlslToken::Lower))
{
AST::FTypeSpecifier* ElementTypeSpecifier = nullptr;
EParseResult InnerResult = ParseGeneralType(Scanner, TemplateTypeFlags, SymbolScope, Allocator, &ElementTypeSpecifier);
if (InnerResult != EParseResult::Matched)
{
Scanner.SourceError(TEXT("Expected type!"));
return ParseResultError();
}
FullType->Specifier->InnerType = ElementTypeSpecifier->TypeName;
if (Scanner.MatchToken(EHlslToken::Comma))
{
auto* Integer = Scanner.GetCurrentToken();
if (!Scanner.MatchIntegerLiteral())
{
Scanner.SourceError(TEXT("Expected constant!"));
return ParseResultError();
}
FullType->Specifier->TextureMSNumSamples = FCString::Atoi(*Integer->String);
}
if (!Scanner.MatchToken(EHlslToken::Greater))
{
Scanner.SourceError(TEXT("Expected '>'!"));
return ParseResultError();
}
}
*OutFullySpecifiedType = FullType;
return EParseResult::Matched;
}
// Unary!(Unary-(Unary+())) would have ! as Top, and + as Inner
EParseResult MatchUnaryOperator(FHlslScanner& Scanner, /*FInfo& Info,*/ FSymbolScope* SymbolScope, FLinearAllocator* Allocator, AST::FExpression** OuterExpression, AST::FExpression** InnerExpression)
{
bool bFoundAny = false;
bool bTryAgain = true;
AST::FExpression*& PrevExpression = *InnerExpression;
while (Scanner.HasMoreTokens() && bTryAgain)
{
auto* Token = Scanner.GetCurrentToken();
AST::EOperators Operator = AST::EOperators::Plus;
switch (Token->Token)
{
case EHlslToken::PlusPlus:
bFoundAny = true;
Scanner.Advance();
Operator = AST::EOperators::PreInc;
break;
case EHlslToken::MinusMinus:
bFoundAny = true;
Scanner.Advance();
Operator = AST::EOperators::PreDec;
break;
case EHlslToken::Plus:
Scanner.Advance();
bFoundAny = true;
Operator = AST::EOperators::Plus;
break;
case EHlslToken::Minus:
Scanner.Advance();
bFoundAny = true;
Operator = AST::EOperators::Minus;
break;
case EHlslToken::Not:
Scanner.Advance();
bFoundAny = true;
Operator = AST::EOperators::LogicNot;
break;
case EHlslToken::Neg:
Scanner.Advance();
bFoundAny = true;
Operator = AST::EOperators::BitNeg;
break;
case EHlslToken::LeftParenthesis:
// Only cast expressions are Unary
{
const auto* Peek1 = Scanner.PeekToken(1);
const auto* Peek2 = Scanner.PeekToken(2);
bool bFoundConst = false;
int32 PeekN = 0;
auto HandleUnaryToken = [&](EHlslToken TokenType)
{
if (Peek1->Token == TokenType)
{
++PeekN;
Peek1 = Scanner.PeekToken(1 + PeekN);
Peek2 = Scanner.PeekToken(2 + PeekN);
return true;
}
return false;
};
//#todo-rco: Workaround for weird const cast in RHS codegen from HLSLTranslator:
// const uint exp2 = ((((const int) ((uRes32>>23)&0xff))-127+15) << 10);
if (HandleUnaryToken(EHlslToken::Const))
{
HandleUnaryToken(EHlslToken::Precise);
}
else if (HandleUnaryToken(EHlslToken::Precise))
{
HandleUnaryToken(EHlslToken::Const);
}
AST::FTypeSpecifier* TypeSpecifier = nullptr;
//#todo-rco: Is precise allowed on casts?
if (Peek1 && ParseGeneralTypeFromToken(Peek1, ETF_BUILTIN_NUMERIC | ETF_USER_TYPES, 0, SymbolScope, Allocator, &TypeSpecifier) == EParseResult::Matched && Peek2 && Peek2->Token == EHlslToken::RightParenthesis)
{
for (; PeekN > 0; --PeekN) //-V::654,621
{
Scanner.Advance();
}
// Cast
Scanner.Advance();
Scanner.Advance();
Scanner.Advance();
bFoundAny = true;
auto* Expression = new(Allocator) AST::FUnaryExpression(Allocator, AST::EOperators::TypeCast, nullptr, Token->SourceInfo);
if (PrevExpression)
{
PrevExpression->Expressions[0] = Expression;
}
Expression->TypeSpecifier = TypeSpecifier;
if (!*OuterExpression)
{
*OuterExpression = Expression;
}
PrevExpression = Expression;
continue;
}
else
{
// Non-unary
return bFoundAny ? EParseResult::Matched : EParseResult::NotMatched;
}
}
break;
default:
return bFoundAny ? EParseResult::Matched : EParseResult::NotMatched;
}
auto* Expression = new(Allocator) AST::FUnaryExpression(Allocator, Operator, nullptr, Token->SourceInfo);
if (PrevExpression)
{
PrevExpression->Expressions[0] = Expression;
}
if (!*OuterExpression)
{
*OuterExpression = Expression;
}
PrevExpression = Expression;
}
// Ran out of tokens!
return ParseResultError();
}
EParseResult MatchSuffixOperator(FHlslScanner& Scanner, /*FInfo& Info,*/ FSymbolScope* SymbolScope, int32 ExpressionFlags, FLinearAllocator* Allocator, AST::FExpression** InOutExpression, AST::FExpression** OutTernaryExpression)
{
bool bFoundAny = false;
bool bTryAgain = true;
AST::FExpression*& PrevExpression = *InOutExpression;
while (Scanner.HasMoreTokens() && bTryAgain)
{
auto* Token = Scanner.GetCurrentToken();
AST::EOperators Operator = AST::EOperators::Plus;
switch (Token->Token)
{
case EHlslToken::LeftSquareBracket:
{
Scanner.Advance();
AST::FExpression* ArrayIndex = nullptr;
auto Result = ComputeExpr(Scanner, 1, /*Info,*/ SymbolScope, ExpressionFlags, Allocator, &ArrayIndex, nullptr);
if (Result != EParseResult::Matched)
{
Scanner.SourceError(TEXT("Expected expression!"));
return ParseResultError();
}
if (!Scanner.MatchToken(EHlslToken::RightSquareBracket))
{
Scanner.SourceError(TEXT("Expected ']'!"));
return ParseResultError();
}
auto* ArrayIndexExpression = new(Allocator) AST::FBinaryExpression(Allocator, AST::EOperators::ArrayIndex, PrevExpression, ArrayIndex, Token->SourceInfo);
PrevExpression = ArrayIndexExpression;
bFoundAny = true;
}
break;
case EHlslToken::Dot:
{
Scanner.Advance();
const auto* Identifier = Scanner.GetCurrentToken();
if (!Scanner.MatchToken(EHlslToken::Identifier))
{
Scanner.SourceError(TEXT("Expected identifier for member or swizzle!"));
return ParseResultError();
}
auto* FieldExpression = new(Allocator) AST::FUnaryExpression(Allocator, AST::EOperators::FieldSelection, PrevExpression, Token->SourceInfo);
FieldExpression->Identifier = AST::FIdentifier::New(Allocator, Identifier->String);
PrevExpression = FieldExpression;
bFoundAny = true;
}
break;
case EHlslToken::LeftParenthesis:
{
Scanner.Advance();
// Function Call
auto* FunctionCall = new(Allocator) AST::FFunctionExpression(Allocator, Token->SourceInfo, PrevExpression);
auto Result = ParseExpressionList(EHlslToken::RightParenthesis, Scanner, SymbolScope, EHlslToken::Invalid, Allocator, FunctionCall);
if (Result != EParseResult::Matched)
{
Scanner.SourceError(TEXT("Expected ')'!"));
return ParseResultError();
}
PrevExpression = FunctionCall;
bFoundAny = true;
}
break;
case EHlslToken::PlusPlus:
{
Scanner.Advance();
auto* IncExpression = new(Allocator) AST::FUnaryExpression(Allocator, AST::EOperators::PostInc, PrevExpression, Token->SourceInfo);
PrevExpression = IncExpression;
bFoundAny = true;
}
break;
case EHlslToken::MinusMinus:
{
Scanner.Advance();
auto* DecExpression = new(Allocator) AST::FUnaryExpression(Allocator, AST::EOperators::PostDec, PrevExpression, Token->SourceInfo);
PrevExpression = DecExpression;
bFoundAny = true;
}
break;
case EHlslToken::Question:
{
Scanner.Advance();
AST::FExpression* Left = nullptr;
if (ComputeExpr(Scanner, 0, /*Info,*/ SymbolScope, (ExpressionFlags | EEF_ALLOW_ASSIGNMENT), Allocator, &Left, nullptr) != EParseResult::Matched)
{
Scanner.SourceError(TEXT("Expected expression!"));
return ParseResultError();
}
if (!Scanner.MatchToken(EHlslToken::Colon))
{
Scanner.SourceError(TEXT("Expected ':'!"));
return ParseResultError();
}
AST::FExpression* Right = nullptr;
if (ComputeExpr(Scanner, 0, /*Info,*/ SymbolScope, (ExpressionFlags | EEF_ALLOW_ASSIGNMENT), Allocator, &Right, nullptr) != EParseResult::Matched)
{
Scanner.SourceError(TEXT("Expected expression!"));
return ParseResultError();
}
auto* Ternary = new(Allocator) AST::FExpression(Allocator, AST::EOperators::Conditional, nullptr, Left, Right, Token->SourceInfo);
*OutTernaryExpression = Ternary;
bFoundAny = true;
bTryAgain = false;
}
break;
default:
bTryAgain = false;
break;
}
}
*InOutExpression = PrevExpression;
return bFoundAny ? EParseResult::Matched : EParseResult::NotMatched;
}
EParseResult ComputeAtom(FHlslScanner& Scanner, /*FInfo& Info,*/ FSymbolScope* SymbolScope, int32 ExpressionFlags, FLinearAllocator* Allocator, AST::FExpression** OutExpression, AST::FExpression** OutTernaryExpression)
{
AST::FExpression* InnerUnaryExpression = nullptr;
auto UnaryResult = MatchUnaryOperator(Scanner, /*Info,*/ SymbolScope, Allocator, OutExpression, &InnerUnaryExpression);
auto* Token = Scanner.GetCurrentToken();
if (!Token || UnaryResult == EParseResult::Error)
{
return ParseResultError();
}
auto ParseScopedIdentifier = [SymbolScope](FHlslScanner& Scanner, FLinearAllocator* Allocator, FString& Name, const FHlslToken* Token, const FHlslToken* Token1, const FHlslToken* Token2)
{
while (Token1 && Token1->Token == EHlslToken::ColonColon && Token2 && Token2->Token == EHlslToken::Identifier)
{
Name += Token1->String;
Name += Token2->String;
Scanner.Advance();
Scanner.Advance();
Token1 = Scanner.PeekToken();
Token2 = Scanner.PeekToken(1);
}
AST::FExpression* AtomExpression = new(Allocator) AST::FExpression(Allocator, AST::EOperators::Identifier, Token->SourceInfo);
AtomExpression->Identifier = FindOrAddIdentifier(Allocator, SymbolScope, *Name);
return AtomExpression;
};
AST::FExpression* AtomExpression = nullptr;
switch (Token->Token)
{
case EHlslToken::Literal:
Scanner.Advance();
AtomExpression = new(Allocator) AST::FExpression(Allocator, AST::EOperators::Literal, Token->SourceInfo);
AtomExpression->Identifier = AST::FIdentifier::New(Allocator, Token->String);
AtomExpression->LiteralType = Token->LiteralType;
break;
case EHlslToken::ColonColon:
{
const FHlslToken* Token1 = Scanner.PeekToken(1);
if (Token1 && Token1->Token == EHlslToken::Identifier)
{
FString Name;
AtomExpression = ParseScopedIdentifier(Scanner, Allocator, Name, Token, Token, Token1);
}
else
{
Scanner.SourceError(TEXT("Expected identifier after ::"));
return ParseResultError();
}
}
break;
case EHlslToken::Identifier:
{
auto* Token1 = Scanner.PeekToken(1);
auto* Token2 = Scanner.PeekToken(2);
if (Token1 && Token1->Token == EHlslToken::ColonColon && Token2 && Token2->Token == EHlslToken::Identifier)
{
FString Name = Token->String;
Scanner.Advance();
AtomExpression = ParseScopedIdentifier(Scanner, Allocator, Name, Token, Token1, Token2);
}
else
{
Scanner.Advance();
AtomExpression = new(Allocator) AST::FExpression(Allocator, AST::EOperators::Identifier, Token->SourceInfo);
AtomExpression->Identifier = FindOrAddIdentifier(Allocator, SymbolScope, Token->String);
}
}
break;
case EHlslToken::LeftParenthesis:
{
Scanner.Advance();
// Parenthesis expression
if (ComputeExpr(Scanner, 1, /*Info,*/ SymbolScope, ExpressionFlags, Allocator, &AtomExpression, nullptr) != EParseResult::Matched)
{
Scanner.SourceError(TEXT("Expected expression!"));
return ParseResultError();
}
if (Scanner.MatchToken(EHlslToken::Comma))
{
AST::FExpression* FirstSequenceEntry = AtomExpression;
AtomExpression = new(Allocator) AST::FExpressionList(Allocator, AST::FExpressionList::EType::Parenthesized, Token->SourceInfo);
AtomExpression->Expressions.Add(FirstSequenceEntry);
do
{
AST::FExpression* NextExpression = nullptr;
if (ComputeExpr(Scanner, 1, /*Info,*/ SymbolScope, (ExpressionFlags), Allocator, &NextExpression, nullptr) != EParseResult::Matched)
{
Scanner.SourceError(TEXT("Expected expression!"));
return ParseResultError();
}
AtomExpression->Expressions.Add(NextExpression);
}
while (Scanner.MatchToken(EHlslToken::Comma));
}
if (!Scanner.MatchToken(EHlslToken::RightParenthesis))
{
Scanner.SourceError(TEXT("Expected ')'!"));
return ParseResultError();
}
}
break;
default:
{
AST::FTypeSpecifier* TypeSpecifier = nullptr;
// Grrr handle Sampler as a variable name... This is safe here since Declarations are always handled first
if (ParseGeneralType(Scanner, ETF_SAMPLER_TEXTURE_BUFFER, nullptr, Allocator, &TypeSpecifier) == EParseResult::Matched)
{
//@todo-rco: Check this var exists on the symbol table
AtomExpression = new(Allocator) AST::FExpression(Allocator, AST::EOperators::Identifier, TypeSpecifier->SourceInfo);
AtomExpression->Identifier = AST::FIdentifier::New(Allocator, TypeSpecifier->TypeName);
break;
}
// Handle float3(x,y,z)
else if (ParseGeneralType(Scanner, ETF_BUILTIN_NUMERIC, nullptr, Allocator, &TypeSpecifier) == EParseResult::Matched)
{
if (Scanner.MatchToken(EHlslToken::LeftParenthesis))
{
auto* TypeExpression = new(Allocator) AST::FExpression(Allocator, AST::EOperators::Identifier, TypeSpecifier->SourceInfo);
TypeExpression->Identifier = AST::FIdentifier::New(Allocator, TypeSpecifier->TypeName);
auto* FunctionCall = new(Allocator) AST::FFunctionExpression(Allocator, Token->SourceInfo, TypeExpression);
auto Result = ParseExpressionList(EHlslToken::RightParenthesis, Scanner, SymbolScope, EHlslToken::Invalid, Allocator, FunctionCall);
if (Result != EParseResult::Matched)
{
Scanner.SourceError(TEXT("Unexpected type in numeric constructor!"));
return ParseResultError();
}
AtomExpression = FunctionCall;
}
else
{
Scanner.SourceError(TEXT("Unexpected type in declaration!"));
return ParseResultError();
}
break;
}
else
{
if (UnaryResult == EParseResult::Matched)
{
Scanner.SourceError(TEXT("Expected expression!"));
return ParseResultError();
}
return EParseResult::NotMatched;
}
}
break;
}
check(AtomExpression);
auto SuffixResult = MatchSuffixOperator(Scanner, /*Info,*/ SymbolScope, ExpressionFlags, Allocator, &AtomExpression, OutTernaryExpression);
//auto* Token = Scanner.GetCurrentToken();
if (/*!Token || */SuffixResult == EParseResult::Error)
{
return ParseResultError();
}
// Patch unary if necessary
if (InnerUnaryExpression)
{
check(!InnerUnaryExpression->Expressions[0]);
InnerUnaryExpression->Expressions[0] = AtomExpression;
}
if (!*OutExpression)
{
*OutExpression = AtomExpression;
}
return EParseResult::Matched;
}
int32 GetPrecedence(const FHlslToken* Token)
{
if (Token)
{
switch (Token->Token)
{
case EHlslToken::Comma:
return 1;
case EHlslToken::Equal:
case EHlslToken::PlusEqual:
case EHlslToken::MinusEqual:
case EHlslToken::TimesEqual:
case EHlslToken::DivEqual:
case EHlslToken::ModEqual:
case EHlslToken::GreaterGreaterEqual:
case EHlslToken::LowerLowerEqual:
case EHlslToken::AndEqual:
case EHlslToken::OrEqual:
case EHlslToken::XorEqual:
return 2;
case EHlslToken::Question:
check(0);
return 3;
case EHlslToken::OrOr:
return 4;
case EHlslToken::AndAnd:
return 5;
case EHlslToken::Or:
return 6;
case EHlslToken::Xor:
return 7;
case EHlslToken::And:
return 8;
case EHlslToken::EqualEqual:
case EHlslToken::NotEqual:
return 9;
case EHlslToken::Lower:
case EHlslToken::Greater:
case EHlslToken::LowerEqual:
case EHlslToken::GreaterEqual:
return 10;
case EHlslToken::LowerLower:
case EHlslToken::GreaterGreater:
return 11;
case EHlslToken::Plus:
case EHlslToken::Minus:
return 12;
case EHlslToken::Times:
case EHlslToken::Div:
case EHlslToken::Mod:
return 13;
default:
break;
}
}
return -1;
}
bool IsTernaryOperator(const FHlslToken* Token)
{
return (Token && Token->Token == EHlslToken::Question);
}
bool IsBinaryOperator(const FHlslToken* Token)
{
return GetPrecedence(Token) > 0;
}
bool IsAssignmentOperator(const FHlslToken* Token)
{
if (Token)
{
switch (Token->Token)
{
case EHlslToken::Equal:
case EHlslToken::PlusEqual:
case EHlslToken::MinusEqual:
case EHlslToken::TimesEqual:
case EHlslToken::DivEqual:
case EHlslToken::ModEqual:
case EHlslToken::GreaterGreaterEqual:
case EHlslToken::LowerLowerEqual:
case EHlslToken::AndEqual:
case EHlslToken::OrEqual:
case EHlslToken::XorEqual:
return true;
default:
break;
}
}
return false;
}
static inline bool IsSequenceOperator(const FHlslToken* Token)
{
if (Token)
{
return (Token->Token == EHlslToken::Comma);
}
return false;
}
bool IsRightAssociative(const FHlslToken* Token)
{
return IsTernaryOperator(Token);
}
// Ternary is handled by popping out so it can right associate
//#todo-rco: Fix the case for right-associative assignment operators
EParseResult ComputeExpr(FHlslScanner& Scanner, int32 MinPrec, /*FInfo& Info,*/ FSymbolScope* SymbolScope, int32 ExpressionFlags, FLinearAllocator* Allocator, AST::FExpression** OutExpression, AST::FExpression** OutTernaryExpression)
{
const bool bAllowAssignment = ((ExpressionFlags & EEF_ALLOW_ASSIGNMENT) == EEF_ALLOW_ASSIGNMENT);
auto OriginalToken = Scanner.GetCurrentTokenIndex();
//FInfoIndentScope Scope(Info);
/*
// Precedence Climbing
// http://eli.thegreenplace.net/2012/08/02/parsing-expressions-by-precedence-climbing
compute_expr(min_prec):
result = compute_atom()
while cur token is a binary operator with precedence >= min_prec:
prec, assoc = precedence and associativity of current token
if assoc is left:
next_min_prec = prec + 1
else:
next_min_prec = prec
rhs = compute_expr(next_min_prec)
result = compute operator(result, rhs)
return result
*/
//Info.PrintWithTabs(FString::Printf(TEXT("Compute Expr %d\n"), MinPrec));
AST::FExpression* TernaryExpression = nullptr;
auto Result = ComputeAtom(Scanner, /*Info,*/ SymbolScope, ExpressionFlags, Allocator, OutExpression, &TernaryExpression);
if (Result != EParseResult::Matched)
{
return Result;
}
check(*OutExpression);
do
{
auto* Token = Scanner.GetCurrentToken();
int32 Precedence = GetPrecedence(Token);
if (!Token || !IsBinaryOperator(Token) || Precedence < MinPrec || (!bAllowAssignment && IsAssignmentOperator(Token)) || IsSequenceOperator(Token) || (OutTernaryExpression && *OutTernaryExpression))
{
break;
}
Scanner.Advance();
auto NextMinPrec = IsRightAssociative(Token) ? Precedence : Precedence + 1;
AST::FExpression* RHSExpression = nullptr;
AST::FExpression* RHSTernaryExpression = nullptr;
Result = ComputeExpr(Scanner, NextMinPrec, /*Info,*/ SymbolScope, ExpressionFlags, Allocator, &RHSExpression, &RHSTernaryExpression);
if (Result == EParseResult::Error)
{
return ParseResultError();
}
else if (Result == EParseResult::NotMatched)
{
break;
}
check(RHSExpression);
auto BinaryOperator = AST::TokenToASTOperator(Token->Token);
*OutExpression = new(Allocator) AST::FBinaryExpression(Allocator, BinaryOperator, *OutExpression, RHSExpression, Token->SourceInfo);
if (RHSTernaryExpression)
{
check(!TernaryExpression);
TernaryExpression = RHSTernaryExpression;
break;
}
}
while (Scanner.HasMoreTokens());
if (OriginalToken == Scanner.GetCurrentTokenIndex())
{
return EParseResult::NotMatched;
}
if (TernaryExpression)
{
if (!OutTernaryExpression)
{
if (!TernaryExpression->Expressions[0])
{
TernaryExpression->Expressions[0] = *OutExpression;
*OutExpression = TernaryExpression;
}
else
{
check(0);
}
}
else
{
*OutTernaryExpression = TernaryExpression;
}
}
return EParseResult::Matched;
}
EParseResult ParseExpression(FHlslScanner& Scanner, FSymbolScope* SymbolScope, int32 ExpressionFlags, FLinearAllocator* Allocator, AST::FExpression** OutExpression)
{
/*FInfo Info(!true);*/
return ComputeExpr(Scanner, 0, /*Info,*/ SymbolScope, ExpressionFlags, Allocator, OutExpression, nullptr);
}
EParseResult ParseExpressionList2(FHlslScanner& Scanner, FSymbolScope* SymbolScope, FLinearAllocator* Allocator, AST::FExpressionList::EType ExpressionType, AST::FExpression** OutExpression)
{
check(OutExpression);
auto* Token = Scanner.GetCurrentToken();
if (!Token)
{
Scanner.SourceError(TEXT("Invalid expression list\n"));
return ParseResultError();
}
AST::FExpressionList* ExpressionList = new(Allocator) AST::FExpressionList(Allocator, ExpressionType, Token->SourceInfo);
while (Scanner.HasMoreTokens())
{
auto* Peek = Scanner.PeekToken();
if ((ExpressionType == AST::FExpressionList::EType::Braced && Peek->Token == EHlslToken::RightBrace) ||
(ExpressionType == AST::FExpressionList::EType::Parenthesized && Peek->Token == EHlslToken::RightParenthesis))
{
*OutExpression = ExpressionList;
return EParseResult::Matched;
}
AST::FExpression* Expression = nullptr;
if (Scanner.MatchToken(EHlslToken::LeftBrace))
{
auto Result = ParseExpressionList2(Scanner, SymbolScope, Allocator, AST::FExpressionList::EType::Braced, &Expression);
if (Result != EParseResult::Matched)
{
Scanner.SourceError(TEXT("Invalid expression list\n"));
return ParseResultError();
}
if (!Scanner.MatchToken(EHlslToken::RightBrace))
{
Scanner.SourceError(TEXT("Invalid expression list; '}' expected\n"));
return ParseResultError();
}
}
else
{
auto Result = ParseExpression(Scanner, SymbolScope, EEF_ALLOW_ASSIGNMENT, Allocator, &Expression);
if (Result == EParseResult::Error)
{
Scanner.SourceError(TEXT("Invalid expression list\n"));
return ParseResultError();
}
else if (Result == EParseResult::NotMatched)
{
Scanner.SourceError(TEXT("Expected expression\n"));
return ParseResultError();
}
}
ExpressionList->Expressions.Add(Expression);
if (!Scanner.MatchToken(EHlslToken::Comma))
{
*OutExpression = ExpressionList;
return EParseResult::Matched;
}
}
return EParseResult::NotMatched;
}
EParseResult ParseExpressionList(EHlslToken EndListToken, FHlslScanner& Scanner, FSymbolScope* SymbolScope, EHlslToken NewStartListToken, FLinearAllocator* Allocator, AST::FExpression* OutExpression)
{
check(OutExpression);
while (Scanner.HasMoreTokens())
{
const auto* Token = Scanner.PeekToken();
if (Token->Token == EndListToken)
{
Scanner.Advance();
return EParseResult::Matched;
}
else if (NewStartListToken != EHlslToken::Invalid && Token->Token == NewStartListToken)
{
Scanner.Advance();
check(0);
/*
auto* SubExpression = new(Allocator) AST::FInitializerListExpression(Allocator, Token->SourceInfo);
auto Result = ParseExpressionList(EndListToken, Scanner, SymbolScope, NewStartListToken, Allocator, SubExpression);
if (Result != EParseResult::Matched)
{
return Result;
}
OutExpression->Expressions.Add(SubExpression);
*/
}
else
{
AST::FExpression* Expression = nullptr;
auto Result = ParseExpression(Scanner, SymbolScope, EEF_ALLOW_ASSIGNMENT, Allocator, &Expression);
if (Result == EParseResult::Error)
{
Scanner.SourceError(TEXT("Invalid expression list\n"));
return ParseResultError();
}
else if (Result == EParseResult::NotMatched)
{
Scanner.SourceError(TEXT("Expected expression\n"));
return ParseResultError();
}
OutExpression->Expressions.Add(Expression);
}
if (Scanner.MatchToken(EHlslToken::Comma))
{
continue;
}
else if (Scanner.MatchToken(EndListToken))
{
return EParseResult::Matched;
}
Scanner.SourceError(TEXT("Expected ','\n"));
break;
}
return ParseResultError();
}
EParseResult ParseResultError()
{
// Extracted into a function so callstacks can be seen/debugged in the case of an error
return EParseResult::Error;
}
}