WingTypes now uses the new tokenizer.
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingTypes.h"
|
||||
#include "Test_TypeToText.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UWing_Test_TypeToText : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="The type name to parse, e.g. 'Array<Vector>'"))
|
||||
FString Input;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Test the type parser by parsing a type name and dumping the resulting FEdGraphPinType.");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
FEdGraphPinType PinType;
|
||||
UWingTypes::Requirements Require;
|
||||
Require.BlueprintType = false;
|
||||
Require.Blueprintable = false;
|
||||
Require.AllowContainer = true;
|
||||
|
||||
bool OK = UWingTypes::TextToType(Input, PinType, Require);
|
||||
|
||||
auto& Out = UWingServer::GetPrintBuffer();
|
||||
Out.Appendf(TEXT("ParseResult: %s\n"), OK ? TEXT("OK") : TEXT("FAILED"));
|
||||
Out.Appendf(TEXT("PinCategory: %s\n"), *PinType.PinCategory.ToString());
|
||||
Out.Appendf(TEXT("PinSubCategory: %s\n"), *PinType.PinSubCategory.ToString());
|
||||
Out.Appendf(TEXT("PinSubCategoryObject: %s\n"),
|
||||
PinType.PinSubCategoryObject.IsValid()
|
||||
? *PinType.PinSubCategoryObject->GetPathName()
|
||||
: TEXT("(none)"));
|
||||
Out.Appendf(TEXT("ContainerType: %d\n"), (int32)PinType.ContainerType);
|
||||
if (PinType.IsMap())
|
||||
{
|
||||
Out.Appendf(TEXT("ValueTerminalCategory: %s\n"), *PinType.PinValueType.TerminalCategory.ToString());
|
||||
Out.Appendf(TEXT("ValueTerminalSubCategory: %s\n"), *PinType.PinValueType.TerminalSubCategory.ToString());
|
||||
Out.Appendf(TEXT("ValueTerminalSubCategoryObject: %s\n"),
|
||||
PinType.PinValueType.TerminalSubCategoryObject.IsValid()
|
||||
? *PinType.PinValueType.TerminalSubCategoryObject->GetPathName()
|
||||
: TEXT("(none)"));
|
||||
}
|
||||
|
||||
if (OK)
|
||||
{
|
||||
FString RoundTrip = UWingTypes::TypeToText(PinType);
|
||||
Out.Appendf(TEXT("TypeToText: %s\n"), RoundTrip.IsEmpty() ? TEXT("(empty)") : *RoundTrip);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -421,195 +421,142 @@ FString UWingTypes::TypeToTextOrDie(const UObject* Obj)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Tokenizer, Parser, and Resolve Short Name
|
||||
// Parser and Resolve Short Name
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void UWingTypes::PrintParseError(const TCHAR* Message)
|
||||
static const FName NAME_TypeArray(TEXT("Array"));
|
||||
static const FName NAME_TypeSet(TEXT("Set"));
|
||||
static const FName NAME_TypeMap(TEXT("Map"));
|
||||
static const FName NAME_TypeSoft(TEXT("Soft"));
|
||||
static const FName NAME_TypeClass(TEXT("Class"));
|
||||
static const FName NAME_TypeSoftClass(TEXT("SoftClass"));
|
||||
|
||||
void UWingTypes::PrintParseError(WingTokenizer& Tok, const TCHAR* Message)
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR parsing type '%s' — %s\n"), *ParseInput, Message);
|
||||
UWingServer::Printf(TEXT("ERROR parsing type '%s' — %s\n"), *Tok.GetInput(), Message);
|
||||
UWingServer::SuggestManual(WingManual::Section::Types);
|
||||
}
|
||||
|
||||
void UWingTypes::Tokenize(const FString& Input)
|
||||
bool UWingTypes::ParseEOF(WingTokenizer& Tok)
|
||||
{
|
||||
Tokens.Empty();
|
||||
Cursor = 0;
|
||||
|
||||
int32 i = 0;
|
||||
while (i < Input.Len())
|
||||
if (Tok.NextType() != 0)
|
||||
{
|
||||
TCHAR Ch = Input[i];
|
||||
|
||||
// Skip whitespace.
|
||||
if (FChar::IsWhitespace(Ch))
|
||||
{
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try to parse an identifier.
|
||||
int32 Start = i;
|
||||
while (i < Input.Len() && (FChar::IsAlnum(Input[i]) || Input[i] == '_'))
|
||||
++i;
|
||||
if (i > Start)
|
||||
{
|
||||
Tokens.Add(Input.Mid(Start, i - Start));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Anything that's not an identifier or whitespace
|
||||
// gets classified as a single-character token.
|
||||
Tokens.Add(FString(1, &Ch));
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
bool UWingTypes::TokenIs(const TCHAR* Text) const
|
||||
{
|
||||
if (Cursor >= Tokens.Num()) return false;
|
||||
return Tokens[Cursor].Equals(Text, ESearchCase::IgnoreCase);
|
||||
}
|
||||
|
||||
bool UWingTypes::TokenIs(const TCHAR* Text, TCHAR Next) const
|
||||
{
|
||||
if (Cursor >= Tokens.Num() - 1) return false;
|
||||
return (Tokens[Cursor].Equals(Text, ESearchCase::IgnoreCase)) &&
|
||||
(Tokens[Cursor+1].Len() == 1) &&
|
||||
(Tokens[Cursor+1][0] == Next);
|
||||
}
|
||||
|
||||
bool UWingTypes::TokenIs(TCHAR Next) const
|
||||
{
|
||||
if (Cursor >= Tokens.Num()) return false;
|
||||
return (Tokens[Cursor].Len() == 1) &&
|
||||
(Tokens[Cursor][0] == Next);
|
||||
}
|
||||
|
||||
bool UWingTypes::TokenIsID() const
|
||||
{
|
||||
if (Cursor >= Tokens.Num()) return false;
|
||||
return FChar::IsAlnum(Tokens[Cursor][0]);
|
||||
}
|
||||
|
||||
bool UWingTypes::ParseEOF()
|
||||
{
|
||||
if (Cursor != Tokens.Num())
|
||||
{
|
||||
PrintParseError(TEXT("extra tokens at end of input"));
|
||||
PrintParseError(Tok, TEXT("extra tokens at end of input"));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UWingTypes::ParseChar(TCHAR c)
|
||||
bool UWingTypes::ParseChar(WingTokenizer& Tok, TCHAR c)
|
||||
{
|
||||
if (!TokenIs(c))
|
||||
if (!Tok.TokenIs(c))
|
||||
{
|
||||
PrintParseError(*FString::Printf(TEXT("expected '%c'"), c));
|
||||
PrintParseError(Tok, *FString::Printf(TEXT("expected '%c'"), c));
|
||||
return false;
|
||||
}
|
||||
Cursor++;
|
||||
Tok.Advance();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UWingTypes::ParsePlainIdentifier(FEdGraphPinType& OutType)
|
||||
bool UWingTypes::ParsePlainIdentifier(WingTokenizer& Tok, FEdGraphPinType& OutType)
|
||||
{
|
||||
if (!TokenIsID())
|
||||
if (!Tok.TokenIs(Tok.Identifier))
|
||||
{
|
||||
PrintParseError(TEXT("expected identifier"));
|
||||
PrintParseError(Tok, TEXT("expected identifier"));
|
||||
return false;
|
||||
}
|
||||
FString Name = Tokens[Cursor++];
|
||||
return ResolveShortName(Name, OutType);
|
||||
FString Name = Tok.NextName().ToString();
|
||||
Tok.Advance();
|
||||
return ResolveShortName(Tok, Name, OutType);
|
||||
}
|
||||
|
||||
bool UWingTypes::ParseWrapped(FName Wrapper, FEdGraphPinType& OutType)
|
||||
bool UWingTypes::ParseWrapped(WingTokenizer& Tok, FName Wrapper, FEdGraphPinType& OutType)
|
||||
{
|
||||
Cursor++;
|
||||
if (!ParseChar('<')) return false;
|
||||
if (!ParsePlainIdentifier(OutType)) return false;
|
||||
Tok.Advance();
|
||||
if (!ParseChar(Tok, '<')) return false;
|
||||
if (!ParsePlainIdentifier(Tok, OutType)) return false;
|
||||
if (OutType.PinCategory != UEdGraphSchema_K2::PC_Object)
|
||||
{
|
||||
PrintParseError(*FString::Printf(TEXT("'%s' is not an object type"), *OutType.PinSubCategoryObject->GetName()));
|
||||
PrintParseError(Tok, *FString::Printf(TEXT("'%s' is not an object type"), *OutType.PinSubCategoryObject->GetName()));
|
||||
return false;
|
||||
}
|
||||
if (!ParseChar('>')) return false;
|
||||
if (!ParseChar(Tok, '>')) return false;
|
||||
OutType.PinCategory = Wrapper;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UWingTypes::ParseMaybeWrapped(FEdGraphPinType& OutType)
|
||||
bool UWingTypes::ParseMaybeWrapped(WingTokenizer& Tok, FEdGraphPinType& OutType)
|
||||
{
|
||||
if (TokenIs(TEXT("Soft"), '<'))
|
||||
if (Tok.TokenIs(NAME_TypeSoft, '<'))
|
||||
{
|
||||
return ParseWrapped(UEdGraphSchema_K2::PC_SoftObject, OutType);
|
||||
return ParseWrapped(Tok, UEdGraphSchema_K2::PC_SoftObject, OutType);
|
||||
}
|
||||
else if (TokenIs(TEXT("Class"), '<'))
|
||||
else if (Tok.TokenIs(NAME_TypeClass, '<'))
|
||||
{
|
||||
return ParseWrapped(UEdGraphSchema_K2::PC_Class, OutType);
|
||||
return ParseWrapped(Tok, UEdGraphSchema_K2::PC_Class, OutType);
|
||||
}
|
||||
else if (TokenIs(TEXT("SoftClass"), '<'))
|
||||
else if (Tok.TokenIs(NAME_TypeSoftClass, '<'))
|
||||
{
|
||||
return ParseWrapped(UEdGraphSchema_K2::PC_SoftClass, OutType);
|
||||
return ParseWrapped(Tok, UEdGraphSchema_K2::PC_SoftClass, OutType);
|
||||
}
|
||||
else return ParsePlainIdentifier(OutType);
|
||||
else return ParsePlainIdentifier(Tok, OutType);
|
||||
}
|
||||
|
||||
bool UWingTypes::ParseArrayOrSet(FEdGraphPinType& OutType)
|
||||
bool UWingTypes::ParseArrayOrSet(WingTokenizer& Tok, FEdGraphPinType& OutType)
|
||||
{
|
||||
Cursor++;
|
||||
if (!ParseChar('<')) return false;
|
||||
if (!ParseMaybeWrapped(OutType)) return false;
|
||||
if (!ParseChar('>')) return false;
|
||||
Tok.Advance();
|
||||
if (!ParseChar(Tok, '<')) return false;
|
||||
if (!ParseMaybeWrapped(Tok, OutType)) return false;
|
||||
if (!ParseChar(Tok, '>')) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UWingTypes::ParseMap(FEdGraphPinType& OutType)
|
||||
bool UWingTypes::ParseMap(WingTokenizer& Tok, FEdGraphPinType& OutType)
|
||||
{
|
||||
Cursor++;
|
||||
if (!ParseChar('<')) return false;
|
||||
if (!ParsePlainIdentifier(OutType)) return false;
|
||||
if (!ParseChar(',')) return false;
|
||||
Tok.Advance();
|
||||
if (!ParseChar(Tok, '<')) return false;
|
||||
if (!ParsePlainIdentifier(Tok, OutType)) return false;
|
||||
if (!ParseChar(Tok, ',')) return false;
|
||||
FEdGraphPinType ValueType;
|
||||
if (!ParseMaybeWrapped(ValueType)) return false;
|
||||
if (!ParseMaybeWrapped(Tok, ValueType)) return false;
|
||||
OutType.PinValueType.TerminalCategory = ValueType.PinCategory;
|
||||
OutType.PinValueType.TerminalSubCategory = ValueType.PinSubCategory;
|
||||
OutType.PinValueType.TerminalSubCategoryObject = ValueType.PinSubCategoryObject;
|
||||
if (!ParseChar('>')) return false;
|
||||
if (!ParseChar(Tok, '>')) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UWingTypes::ParseType(FEdGraphPinType& OutType)
|
||||
bool UWingTypes::ParseType(WingTokenizer& Tok, FEdGraphPinType& OutType)
|
||||
{
|
||||
if (TokenIs(TEXT("Array"), '<'))
|
||||
if (Tok.TokenIs(NAME_TypeArray, '<'))
|
||||
{
|
||||
OutType.ContainerType = EPinContainerType::Array;
|
||||
if (!ParseArrayOrSet(OutType)) return false;
|
||||
}
|
||||
else if (TokenIs(TEXT("Set"), '<'))
|
||||
if (!ParseArrayOrSet(Tok, OutType)) return false;
|
||||
}
|
||||
else if (Tok.TokenIs(NAME_TypeSet, '<'))
|
||||
{
|
||||
OutType.ContainerType = EPinContainerType::Set;
|
||||
if (!ParseArrayOrSet(OutType)) return false;
|
||||
if (!ParseArrayOrSet(Tok, OutType)) return false;
|
||||
}
|
||||
else if (TokenIs(TEXT("Map"), '<'))
|
||||
else if (Tok.TokenIs(NAME_TypeMap, '<'))
|
||||
{
|
||||
OutType.ContainerType = EPinContainerType::Map;
|
||||
if (!ParseMap(OutType)) return false;
|
||||
if (!ParseMap(Tok, OutType)) return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!ParseMaybeWrapped(OutType)) return false;
|
||||
if (!ParseMaybeWrapped(Tok, OutType)) return false;
|
||||
}
|
||||
if (!ParseEOF()) return false;
|
||||
if (!ParseEOF(Tok)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UWingTypes::ResolveShortName(const FString &Name, FEdGraphPinType &OutType)
|
||||
bool UWingTypes::ResolveShortName(WingTokenizer& Tok, const FString &Name, FEdGraphPinType &OutType)
|
||||
{
|
||||
Info* TypeInfo = ShortToInfo.Find(Name.ToLower());
|
||||
if (!TypeInfo)
|
||||
{
|
||||
PrintParseError(*FString::Printf(TEXT("unrecognized type '%s'"), *Name));
|
||||
PrintParseError(Tok, *FString::Printf(TEXT("unrecognized type '%s'"), *Name));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -625,7 +572,7 @@ bool UWingTypes::ResolveShortName(const FString &Name, FEdGraphPinType &OutType)
|
||||
UObject* Obj = LoadObject<UObject>(nullptr, *TypeInfo->PinSubCategoryObject);
|
||||
if (!Obj)
|
||||
{
|
||||
PrintParseError(*FString::Printf(TEXT("failed to load type '%s' at path '%s'"), *Name, *TypeInfo->PinSubCategoryObject));
|
||||
PrintParseError(Tok, *FString::Printf(TEXT("failed to load type '%s' at path '%s'"), *Name, *TypeInfo->PinSubCategoryObject));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -676,10 +623,9 @@ bool UWingTypes::TextToType(const FString& Text, FEdGraphPinType& OutPinType, co
|
||||
|
||||
UWingTypes* Types = GEditor->GetEditorSubsystem<UWingTypes>();
|
||||
check(Types);
|
||||
Types->ParseInput = Text;
|
||||
Types->Tokenize(Text);
|
||||
WingTokenizer Tok(Text);
|
||||
OutPinType = FEdGraphPinType();
|
||||
if (!Types->ParseType(OutPinType))
|
||||
if (!Types->ParseType(Tok, OutPinType))
|
||||
{
|
||||
OutPinType = FEdGraphPinType(); return false;
|
||||
}
|
||||
|
||||
@@ -123,10 +123,13 @@ struct WingTokenizer
|
||||
// Advance the cursor. Don't move past the two sentinels.
|
||||
void Advance() { int I = Next-Tokens.GetData(); if (I + 2 < Tokens.Num()) Next++; }
|
||||
|
||||
// Get the original input string.
|
||||
const FString& GetInput() const { return Input; }
|
||||
|
||||
// Tokenize a line of input. The tokens are stored in
|
||||
// the token array, and the cursor is positioned on the first
|
||||
// token. If there's an error, the error is stored in the
|
||||
// error field and the cursor points to the empty token.
|
||||
// error field and the cursor points to the empty token.
|
||||
WingTokenizer(const FString& Input);
|
||||
|
||||
// Print all tokens into a string builder for debugging.
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include "EdGraph/EdGraphPin.h"
|
||||
#include "WingTypes.generated.h"
|
||||
|
||||
struct WingTokenizer;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// UWingTypes — converts between FEdGraphPinType and a concise C++-like string.
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -112,25 +114,20 @@ public:
|
||||
|
||||
private:
|
||||
// ---------------------------------------------------------------------------
|
||||
// Tokenizer, Parser, and ResolveShortName
|
||||
// Parser and ResolveShortName
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void Tokenize(const FString& Input);
|
||||
bool TokenIs(const TCHAR* Text) const;
|
||||
bool TokenIs(const TCHAR* Text, TCHAR ch) const;
|
||||
bool TokenIs(TCHAR Ch) const;
|
||||
bool TokenIsID() const;
|
||||
static void PrintParseError(WingTokenizer& Tok, const TCHAR* Message);
|
||||
static bool ParseEOF(WingTokenizer& Tok);
|
||||
static bool ParseChar(WingTokenizer& Tok, TCHAR c);
|
||||
bool ParsePlainIdentifier(WingTokenizer& Tok, FEdGraphPinType& OutType);
|
||||
bool ParseWrapped(WingTokenizer& Tok, FName Wrapper, FEdGraphPinType& OutType);
|
||||
bool ParseMaybeWrapped(WingTokenizer& Tok, FEdGraphPinType& OutType);
|
||||
bool ParseArrayOrSet(WingTokenizer& Tok, FEdGraphPinType& OutType);
|
||||
bool ParseMap(WingTokenizer& Tok, FEdGraphPinType& OutType);
|
||||
bool ParseType(WingTokenizer& Tok, FEdGraphPinType& OutType);
|
||||
|
||||
bool ParseEOF();
|
||||
bool ParseChar(TCHAR c);
|
||||
bool ParsePlainIdentifier(FEdGraphPinType& OutType);
|
||||
bool ParseWrapped(FName Wrapper, FEdGraphPinType& OutType);
|
||||
bool ParseMaybeWrapped(FEdGraphPinType& OutType);
|
||||
bool ParseArrayOrSet(FEdGraphPinType& OutType);
|
||||
bool ParseMap(FEdGraphPinType& OutType);
|
||||
bool ParseType(FEdGraphPinType& OutType);
|
||||
|
||||
bool ResolveShortName(const FString &Name, FEdGraphPinType &OutType);
|
||||
bool ResolveShortName(WingTokenizer& Tok, const FString &Name, FEdGraphPinType &OutType);
|
||||
|
||||
public:
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -180,9 +177,4 @@ private:
|
||||
// Given a class path, return the short name, e.g. "/Script/CoreUObject.Vector" -> "Vector"
|
||||
TMap<FString, FString> PathToShort;
|
||||
|
||||
// These fields are only used during the parsing of a type.
|
||||
TArray<FString> Tokens;
|
||||
int32 Cursor = 0;
|
||||
FString ParseInput;
|
||||
void PrintParseError(const TCHAR* Message);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user