TValueOrError<FString, FExpressionError> FStringFormatter::FormatInternal(const TCHAR* InExpression, const TMap<FString, FStringFormatArg>& Args, bool bStrict) const { TValueOrError<TArray<FExpressionToken>, FExpressionError> Result = ExpressionParser::Lex(InExpression, bStrict ? StrictNamedDefinitions : NamedDefinitions); if (!Result.IsValid()) { return MakeError(Result.StealError()); } TArray<FExpressionToken>& Tokens = Result.GetValue(); if (Tokens.Num() == 0) { return MakeValue(InExpression); } // This code deliberately tries to reallocate as little as possible FString Formatted; Formatted.Reserve(Tokens.Last().Context.GetTokenEndPos() - InExpression); for (auto& Token : Tokens) { if (const auto* Literal = Token.Node.Cast<FStringLiteral>()) { Formatted.AppendChars(Literal->String.GetTokenStartPos(), Literal->Len); } else if (auto* Escaped = Token.Node.Cast<FEscapedCharacter>()) { Formatted.AppendChar(Escaped->Character); } else if (const auto* FormatToken = Token.Node.Cast<FFormatSpecifier>()) { const FStringFormatArg* Arg = nullptr; for (auto& Pair : Args) { if (Pair.Key.Len() == FormatToken->Len && FCString::Strnicmp(FormatToken->Identifier.GetTokenStartPos(), *Pair.Key, FormatToken->Len) == 0) { Arg = &Pair.Value; break; } } if (Arg) { AppendToString(*Arg, Formatted); } else if (bStrict) { return MakeError(FText::Format(LOCTEXT("UndefinedFormatSpecifier", "Undefined format token: {0}"), FText::FromString(FormatToken->Identifier.GetString()))); } else { // No replacement found, so just add the original token string const int32 Length = FormatToken->EntireToken.GetTokenEndPos() - FormatToken->EntireToken.GetTokenStartPos(); Formatted.AppendChars(FormatToken->EntireToken.GetTokenStartPos(), Length); } } } return MakeValue(Formatted); }
TValueOrError<FString, FExpressionError> FStringFormatter::FormatInternal(const TCHAR* InExpression, const TArray<FStringFormatArg>& Args, bool bStrict) const { TValueOrError<TArray<FExpressionToken>, FExpressionError> Result = ExpressionParser::Lex(InExpression, bStrict ? StrictOrderedDefinitions : OrderedDefinitions); if (!Result.IsValid()) { return MakeError(Result.StealError()); } TArray<FExpressionToken>& Tokens = Result.GetValue(); if (Tokens.Num() == 0) { return MakeValue(InExpression); } // This code deliberately tries to reallocate as little as possible FString Formatted; Formatted.Reserve(Tokens.Last().Context.GetTokenEndPos() - InExpression); for (auto& Token : Tokens) { if (const auto* Literal = Token.Node.Cast<FStringLiteral>()) { Formatted.AppendChars(Literal->String.GetTokenStartPos(), Literal->Len); } else if (auto* Escaped = Token.Node.Cast<FEscapedCharacter>()) { Formatted.AppendChar(Escaped->Character); } else if (const auto* IndexToken = Token.Node.Cast<FIndexSpecifier>()) { if (Args.IsValidIndex(IndexToken->Index)) { AppendToString(Args[IndexToken->Index], Formatted); } else if (bStrict) { return MakeError(FText::Format(LOCTEXT("InvalidArgumentIndex", "Invalid argument index: {0}"), FText::AsNumber(IndexToken->Index))); } else { // No replacement found, so just add the original token string const int32 Length = IndexToken->EntireToken.GetTokenEndPos() - IndexToken->EntireToken.GetTokenStartPos(); Formatted.AppendChars(IndexToken->EntireToken.GetTokenStartPos(), Length); } } } return MakeValue(Formatted); }