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); }
FExpressionResult Evaluate(const TCHAR* InExpression, const FTokenDefinitions& InTokenDefinitions, const FExpressionGrammar& InGrammar, const IOperatorEvaluationEnvironment& InEnvironment) { TValueOrError<TArray<FCompiledToken>, FExpressionError> CompilationResult = Compile(InExpression, InTokenDefinitions, InGrammar); if (!CompilationResult.IsValid()) { return MakeError(CompilationResult.GetError()); } return Evaluate(CompilationResult.GetValue(), InEnvironment); }
CompileResultType Compile(const TCHAR* InExpression, const FTokenDefinitions& InTokenDefinitions, const FExpressionGrammar& InGrammar) { TValueOrError<TArray<FExpressionToken>, FExpressionError> Result = Lex(InExpression, InTokenDefinitions); if (!Result.IsValid()) { return MakeError(Result.GetError()); } return Compile(MoveTemp(Result.GetValue()), InGrammar); }
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); }
void FTextFormatData::Compile_NoLock() { LexedExpression.Reset(); if (SourceType == ESourceType::Text) { SourceExpression = SourceText.ToString(); CompiledTextSnapshot = FTextSnapshot(SourceText); } CompiledExpressionType = FTextFormat::EExpressionType::Simple; BaseFormatStringLength = 0; FormatArgumentEstimateMultiplier = 1; TValueOrError<TArray<FExpressionToken>, FExpressionError> Result = ExpressionParser::Lex(*SourceExpression, FTextFormatter::Get().GetTextFormatDefinitions()); bool bValidExpression = Result.IsValid(); if (bValidExpression) { LexedExpression = Result.StealValue(); // Quickly make sure the tokens are valid (argument modifiers may only follow an argument token) for (int32 TokenIndex = 0; TokenIndex < LexedExpression.Num(); ++TokenIndex) { const FExpressionToken& Token = LexedExpression[TokenIndex]; if (const auto* Literal = Token.Node.Cast<TextFormatTokens::FStringLiteral>()) { BaseFormatStringLength += Literal->StringLen; } else if (auto* Escaped = Token.Node.Cast<TextFormatTokens::FEscapedCharacter>()) { BaseFormatStringLength += 1; } else if (const auto* ArgumentToken = Token.Node.Cast<TextFormatTokens::FArgumentTokenSpecifier>()) { CompiledExpressionType = FTextFormat::EExpressionType::Complex; if (LexedExpression.IsValidIndex(TokenIndex + 1)) { const FExpressionToken& NextToken = LexedExpression[TokenIndex + 1]; // Peek to see if the next token is an argument modifier if (const auto* ArgumentModifierToken = NextToken.Node.Cast<TextFormatTokens::FArgumentModifierTokenSpecifier>()) { int32 ArgModLength = 0; bool ArgModUsesFormatArgs = false; ArgumentModifierToken->TextFormatArgumentModifier->EstimateLength(ArgModLength, ArgModUsesFormatArgs); BaseFormatStringLength += ArgModLength; FormatArgumentEstimateMultiplier += (ArgModUsesFormatArgs) ? 1 : 0; ++TokenIndex; // walk over the argument token so that the next iteration will skip over the argument modifier continue; } } } else if (Token.Node.Cast<TextFormatTokens::FArgumentModifierTokenSpecifier>()) { // Unexpected argument modifier token! const FText ErrorSourceText = FText::FromString(Token.Context.GetString()); Result = MakeError(FExpressionError(FText::Format(LOCTEXT("UnexpectedArgumentModifierToken", "Unexpected 'argument modifier' token: {0} (token started at index {1})"), ErrorSourceText, Token.Context.GetCharacterIndex()))); bValidExpression = false; break; } } } if (!bValidExpression) { LexedExpression.Reset(); CompiledExpressionType = FTextFormat::EExpressionType::Invalid; UE_LOG(LogTextFormatter, Warning, TEXT("Failed to compile text format string '%s': %s"), *SourceExpression, *Result.GetError().Text.ToString()); } }