Files
UnrealEngine/Engine/Source/Editor/BlueprintGraph/Private/BasicTokenParser.h
2025-05-18 13:04:45 +08:00

413 lines
12 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Containers/EnumAsByte.h"
#include "Containers/UnrealString.h"
#include "HAL/PlatformMath.h"
#include "Internationalization/Text.h"
#include "Misc/CString.h"
#include "UObject/NameTypes.h"
#include "UObject/Script.h"
#include "UObject/Stack.h"
/*******************************************************************************
* FBasicToken
*******************************************************************************/
/**
* Information regarding a token that was parsed from some expression string
* (type, value, etc.).
*/
class FBasicToken //: public FPropertyBase
{
public:
FBasicToken(); // invokes InitToken()
/**
* Resets this token, clearing any details that were previously set (allows
* you to init the token to some predefined value type).
*
* @param InConstType Optional param that lets you init this to some known value type.
*/
void InitToken(EPropertyType InConstType = CPT_None);
/**
* Copies the properties from another token into this one.
*
* @param Other The token to copy properties from.
*/
void Clone(const FBasicToken& Other);
//--------------------------------------
// Queries
//--------------------------------------
/** Determines if this token matches the specified name.*/
bool Matches(const FName& Name) const;
/** Determines if this token matches the specified string. */
bool Matches(const TCHAR* Str, ESearchCase::Type SearchCase = ESearchCase::IgnoreCase) const;
/** Determines if this token has the specified string as a prefix. */
bool StartsWith(const TCHAR* Str, bool bCaseSensitive = false) const;
/** Determines if this token has a boolean ConstantType.*/
bool IsBool() const;
//--------------------------------------
// Constant value setters
//--------------------------------------
void SetConstInt(int32 InInt);
void SetConstBool(bool InBool);
void SetConstDouble(double InDouble);
void SetConstName(FName InName);
void SetConstString(TCHAR* InString, int32 MaxLength=MAX_STRING_CONST_SIZE);
void SetGuid(TCHAR* InString, int32 MaxLength=MAX_STRING_CONST_SIZE);
//--------------------------------------
// Constant value getters
//--------------------------------------
/**
* Retrieves an int value from this token (if it is a constant
* int/byte/double/etc. type).
*
* @param ValOut Will hold the integer value if this is successfully.
* @return True if this represents a constant that can be converted to an integer, otherwise false (ValOut is invalid).
*/
bool GetConstInt(int32& ValOut) const;
/**
* If this represents a constant value, then this returns a string
* representing the value of said constant (formated according to the type).
*
* @return A default error string if this is not a constant (or an
* unsupported type), else a string containing the constant value.
*/
FString GetConstantValue() const;
public:
enum ETokenType
{
TOKEN_None = 0x00, // No token.
TOKEN_Identifier = 0x01, // Alphanumeric identifier.
TOKEN_Symbol = 0x02, // Symbol.
TOKEN_Const = 0x03, // A constant.
TOKEN_Guid = 0x04, // A variable guid
TOKEN_Max = 0x0D
};
/** Type of this token. */
ETokenType TokenType;
/** Name of this token. */
FName TokenName;
/** Starting position in expression stream where this token came from. */
int32 StartPos;
/** Starting line in the expression. */
int32 StartLine;
/** Always valid. */
TCHAR Identifier[NAME_SIZE];
/** Only valid when TokenType is TOKEN_Const */
EPropertyType ConstantType;
/* TOKEN_Const values */
union
{
uint8 Byte; // If CPT_Byte.
int32 Int; // If CPT_Int.
bool NativeBool; // If CPT_Bool
double Double; // If CPT_Double.
uint8 NameBytes[sizeof(FScriptName)]; // If CPT_Name.
TCHAR String[MAX_STRING_CONST_SIZE]; // If CPT_String
};
};
/*******************************************************************************
* FBasicTokenParser
*******************************************************************************/
/**
* Provides a base set of expression parsing functionality for sub-classes to
* utilize when tokenizing a TCHAR stream (discerns operators from literals/
* variables, strips out comments, etc.).
*/
class FBasicTokenParser
{
protected:
/**
* FBasicTokenParser is conceptually abstract, and should only be instantiated
* through a subclass.
*/
FBasicTokenParser() {}
protected:
/**
* Sets up this to start parsing a new expression string and resets any
* state lingering from the previous one.
*
* @param SourceBuffer The expression string you wish to parse next.
* @param StartingLineNumber The base line number to start counting from as this parses.
*/
void ResetParser(const TCHAR* SourceBuffer, int32 StartingLineNumber = 1);
/**
* Clears out the most recent parsed comment.
*/
void ClearCachedComment();
//--------------------------------------
// Tokenizing functions
//--------------------------------------
/**
* Gets the next token from the input stream, advancing the variables which
* keep track of the current input position and line.
*
* @param Token [out] Receives the value of the parsed text; if Token is
* pre-initialized, special logic is performed to attempt
* to evaluated Token in the context of that type. Useful
* for distinguishing between ambiguous symbols like enum tags.
* @param bNoConsts Specify true to indicate that tokens representing
* literal const values are not allowed.
* @return True if a token was successfully processed, false otherwise.
*/
bool GetToken(FBasicToken& Token, bool bNoConsts = false);
/**
* Put all text from the current position up to either EOL or the StopToken
* into Token. Advances the compiler's current position.
*
* @param Token [out] Will contain the text that was parsed.
* @param StopChar Stop processing when this character is reached.
*
* @return True if a token was successfully processed, false otherwise.
*/
bool GetRawToken(FBasicToken& Token, TCHAR StopChar = TCHAR('\n'));
/**
* Doesn't quit if StopChar is found inside a double-quoted string, but does
* NOT support quote escapes.
*
* @return True if a token was successfully processed, false otherwise.
*/
bool GetRawTokenRespectingQuotes(FBasicToken& Token, TCHAR StopChar = TCHAR('\n'));
/**
* Parse out an identifier from the expression stream.
*
* @return True if a token was successfully processed, false otherwise.
*/
bool GetIdentifier(FBasicToken& Token, bool bNoConsts = false);
/**
* Parse out a symbol from the expression stream.
*
* @return True if a token was successfully processed, false otherwise.
*/
bool GetSymbol(FBasicToken& Token);
/**
* Parse out an int constant from the expression stream.
*
* @return True if a int was successfully processed, false otherwise.
*/
bool GetConstInt(int32& Result, const TCHAR* ErrorContext = NULL);
/**
* Rolls back the line number and moves the parsing pointer back to where
* the specified token started.
*/
void UngetToken(FBasicToken& Token);
//--------------------------------------
// Low-level parsing functions
//--------------------------------------
/**
* Look at a single character from the input stream and return it, or 0=end.
* Has no effect on the input stream.
*/
TCHAR PeekChar();
/**
* Get a single character from the input stream and return it, or 0=end.
*/
TCHAR GetChar(bool bLiteral = false);
/**
* Skip past all spaces, tabs, and comments in the input stream.
*/
TCHAR GetLeadingChar();
/**
* Unget the previous character retrieved with GetChar().
*/
void UngetChar();
//--------------------------------------
// Match queries
//--------------------------------------
/**
* Determines if the next token in the stream is an identifier that matches
* the specified FName (and consumes it if it does). This is used primarily
* for checking for required symbols during parsing.
*
* @param Match The name you wish to check for.
* @return True if a match was consumed, false otherwise.
*/
bool MatchIdentifier(FName Match);
/**
* Determines if the next token in the stream is an identifier that matches
* the specified string (and consumes it if it does).
*/
bool MatchIdentifier(const TCHAR* Match);
/**
* Determines if the next token in the stream is an identifier that matches
* the specified name (does NOT consume it).
*/
bool PeekIdentifier(FName Match);
/**
* Determines if the next token in the stream is an identifier that matches
* the specified string (does NOT consume it).
*/
bool PeekIdentifier(const TCHAR* Match);
/**
* Determines if the next token in the stream is a symbol that matches
* the specified string (and consumes it if it does).
*/
bool MatchSymbol(const TCHAR* Match);
/**
* Determines if the next token in the stream is a symbol that matches
* the specified string (does NOT consume it).
*/
bool PeekSymbol(const TCHAR* Match);
//--------------------------------------
// Requiring checks
//--------------------------------------
/**
* Ensures that the next token in the stream is an identifier that matches
* the specified name (and errors out if it isn't).
*/
bool RequireIdentifier(FName Match, const TCHAR* ErrorContext);
/**
* Ensures that the next token in the stream is an identifier that matches
* the specified string (and errors out if it isn't).
*/
bool RequireIdentifier(const TCHAR* Match, const TCHAR* ErrorContext);
/**
* Ensures that the next token in the stream is a symbol that matches
* the specified string (and errors out if it isn't).
*/
bool RequireSymbol(const TCHAR* Match, const TCHAR* ErrorContext);
/**
* Ensures that the next token in the stream is a semi-colon character (and
* errors out if it isn't).
*/
bool RequireSemi();
//--------------------------------------
// Error state
//--------------------------------------
public:
/**
* Checks to see if an error has been caught by the internal error state.
*/
bool IsValid() const;
/** A struct for easily describing a specific error that occurred while parsing */
struct FErrorState
{
enum EErrorType
{
NoError,
ParseError, // generic error that occurred while tokenizing the expression
RequireError, // explicit error that was invoked through a call to one of the Require methods
SubClassErrorStart, // the starting value for a subclass's
};
FErrorState()
: State(NoError)
{}
/** Will take the current error and logs it, optionally fatally */
void Throw(bool bLogFatal) const;
/** Should match up with EErrorType, but is extensible by subclasses
(comes from a subclass if >= SubClassErrorStart) */
TEnumAsByte<EErrorType> State;
/** A detailed localized string, describing what exactly went wrong
(meant to be user facing) */
FText Description;
};
/**
* Retrieves the parser's internal error state (so that users can interpret
* what might have gone wrong while tokenizing).
*/
const FErrorState& GetErrorState() const;
protected:
/**
* Takes the provided error and throws it (if the ErrorCode isn't
* FErrorState::NoError).
*
* @param ErrorCode Should match up with FErrorState::EErrorType, but is
* meant to be extensible by subclasses (so it could be
* out of that range).
* @param Description A detailed localized string, describing what exactly
* went wrong (meant to be user facing).
* @param bLogFatal Optional param where you can make a fatal log along
* with the thrown error (in case error handling is turned off).
*/
void SetError(TEnumAsByte<FErrorState::EErrorType> ErrorCode, FText Description, bool bLogFatal = false);
/**
* Resets the internal error state, such that the parser can continue on.
*/
void ClearErrorState();
protected:
/** Input text */
const TCHAR* Input;
/** Length of input text */
int32 InputLen;
/** Current position in text */
int32 InputPos;
/** Current line in text */
int32 InputLine;
/** Position previous to last GetChar() call */
int32 PrevPos;
/** Line previous to last GetChar() call. */
int32 PrevLine;
/** Previous comment parsed by GetChar() call. */
FString PrevComment;
/** Keeps track of the last error to occur */
FErrorState CurrentError;
};