1919 lines
67 KiB
C++
1919 lines
67 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#pragma once
|
|
|
|
#include "uLang/Common/Algo/Cases.h"
|
|
#include "uLang/Common/Containers/Array.h"
|
|
#include "uLang/Common/Containers/RangeView.h"
|
|
#include "uLang/Common/Containers/SharedPointer.h"
|
|
#include "uLang/Common/Containers/SharedPointerArray.h"
|
|
#include "uLang/Common/Misc/EnumUtils.h"
|
|
#include "uLang/Common/Misc/Optional.h"
|
|
#include "uLang/Common/Text/TextRange.h"
|
|
#include "uLang/Common/Text/UTF8String.h"
|
|
#include "uLang/Common/Text/UTF8StringBuilder.h"
|
|
#include "uLang/Common/Text/UTF8StringView.h"
|
|
#include "uLang/SourceProject/PackageRole.h"
|
|
#include "uLang/SourceProject/UploadedAtFNVersion.h"
|
|
#include "uLang/SourceProject/VerseScope.h"
|
|
#include "uLang/Syntax/NodeDecls.inl"
|
|
|
|
#define UE_API VERSECOMPILER_API
|
|
|
|
namespace Verse
|
|
{
|
|
namespace Vst
|
|
{
|
|
struct Node;
|
|
}
|
|
} // namespace Verse
|
|
|
|
namespace uLang
|
|
{
|
|
class CAstNode;
|
|
class CExpressionBase;
|
|
|
|
/// This is used as a helper struct for storing the result of a signed distance check.
|
|
struct LocusDistanceResult
|
|
{
|
|
TSRef<Verse::Vst::Node> Node;
|
|
int32_t Distance;
|
|
};
|
|
} // namespace uLang
|
|
|
|
namespace Verse
|
|
{
|
|
class FTile;
|
|
|
|
using string = uLang::CUTF8String;
|
|
using CUTF8String = uLang::CUTF8String;
|
|
using CUTF8StringView = uLang::CUTF8StringView;
|
|
using CUTF8StringBuilder = uLang::CUTF8StringBuilder;
|
|
|
|
using ChType = uLang::UTF8Char;
|
|
|
|
// uLang Types
|
|
template<typename T>
|
|
using TSPtr = uLang::TSPtr<T>;
|
|
|
|
template<typename T>
|
|
using TSRef = uLang::TSRef<T>;
|
|
|
|
template<typename T>
|
|
using LArray = uLang::TArray<T>;
|
|
|
|
using EResult = uLang::EResult;
|
|
using CSharedMix = uLang::CSharedMix;
|
|
using SLocus = uLang::STextRange;
|
|
using SPosition = uLang::STextPosition;
|
|
using EVerseScope = uLang::EVerseScope;
|
|
|
|
ULANG_FORCEINLINE SLocus NullWhence()
|
|
{
|
|
return SLocus(0, 0, 0, 0);
|
|
}
|
|
|
|
namespace Vst
|
|
{
|
|
class CAtom;
|
|
struct Clause;
|
|
struct Snippet;
|
|
struct Identifier;
|
|
|
|
enum class EChildDeletionBehavior : uint8_t
|
|
{
|
|
CreatePlaceholder,
|
|
Delete,
|
|
Default
|
|
};
|
|
|
|
enum class ESupportsManyChildren : uint8_t
|
|
{
|
|
Anywhere,
|
|
TrailingOnly,
|
|
Nowhere
|
|
};
|
|
|
|
#define VISIT_VSTNODE(NodeName, RequiredChildren, SupportsManyChildren, Precedence, ChildDeletionBehavior, IsCAtom) NodeName,
|
|
enum class NodeType : uint8_t
|
|
{
|
|
VERSE_ENUM_VSTNODES(VISIT_VSTNODE)
|
|
};
|
|
#undef VISIT_VSTNODE
|
|
|
|
#define VISIT_VSTNODE(NodeName, RequiredChildren, Precedence, SupportsManyChildren, ChildDeletionBehavior, IsCAtom) {#NodeName, RequiredChildren, Precedence, ESupportsManyChildren::SupportsManyChildren, EChildDeletionBehavior::ChildDeletionBehavior, IsCAtom},
|
|
constexpr struct NodeInfo
|
|
{
|
|
const char* FormalName;
|
|
int32_t RequiredChildren;
|
|
int32_t Precedence;
|
|
ESupportsManyChildren SupportsManyChildren;
|
|
EChildDeletionBehavior ChildDeletionBehavior;
|
|
bool bIsCAtom;
|
|
} NodeInfos[] = {
|
|
VERSE_ENUM_VSTNODES(VISIT_VSTNODE)};
|
|
#undef VISIT_VSTNODE
|
|
|
|
static constexpr uint8_t NumNodeTypes = static_cast<uint8_t>(ULANG_COUNTOF(NodeInfos));
|
|
|
|
static const uint8_t TagNone = 255;
|
|
|
|
ULANG_FORCEINLINE const char* GetNodeTypeName(const NodeType TypeOfNode)
|
|
{
|
|
return NodeInfos[static_cast<uint8_t>(TypeOfNode)].FormalName;
|
|
}
|
|
|
|
ULANG_FORCEINLINE int32_t GetNumRequiredChildren(const NodeType TypeOfNode)
|
|
{
|
|
return NodeInfos[static_cast<uint8_t>(TypeOfNode)].RequiredChildren;
|
|
}
|
|
|
|
ULANG_FORCEINLINE int32_t GetOperatorPrecedence(const NodeType TypeOfNode)
|
|
{
|
|
return NodeInfos[static_cast<uint8_t>(TypeOfNode)].Precedence;
|
|
}
|
|
|
|
ULANG_FORCEINLINE ESupportsManyChildren GetSupportsManyChildren(const NodeType TypeOfNode)
|
|
{
|
|
return NodeInfos[static_cast<uint8_t>(TypeOfNode)].SupportsManyChildren;
|
|
}
|
|
|
|
ULANG_FORCEINLINE EChildDeletionBehavior GetChildDeletionBehavior(const NodeType TypeOfNode)
|
|
{
|
|
return NodeInfos[static_cast<uint8_t>(TypeOfNode)].ChildDeletionBehavior;
|
|
}
|
|
|
|
struct Node : public CSharedMix
|
|
{
|
|
using NodeArray = LArray<TSRef<Node>>;
|
|
|
|
Node()
|
|
: _Children()
|
|
, _Parent(nullptr)
|
|
, _Aux()
|
|
, _PreComments()
|
|
, _PostComments()
|
|
, _NumNewLinesBefore(0)
|
|
, _NumNewLinesAfter(0)
|
|
, _Tag(0)
|
|
, _Type(static_cast<NodeType>(0))
|
|
, _MappedAstNode(nullptr)
|
|
, _Tile(nullptr)
|
|
{}
|
|
|
|
Node(NodeType in_type) : Node()
|
|
{
|
|
_Type = in_type;
|
|
ULANG_ASSERTF(
|
|
in_type == NodeType::Project
|
|
|| in_type == NodeType::Package
|
|
|| in_type == NodeType::Module
|
|
|| in_type == NodeType::Snippet,
|
|
"Invalid use of locus-free Node constructor for node type that requires a locus");
|
|
}
|
|
|
|
Node(NodeType in_type, const SLocus& whence) : Node()
|
|
{
|
|
_Type = in_type;
|
|
_Whence = whence;
|
|
ULANG_ASSERTF(_Whence.IsValid(), "Node created with invalid locus");
|
|
}
|
|
|
|
UE_API virtual ~Node();
|
|
|
|
static SLocus CombineLocii(const NodeArray& Nodes)
|
|
{
|
|
if (Nodes.IsFilled())
|
|
{
|
|
SLocus Whence = Nodes[0]->Whence();
|
|
for (int32_t Index = 1; Index < Nodes.Num(); ++Index)
|
|
{
|
|
Whence |= Nodes[Index]->Whence();
|
|
}
|
|
return Whence;
|
|
}
|
|
return SLocus();
|
|
}
|
|
|
|
TSRef<Node> AsShared() { return CSharedMix::SharedThis(this); }
|
|
//TSRef<const Node> AsShared() const { return CSharedMix::SharedThis(this); }
|
|
|
|
template<typename TNodeType>
|
|
TNodeType& As() { ULANG_ASSERTF(IsA<TNodeType>(), "Vst Node is type `%s` not of expected type `%s` so cannot cast!", NodeInfos[GetElementTypeInt()].FormalName, NodeInfos[static_cast<int32_t>(TNodeType::StaticType)].FormalName); return *static_cast<TNodeType*>(this); }
|
|
|
|
template<typename TNodeType>
|
|
const TNodeType& As() const { ULANG_ASSERTF(IsA<TNodeType>(), "Vst Node is type `%s` not of expected type `%s` so cannot cast!", NodeInfos[GetElementTypeInt()].FormalName, NodeInfos[static_cast<int32_t>(TNodeType::StaticType)].FormalName); return *static_cast<const TNodeType*>(this); }
|
|
|
|
template <typename TNodeType>
|
|
TNodeType* AsNullable() { return IsA<TNodeType>()? static_cast<TNodeType*>(this) : nullptr; }
|
|
|
|
template <typename TNodeType>
|
|
const TNodeType* AsNullable() const { return IsA<TNodeType>() ? static_cast<const TNodeType*>(this) : nullptr; }
|
|
|
|
template<typename TNodeType>
|
|
bool IsA() const { return GetElementType() == TNodeType::StaticType; }
|
|
|
|
int GetChildCount() const { return _Children.Num(); }
|
|
NodeType GetElementType() const { return _Type; }
|
|
int32_t GetElementTypeInt() const { return static_cast<int32_t>(GetElementType()); }
|
|
template<typename OpType>
|
|
OpType GetTag() const { return static_cast<OpType>(_Tag); }
|
|
template<typename OpType=uint8_t>
|
|
void SetTag(OpType in_op) { _Tag = static_cast<uint8_t>(in_op); }
|
|
|
|
UE_API bool HasAttributes() const;
|
|
UE_API const Identifier* GetAttributeIdentifier(const CUTF8StringView& AttributeName) const;
|
|
UE_API bool IsAttributePresent(const CUTF8StringView& AttributeName) const;
|
|
|
|
UE_API const Node* TryGetFirstAttributeOfType(NodeType Type) const;
|
|
|
|
template<typename TNodeType>
|
|
const TNodeType* TryGetFirstAttributeOfType() const
|
|
{
|
|
const Node* Result = TryGetFirstAttributeOfType(TNodeType::StaticType);
|
|
|
|
if(Result != nullptr)
|
|
{
|
|
return &Result->As<TNodeType>();
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
UE_API void PrependAux(const TSRef<Node>& AuxChild);
|
|
UE_API void PrependAux(const NodeArray& AuxChildren);
|
|
UE_API void AppendAux(const TSRef<Node>& AuxChild);
|
|
UE_API void AppendAux(const NodeArray& AuxChildren);
|
|
UE_API void AppendAuxAt(const TSRef<Node>& AuxChild, int32_t Idx);
|
|
const TSPtr<Clause>& GetAux() const { return _Aux; }
|
|
UE_API void SetAux(const TSRef<Clause>& Aux);
|
|
void RemoveAux() { _Aux.Reset(); }
|
|
|
|
UE_API void AppendPrefixComment(const TSRef<Node>& CommentNode);
|
|
UE_API void AppendPrefixComments(const NodeArray& CommentNodes);
|
|
UE_API void AppendPostfixComment(const TSRef<Node>& CommentNode);
|
|
UE_API void AppendPostfixComments(const NodeArray& CommentNodes);
|
|
const NodeArray& GetPrefixComments() const { return _PreComments; }
|
|
const NodeArray& GetPostfixComments() const { return _PostComments; }
|
|
NodeArray& AccessPrefixComments() { return _PreComments; }
|
|
NodeArray& AccessPostfixComments() { return _PostComments; }
|
|
|
|
void SetWhence(const SLocus& Whence) { _Whence = Whence; }
|
|
void CombineWhenceWith(const SLocus& Whence) { _Whence |= Whence; }
|
|
const SLocus& Whence() const { return _Whence; }
|
|
UE_API const CUTF8String& GetSnippetPath() const;
|
|
UE_API const Snippet* FindSnippetByFilePath(const CUTF8StringView& FilePath) const;
|
|
UE_API const Node* FindChildByPosition(const SPosition& TextPosition) const;
|
|
UE_API const TSRef<Node> FindChildClosestToPosition(const SPosition& TextPosition, const CUTF8StringView& SourceText) const;
|
|
|
|
template<class VisitPolicy, typename ReturnType = void >
|
|
static void VisitWith(const TSRef<Vst::Node>& node, VisitPolicy& visit_policy);
|
|
|
|
const NodeInfo& GetElementInfo() const { return NodeInfos[GetElementTypeInt()]; }
|
|
const ChType* GetElementName() const { return (const ChType*)NodeInfos[GetElementTypeInt()].FormalName; }
|
|
int32_t GetPrecedence() const { return GetOperatorPrecedence(GetElementType()); }
|
|
int32_t NumRequiredChildren() const
|
|
{
|
|
return GetNumRequiredChildren(GetElementType());
|
|
}
|
|
ESupportsManyChildren IsManyChildrenSupported() const
|
|
{
|
|
return GetSupportsManyChildren(GetElementType());
|
|
}
|
|
|
|
int32_t NumNewLinesBefore() const
|
|
{
|
|
return _NumNewLinesBefore;
|
|
}
|
|
|
|
void SetNumNewLinesBefore(const int32_t Num)
|
|
{
|
|
_NumNewLinesBefore = Num;
|
|
}
|
|
|
|
int32_t NumNewLinesAfter() const
|
|
{
|
|
return _NumNewLinesAfter;
|
|
}
|
|
|
|
void SetNumNewLinesAfter(const int32_t Num)
|
|
{
|
|
_NumNewLinesAfter = Num;
|
|
}
|
|
|
|
void SetNewLineAfter(const bool bNewLineAfter)
|
|
{
|
|
if (bNewLineAfter)
|
|
{
|
|
// Don't touch the node if it already has new lines after it; we preserve the information.
|
|
if (NumNewLinesAfter() > 0)
|
|
{
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
SetNumNewLinesAfter(1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetNumNewLinesAfter(0);
|
|
}
|
|
}
|
|
|
|
bool HasNewLineAfter() const
|
|
{
|
|
return _NumNewLinesAfter > 0;
|
|
}
|
|
|
|
bool HasNewLinesBefore() const
|
|
{
|
|
return _NumNewLinesBefore > 0;
|
|
}
|
|
|
|
bool IsEmpty() const { return _Children.IsEmpty(); }
|
|
const NodeArray& GetChildren() const { return _Children; }
|
|
NodeArray& AccessChildren() { return _Children; }
|
|
NodeArray TakeChildren()
|
|
{
|
|
for (const TSRef<Node>& Child : _Children)
|
|
{
|
|
Child->_Parent = nullptr;
|
|
}
|
|
return Move(_Children);
|
|
}
|
|
TSPtr<Node> GetRightmostChild() const
|
|
{
|
|
return GetChildCount() > 0
|
|
? GetChildren()[GetChildCount() - 1]
|
|
: TSPtr<Node>(nullptr);
|
|
}
|
|
Node* AccessParent() { return _Parent; }
|
|
TSRef<Node> GetSharedSelf() { return SharedThis(this); }
|
|
TSRef<Node> GetSharedSelf() const { return SharedThis(const_cast<Node*>(this)); }
|
|
const Node* GetParent() const { return _Parent; }
|
|
bool HasParent() const
|
|
{
|
|
return this->GetParent() != nullptr;
|
|
}
|
|
template<class Type>
|
|
const Type* GetParentOfType() const
|
|
{
|
|
for (const Node* CurNode = this; CurNode; CurNode = CurNode->_Parent)
|
|
{
|
|
if (CurNode->GetElementType() == Type::StaticType)
|
|
{
|
|
return static_cast<const Type*>(CurNode);
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
bool IsElementType(NodeType InType) const { return InType == _Type; }
|
|
|
|
UE_API const CAtom* AsAtomNullable() const;
|
|
|
|
bool IsChildElementType(int32_t idx, NodeType InType) const { return _Children.Num() > idx && _Children[idx]->IsElementType(InType); }
|
|
|
|
bool IsError() const { return GetElementType() == NodeType::ParseError; }
|
|
|
|
void DebugOrphanCheck()
|
|
{
|
|
#ifdef _DEBUG
|
|
const Node* OrphanedNode = FindOrphanedNode(*this);
|
|
ULANG_ASSERTF(!OrphanedNode, "An orphaned node was encountered!");
|
|
#endif
|
|
}
|
|
|
|
bool Contains(const Node& Target, const bool bRecursive = true) const
|
|
{
|
|
struct FVstContains_Visitor
|
|
{
|
|
static bool Contains(const TSRef<Node>& Root, const Node& RecursiveTarget)
|
|
{
|
|
bool bRecursiveContains = false;
|
|
for (const TSPtr<Node> Child : Root->AccessChildren())
|
|
{
|
|
if (Child.Get() == &RecursiveTarget)
|
|
{
|
|
bRecursiveContains = true;
|
|
}
|
|
else if (Child.IsValid())
|
|
{
|
|
bRecursiveContains = Contains(Child.AsRef(), RecursiveTarget);
|
|
}
|
|
|
|
if (bRecursiveContains)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return bRecursiveContains;
|
|
}
|
|
};
|
|
|
|
bool bContains = false;
|
|
for (const TSRef<Node>& Child : _Children)
|
|
{
|
|
if (Child.Get() == &Target)
|
|
{
|
|
bContains = true;
|
|
}
|
|
else if (bRecursive)
|
|
{
|
|
bContains = FVstContains_Visitor::Contains(Child, Target);
|
|
}
|
|
|
|
if (bContains)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return bContains;
|
|
}
|
|
|
|
int32_t FindPreviousSibling()
|
|
{
|
|
if (!this->HasParent())
|
|
{
|
|
return uLang::IndexNone;
|
|
}
|
|
const Node* Parent = this->GetParent();
|
|
const int32_t NumChildren = Parent->GetChildCount();
|
|
if (NumChildren == 1)
|
|
{
|
|
return uLang::IndexNone;
|
|
}
|
|
const NodeArray& Children = Parent->GetChildren();
|
|
for (int32_t Index = 0; Index < NumChildren; ++Index)
|
|
{
|
|
const TSRef<Node>& CurNode = Children[Index];
|
|
if (CurNode == this)
|
|
{
|
|
return Index - 1;
|
|
}
|
|
}
|
|
return uLang::IndexNone;
|
|
}
|
|
|
|
const TSRef<Node>& AppendChild(const TSRef<Node>& child)
|
|
{
|
|
DropParent(child);
|
|
child->_Parent = this;
|
|
_Children.Push(child);
|
|
DebugOrphanCheck();
|
|
return _Children.Last();
|
|
}
|
|
|
|
const TSRef<Node>& AppendChildAt(const TSRef<Node>& child, int32_t idx)
|
|
{
|
|
DropParent(child);
|
|
child->_Parent = this;
|
|
_Children.Insert(child, idx);
|
|
DebugOrphanCheck();
|
|
return _Children[idx];
|
|
}
|
|
|
|
void SetChildAt(int32_t Index, TSRef<Node> Child)
|
|
{
|
|
DropParent(Child);
|
|
Child->_Parent = this;
|
|
_Children[Index] = Move(Child);
|
|
DebugOrphanCheck();
|
|
}
|
|
|
|
TSRef<Node> TakeChildAt(int32_t idx, const TSPtr<Node> Replacement = TSPtr<Node>())
|
|
{
|
|
const auto ChildAtIdx = _Children[idx];
|
|
if (Replacement.IsValid())
|
|
{
|
|
Replacement->_Parent = this;
|
|
_Children[idx] = Replacement.AsRef();
|
|
}
|
|
else
|
|
{
|
|
_Children.RemoveAt(idx);
|
|
}
|
|
ChildAtIdx->_Parent = nullptr;
|
|
return ChildAtIdx;
|
|
}
|
|
|
|
void AppendChildren(const uLang::TArray<TSRef<Node>>& Children, int32_t NumToAppend = -1)
|
|
{
|
|
NumToAppend = NumToAppend == -1 ? Children.Num() : NumToAppend;
|
|
_Children.Reserve(_Children.Num() + NumToAppend);
|
|
for(int32_t i=0; i<NumToAppend; i+=1)
|
|
{
|
|
const auto& Expr = Children[i];
|
|
AppendChild(Expr);
|
|
}
|
|
}
|
|
|
|
void AppendChildren(const uLang::TSRefArray<Node>& Children, int32_t NumToAppend = -1)
|
|
{
|
|
NumToAppend = NumToAppend == -1 ? Children.Num() : NumToAppend;
|
|
_Children.Reserve(_Children.Num() + NumToAppend);
|
|
for (int32_t i = 0; i < NumToAppend; i += 1)
|
|
{
|
|
const auto& Expr = Children[i];
|
|
AppendChild(Expr);
|
|
}
|
|
}
|
|
|
|
// Prepend the given nodes to this node's child list in reverse order
|
|
// i.e. the last node in the given list will end up as the first child of this node.
|
|
void PrependChildren(const uLang::TArray<TSRef<Node>>& Children, int32_t NumToAppend = -1)
|
|
{
|
|
NumToAppend = NumToAppend == -1 ? Children.Num() : NumToAppend;
|
|
_Children.Reserve(_Children.Num() + NumToAppend);
|
|
for (int32_t i = 0; i < NumToAppend; i += 1)
|
|
{
|
|
const auto& Expr = Children[i];
|
|
AppendChildAt(Expr, 0);
|
|
}
|
|
}
|
|
|
|
UE_API void ReplaceSelfWith(const TSRef<Node>& replacement);
|
|
|
|
// Supply an index if you have one
|
|
UE_API bool RemoveFromParent(int32_t idx = uLang::IndexNone);
|
|
|
|
void Empty()
|
|
{
|
|
for (const auto& Child : _Children)
|
|
{
|
|
if (ULANG_ENSUREF(Child->_Parent == this, "Child does not belong to me!"))
|
|
{
|
|
Child->_Parent = nullptr;
|
|
}
|
|
}
|
|
|
|
_Children.Reset();
|
|
}
|
|
|
|
const Snippet* FindSnippet() const
|
|
{
|
|
return GetParentOfType<Snippet>();
|
|
}
|
|
|
|
Node* FindRoot()
|
|
{
|
|
Node* root = this;
|
|
while (root->_Parent != nullptr && root->_Parent->GetElementType() != NodeType::Snippet)
|
|
{
|
|
root = root->AccessParent();
|
|
}
|
|
|
|
return root;
|
|
}
|
|
|
|
static void TransferChildren(const TSRef<Node>& From, const TSRef<Node>& To, int32_t First, int32_t Last)
|
|
{
|
|
ULANG_ASSERTF(Last <= From->GetChildCount() - 1, "Not enough elements in source array");
|
|
|
|
To->AccessChildren().Reserve(To->GetChildCount() + Last - First + 1);
|
|
|
|
for (int32_t i = First; i <= Last; i += 1)
|
|
{
|
|
const auto & CurChild = From->GetChildren()[i]; //-V758
|
|
CurChild->_Parent = nullptr;
|
|
To->AppendChild(CurChild);
|
|
}
|
|
|
|
From->AccessChildren().RemoveAt(First, Last - First + 1);
|
|
}
|
|
|
|
static void TransferChildren(const TSRef<Node>& From, const TSRef<Node>& To)
|
|
{
|
|
TransferChildren(From, To, 0, From->GetChildCount() - 1);
|
|
}
|
|
|
|
static void TransferPrefixComments(const TSRef<Node>& From, const TSRef<Node>& To)
|
|
{
|
|
NodeArray& ToPrefixComments = To->AccessPrefixComments();
|
|
NodeArray& FromPrefixComments = From->AccessPrefixComments();
|
|
ToPrefixComments.Reserve(ToPrefixComments.Num() + FromPrefixComments.Num());
|
|
for (TSRef<Node>& FromPrefixComment : FromPrefixComments)
|
|
{
|
|
FromPrefixComment->_Parent = nullptr;
|
|
To->AppendPrefixComment(FromPrefixComment);
|
|
}
|
|
FromPrefixComments.Empty();
|
|
}
|
|
|
|
static void TransferPostfixComments(const TSRef<Node>& From, const TSRef<Node>& To)
|
|
{
|
|
NodeArray& ToPostfixComments = To->AccessPostfixComments();
|
|
NodeArray& FromPostfixComments = From->AccessPostfixComments();
|
|
ToPostfixComments.Reserve(ToPostfixComments.Num() + FromPostfixComments.Num());
|
|
for (TSRef<Node>& FromPostfixComment : FromPostfixComments)
|
|
{
|
|
FromPostfixComment->_Parent = nullptr;
|
|
To->AppendPostfixComment(FromPostfixComment);
|
|
}
|
|
FromPostfixComments.Empty();
|
|
}
|
|
|
|
const uLang::CAstNode* GetMappedAstNode() const { return _MappedAstNode; }
|
|
UE_API void AddMapping(uLang::CAstNode* AstNode) const;
|
|
static UE_API void RemoveMapping( uLang::CAstNode* AstNode );
|
|
|
|
UE_API void EnsureAuxAllocated();
|
|
|
|
void SetTile(FTile* Tile)
|
|
{
|
|
_Tile = Tile;
|
|
}
|
|
|
|
FTile* GetTile()
|
|
{
|
|
return _Tile;
|
|
}
|
|
|
|
protected:
|
|
|
|
friend class uLang::CAstNode;
|
|
|
|
/**
|
|
* Checks for any nodes that have their parent set incorrectly within the hierarchy.
|
|
*
|
|
* @param Root A pointer to the root of the tree to inspect for any orphaned nodes.
|
|
*
|
|
* @return A pointer to the first node that was found to have an incorrect parent set.
|
|
* If no such node was found, returns `nullptr`.
|
|
*/
|
|
static const Node* FindOrphanedNode(const Node& InNode)
|
|
{
|
|
struct SChildParent
|
|
{
|
|
const Node* _Child;
|
|
const Node* _Parent;
|
|
};
|
|
LArray<SChildParent> Stack;
|
|
// NOTE: (YiLiangSiew) Arbitrary stack size chosen here to try avoiding re-allocation during traversal.
|
|
Stack.Reserve(256);
|
|
for (const TSRef<Node>& CurChild : InNode.GetChildren())
|
|
{
|
|
Stack.Add({CurChild.Get(), &InNode});
|
|
}
|
|
while (!Stack.IsEmpty())
|
|
{
|
|
const SChildParent& CurPair = Stack.Pop();
|
|
if (CurPair._Child->GetParent() != CurPair._Parent)
|
|
{
|
|
return CurPair._Child;
|
|
}
|
|
for (const TSRef<Node>& CurChild : CurPair._Child->GetChildren())
|
|
{
|
|
Stack.Add({CurChild, CurPair._Child});
|
|
}
|
|
}
|
|
return nullptr;
|
|
};
|
|
|
|
/**
|
|
* Validates that the child being added to this node is currently orphaned.
|
|
* If the child has a parent, then two nodes would own the same child, which is impossible.
|
|
*/
|
|
void DropParent(const TSRef<Node>& Child)
|
|
{
|
|
if (ULANG_ENSUREF(Child->GetParent()==nullptr, "Child already has a parent!"))
|
|
{
|
|
}
|
|
else
|
|
{
|
|
Child->AccessParent()->AccessChildren().Remove(Child);
|
|
Child->_Parent = nullptr;
|
|
}
|
|
}
|
|
|
|
void AppendChildInternal(const TSRef<Node>& child)
|
|
{
|
|
DropParent(child);
|
|
|
|
child->_Parent = this;
|
|
_Children.Push(child);
|
|
|
|
DebugOrphanCheck();
|
|
}
|
|
|
|
NodeArray _Children;
|
|
/** VstNodes point at their parent. We guarantee that a VstNode can only exist in one place in the Vst tree at a time, so the Parent<->Child relationship is unique. */
|
|
Node* _Parent;
|
|
/** Auxiliary data such as attributes associated with this VstNode. The transaction system addresses this via child index -1. */
|
|
TSPtr<Clause> _Aux;
|
|
|
|
// list of comment nodes that appear before or after this node
|
|
NodeArray _PreComments;
|
|
NodeArray _PostComments;
|
|
|
|
/**
|
|
* Text location from whence this node was parsed.
|
|
* (1) must be contained in (= not be partially outside of) parent's _Whence;
|
|
* (2) must not overlap any sibling's _Whence;
|
|
* (3) snippets (text files), programs, and some modules will not have a valid locus
|
|
*/
|
|
SLocus _Whence;
|
|
|
|
/// The number of trailing newlines that should follow this node.
|
|
int32_t _NumNewLinesBefore;
|
|
int32_t _NumNewLinesAfter;
|
|
|
|
/** Describes the role of this node in the context of its parent. e.g. Children of BinaryOpAddSub are tagged as `Operator` or `Operand` */
|
|
uint8_t _Tag;
|
|
/** Runtime type information about this node */
|
|
NodeType _Type;
|
|
mutable const uLang::CAstNode* _MappedAstNode;
|
|
FTile* _Tile;
|
|
}; // Node
|
|
|
|
|
|
using NodeArray = Node::NodeArray;
|
|
|
|
|
|
struct Clause : public Node
|
|
{
|
|
static const Vst::NodeType StaticType = NodeType::Clause;
|
|
enum class EForm : int8_t
|
|
{
|
|
Synthetic, // The clause doesn't occur in source, but is instead used as a way to group multiple subexpressions.
|
|
NoSemicolonOrNewline, // The clause does not contains a semicolon or a newline: x or {x} or {x,y} but not {x;}
|
|
HasSemicolonOrNewline, // The clause does contain a semicolon or a newline: {x;} or {x;y,z} or \n\tx
|
|
|
|
/// Used for clauses that have a single attribute identifier VST node within it. This means the clause should use angle brackets (i.e. `<`/`>`) instead of curly braces.
|
|
/// This also means that the clause is before the identifier (i.e. `<@custom_attribute>identifier`).
|
|
IsPrependAttributeHolder,
|
|
/// Used for clauses that have a single attribute identifier VST node within it. This means the clause should use angle brackets (i.e. `<`/`>`) instead of curly braces.
|
|
/// This also means that the clause is after the identifier (i.e. `class<pure>`).
|
|
IsAppendAttributeHolder
|
|
};
|
|
|
|
enum class EPunctuation : int8_t
|
|
{
|
|
Unknown,
|
|
Braces,
|
|
Colon,
|
|
Indentation
|
|
};
|
|
|
|
Clause(const SLocus& Whence, const EForm Form, const EPunctuation Punctuation) : Node(StaticType, Whence), _Form(Form), _Punctuation(Punctuation) {}
|
|
|
|
Clause(const SLocus& Whence, const EForm Form) : Node(StaticType, Whence), _Form(Form), _Punctuation(EPunctuation::Unknown) {}
|
|
|
|
Clause(uint8_t ClauseType, const SLocus& Whence, EForm Form) : Node(StaticType, Whence), _Form(Form), _Punctuation(EPunctuation::Unknown)
|
|
{
|
|
_Tag = ClauseType;
|
|
}
|
|
|
|
Clause(const TSRef<Node>& Child, const SLocus& Whence, EForm Form) : Clause(0, Whence, Form)
|
|
{
|
|
AppendChild(Child);
|
|
}
|
|
|
|
Clause(const NodeArray& Children, const SLocus& Whence, EForm Form) : Clause(0, Whence, Form)
|
|
{
|
|
AppendChildren(Children);
|
|
}
|
|
|
|
Clause(const uLang::TSRefArray<Node>& Children, const SLocus& Whence, EForm Form) : Clause(0, Whence, Form)
|
|
{
|
|
AppendChildren(Children);
|
|
}
|
|
|
|
Clause(const NodeArray& Children, int32_t NumToAdd, const SLocus& Whence, EForm Form) : Clause(0, Whence, Form)
|
|
{
|
|
AppendChildren(Children, NumToAdd);
|
|
}
|
|
|
|
Clause(const uLang::TSRefArray<Node>& Children, int32_t NumToAdd, const SLocus& Whence, EForm Form) : Clause(0, Whence, Form)
|
|
{
|
|
AppendChildren(Children, NumToAdd);
|
|
}
|
|
|
|
EForm GetForm() const { return _Form; }
|
|
void SetForm(const EForm InForm)
|
|
{
|
|
this->_Form = InForm;
|
|
}
|
|
|
|
EPunctuation GetPunctuation() const
|
|
{
|
|
return _Punctuation;
|
|
}
|
|
void SetPunctuation(const EPunctuation InPunctuation)
|
|
{
|
|
this->_Punctuation = InPunctuation;
|
|
}
|
|
|
|
private:
|
|
EForm _Form;
|
|
|
|
/// Tells us whether the clause should be using either colon and newlines, or curly braces and semicolons to separate expressions.
|
|
EPunctuation _Punctuation;
|
|
};
|
|
|
|
VERSECOMPILER_API const TSRef<Node> MakeStub(const SLocus& Whence);
|
|
|
|
/**
|
|
* Syntax element that does not need children. These nodes do not have a structure (i.e. no children), but
|
|
* rather are leaves in the hierarchy.
|
|
**/
|
|
class CAtom : public Node
|
|
{
|
|
public:
|
|
|
|
// Public Data Members
|
|
|
|
CUTF8String _OriginalCode;
|
|
|
|
// Methods
|
|
|
|
CAtom(const uLang::CUTF8StringView& CodeStr, NodeType InType, const SLocus& Whence) : Node(InType, Whence), _OriginalCode(CodeStr) {}
|
|
|
|
const string& GetStringValue() const { return _OriginalCode; }
|
|
const CUTF8String& GetSourceText() const { return _OriginalCode; }
|
|
const char* GetSourceCStr() const { return _OriginalCode.AsCString(); }
|
|
};
|
|
|
|
struct Comment : public CAtom
|
|
{
|
|
enum class EType : uint8_t { block, line, ind, frag };
|
|
|
|
static const Vst::NodeType StaticType = NodeType::Comment;
|
|
|
|
Comment(EType Type, const CUTF8StringView& InText, const SLocus& Whence) : CAtom(InText, StaticType, Whence), _Type(Type) {}
|
|
|
|
EType _Type;
|
|
};
|
|
|
|
const char* CommentTypeToString(Comment::EType Type);
|
|
|
|
// A collection of sub trees that are stored in the same source, e.g. a text file or UProperty
|
|
struct Snippet : public Node
|
|
{
|
|
static const Vst::NodeType StaticType = NodeType::Snippet;
|
|
Snippet() : Node(StaticType) {}
|
|
Snippet(const CUTF8StringView& Path) : Node(StaticType), _Path(Path) {}
|
|
Snippet(const CUTF8StringView& Path, const TSRef<Vst::Node>& FirstChild) : Node(StaticType, FirstChild->Whence()), _Path(Path) { AppendChild(FirstChild); }
|
|
Snippet(const TSRef<Vst::Node>& FirstChild) : Node(StaticType, FirstChild->Whence()) { AppendChild(FirstChild); }
|
|
|
|
bool HasErrors() const
|
|
{
|
|
bool bHasErrors = false;
|
|
for (const TSRef<Node>& Node : _Children)
|
|
{
|
|
if (Node->IsError())
|
|
{
|
|
bHasErrors = true;
|
|
break;
|
|
}
|
|
}
|
|
return bHasErrors;
|
|
}
|
|
|
|
Clause::EForm GetForm() const { return _Form; }
|
|
void SetForm(Clause::EForm Form) { _Form = Form; }
|
|
|
|
// Where this snippet came from - usually this is the fully qualified path of the associated text file
|
|
CUTF8String _Path;
|
|
|
|
private:
|
|
Clause::EForm _Form{Clause::EForm::Synthetic};
|
|
};
|
|
|
|
// A collection of snippets
|
|
struct Module : public Node
|
|
{
|
|
static const Vst::NodeType StaticType = NodeType::Module;
|
|
Module(const CUTF8StringView& Name) : Node(StaticType), _Name(Name) {}
|
|
|
|
// The name of this module
|
|
CUTF8String _Name;
|
|
|
|
// File path of vmodule file if exists, or directory path with trailing slash
|
|
CUTF8String _FilePath;
|
|
};
|
|
|
|
// A collection of Module nodes
|
|
struct Package : public Node
|
|
{
|
|
static const Vst::NodeType StaticType = NodeType::Package;
|
|
|
|
Package(const CUTF8StringView& Name) : Node(StaticType), _Name(Name), _DependencyPackages() //-V730
|
|
{} //-V730
|
|
|
|
// The name of this package
|
|
CUTF8String _Name;
|
|
|
|
// Directory path where the source files are located
|
|
CUTF8String _DirPath;
|
|
|
|
// File path of vpackage file if exists, empty otherwise
|
|
CUTF8String _FilePath;
|
|
|
|
// Verse path of the root module of this package
|
|
CUTF8String _VersePath;
|
|
|
|
// Names of packages this package is dependent on
|
|
LArray<CUTF8String> _DependencyPackages;
|
|
|
|
// Destination directory for VNI generated C++ code (fully qualified)
|
|
uLang::TOptional<CUTF8String> _VniDestDir;
|
|
|
|
// The role this package plays in the project.
|
|
uLang::EPackageRole _Role = uLang::EPackageRole::Source;
|
|
|
|
// Origin/visibility of Verse code in this package
|
|
EVerseScope _VerseScope = EVerseScope::PublicUser;
|
|
|
|
// The language version targetted by the Verse code in this package.
|
|
uLang::TOptional<uint32_t> _VerseVersion;
|
|
|
|
/// This allows us to determine when a package was uploaded for a given Fortnite release version.
|
|
/// It is a HACK that conditionally enables/disables behaviour in the compiler in order to
|
|
/// support previous mistakes allowed to slip through in previous Verse langauge releases but
|
|
/// now need to be supported for backwards compatability.
|
|
/// When we can confirm that all Fortnite packages that are currently uploaded are beyond this
|
|
/// version being used in all instances of the codebase, this can then be removed.
|
|
uint32_t _UploadedAtFNVersion = VerseFN::UploadedAtFNVersion::Latest;
|
|
|
|
// If true, module macros in this package's source and digest will be treated as implicit
|
|
bool _bTreatModulesAsImplicit = false;
|
|
|
|
// Whether to allow the use of experimental definitions in this package.
|
|
bool _bAllowExperimental = false;
|
|
|
|
// Whether Scene Graph is enabled or not. Impacts the asset digest generated.
|
|
bool _bEnableSceneGraph = false;
|
|
|
|
VERSECOMPILER_API TSRef<Module> FindOrAddModule(const CUTF8StringView& ModuleName, const CUTF8StringView& ParentModuleName = CUTF8StringView());
|
|
VERSECOMPILER_API static uLang::TOptional<TSRef<Module>> FindModule(const Node& ModuleContainer, const CUTF8StringView& ModuleName);
|
|
};
|
|
|
|
// A collection of Package nodes
|
|
// Packages (children) are sorted in dependency order (i.e. dependents always follow their dependencies)
|
|
struct Project : public Node
|
|
{
|
|
static const Vst::NodeType StaticType = NodeType::Project;
|
|
Project(const CUTF8StringView& Name) : Node(StaticType), _Name(Name) {}
|
|
|
|
// The name of this project
|
|
CUTF8String _Name;
|
|
|
|
// File path of vproject file if exists, empty otherwise
|
|
CUTF8String _FilePath;
|
|
|
|
VERSECOMPILER_API const TSRef<Module>& FindOrAddModule(const CUTF8StringView& ModuleName, const CUTF8StringView& FilePath, const CUTF8StringView& ParentModuleName = CUTF8StringView());
|
|
VERSECOMPILER_API static uLang::TOptional<TSRef<Module>> FindModule(const Node& ModuleContainer, const CUTF8StringView& ModuleName);
|
|
|
|
/**
|
|
* Removes any packages from the project that have the given name.
|
|
*
|
|
* @param PackageName Any package that has this name in the project will be removed.
|
|
*
|
|
* @return `true` if any packages were removed, `false` if no packages with the name were found
|
|
* or another error occurred.
|
|
*/
|
|
bool RemovePackagesWithName(const CUTF8StringView& PackageName)
|
|
{
|
|
// NOTE: (YiLiangSiew) This assumes that packages are not stored recursively in the project,
|
|
// or that projects do not have other projects as their descendents.
|
|
NodeArray& Children = this->AccessChildren();
|
|
const int32_t NumChildren = Children.Num();
|
|
if (NumChildren == 0)
|
|
{
|
|
return false;
|
|
}
|
|
int32_t NumPackagesRemoved = 0;
|
|
for (int32_t Index = NumChildren - 1; Index >= 0; --Index)
|
|
{
|
|
TSRef<Node> CurChild = Children[Index];
|
|
if (!CurChild->IsA<Package>())
|
|
{
|
|
continue;
|
|
}
|
|
if (CurChild->As<Package>()._Name == PackageName)
|
|
{
|
|
Children.RemoveAt(Index);
|
|
++NumPackagesRemoved;
|
|
}
|
|
}
|
|
return NumPackagesRemoved > 0;
|
|
}
|
|
|
|
bool ReplaceSnippet(const CUTF8StringView& PathOfOldSnippetToReplace, TSRef<Snippet> NewSnippet, TSPtr<Snippet>* OutOldSnippet = nullptr)
|
|
{
|
|
for (const TSRef<Node>& CurChild : this->_Children)
|
|
{
|
|
if (CurChild->IsA<Package>())
|
|
{
|
|
Package& CurPackage = CurChild->As<Package>();
|
|
for (const TSRef<Node>& CurPkgChild : CurPackage.GetChildren())
|
|
{
|
|
if (CurPkgChild->IsA<Module>())
|
|
{
|
|
Module& CurModule = CurPkgChild->As<Module>();
|
|
for (const TSRef<Node>& CurModuleChild : CurModule.GetChildren())
|
|
{
|
|
if (CurModuleChild->IsA<Snippet>())
|
|
{
|
|
Snippet& CurSnippet = CurModuleChild->As<Snippet>();
|
|
if (CurSnippet._Path == PathOfOldSnippetToReplace)
|
|
{
|
|
if (OutOldSnippet != nullptr)
|
|
{
|
|
*OutOldSnippet = CurModuleChild.As<Snippet>();
|
|
}
|
|
CurSnippet.ReplaceSelfWith(NewSnippet);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (CurPkgChild->IsA<Snippet>())
|
|
{
|
|
Snippet& CurSnippet = CurPkgChild->As<Snippet>();
|
|
if (CurSnippet._Path == PathOfOldSnippetToReplace)
|
|
{
|
|
if (OutOldSnippet != nullptr)
|
|
{
|
|
*OutOldSnippet = CurPkgChild.As<Snippet>();
|
|
}
|
|
CurSnippet.ReplaceSelfWith(NewSnippet);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Corresponds to `$1:$2 = $3` syntax, where the `$2` is optional. If and
|
|
* only if `$2` is present, the left-hand side is a `TypeSpec`. For example,
|
|
* @code
|
|
* X:int = Y
|
|
* @endcode
|
|
* becomes
|
|
* @code
|
|
* Definition
|
|
* TypeSpec
|
|
* Identifier
|
|
* X
|
|
* Identifier
|
|
* int
|
|
* Identifier
|
|
* Y
|
|
* @endcode
|
|
* whereas
|
|
* @code
|
|
* X := Y
|
|
* @endcode
|
|
* becomes
|
|
* @code
|
|
* Definition
|
|
* Identifier
|
|
* X
|
|
* Identifier
|
|
* Y
|
|
* @endcode
|
|
*/
|
|
struct Definition : public Node
|
|
{
|
|
static const Vst::NodeType StaticType = NodeType::Definition;
|
|
|
|
Definition(const SLocus& Whence, const TSRef<Vst::Node>& Lhs, const TSRef<Vst::Node>& Rhs) : Node(StaticType, Whence)
|
|
{
|
|
_Children.Reserve(2);
|
|
AppendChildInternal(Lhs);
|
|
AppendChildInternal(Rhs);
|
|
}
|
|
const TSRef<Vst::Node>& GetOperandLeft() const { return _Children[0]; }
|
|
const TSRef<Vst::Node>& GetOperandRight() const { return _Children[1]; }
|
|
};
|
|
|
|
/**
|
|
* Corresponds to `set $1 $op $2` syntax. The left-hand side will always be
|
|
* a `Mutation` node. `$op` may be `=`, `+=`, `-=`, `*=`, or `/=` -
|
|
* importantly, not `:=`. For example,
|
|
* @code
|
|
* set X = Y
|
|
* @endcode
|
|
* becomes
|
|
* @code
|
|
* Assignment
|
|
* Mutation
|
|
* Set
|
|
* Identifier
|
|
* X
|
|
* Identifier
|
|
* Y
|
|
* @endcode
|
|
*/
|
|
struct Assignment : public Node
|
|
{
|
|
static const Vst::NodeType StaticType = NodeType::Assignment;
|
|
enum class EOp : uint8_t { assign, addAssign, subAssign, mulAssign, divAssign };
|
|
|
|
Assignment(const SLocus& Whence, const TSRef<Vst::Node>& lhs, EOp InOp, const TSRef<Vst::Node>& rhs) : Node(StaticType, Whence)
|
|
{
|
|
_Children.Reserve(2);
|
|
AppendChildInternal(lhs);
|
|
|
|
rhs->SetTag(static_cast<uint8_t>(InOp));
|
|
AppendChildInternal(rhs);
|
|
}
|
|
const TSRef<Vst::Node>& GetOperandLeft() const { return _Children[0]; }
|
|
const TSRef<Vst::Node>& GetOperandRight() const { return _Children[1]; }
|
|
};
|
|
|
|
inline const char* AssignmentOpAsCString(Assignment::EOp Op)
|
|
{
|
|
switch(Op)
|
|
{
|
|
case Assignment::EOp::assign: return "assign";
|
|
case Assignment::EOp::addAssign: return "addAssign";
|
|
case Assignment::EOp::subAssign: return "subAssign";
|
|
case Assignment::EOp::mulAssign: return "mulAssign";
|
|
case Assignment::EOp::divAssign: return "divAssign";
|
|
default: ULANG_UNREACHABLE();
|
|
};
|
|
}
|
|
|
|
// Conditional flow control with failure context in test condition.
|
|
//
|
|
// Children are Clause block nodes in the following order:
|
|
// - if_identifier ]
|
|
// - condition |- Repeating
|
|
// - [then_body] ]
|
|
// - [else_body] -- Optional last node
|
|
//
|
|
// If Identifier blocks - must be present and is simply an empty Clause meant to
|
|
// hold information about comments surrounding the 'if' identifier in the
|
|
// original source code
|
|
// Condition blocks - must be present (they cannot be omitted - there must be at
|
|
// least 1) and they must have 1 or more expressions where 1 or more of the
|
|
// expressions can fail. Any local variables within it's top scope are made
|
|
// available to any immediately following then block.
|
|
// Then blocks - are optional (they can be omitted) and when present must follow a
|
|
// conditional block and may have zero (can be empty) or more expressions.
|
|
// Else block - is optional (it can be omitted) and when present must follow a
|
|
// conditional or then block (it cannot be the only block), must be the last
|
|
// block and it may have zero (can be empty) or more expressions.
|
|
//
|
|
// Chained `else if` are automatically flattened into a single FlowIf node with
|
|
// multiple condition/[then] block pairs followed by an optional else block.
|
|
//
|
|
// An `if` may be used as an expression with a result if all the control flow paths
|
|
// have a common result - i.e. it will have a result if every condition is paired
|
|
// with a then block and there is an ending else block and all the then blocks and
|
|
// else block have a most common result type.
|
|
struct FlowIf : public Node
|
|
{
|
|
static const Vst::NodeType StaticType = NodeType::FlowIf;
|
|
|
|
// Tags for different kinds of clause block children.
|
|
enum class ClauseTag : uint8_t { if_identifier, condition, then_body, else_body };
|
|
|
|
FlowIf(const SLocus& Whence) : Node(StaticType, Whence) {}
|
|
|
|
void AddIfIdentifier(const TSRef<Node>& Child) { Child->SetTag(static_cast<uint8_t>(ClauseTag::if_identifier)); AppendChildInternal(Child); }
|
|
void AddCondition(const TSRef<Node>& Child) { Child->SetTag(static_cast<uint8_t>(ClauseTag::condition)); AppendChildInternal(Child); }
|
|
void AddBody(const TSRef<Node>& Child) { Child->SetTag(static_cast<uint8_t>(ClauseTag::then_body)); AppendChildInternal(Child); }
|
|
void AddElseBody(const TSRef<Node>& Child) { Child->SetTag(static_cast<uint8_t>(ClauseTag::else_body)); AppendChildInternal(Child); }
|
|
};
|
|
|
|
struct BinaryOpLogicalOr : public Node
|
|
{
|
|
static const Vst::NodeType StaticType = NodeType::BinaryOpLogicalOr;
|
|
BinaryOpLogicalOr(const SLocus& Whence, const TSRef<Vst::Node>& Lhs, const TSRef<Vst::Node>& Rhs)
|
|
: Node(StaticType, Whence)
|
|
{
|
|
_Children.Reserve(2);
|
|
AppendChild(Lhs);
|
|
AppendChild(Rhs);
|
|
}
|
|
|
|
void AppendChild(const TSRef<Vst::Node>& Rhs) { AppendChildInternal(Rhs); }
|
|
};
|
|
|
|
struct BinaryOpLogicalAnd : public Node
|
|
{
|
|
static const Vst::NodeType StaticType = NodeType::BinaryOpLogicalAnd;
|
|
BinaryOpLogicalAnd(const SLocus& Whence, const TSRef<Node>& Lhs, const TSRef<Node>& Rhs)
|
|
: Node(StaticType, Whence)
|
|
{
|
|
_Children.Reserve(2);
|
|
AppendChild(Lhs);
|
|
AppendChild(Rhs);
|
|
}
|
|
|
|
void AppendChild(const TSRef<Vst::Node>& Rhs) { AppendChildInternal(Rhs); }
|
|
};
|
|
|
|
struct PrefixOpLogicalNot : public Node
|
|
{
|
|
static const Vst::NodeType StaticType = NodeType::PrefixOpLogicalNot;
|
|
PrefixOpLogicalNot( const SLocus& Whence, const TSRef<Node>& Rhs)
|
|
: Node(StaticType, Whence)
|
|
{
|
|
AppendChildInternal(Rhs);
|
|
}
|
|
|
|
const TSRef<Vst::Node>& GetInnerNode() const { return _Children[0]; }
|
|
};
|
|
|
|
struct BinaryOpCompare : public Node
|
|
{
|
|
static const Vst::NodeType StaticType = NodeType::BinaryOpCompare;
|
|
|
|
enum class op : uint8_t { lt, lteq, gt, gteq, eq, noteq };
|
|
|
|
BinaryOpCompare(const SLocus& Whence, const TSRef<Vst::Node>& lhs, op in_op, const TSRef<Vst::Node>& rhs)
|
|
: Node(StaticType, Whence)
|
|
{
|
|
_Children.Reserve(2);
|
|
AppendChildInternal(lhs);
|
|
AppendChildInternal(rhs);
|
|
rhs->SetTag(static_cast<uint8_t>(in_op));
|
|
}
|
|
|
|
TSRef<Vst::Node> GetOperandLeft() const { return _Children[0]; }
|
|
TSRef<Vst::Node> GetOperandRight() const { return _Children[1]; }
|
|
op GetOp() const { return _Children[1]->GetTag<op>(); }
|
|
};
|
|
|
|
inline const char* BinaryCompareOpAsCString(BinaryOpCompare::op Op)
|
|
{
|
|
switch(Op)
|
|
{
|
|
case BinaryOpCompare::op::lt: return "lt";
|
|
case BinaryOpCompare::op::lteq: return "lteq";
|
|
case BinaryOpCompare::op::gt: return "gt";
|
|
case BinaryOpCompare::op::gteq: return "gteq";
|
|
case BinaryOpCompare::op::eq: return "eq";
|
|
case BinaryOpCompare::op::noteq: return "noteq";
|
|
default: ULANG_UNREACHABLE();
|
|
};
|
|
}
|
|
|
|
inline BinaryOpCompare::op BinaryCompareOpFlip(BinaryOpCompare::op Op)
|
|
{
|
|
switch(Op)
|
|
{
|
|
case BinaryOpCompare::op::lt: return BinaryOpCompare::op::gt;
|
|
case BinaryOpCompare::op::lteq: return BinaryOpCompare::op::gteq;
|
|
case BinaryOpCompare::op::gt: return BinaryOpCompare::op::lt;
|
|
case BinaryOpCompare::op::gteq: return BinaryOpCompare::op::lteq;
|
|
case BinaryOpCompare::op::eq: return BinaryOpCompare::op::noteq;
|
|
case BinaryOpCompare::op::noteq: return BinaryOpCompare::op::eq;
|
|
default: ULANG_UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
struct Where : Node
|
|
{
|
|
static const NodeType StaticType = NodeType::Where;
|
|
|
|
using RhsView = uLang::TRangeView<const TSRef<Node>*, const TSRef<Node>*>;
|
|
|
|
Where(const SLocus& Whence, const TSRef<Node>& Lhs, const uLang::TArray<TSRef<Node>>& RhsArray)
|
|
: Node(StaticType, Whence)
|
|
{
|
|
_Children.Reserve(1 + RhsArray.Num());
|
|
AppendChildInternal(Lhs);
|
|
for (const TSRef<Node>& Rhs : RhsArray)
|
|
{
|
|
AppendChildInternal(Rhs);
|
|
}
|
|
}
|
|
|
|
Where(const SLocus& Whence, const TSRef<Node>& Lhs, const uLang::TSRefArray<Node>& RhsArray)
|
|
: Node(StaticType, Whence)
|
|
{
|
|
_Children.Reserve(1 + RhsArray.Num());
|
|
AppendChildInternal(Lhs);
|
|
for (const TSRef<Node>& Rhs : RhsArray)
|
|
{
|
|
AppendChildInternal(Rhs);
|
|
}
|
|
}
|
|
|
|
TSRef<Node> GetLhs() const
|
|
{
|
|
return _Children[0];
|
|
}
|
|
|
|
void SetLhs(TSRef<Node> Lhs)
|
|
{
|
|
SetChildAt(0, Move(Lhs));
|
|
}
|
|
|
|
RhsView GetRhs() const
|
|
{
|
|
return {_Children.begin() + 1, _Children.end()};
|
|
}
|
|
};
|
|
|
|
struct Mutation : Node
|
|
{
|
|
static const NodeType StaticType = NodeType::Mutation;
|
|
|
|
enum class EKeyword : uint8_t
|
|
{
|
|
Var,
|
|
Set
|
|
};
|
|
|
|
Mutation(const SLocus& Whence, const TSRef<Node>& Child, EKeyword Keyword)
|
|
: Node(StaticType, Whence)
|
|
, _Keyword(Keyword)
|
|
{
|
|
_Children.Reserve(1);
|
|
AppendChildInternal(Child);
|
|
}
|
|
|
|
TSRef<Node> Child() const
|
|
{
|
|
return _Children[0];
|
|
}
|
|
|
|
EKeyword _Keyword;
|
|
};
|
|
|
|
struct TypeSpec : public Node
|
|
{
|
|
static const Vst::NodeType StaticType = NodeType::TypeSpec;
|
|
|
|
// comments that go after the ':' of the typespec
|
|
NodeArray _TypeSpecComments;
|
|
|
|
TypeSpec(const SLocus& Whence, const TSRef<Node>& Lhs, const TSRef<Node>& Rhs)
|
|
:Node(NodeType::TypeSpec, Whence)
|
|
{
|
|
_Children.Reserve(2);
|
|
AppendChildInternal(Lhs);
|
|
AppendChildInternal(Rhs);
|
|
}
|
|
|
|
TypeSpec(const SLocus& Whence, const TSRef<Node>& Rhs)
|
|
:Node(NodeType::TypeSpec, Whence)
|
|
{
|
|
_Children.Reserve(1);
|
|
AppendChildInternal(Rhs);
|
|
}
|
|
|
|
bool HasLhs() const { return _Children.Num() == 2; }
|
|
TSRef<Vst::Node> GetLhs() const { ULANG_ASSERTF(HasLhs(), "Lhs assumes we have at least two children."); return _Children[0]; }
|
|
TSRef<Vst::Node> TakeLhs() { ULANG_ASSERTF(HasLhs(), "Lhs assumes we have at least two children."); return TakeChildAt(0, MakeStub(Whence())); }
|
|
TSRef<Vst::Node> GetRhs() const { return _Children[_Children.Num()-1]; }
|
|
TSRef<Vst::Node> TakeRhs() { return TakeChildAt(_Children.Num()-1, MakeStub(Whence())); }
|
|
};
|
|
|
|
struct Identifier : public CAtom
|
|
{
|
|
static const Vst::NodeType StaticType = NodeType::Identifier;
|
|
|
|
/// Comments that are to be suffixed to the qualifiers of this identifier.
|
|
NodeArray _QualifierPostComments;
|
|
|
|
/// Comments that are to be prefixed to the qualifiers of this identifier.
|
|
NodeArray _QualifierPreComments;
|
|
|
|
Identifier(const CUTF8StringView& InName, const SLocus& Whence, const bool bCanBeQualified)
|
|
: CAtom(InName, StaticType, Whence)
|
|
, _QualifierPostComments()
|
|
, _QualifierPreComments()
|
|
, _bCanBeQualified(bCanBeQualified) {}
|
|
Identifier(const CUTF8StringView& InName, const SLocus& Whence)
|
|
: CAtom(InName, StaticType, Whence)
|
|
, _QualifierPostComments()
|
|
, _QualifierPreComments()
|
|
, _bCanBeQualified(true) {}
|
|
|
|
void SetCanBeQualified(const bool bCanBeQualified) { _bCanBeQualified = bCanBeQualified; }
|
|
bool CanBeQualified() const { return _bCanBeQualified; }
|
|
bool IsQualified() const { return _Children.Num() > 0; }
|
|
const TSRef<Node>& GetQualification() const { return _Children[0]; }
|
|
|
|
/**
|
|
* Adds a qualifier if un-qualified.
|
|
*
|
|
* @param InQualifier The text of the qualification to add.
|
|
*
|
|
* @return `true` if the qualification succeeded, or `false` if a qualifier was already present.
|
|
*/
|
|
bool AddQualifier(const uLang::CUTF8StringView& InQualifier);
|
|
|
|
private:
|
|
bool _bCanBeQualified;
|
|
};
|
|
|
|
struct Operator : public CAtom
|
|
{
|
|
static const Vst::NodeType StaticType = NodeType::Operator;
|
|
|
|
Operator(const CUTF8StringView& InSourceText, const SLocus& Whence)
|
|
: CAtom(InSourceText, StaticType, Whence)
|
|
{}
|
|
};
|
|
|
|
struct BinaryOp : public Node
|
|
{
|
|
enum class op : uint8_t { Operator, Operand };
|
|
|
|
BinaryOp(const SLocus& Whence, Vst::NodeType NodeType) : Node(NodeType, Whence) {}
|
|
BinaryOp(const SLocus& Whence, const TSRef<Node>& LhsOperand, Vst::NodeType NodeType)
|
|
: Node(NodeType, Whence)
|
|
{
|
|
LhsOperand->SetTag((uint8_t)op::Operand);
|
|
AppendChildInternal(LhsOperand);
|
|
}
|
|
|
|
void AppendChild(op in_op, const TSRef<Node> in_child)
|
|
{
|
|
in_child->SetTag(static_cast<uint8_t>(in_op));
|
|
AppendChildInternal(in_child);
|
|
}
|
|
|
|
protected:
|
|
void AppendOperation_Internal(const TSRef<Node>& InOperator, const TSRef<Node>& Operand)
|
|
{
|
|
InOperator->SetTag((uint8_t)op::Operator);
|
|
AppendChildInternal(InOperator);
|
|
|
|
Operand->SetTag((uint8_t)op::Operand);
|
|
AppendChildInternal(Operand);
|
|
}
|
|
};
|
|
|
|
struct BinaryOpAddSub : public BinaryOp
|
|
{
|
|
static const Vst::NodeType StaticType = NodeType::BinaryOpAddSub;
|
|
|
|
BinaryOpAddSub(const SLocus& Whence) : BinaryOp(Whence, StaticType) {}
|
|
BinaryOpAddSub(const SLocus& Whence, const TSRef<Node>& LhsOperand)
|
|
: BinaryOp(Whence, LhsOperand, StaticType)
|
|
{
|
|
}
|
|
|
|
VERSECOMPILER_API void AppendAddOperation(const SLocus& AddWhence, const TSRef<Node>& RhsOperand);
|
|
VERSECOMPILER_API void AppendSubOperation(const SLocus& SubWhence, const TSRef<Node>& RhsOperand);
|
|
};
|
|
|
|
struct BinaryOpMulDivInfix : public BinaryOp
|
|
{
|
|
static const Vst::NodeType StaticType = NodeType::BinaryOpMulDivInfix;
|
|
|
|
BinaryOpMulDivInfix(const SLocus& Whence) : BinaryOp(Whence, StaticType) {}
|
|
BinaryOpMulDivInfix(const SLocus& Whence, const TSRef<Node>& LhsOperand)
|
|
: BinaryOp(Whence, LhsOperand, StaticType)
|
|
{
|
|
}
|
|
|
|
void AppendInfixOperation(const TSRef<Identifier>& OpIdentifier, const TSRef<Node>& RhsOperand)
|
|
{
|
|
AppendOperation_Internal(OpIdentifier, RhsOperand);
|
|
}
|
|
|
|
VERSECOMPILER_API void AppendMulOperation(const SLocus& MulWhence, const TSRef<Node>& RhsOperand);
|
|
VERSECOMPILER_API void AppendDivOperation(const SLocus& DivWhence, const TSRef<Node>& RhsOperand);
|
|
};
|
|
|
|
struct BinaryOpRange : public Node
|
|
{
|
|
static const Vst::NodeType StaticType = NodeType::BinaryOpRange;
|
|
BinaryOpRange(const SLocus& Whence, const TSRef<Node>& Lhs, const TSRef<Node>& Rhs)
|
|
: Node(StaticType, Whence)
|
|
{
|
|
_Children.Reserve(2);
|
|
AppendChild(Lhs);
|
|
AppendChild(Rhs);
|
|
}
|
|
|
|
void AppendChild(const TSRef<Vst::Node>& Rhs) { AppendChildInternal(Rhs); }
|
|
};
|
|
|
|
struct BinaryOpArrow : public Node
|
|
{
|
|
static const Vst::NodeType StaticType = NodeType::BinaryOpArrow;
|
|
BinaryOpArrow(const SLocus& Whence, const TSRef<Node>& Lhs, const TSRef<Node>& Rhs)
|
|
: Node(StaticType, Whence)
|
|
{
|
|
_Children.Reserve(2);
|
|
AppendChild(Lhs);
|
|
AppendChild(Rhs);
|
|
}
|
|
};
|
|
|
|
struct PrePostCall : public Node
|
|
{
|
|
static const Vst::NodeType StaticType = NodeType::PrePostCall;
|
|
|
|
enum Op { Expression, Option, Pointer, DotIdentifier, SureCall, FailCall };
|
|
|
|
PrePostCall(const TSRef<Node>& FirstChild, const SLocus& Whence) : Node(StaticType, Whence)
|
|
{
|
|
AppendChild(FirstChild);
|
|
}
|
|
|
|
PrePostCall(const SLocus& Whence) : Node(StaticType, Whence)
|
|
{
|
|
}
|
|
|
|
bool IsSimpleCall() const
|
|
{
|
|
return (GetChildCount() == 2)
|
|
&& (GetChildren()[0]->GetTag<Op>() == Expression)
|
|
&& ((GetChildren()[1]->GetTag<Op>() == SureCall) || (GetChildren()[1]->GetTag<Op>() == FailCall));
|
|
}
|
|
|
|
bool IsPostHat() const
|
|
{
|
|
return (GetChildCount() == 2)
|
|
&& (GetChildren()[0]->GetTag<Op>() == Expression)
|
|
&& (GetChildren()[1]->GetTag<Op>() == Pointer);
|
|
}
|
|
|
|
VERSECOMPILER_API
|
|
TSRef<Clause> PrependQMark(const SLocus& Whence);
|
|
|
|
VERSECOMPILER_API
|
|
TSRef<Clause> PrependHat(const SLocus& Whence);
|
|
|
|
VERSECOMPILER_API
|
|
void PrependCallArgs(bool bCanFail, const TSRef<Clause>& Args);
|
|
|
|
VERSECOMPILER_API
|
|
void AppendQMark(const SLocus& Whence);
|
|
|
|
VERSECOMPILER_API
|
|
void AppendHat(const SLocus& Whence);
|
|
|
|
VERSECOMPILER_API
|
|
void AppendCallArgs(bool bCanFail, const TSRef<Clause>& Args);
|
|
|
|
VERSECOMPILER_API
|
|
void AppendDotIdent(const SLocus& Whence, const TSRef<Identifier>& Ident);
|
|
|
|
VERSECOMPILER_API
|
|
TSPtr<Clause> TakeLastArgs();
|
|
|
|
};
|
|
|
|
struct IntLiteral : public CAtom
|
|
{
|
|
static const Vst::NodeType StaticType = NodeType::IntLiteral;
|
|
|
|
IntLiteral(const CUTF8StringView& InSourceText, const SLocus& Whence) : CAtom(InSourceText, StaticType, Whence) {}
|
|
};
|
|
|
|
struct FloatLiteral : public CAtom
|
|
{
|
|
static const Vst::NodeType StaticType = NodeType::FloatLiteral;
|
|
|
|
enum class EFormat
|
|
{
|
|
Unspecified,
|
|
F16,
|
|
F32,
|
|
F64
|
|
};
|
|
|
|
EFormat _Format;
|
|
|
|
FloatLiteral(const CUTF8StringView& InSourceText, EFormat Format, const SLocus& Whence)
|
|
: CAtom(InSourceText, StaticType, Whence)
|
|
, _Format(Format)
|
|
{}
|
|
};
|
|
|
|
struct CharLiteral : public CAtom
|
|
{
|
|
static const Vst::NodeType StaticType = NodeType::CharLiteral;
|
|
|
|
enum class EFormat
|
|
{
|
|
UTF8CodeUnit, // char8
|
|
UnicodeCodePoint // char32
|
|
};
|
|
|
|
EFormat _Format;
|
|
|
|
CharLiteral(const CUTF8StringView& InSourceText, EFormat Format, const SLocus& Whence)
|
|
: CAtom(InSourceText, StaticType, Whence)
|
|
, _Format(Format)
|
|
{}
|
|
};
|
|
|
|
struct StringLiteral : public CAtom
|
|
{
|
|
static const Vst::NodeType StaticType = NodeType::StringLiteral;
|
|
|
|
StringLiteral( const SLocus& Whence, const CUTF8StringView& SyntaxSource)
|
|
: CAtom(SyntaxSource, StaticType, Whence)
|
|
{}
|
|
};
|
|
|
|
struct PathLiteral : public CAtom
|
|
{
|
|
static const Vst::NodeType StaticType = NodeType::PathLiteral;
|
|
|
|
PathLiteral(const CUTF8StringView& InPath, const SLocus& Whence) : CAtom(InPath, StaticType, Whence) {}
|
|
};
|
|
|
|
struct Interpolant : public CAtom
|
|
{
|
|
static const Vst::NodeType StaticType = NodeType::Interpolant;
|
|
|
|
Interpolant(const SLocus& Whence, const CUTF8StringView& SyntaxSource)
|
|
: CAtom(SyntaxSource, StaticType, Whence)
|
|
{}
|
|
};
|
|
|
|
struct InterpolatedString : public CAtom
|
|
{
|
|
static const Vst::NodeType StaticType = NodeType::InterpolatedString;
|
|
|
|
InterpolatedString(const SLocus& Whence, const CUTF8StringView& SyntaxSource)
|
|
: CAtom(SyntaxSource, StaticType, Whence)
|
|
{}
|
|
};
|
|
|
|
struct Lambda : public Node
|
|
{
|
|
static const Vst::NodeType StaticType = NodeType::Lambda;
|
|
|
|
Lambda(const SLocus& Whence, const TSRef<Node>& Domain, const TSRef<Node>& Range)
|
|
: Node(StaticType, Whence)
|
|
{
|
|
_Children.Reserve(2);
|
|
AppendChild(Domain);
|
|
AppendChild(Range);
|
|
}
|
|
|
|
const TSRef<Node>& GetDomain() const { return _Children[0]; }
|
|
const TSRef<Clause>& GetRange() const { return _Children[1].As<Clause>(); }
|
|
};
|
|
|
|
struct Control : public Node
|
|
{
|
|
static const Vst::NodeType StaticType = NodeType::Control;
|
|
|
|
enum class EKeyword : uint8_t
|
|
{
|
|
Return,
|
|
Break,
|
|
Yield,
|
|
Continue
|
|
};
|
|
|
|
Control(const SLocus& Whence, Control::EKeyword Keyword) : Node(StaticType, Whence), _Keyword(Keyword)
|
|
{
|
|
}
|
|
|
|
Control(const TSRef<Node>& Child, const SLocus& Whence, EKeyword Keyword)
|
|
: Control(Whence, Keyword)
|
|
{
|
|
AppendChild(Child);
|
|
}
|
|
|
|
const TSRef<Vst::Node>& GetReturnExpression() const { return _Children[0]; }
|
|
|
|
EKeyword _Keyword;
|
|
};
|
|
|
|
using ClauseArray = LArray<TSRef<Clause>>;
|
|
|
|
struct Macro : public Node
|
|
{
|
|
static const Vst::NodeType StaticType = NodeType::Macro;
|
|
|
|
Macro(SLocus Whence, const TSRef<Vst::Node>& MacroName, const uLang::TArray<TSRef<Vst::Clause>>& InChildren) : Node(StaticType, Whence)
|
|
{
|
|
_Children.Empty(InChildren.Num() + 1);
|
|
AppendChildInternal(MacroName);
|
|
for (const auto& Child : InChildren)
|
|
{
|
|
AppendChildInternal(Child);
|
|
}
|
|
}
|
|
|
|
const TSRef<Vst::Node>& GetName() const { return GetChildren()[0]; }
|
|
const TSRef<Vst::Clause>& GetClause(int32_t ClauseIndex) const { return GetChildren()[ClauseIndex + 1].As<Clause>(); }
|
|
TSRef<Clause> TakeClause(int32_t ClauseIndex, const TSPtr<Node> Replacement = TSPtr<Clause>())
|
|
{
|
|
return TakeChildAt(ClauseIndex + 1, Replacement).As<Clause>();
|
|
}
|
|
};
|
|
|
|
struct Parens : public Node
|
|
{
|
|
static const Vst::NodeType StaticType = NodeType::Parens;
|
|
|
|
Parens(SLocus Whence, Clause::EForm Form, const NodeArray& InChildren = {})
|
|
: Node(StaticType, Whence), _Form(Form)
|
|
{
|
|
_Children.Reserve(InChildren.Num());
|
|
for (const auto& Child : InChildren)
|
|
{
|
|
AppendChild(Child);
|
|
}
|
|
}
|
|
|
|
Parens(SLocus Whence, Clause::EForm Form, const uLang::TSRefArray<Node>& InChildren)
|
|
: Node(StaticType, Whence), _Form(Form)
|
|
{
|
|
_Children.Reserve(InChildren.Num());
|
|
for (const auto& Child : InChildren)
|
|
{
|
|
AppendChild(Child);
|
|
}
|
|
}
|
|
|
|
Clause::EForm GetForm() const { return _Form; }
|
|
|
|
private:
|
|
Clause::EForm _Form;
|
|
};
|
|
|
|
struct Commas : Node
|
|
{
|
|
static const Vst::NodeType StaticType = NodeType::Commas;
|
|
|
|
Commas(SLocus Whence, const uLang::TSRefArray<Node>& InChildren = {})
|
|
: Node(StaticType, Whence)
|
|
{
|
|
AppendChildren(InChildren);
|
|
}
|
|
};
|
|
|
|
struct Placeholder : public CAtom
|
|
{
|
|
static const Vst::NodeType StaticType = NodeType::Placeholder;
|
|
|
|
Placeholder(const SLocus& Whence) : CAtom("", StaticType, Whence) {};
|
|
Placeholder(const uLang::CUTF8StringView& CodeStr, const SLocus& Whence) : CAtom(CodeStr, StaticType, Whence) {};
|
|
};
|
|
|
|
struct ParseError : public Node
|
|
{
|
|
public:
|
|
static const Vst::NodeType StaticType = NodeType::ParseError;
|
|
|
|
ParseError(const char* Error, const SLocus& Whence)
|
|
: Node(NodeType::ParseError, Whence)
|
|
, _Error(Error)
|
|
{
|
|
}
|
|
|
|
const char* GetError() const { return _Error; }
|
|
|
|
void AddChild(const TSRef<Vst::Node>& InnerError) { AppendChildInternal(InnerError); }
|
|
|
|
private:
|
|
const char* _Error;
|
|
|
|
};
|
|
|
|
struct Escape : public Node
|
|
{
|
|
static const Vst::NodeType StaticType = NodeType::Escape;
|
|
|
|
Escape(const SLocus& Whence, const TSRef<Node>& Child)
|
|
: Node(StaticType, Whence)
|
|
{
|
|
AppendChild(Child);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* WHY THIS DESIGN ?
|
|
* -----------------
|
|
*
|
|
*
|
|
* (1) Readability. All the pretty printing code is co-located. It is easier
|
|
* to work on and to understand.
|
|
* (2) Design flexibility: Policy Pattern lets pipeline stages live outside
|
|
* of the node class.
|
|
*
|
|
* e.g. The PrettyPrinter is easier to understand because you can read and
|
|
* step through the code without leaving a single file.
|
|
* The implementation can live in a Toolchain module.
|
|
* You can easily swap out to a different pretty printer implementation
|
|
* without touching the Vst nodes.
|
|
*
|
|
* If changing this design (perhaps to improve performance), make sure
|
|
* to preserve both properties.
|
|
*
|
|
*
|
|
* NEEDS IMPROVEMENT
|
|
* ------------------
|
|
*
|
|
* When implementing a VisitPolicy, a programmer must implement every method for every type.
|
|
* This is not necessarily desirable. Especially true for certain semantic compiler passes.
|
|
**/
|
|
|
|
template <class VisitPolicy, typename ReturnType>
|
|
void Node::VisitWith(const TSRef<Vst::Node>& node, VisitPolicy& visit_policy)
|
|
{
|
|
const Vst::NodeType node_type = node->GetElementType();
|
|
switch (node_type)
|
|
{
|
|
#define VISIT_VSTNODE(NodeName, RequiredChildren, SupportsManyChildren, Precedence, ChildDeletionBehavior, IsCAtom) \
|
|
case Vst::NodeType::NodeName: \
|
|
return visit_policy.visit(node->As<Vst::NodeName>()); \
|
|
break;
|
|
VERSE_ENUM_VSTNODES(VISIT_VSTNODE)
|
|
#undef VISIT_VSTNODE
|
|
default:
|
|
return ReturnType();
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Verify all Vst::Node::StaticType are valid
|
|
#define VISIT_VSTNODE(NodeName, RequiredChildren, SupportsManyChildren, Precedence, ChildDeletionBehavior, IsCAtom) static_assert(NodeName::StaticType == NodeType::NodeName, #NodeName "::StaticType must be Vst::NodeType::" #NodeName);
|
|
VERSE_ENUM_VSTNODES(VISIT_VSTNODE)
|
|
#undef VISIT_VSTNODE
|
|
|
|
} // namespace Vst
|
|
|
|
struct SPathToNode
|
|
{
|
|
LArray<int32_t> Path;
|
|
LArray<int32_t> AuxPath;
|
|
|
|
int32_t PreCommentIndex;
|
|
int32_t PostCommentIndex;
|
|
// @sree @nicka This should be a UID into a Map of Snippets, or something. Anything but a ptr.
|
|
};
|
|
|
|
enum class EPrettyPrintBehaviour : uint32_t
|
|
{
|
|
Default = 0,
|
|
NewlinesAfterDefinitions = 1 << 0,
|
|
NewlinesAfterAttributes = 1 << 1,
|
|
NewlinesBetweenModuleMembers = 1 << 2,
|
|
UseVerticalFormForEnumerations = 1 << 3
|
|
};
|
|
ULANG_ENUM_BIT_FLAGS(EPrettyPrintBehaviour, inline);
|
|
|
|
/**
|
|
* Appends the text code version of this syntax snippet as closely as possible to how it
|
|
* was originally authored and if it was not authored in text originally (such as added
|
|
* via a VPL) then in as human readable canonical form as possible.
|
|
*
|
|
* @param Source string to append to
|
|
* @see VstSnippetAsCodeSourceChain(), VstSnippetAsCodeSource(), VstAsCodeSourceAppend()
|
|
**/
|
|
VERSECOMPILER_API void VstAsCodeSourceAppend(const TSRef<Vst::Node>& VstNode, CUTF8StringBuilder& Source);
|
|
VERSECOMPILER_API void VstAsCodeSourceAppend(const TSRef<Vst::Node>& VstNode, const EPrettyPrintBehaviour Flags, CUTF8StringBuilder& Source);
|
|
VERSECOMPILER_API void VstAsCodeSourceAppend(const TSRef<Vst::PrePostCall>& VstNode, CUTF8StringBuilder& OutSource, int32_t FirstChildIndex, int32_t LastChildIndex);
|
|
VERSECOMPILER_API void VstAsCodeSourceAppend(const TSRef<Vst::Clause>& VstClause, CUTF8StringBuilder& OutSource, int32_t InitialIndent, CUTF8String const& Separator);
|
|
|
|
ULANG_FORCEINLINE CUTF8String PrettyPrintVst(const TSRef<Vst::Node>& VstNode)
|
|
{
|
|
CUTF8StringBuilder Source; VstAsCodeSourceAppend(VstNode, Source); return Source.MoveToString();
|
|
}
|
|
|
|
ULANG_FORCEINLINE CUTF8String PrettyPrintVst(const TSRef<Vst::Node>& VstNode, const EPrettyPrintBehaviour Flags)
|
|
{
|
|
CUTF8StringBuilder Source; VstAsCodeSourceAppend(VstNode, Flags, Source); return Source.MoveToString();
|
|
}
|
|
|
|
ULANG_FORCEINLINE CUTF8String PrettyPrintVst(const TSRef<Vst::PrePostCall>& VstNode, int32_t FirstChildIndex, int32_t LastChildIndex)
|
|
{
|
|
CUTF8StringBuilder Source; VstAsCodeSourceAppend(VstNode, Source, FirstChildIndex, LastChildIndex); return Source.MoveToString();
|
|
}
|
|
|
|
ULANG_FORCEINLINE CUTF8String PrettyPrintClause(const TSRef<Vst::Clause>& VstClause, int32_t InitialIndent, CUTF8String const& Separator)
|
|
{
|
|
CUTF8StringBuilder OutSource;
|
|
VstAsCodeSourceAppend(VstClause, OutSource, InitialIndent, Separator);
|
|
return OutSource.MoveToString();
|
|
}
|
|
|
|
VERSECOMPILER_API bool GeneratePathToNode(const TSRef<Vst::Node>& Node, const TSRef<Vst::Snippet>& VstSnippet, SPathToNode& PathToNode);
|
|
VERSECOMPILER_API TSPtr<Vst::Node> GetNodeFromPath(const TSRef<Vst::Snippet>& VstSnippet, const SPathToNode& Path, bool bReturnParent=false);
|
|
|
|
/**
|
|
* Gets the signed distance between a locus and a row/column text position. The closest distance to the locus range is used.
|
|
*
|
|
* @param A The locus range to compare.
|
|
* @param B The text position to compare.
|
|
* @param SourceText The original source text that both the locus and position refer to ranges of.
|
|
*
|
|
* @return The signed distance between the two. If A comes before B, the result is positive and vice versa.
|
|
*/
|
|
VERSECOMPILER_API int32_t GetSignedDistanceBetweenPositionAndLocus(const SLocus& A, const SPosition& B, const CUTF8StringView& SourceText);
|
|
|
|
} // namespace Verse
|
|
|
|
#undef UE_API
|