Files
2025-05-18 13:04:45 +08:00

1466 lines
46 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "RigVMCore/RigVMRegistry.h"
#include "RigVMModel/RigVMPin.h"
#include "RigVMModel/RigVMLink.h"
#include "RigVMModel/RigVMNode.h"
#include "RigVMASTProxy.h"
#include "Logging/TokenizedMessage.h"
#include "RigVMAST.generated.h"
class FRigVMParserAST;
class FRigVMBlockExprAST;
class FRigVMNodeExprAST;
class FRigVMEntryExprAST;
class FRigVMInvokeEntryExprAST;
class FRigVMCallExternExprAST;
class FRigVMInlineFunctionExprAST;
class FRigVMNoOpExprAST;
class FRigVMVarExprAST;
class FRigVMLiteralExprAST;
class FRigVMExternalVarExprAST;
class FRigVMAssignExprAST;
class FRigVMCopyExprAST;
class FRigVMCachedValueExprAST;
class FRigVMExitExprAST;
class URigVMPin;
class URigVMLink;
class URigVMNode;
class URigVMGraph;
class URigVMController;
class URigVMLibraryNode;
/*
* A structure to describe a link between two proxies
*/
struct RIGVMDEVELOPER_API FRigVMASTLinkDescription
{
FRigVMASTLinkDescription() {}
FRigVMASTLinkDescription(const FRigVMASTProxy& InSourceProxy, const FRigVMASTProxy& InTargetProxy, const FString& InSegmentPath)
: SourceProxy(InSourceProxy)
, TargetProxy(InTargetProxy)
, SegmentPath(InSegmentPath)
, LinkIndex(INDEX_NONE)
{}
FRigVMASTLinkDescription(const FRigVMASTLinkDescription& InOther)
: SourceProxy(InOther.SourceProxy)
, TargetProxy(InOther.TargetProxy)
, SegmentPath(InOther.SegmentPath)
, LinkIndex(INDEX_NONE)
{}
// Note that unlike the copy constructor, LinkIndex is copied by the assignment operator.
FRigVMASTLinkDescription& operator=(const FRigVMASTLinkDescription&) = default;
FRigVMASTProxy SourceProxy;
FRigVMASTProxy TargetProxy;
FString SegmentPath;
int32 LinkIndex;
};
/*
* Base class for an expression within an abstract syntax tree.
* The base implements parent / child relationships as well as a
* simple typing system.
* An expression is a multi child / multi parent element of a
* directed tree (there can be no cycles).
* Expressions can only be constructed by an AST parser, and
* are also memory-owned by the parser.
*/
class RIGVMDEVELOPER_API FRigVMExprAST
{
public:
// Simple enum for differentiating expression types.
enum EType
{
Block,
Entry,
InvokeEntry,
CallExtern,
InlineFunction,
NoOp,
Var,
Literal,
ExternalVar,
Assign,
Copy,
CachedValue,
Exit,
Invalid
};
// virtual destructor
virtual ~FRigVMExprAST() {}
// disable copy constructor
FRigVMExprAST(const FRigVMExprAST&) = delete;
/// returns the parser this expression is owned by
/// @return the parser this expression is owned by
const FRigVMParserAST* GetParser() const { return ParserPtr; }
/// returns the name of the expression (can be NAME_None)
/// @return the name of the expression
FName GetName() const { return Name; }
/// returns the exact type of the expression
/// @return the exact type of the expression
EType GetType() const { return Type; }
/// returns the name of the expression's type
/// @return the name of the expression's type
FName GetTypeName() const;
/// provides type checking for inherited types
/// @param InType the type to check against
/// @return true if this expression is of a given type
virtual bool IsA(EType InType) const = 0;
/// returns true if the expression is a node expression
virtual bool IsNode() const
{
return false;
}
/// returns true if the expression is a var expression
virtual bool IsVar() const
{
return false;
}
/// returns the index of this expression within the parser's storage
/// @return the index of this expression within the parser's storage
int32 GetIndex() const { return Index; }
/// returns true if the expressoin is valid
bool IsValid() const { return GetIndex() != INDEX_NONE; }
/// returns the model proxy this expression relates to
virtual const FRigVMASTProxy& GetProxy() const
{
static const FRigVMASTProxy EmptyProxy;
return EmptyProxy;
}
/// returns the parent of this expression
/// @return the parent of this expression
const FRigVMExprAST* GetParent() const;
/// returns the first parent in the tree of a given type
/// @param InExprType The type of expression to look for in the parent tree
/// @return the first parent in the tree of a given type
virtual const FRigVMExprAST* GetFirstParentOfType(EType InExprType) const;
/// returns true if a given expression is parented to another expression
/// @param InParentExpr The potential parent to check
/// @return True if this expression is parented to the InParentExpr
bool IsParentedTo(const FRigVMExprAST* InParentExpr) const;
/// returns true if a given expression is a parent of another expression
/// @param InChildExpr The potential child to check
/// @return True if this expression is a parent of the InChildExpr
bool IsParentOf(const FRigVMExprAST* InChildExpr) const;
/// returns the block of this expression
/// @return the block of this expression
const FRigVMBlockExprAST* GetBlock() const;
/// returns the root / top level block of this expression
/// @return the root / top level block of this expression
const FRigVMBlockExprAST* GetRootBlock() const;
/// returns all of the block this expression is in
/// @param bSortByDepth If true the blocks will be returned sorted (head to tail)
/// @return all of the block this expression is in
typedef TArray<const FRigVMBlockExprAST*, TInlineAllocator<5>> FRigVMBlockArray;
FRigVMBlockArray GetBlocks(bool bSortByDepth) const;
/// returns the unique block combination hash for this expression (or INDEX_NONE)
/// @return the unique block combination name for this expression (or INDEX_NONE)
TOptional<uint32> GetBlockCombinationHash() const;
/// returns the unique block combination name for this expression (or an empty string)
/// @return the unique block combination name for this expression (or an empty string)
const FString& GetBlockCombinationName() const;
/// returns the lowest child index found for this expression within a parent (or INDEX_NONE)
/// @return the lowest child index found for this expression within a parent (or INDEX_NONE)
int32 GetMinChildIndexWithinParent(const FRigVMExprAST* InParentExpr) const;
/// returns the number of children of this expression
/// @return the number of children of this expression
int32 NumChildren() const { return Children.Num(); }
/// returns true if this expressions is constant (non varying)
/// @return true if this expressions is constant (non varying)
virtual bool IsConstant() const;
/// returns true if this expressions is varying (non constant)
/// @return true if this expressions is varying (non constant)
bool IsVarying() const { return !IsConstant(); }
/// returns the maximum depth of the expression in tree (number of recursive parents)
/// @return the maximum depth of the expression in tree (number of recursive parents)
int32 GetMaximumDepth() const;
/// accessor operator for a given child
/// @param InIndex the index of the child to retrieve (bound = NumChildren() - 1)
/// @return the child at the given index
const FRigVMExprAST* operator[](int32 InIndex) const { return Children[InIndex]; }
// begin iterator accessor for the children
TArray<FRigVMExprAST*>::RangedForConstIteratorType begin() const { return Children.begin(); }
// end iterator accessor for the children
TArray<FRigVMExprAST*>::RangedForConstIteratorType end() const { return Children.end(); }
/// templated getter to retrieve a child with a given index
/// type checking will occur within the ::To method and raise
/// @param InIndex the index of the child to retrieve
/// @return the child at a given index cast to the provided class
template<class ObjectType>
const ObjectType* ChildAt(int32 InIndex) const
{
return Children[InIndex]->To<ObjectType>();
}
/// getter to retrieve a child with a given index
/// @param InIndex the index of the child to retrieve
/// @return the child at a given index
const FRigVMExprAST* ChildAt(int32 InIndex) const
{
return Children[InIndex];
}
/// returns the first parent in the tree of a given type
/// @param InExprType The type of expression to look for in the parent tree
/// @return the first parent in the tree of a given type
virtual const FRigVMExprAST* GetFirstChildOfType(EType InExprType) const;
/// returns the number of parents of this expression
/// @return the number of parents of this expression
int32 NumParents() const { return Parents.Num(); }
/// templated getter to retrieve a parent with a given index
/// type checking will occur within the ::To method and raise
/// @param InIndex the index of the parent to retrieve
/// @return the parent at a given index cast to the provided class
template<class ObjectType>
const ObjectType* ParentAt(int32 InIndex) const
{
return Parents[InIndex]->To<ObjectType>();
}
/// getter to retrieve a parent with a given index
/// @param InIndex the index of the parent to retrieve
/// @return the parent at a given index
const FRigVMExprAST* ParentAt(int32 InIndex) const
{
return Parents[InIndex];
}
/// const templated cast for casting between
/// different expression types.
/// specializations below are used for type checking
/// @return this object cast to the provided class
template<class ObjectType>
const ObjectType* To() const
{
checkNoEntry();
return nullptr;
}
/// templated cast for casting between
/// different expression types.
/// specializations below are used for type checking
/// @return this object cast to the provided class
template<class ObjectType>
ObjectType* To()
{
return (ObjectType*)this;
}
protected:
// default constructor (protected so that only parser can access it)
FRigVMExprAST(EType InType = EType::Invalid, const FRigVMASTProxy& InProxy = FRigVMASTProxy());
// adds a parent to this expression
// this in consequence also adds this as a child to the parent
// @param InParent the parent to add
void AddParent(FRigVMExprAST* InParent);
// removes a parent from this expression
// this in consequence also removes this as a child from the parent
// @param InParent the parent to remove
void RemoveParent(FRigVMExprAST* InParent);
// removes a child from this expression
// this in consequence also removes this as a parent from the child
// @param InChild the child to remove
void RemoveChild(FRigVMExprAST* InChild);
// replaces a parent of this expression with a new one
// @param InCurrentParent the current parent to replace
// @param InNewParent the new parent to replace it with
void ReplaceParent(FRigVMExprAST* InCurrentParent, FRigVMExprAST* InNewParent);
// replaces a child of this expression with a new one
// @param InCurrentChild the current child to replace
// @param InNewChild the new child to replace it with
void ReplaceChild(FRigVMExprAST* InCurrentChild, FRigVMExprAST* InNewChild);
// replaces this expression with another one
// @param InReplacement the expression to replace this one
void ReplaceBy(FRigVMExprAST* InReplacement);
// computes all of the block this expression is in
void GetBlocksImpl(FRigVMBlockArray& InOutBlocks) const;
// returns a string containing an indented tree structure
// for debugging purposes. this is only used by the parser
// @param InPrefix the prefix to use for indentation
// @return the text representation of this part of the tree
virtual FString DumpText(const FString& InPrefix = FString()) const;
// Resets the internal caches of the expression
void InvalidateCaches();
void InvalidateCachesImpl(TArray<bool>& OutProcessed);
FName Name;
EType Type;
int32 Index;
const FRigVMParserAST* ParserPtr;
TArray<FRigVMExprAST*> Parents;
TArray<FRigVMExprAST*> Children;
TMap<FName, int32> PinNameToChildIndex;
mutable TOptional<TOptional<uint32>> BlockCombinationHash;
mutable TOptional<int32> MaximumDepth;
mutable TOptional<int32> BlocksCacheVersion;
mutable FRigVMBlockArray CachedBlocks;
mutable TOptional<int32> FirstChildOfTypeCacheVersion;
mutable TMap<int32, const FRigVMExprAST*> CachedFirstChildOfType;
mutable TOptional<int32> FirstParentOfTypeCacheVersion;
mutable TMap<int32, const FRigVMExprAST*> CachedFirstParentOfType;
friend class FRigVMParserAST;
friend class URigVMCompiler;
};
// specialized cast for type checking
// for a Block / FRigVMBlockExprAST expression
// will raise if types are not compatible
// @return this expression cast to FRigVMBlockExprAST
template<>
inline const FRigVMBlockExprAST* FRigVMExprAST::To() const
{
ensure(IsA(EType::Block));
return (const FRigVMBlockExprAST*)this;
}
// specialized cast for type checking
// for a Node / FRigVMNodeExprAST expression
// will raise if types are not compatible
// @return this expression cast to FRigVMNodeExprAST
template<>
inline const FRigVMNodeExprAST* FRigVMExprAST::To() const
{
ensure(
IsA(EType::Entry) ||
IsA(EType::InvokeEntry) ||
IsA(EType::CallExtern) ||
IsA(EType::InlineFunction) ||
IsA(EType::NoOp)
);
return (const FRigVMNodeExprAST*)this;
}
// specialized cast for type checking
// for a Entry / FRigVMEntryExprAST expression
// will raise if types are not compatible
// @return this expression cast to FRigVMEntryExprAST
template<>
inline const FRigVMEntryExprAST* FRigVMExprAST::To() const
{
ensure(IsA(EType::Entry));
return (const FRigVMEntryExprAST*)this;
}
// specialized cast for type checking
// for a InvokeEntry / FRigVMInvokeEntryExprAST expression
// will raise if types are not compatible
// @return this expression cast to FRigVMInvokeEntryExprAST
template<>
inline const FRigVMInvokeEntryExprAST* FRigVMExprAST::To() const
{
ensure(IsA(EType::InvokeEntry));
return (const FRigVMInvokeEntryExprAST*)this;
}
// specialized cast for type checking
// for a CallExtern / FRigVMCallExternExprAST expression
// will raise if types are not compatible
// @return this expression cast to FRigVMCallExternExprAST
template<>
inline const FRigVMCallExternExprAST* FRigVMExprAST::To() const
{
ensure(IsA(EType::CallExtern));
return (const FRigVMCallExternExprAST*)this;
}
// specialized cast for type checking
// for a InlineFunction / FRigVMInlineFunctionExprAST expression
// will raise if types are not compatible
// @return this expression cast to FRigVMInlineFunctionExprAST
template<>
inline const FRigVMInlineFunctionExprAST* FRigVMExprAST::To() const
{
ensure(IsA(EType::InlineFunction));
return (const FRigVMInlineFunctionExprAST*)this;
}
// specialized cast for type checking
// for a NoOp / FRigVMNoOpExprAST expression
// will raise if types are not compatible
// @return this expression cast to FRigVMNoOpExprASTo
template<>
inline const FRigVMNoOpExprAST* FRigVMExprAST::To() const
{
ensure(IsA(EType::NoOp));
return (const FRigVMNoOpExprAST*)this;
}
// specialized cast for type checking
// for a Var / FRigVMVarExprAST expression
// will raise if types are not compatible
// @return this expression cast to FRigVMVarExprAST
template<>
inline const FRigVMVarExprAST* FRigVMExprAST::To() const
{
ensure(IsA(EType::Var));
return (const FRigVMVarExprAST*)this;
}
// specialized cast for type checking
// for a Literal / FRigVMLiteralExprAST expression
// will raise if types are not compatible
// @return this expression cast to FRigVMLiteralExprAST
template<>
inline const FRigVMLiteralExprAST* FRigVMExprAST::To() const
{
ensure(IsA(EType::Literal));
return (const FRigVMLiteralExprAST*)this;
}
// specialized cast for type checking
// for a External Variable / FRigVMExternalVarExprAST expression
// will raise if types are not compatible
// @return this expression cast to FRigVMExternalVarExprAST
template<>
inline const FRigVMExternalVarExprAST* FRigVMExprAST::To() const
{
ensure(IsA(EType::ExternalVar));
return (const FRigVMExternalVarExprAST*)this;
}
// specialized cast for type checking
// for a Assign / FRigVMAssignExprAST expression
// will raise if types are not compatible
// @return this expression cast to FRigVMAssignExprAST
template<>
inline const FRigVMAssignExprAST* FRigVMExprAST::To() const
{
ensure(IsA(EType::Assign));
return (const FRigVMAssignExprAST*)this;
}
// specialized cast for type checking
// for a Copy / FRigVMCopyExprAST expression
// will raise if types are not compatible
// @return this expression cast to FRigVMCopyExprASTo
template<>
inline const FRigVMCopyExprAST* FRigVMExprAST::To() const
{
ensure(IsA(EType::Copy));
return (const FRigVMCopyExprAST*)this;
}
// specialized cast for type checking
// for a CachedValue / FRigVMCachedValueExprAST expression
// will raise if types are not compatible
// @return this expression cast to FRigVMCachedValueExprAST
template<>
inline const FRigVMCachedValueExprAST* FRigVMExprAST::To() const
{
ensure(IsA(EType::CachedValue));
return (const FRigVMCachedValueExprAST*)this;
}
// specialized cast for type checking
// for a Exit / FRigVMExitExprAST expression
// will raise if types are not compatible
// @return this expression cast to FRigVMExitExprAST
template<>
inline const FRigVMExitExprAST* FRigVMExprAST::To() const
{
ensure(IsA(EType::Exit));
return (const FRigVMExitExprAST*)this;
}
/*
* An abstract syntax tree block expression represents a sequence
* of child expressions to be executed in order.
* In C++ a block is represented by the curly braces { expr1, expr2, ...}.
*/
class RIGVMDEVELOPER_API FRigVMBlockExprAST : public FRigVMExprAST
{
public:
// virtual destructor
virtual ~FRigVMBlockExprAST() {}
// disable copy constructor
FRigVMBlockExprAST(const FRigVMBlockExprAST&) = delete;
// returns true if this block needs to execute
// this is determined by the block containing an entry expression
// @return true if this block needs to execute
bool ShouldExecute() const;
// returns true if this block contains an entry expression
// @return true if this block contains an entry expression
bool ContainsEntry() const;
// returns true if this block contains a given expression
// @param InExpression the expression to check
// @return true if this block contains a given expression
bool Contains(const FRigVMExprAST* InExpression, TMap<const FRigVMExprAST*, bool>* ContainedExpressionsCache = nullptr) const;
// overload of the type checking mechanism
virtual bool IsA(EType InType) const override
{
return InType == EType::Block;
};
// returns true in case this block should not be executed
bool IsObsolete() const;
protected:
// default constructor (protected so that only parser can access it)
FRigVMBlockExprAST(EType InType = EType::Block, const FRigVMASTProxy& InProxy = FRigVMASTProxy())
: FRigVMExprAST(InType, InProxy)
, bIsObsolete(false)
{}
private:
bool bIsObsolete;
friend class FRigVMParserAST;
};
/*
* An abstract syntax tree node expression represents any expression
* which references a node from the RigVM model.
*/
class RIGVMDEVELOPER_API FRigVMNodeExprAST : public FRigVMBlockExprAST
{
public:
// virtual destructor
virtual ~FRigVMNodeExprAST() {}
// disable copy constructor
FRigVMNodeExprAST(const FRigVMNodeExprAST&) = delete;
// returns the proxy this expression is using
virtual const FRigVMASTProxy& GetProxy() const override { return Proxy; }
virtual bool IsNode() const
{
return true;
}
// returns the node from the model this expression is referencing
// @return the node from the model this expression is referencing
URigVMNode* GetNode() const { return GetProxy().GetSubjectChecked<URigVMNode>(); }
virtual bool IsConstant() const override;
const FRigVMExprAST* FindExprWithPinName(const FName& InPinName) const;
const FRigVMVarExprAST* FindVarWithPinName(const FName& InPinName) const;
protected:
// default constructor (protected so that only parser can access it)
FRigVMNodeExprAST(EType InType, const FRigVMASTProxy& InNodeProxy);
// overload of the type checking mechanism
virtual bool IsA(EType InType) const override
{
return false;
};
private:
FRigVMASTProxy Proxy;
friend class FRigVMParserAST;
};
/*
* An abstract syntax tree entry expression represents an entry point
* for a function or an event in an event graph.
* In C++ the entry point is the declaration: void main(...);
*/
class RIGVMDEVELOPER_API FRigVMEntryExprAST : public FRigVMNodeExprAST
{
public:
// virtual destructor
virtual ~FRigVMEntryExprAST() {}
// disable copy constructor
FRigVMEntryExprAST(const FRigVMEntryExprAST&) = delete;
// overload of the type checking mechanism
virtual bool IsA(EType InType) const override
{
if(FRigVMBlockExprAST::IsA(InType))
{
return true;
}
return InType == EType::Entry;
};
// returns the name of the entry / event
// @return the name of the entry / event
FName GetEventName() const;
protected:
// default constructor (protected so that only parser can access it)
FRigVMEntryExprAST(const FRigVMASTProxy& InNodeProxy)
: FRigVMNodeExprAST(EType::Entry, InNodeProxy)
{}
private:
friend class FRigVMParserAST;
};
/*
* An abstract syntax tree entry expression represents an invocation
* of an entry point.
*/
class RIGVMDEVELOPER_API FRigVMInvokeEntryExprAST : public FRigVMNodeExprAST
{
public:
// virtual destructor
virtual ~FRigVMInvokeEntryExprAST() {}
// disable copy constructor
FRigVMInvokeEntryExprAST(const FRigVMInvokeEntryExprAST&) = delete;
// overload of the type checking mechanism
virtual bool IsA(EType InType) const override
{
if(FRigVMNodeExprAST::IsA(InType))
{
return true;
}
return InType == EType::InvokeEntry;
};
// returns the name of the entry / event
// @return the name of the entry / event
FName GetEventName() const;
protected:
// default constructor (protected so that only parser can access it)
FRigVMInvokeEntryExprAST(const FRigVMASTProxy& InNodeProxy)
: FRigVMNodeExprAST(EType::InvokeEntry, InNodeProxy)
{}
private:
friend class FRigVMParserAST;
};
/*
* An abstract syntax tree call extern expression represents the invocation
* of an extern function.
* In C++ the call extern is an invocation: FMath::Clamp(1.4, 0.0, 1.0);
* The call extern expression references a node (through parent class)
* from the model providing all of the relevant information for the invocation.
*/
class RIGVMDEVELOPER_API FRigVMCallExternExprAST: public FRigVMNodeExprAST
{
public:
// virtual destructor
virtual ~FRigVMCallExternExprAST() {}
// disable copy constructor
FRigVMCallExternExprAST(const FRigVMCallExternExprAST&) = delete;
// overload of the type checking mechanism
virtual bool IsA(EType InType) const override
{
return InType == EType::CallExtern;
};
protected:
// default constructor (protected so that only parser can access it)
FRigVMCallExternExprAST(const FRigVMASTProxy& InNodeProxy)
: FRigVMNodeExprAST(EType::CallExtern, InNodeProxy)
{}
private:
friend class FRigVMParserAST;
};
/*
*
*/
class RIGVMDEVELOPER_API FRigVMInlineFunctionExprAST: public FRigVMNodeExprAST
{
public:
// virtual destructor
virtual ~FRigVMInlineFunctionExprAST() {}
// disable copy constructor
FRigVMInlineFunctionExprAST(const FRigVMInlineFunctionExprAST&) = delete;
// overload of the type checking mechanism
virtual bool IsA(EType InType) const override
{
return InType == EType::InlineFunction;
};
// todo: For now, all function references are considered varying (UE-170129)
virtual bool IsConstant() const override { return false; }
protected:
// default constructor (protected so that only parser can access it)
FRigVMInlineFunctionExprAST(const FRigVMASTProxy& InNodeProxy)
: FRigVMNodeExprAST(EType::InlineFunction, InNodeProxy)
{}
private:
friend class FRigVMParserAST;
};
/*
* An abstract syntax tree no-op expression represents an expression which is
* relevant for the structure of the tree (for grouping for example) but which
* itself has no operation connected to it.
* For the RigVM AST we use the no-op expression for representing reroute nodes
* in the model as well as parameter and variable getter nodes.
*/
class RIGVMDEVELOPER_API FRigVMNoOpExprAST : public FRigVMNodeExprAST
{
public:
// virtual destructor
virtual ~FRigVMNoOpExprAST() {}
// disable copy constructor
FRigVMNoOpExprAST(const FRigVMNoOpExprAST&) = delete;
// overload of the type checking mechanism
virtual bool IsA(EType InType) const override
{
return InType == EType::NoOp;
};
protected:
// default constructor (protected so that only parser can access it)
FRigVMNoOpExprAST(const FRigVMASTProxy& InNodeProxy)
: FRigVMNodeExprAST(EType::NoOp, InNodeProxy)
{}
private:
friend class FRigVMParserAST;
};
/*
* An abstract syntax tree var expression represents the definition of
* mutable memory for a single variable.
* In C++ the var expression is a variable declaration: int A;
* The var expression references a pin from the model.
*/
class RIGVMDEVELOPER_API FRigVMVarExprAST: public FRigVMExprAST
{
public:
// virtual destructor
virtual ~FRigVMVarExprAST() {}
// disable copy constructor
FRigVMVarExprAST(const FRigVMVarExprAST&) = delete;
// overload of the type checking mechanism
virtual bool IsA(EType InType) const override
{
return InType == EType::Var;
};
virtual bool IsVar() const
{
return true;
}
virtual bool IsConstant() const override;
// returns the proxy this expression is using
virtual const FRigVMASTProxy& GetProxy() const override { return Proxy; }
// returns the pin in the model this variable is representing
// @return the pin in the model this variable is representing
URigVMPin* GetPin() const { return GetProxy().GetSubjectChecked<URigVMPin>(); }
// returns the C++ data type of this variable
// @return the C++ data type of this variable
FString GetCPPType() const;
// returns the C++ data type object (ustruct / uenum)
// @return the C++ data type object (ustruct / uenum)
UObject* GetCPPTypeObject() const;
// returns the pin direction of this variable (input, output, hidden etc)
// @return the pin direction of this variable (input, output, hidden etc)
ERigVMPinDirection GetPinDirection() const;
// returns the default value on the pin for this variable
// @return the default value on the pin for this variable
FString GetDefaultValue() const;
// returns true if this variable is an execute context
// @return true if this variable is an execute context
bool IsExecuteContext() const;
// returns true if this variable is a graph variable
// @return true if this variable is a graph variable
bool IsGraphVariable() const;
// returns true if this is a constant enum index
bool IsEnumValue() const;
// returns true if this variable allows links to be "soft", so without a cache / computed value.
bool SupportsSoftLinks() const;
protected:
// default constructor (protected so that only parser can access it)
FRigVMVarExprAST(EType InType, const FRigVMASTProxy& InPinProxy)
: FRigVMExprAST(InType)
, Proxy(InPinProxy)
{
}
private:
FRigVMASTProxy Proxy;
friend class FRigVMParserAST;
friend class URigVMCompiler;
};
/*
* An abstract syntax tree literal expression represents the definition of
* const memory for a single variable - vs a var expression which is mutable.
* In C++ the literal expression is a literal declaration, for ex: const float PI = 3.14f;
* The literal expression references a pin from the model.
*/
class RIGVMDEVELOPER_API FRigVMLiteralExprAST : public FRigVMVarExprAST
{
public:
// virtual destructor
virtual ~FRigVMLiteralExprAST() {}
// disable copy constructor
FRigVMLiteralExprAST(const FRigVMLiteralExprAST&) = delete;
// overload of the type checking mechanism
virtual bool IsA(EType InType) const override
{
if(FRigVMVarExprAST::IsA(InType))
{
return true;
}
return InType == EType::Literal;
};
virtual bool IsConstant() const override
{
return true;
}
protected:
// default constructor (protected so that only parser can access it)
FRigVMLiteralExprAST(const FRigVMASTProxy& InPinProxy)
: FRigVMVarExprAST(EType::Literal, InPinProxy)
{}
private:
friend class FRigVMParserAST;
};
/*
* An abstract syntax tree external variable expression represents the reference
* of unowned / external memory.
*/
class RIGVMDEVELOPER_API FRigVMExternalVarExprAST : public FRigVMVarExprAST
{
public:
// virtual destructor
virtual ~FRigVMExternalVarExprAST() {}
// disable copy constructor
FRigVMExternalVarExprAST(const FRigVMExternalVarExprAST&) = delete;
// overload of the type checking mechanism
virtual bool IsA(EType InType) const override
{
if (FRigVMVarExprAST::IsA(InType))
{
return true;
}
return InType == EType::ExternalVar;
};
protected:
// default constructor (protected so that only parser can access it)
FRigVMExternalVarExprAST(const FRigVMASTProxy& InPinProxy)
: FRigVMVarExprAST(EType::ExternalVar, InPinProxy)
{}
private:
friend class FRigVMParserAST;
};
/*
* An abstract syntax tree assign expression represents the assignment of one
* expression to another. This can result in referencing memory from a to b or
* copying memory from a to b (thus the copy expression inherits the assign).
* In C++ the assign expression used for construction and copy, for ex: int32 A = B + 1;
* The assign expression references two pins / a link from the model.
*/
class RIGVMDEVELOPER_API FRigVMAssignExprAST : public FRigVMExprAST
{
public:
// virtual destructor
virtual ~FRigVMAssignExprAST() {}
// disable copy constructor
FRigVMAssignExprAST(const FRigVMAssignExprAST&) = delete;
// returns the source proxy this expression is using
const FRigVMASTLinkDescription& GetLink() const { return Link; }
// returns the source proxy this expression is using
const FRigVMASTProxy& GetSourceProxy() const { return Link.SourceProxy; }
// returns the source pin for this assignment
// @return the source pin for this assignment
URigVMPin* GetSourcePin() const { return Link.SourceProxy.GetSubjectChecked<URigVMPin>(); }
// returns the target proxy this expression is using
const FRigVMASTProxy& GetTargetProxy() const { return Link.TargetProxy; }
// returns the target pin for this assignment
// @return the target pin for this assignment
URigVMPin* GetTargetPin() const { return Link.TargetProxy.GetSubjectChecked<URigVMPin>(); }
virtual const FRigVMASTProxy& GetProxy() const override { return GetTargetProxy(); }
// overload of the type checking mechanism
virtual bool IsA(EType InType) const override
{
return InType == EType::Assign;
};
protected:
// default constructor (protected so that only parser can access it)
FRigVMAssignExprAST(EType InType, const FRigVMASTLinkDescription& InLink)
: FRigVMExprAST(InType)
, Link(InLink)
{
}
private:
FRigVMASTLinkDescription Link;
friend class FRigVMParserAST;
};
/*
* An abstract syntax tree copy expression represents the an assignment of one
* expression to another which causes / requires a copy operation.
* Within the RigVM AST this is only used for copying work state out of / into parameters
* or when composing / decomposing a structure (for ex: assigning a float to a vector.x).
* In C++ the copy expression is used for structures, for ex: FVector A = B;
* The copy expression references two pins / a link from the model.
*/
class RIGVMDEVELOPER_API FRigVMCopyExprAST : public FRigVMAssignExprAST
{
public:
// virtual destructor
virtual ~FRigVMCopyExprAST() {}
// disable copy constructor
FRigVMCopyExprAST(const FRigVMCopyExprAST&) = delete;
// overload of the type checking mechanism
virtual bool IsA(EType InType) const override
{
if(FRigVMAssignExprAST::IsA(InType))
{
return true;
}
return InType == EType::Copy;
};
protected:
// default constructor (protected so that only parser can access it)
FRigVMCopyExprAST(const FRigVMASTLinkDescription& InLink)
: FRigVMAssignExprAST(EType::Copy, InLink)
{}
private:
friend class FRigVMParserAST;
};
/*
* An abstract syntax tree cached value expression represents the reference to
* a variable which needs to be calculated by a call extern expression.
* The first child of the cached value expression is the var expression to be
* computed / cached, the second child is the call extern expression to use.
*/
class RIGVMDEVELOPER_API FRigVMCachedValueExprAST : public FRigVMExprAST
{
public:
// virtual destructor
virtual ~FRigVMCachedValueExprAST() {}
// disable copy constructor
FRigVMCachedValueExprAST(const FRigVMCachedValueExprAST&) = delete;
// overload of the type checking mechanism
virtual bool IsA(EType InType) const override
{
return InType == EType::CachedValue;
};
// returns the var expression of this cached value (const)
// @return the var expression of this cached value (const)
const FRigVMVarExprAST* GetVarExpr() const { return ChildAt<FRigVMVarExprAST>(0); }
// returns the call extern expression of this cached value (const)
// @return the call extern expression of this cached value (const)
const FRigVMCallExternExprAST* GetCallExternExpr() const { return ChildAt<FRigVMCallExternExprAST>(1); }
protected:
// default constructor (protected so that only parser can access it)
FRigVMCachedValueExprAST(const FRigVMASTProxy& InProxy)
: FRigVMExprAST(EType::CachedValue, InProxy)
{}
private:
friend class FRigVMParserAST;
};
/*
* An abstract syntax tree exit expression represents the exit out of an entry expression.
* In C++ the exit expression is a return from a main function.
*/
class RIGVMDEVELOPER_API FRigVMExitExprAST : public FRigVMExprAST
{
public:
// virtual destructor
virtual ~FRigVMExitExprAST() {}
// disable copy constructor
FRigVMExitExprAST(const FRigVMExitExprAST&) = delete;
// overload of the type checking mechanism
virtual bool IsA(EType InType) const override
{
return InType == EType::Exit;
};
virtual bool IsConstant() const override
{
return true;
}
protected:
// default constructor (protected so that only parser can access it)
FRigVMExitExprAST(const FRigVMASTProxy& InProxy)
: FRigVMExprAST(EType::Exit, InProxy)
{}
private:
friend class FRigVMParserAST;
};
/*
* The settings to apply during the parse of the abstract syntax tree.
* The folding settings can affect the performance of the parse dramatically.
*/
USTRUCT(BlueprintType)
struct RIGVMDEVELOPER_API FRigVMParserASTSettings
{
GENERATED_BODY()
// fold assignments / copies
UPROPERTY(EditAnywhere, Category = "AST")
bool bFoldAssignments = false;
// fold literals and share memory
UPROPERTY(EditAnywhere, Category = "AST")
bool bFoldLiterals = false;
UPROPERTY(EditAnywhere, Transient, BlueprintReadWrite, Category = "AST")
bool bSetupTraits = true;
// links to be ignored during the parse
UPROPERTY()
TArray<TObjectPtr<URigVMLink>> LinksToSkip;
FRigVMReportDelegate ReportDelegate;
UPROPERTY(VisibleAnywhere, Category = "AST", transient)
TObjectPtr<UScriptStruct> ExecuteContextStruct;
// static method to provide fast AST parse settings
static FRigVMParserASTSettings Fast(UScriptStruct* InExecuteContextStruct = nullptr)
{
FRigVMParserASTSettings Settings;
Settings.bFoldAssignments = false;
Settings.bFoldLiterals = false;
Settings.ExecuteContextStruct = InExecuteContextStruct ? InExecuteContextStruct : FRigVMExecuteContext::StaticStruct();
return Settings;
}
// static method to provide AST parse settings
// tuned for a fast executing runtime, but slow parse
static FRigVMParserASTSettings Optimized(UScriptStruct* InExecuteContextStruct = nullptr)
{
FRigVMParserASTSettings Settings;
Settings.bFoldAssignments = true;
Settings.bFoldLiterals = true;
Settings.ExecuteContextStruct = InExecuteContextStruct ? InExecuteContextStruct : FRigVMExecuteContext::StaticStruct();
return Settings;
}
void Report(EMessageSeverity::Type InSeverity, UObject* InSubject, const FString& InMessage) const;
template <typename... Types>
void Reportf(EMessageSeverity::Type InSeverity,
UObject* InSubject,
UE::Core::TCheckedFormatString<FString::FmtCharType, Types...> Fmt,
Types... Args) const
{
Report(InSeverity, InSubject, FString::Printf(Fmt, Args...));
}
};
/*
* The abstract syntax tree parser is the main object to parse a
* RigVM model graph. It's the memory owner for all expressions and
* provides functionality for introspection of the tree.
* The abstract syntax tree is then fed into the RigVMCompiler to
* generate the byte code for the virtual machine.
*/
class RIGVMDEVELOPER_API FRigVMParserAST : public TSharedFromThis<FRigVMParserAST>
{
public:
// default constructor
// @param InGraph The graph / model to parse
// @param InSettings The parse settings to use
FRigVMParserAST(TArray<URigVMGraph*> InGraphs, URigVMController* InController = nullptr, const FRigVMParserASTSettings& InSettings = FRigVMParserASTSettings::Fast(), const TArray<FRigVMExternalVariable>& InExternalVariables = TArray<FRigVMExternalVariable>());
// default destructor
~FRigVMParserAST();
// returns the number of root expressions
int32 Num() const { return RootExpressions.Num(); }
// operator accessor for a given root expression
// @param InIndex the index of the expression to retrieve (bound = Num() - 1)
// @return the root expression with the given index
const FRigVMExprAST* operator[](int32 InIndex) const { return RootExpressions[InIndex]; }
// begin iterator accessor for the root expressions
TArray<FRigVMExprAST*>::RangedForConstIteratorType begin() const { return RootExpressions.begin(); }
// end iterator accessor for the root expressions
TArray<FRigVMExprAST*>::RangedForConstIteratorType end() const { return RootExpressions.end(); }
// accessor method for a given root expression
// @param InIndex the index of the expression to retrieve (bound = Num() - 1)
// @return the root expression with the given index
const FRigVMExprAST* At(int32 InIndex) const { return RootExpressions[InIndex]; }
// returns the expression for a given subject. subjects include nodes and pins.
// @param InSubject the subject to retrieve the expression for (node or pin)
// @return the expressoin for the given subject (or nullptr)
const FRigVMExprAST* GetExprForSubject(const FRigVMASTProxy& InProxy) const;
// returns all expressions for a given subject. subjects include nodes and pins.
// @param InSubject the subject to retrieve the expression for (node or pin)
// @return all expressions for the given subject
TArray<const FRigVMExprAST*> GetExpressionsForSubject(UObject* InSubject) const;
// Prepares the parser for cycle checking on a given pin.
// This marks up the parents and childen of the corresponding expression in the graph,
// to allow the client to determine if a new parent / child relationship could cause a cycle.
// @param InPin the pin to initialize the cycle checking for
void PrepareCycleChecking(URigVMPin* InPin);
// Performs a cycle check for a new potential link (assign or copy) between two pins.
// @param InSourcePin the source (left) pin of the potential assign / copy
// @param InTargetPin the target (right) pin of the potential assign / copy
// @param OutFailureReason an optional storage for the possible failure reason description
// @return true if the potential link (assign / copy) can be established
bool CanLink(URigVMPin* InSourcePin, URigVMPin* InTargetPin, FString* OutFailureReason);
// returns a string containing an indented tree structure
// for debugging purposes.
// @return the simple text representing this abstract syntax tree
FString DumpText() const;
// returns a string containing a dot file notation
// for debugging purposes.
// @return the dot file text representing this abstract syntax tree
FString DumpDot() const;
// returns an obsolete block for unmanaged expression
FRigVMBlockExprAST* GetObsoleteBlock(bool bCreateIfMissing = true);
const FRigVMBlockExprAST* GetObsoleteBlock(bool bCreateIfMissing = true) const;
// returns the AST's override table for pin defaults
const URigVMPin::FPinOverrideMap& GetPinOverrides() const { return PinOverrides; }
// returns the settings used for this parser
const FRigVMParserASTSettings& GetSettings() const { return Settings; }
private:
// make function to create an expression
template<class ExprType>
ExprType* MakeExpr(FRigVMExprAST::EType InType, const FRigVMASTProxy& InProxy)
{
ExprType* Expr = new ExprType(InType, InProxy);
Expr->ParserPtr = this;
Expr->Index = Expressions.Add(Expr);
return Expr;
}
// make function to create an expression
template<class ExprType>
ExprType* MakeExpr(FRigVMExprAST::EType InType, const FRigVMASTLinkDescription& InLink)
{
ExprType* Expr = new ExprType(InType, InLink);
Expr->ParserPtr = this;
Expr->Index = Expressions.Add(Expr);
return Expr;
}
// make function to create an expression
template<class ExprType>
ExprType* MakeExpr(FRigVMExprAST::EType InType, const FRigVMASTProxy& InProxyA, const FRigVMASTProxy& InProxyB)
{
ExprType* Expr = new ExprType(InType, InProxyA, InProxyB);
Expr->ParserPtr = this;
Expr->Index = Expressions.Add(Expr);
return Expr;
}
// make function to create an expression
template<class ExprType>
ExprType* MakeExpr(const FRigVMASTProxy& InProxyA, const FRigVMASTProxy& InProxyBB)
{
ExprType* Expr = new ExprType(InProxyA, InProxyBB);
Expr->ParserPtr = this;
Expr->Index = Expressions.Add(Expr);
return Expr;
}
// make function to create an expression
template<class ExprType>
ExprType* MakeExpr(const FRigVMASTProxy& InProxy)
{
ExprType* Expr = new ExprType(InProxy);
Expr->ParserPtr = this;
Expr->Index = Expressions.Add(Expr);
return Expr;
}
// make function to create an expression
template<class ExprType>
ExprType* MakeExpr(const FRigVMASTLinkDescription& InLink)
{
ExprType* Expr = new ExprType(InLink);
Expr->ParserPtr = this;
Expr->Index = Expressions.Add(Expr);
return Expr;
}
// make function to create an expression
template<class ExprType>
ExprType* MakeExpr()
{
ExprType* Expr = new ExprType();
Expr->ParserPtr = this;
Expr->Index = Expressions.Add(Expr);
return Expr;
}
// removes an array of expressions from the parser
// @param InExprs the expressions to remove
void RemoveExpressions(TArray<FRigVMExprAST*> InExprs);
// a static helper function to traverse along all parents of an expression,
// provided a predicate to return true if the traverse should continue,
// and false if the traverse should stop.
// @param InExpr the expression to start the traverse at
// @param InContinuePredicate the predicate to use to break out of the traverse (return false)
static void TraverseParents(const FRigVMExprAST* InExpr, TFunctionRef<bool(const FRigVMExprAST*)> InContinuePredicate);
// a static helper function to traverse along all children of an expression,
// provided a predicate to return true if the traverse should continue,
// and false if the traverse should stop.
// @param InExpr the expression to start the traverse at
// @param InContinuePredicate the predicate to use to break out of the traverse (return false)
static void TraverseChildren(const FRigVMExprAST* InExpr, TFunctionRef<bool(const FRigVMExprAST*)> InContinuePredicate);
// helper function to fold all entries with the same event name into one block
void FoldEntries();
// helper function to inject an exit expression at the end of every entry expressoin
void InjectExitsToEntries();
// helper function to refresh the expression indices (used after deleting an expression)
void RefreshExprIndices();
// helper function to fold / remove the no op expressions (parameters mostly)
void FoldNoOps();
// helper function to fold / merge redundant literals with the same value
void FoldLiterals();
// helper function to fold / remove obsolete assignments and reduce assignment chains
void FoldAssignments();
// helper function to inline all contributing nodes of the graph
void Inline(const TArray<URigVMGraph*>& InGraphs);
void Inline(const TArray<URigVMGraph*>& InGraphs, const TArray<FRigVMASTProxy>& InNodeProxies);
// helper functions to retrieve links for a given pin
TArray<int32> GetSourceLinkIndices(const FRigVMASTProxy& InPinProxy, bool bRecursive = false) const;
TArray<int32> GetTargetLinkIndices(const FRigVMASTProxy& InPinProxy, bool bRecursive = false) const;
TArray<int32> GetLinkIndices(const FRigVMASTProxy& InPinProxy, bool bGetSource, bool bRecursive) const;
// function to retrieve a link given its index
const FRigVMASTLinkDescription& GetLink(int32 InLinkIndex) const;
// traverse a single mutable node (constructs entry, call extern and other expressions)
FRigVMExprAST* TraverseMutableNode(const FRigVMASTProxy& InNodeProxy, FRigVMExprAST* InParentExpr);
// traverse a single pure node (constructs call extern expressions)
FRigVMExprAST* TraverseNode(const FRigVMASTProxy& InNodeProxy, FRigVMExprAST* InParentExpr);
// returns the expression for a given node
FRigVMExprAST* CreateExpressionForNode(const FRigVMASTProxy& InNodeProxy, FRigVMExprAST* InParentExpr);
// traverse an array of pins for a given node
TArray<FRigVMExprAST*> TraversePins(const FRigVMASTProxy& InNodeProxy, FRigVMExprAST* InParentExpr);
// traverse a single pin (constructs var + literal expressions)
FRigVMExprAST* TraversePin(const FRigVMASTProxy& InPinProxy, FRigVMExprAST* InParentExpr);
// traverse a single link (constructs assign + copy expressions)
FRigVMExprAST* TraverseLink(int32 InLinkIndex, FRigVMExprAST* InParentExpr);
bool ShouldLinkBeSkipped(const FRigVMASTLinkDescription& InLink) const;
static FString GetLinkAsString(const FRigVMASTLinkDescription& InLink);
void IncrementCacheVersion() const;
TMap<FRigVMASTProxy, FRigVMExprAST*> SubjectToExpression;
TMap<FRigVMASTProxy, int32> NodeExpressionIndex;
TArray<FRigVMExprAST*> Expressions;
TArray<FRigVMExprAST*> RootExpressions;
TArray<FRigVMExprAST*> DeletedExpressions;
FRigVMBlockExprAST* ObsoleteBlock;
mutable TMap<uint32, FString> BlockCombinationHashToName;
mutable int32 CacheVersion;
TArray<FRigVMASTProxy> NodeProxies;
TMap<FRigVMASTProxy, FRigVMASTProxy> SharedOperandPins;
TMap<FRigVMASTProxy, TArray<int32>> TargetLinkIndices;
TMap<FRigVMASTProxy, TArray<int32>> SourceLinkIndices;
TArray<FRigVMASTLinkDescription> Links;
static const TArray<FRigVMASTProxy> EmptyProxyArray;
URigVMPin::FPinOverrideMap PinOverrides;
enum ETraverseRelationShip
{
ETraverseRelationShip_Unknown,
ETraverseRelationShip_Parent,
ETraverseRelationShip_Child,
ETraverseRelationShip_Self
};
const FRigVMExprAST* LastCycleCheckExpr;
TArray<ETraverseRelationShip> CycleCheckFlags;
TArray<URigVMLink*> LinksToSkip;
FRigVMParserASTSettings Settings;
mutable TMap<TTuple<const FRigVMExprAST*,const FRigVMExprAST*>, int32> MinIndexOfChildWithinParent;
URigVMLibraryNode* LibraryNodeBeingCompiled;
friend class FRigVMExprAST;
friend class FRigVMAssignExprAST;
friend class URigVMCompiler;
};