Files
UnrealEngine/Engine/Source/Runtime/VerseCompiler/Private/uLang/VerseLocalizationGen.cpp
2025-05-18 13:04:45 +08:00

230 lines
7.0 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "uLang/VerseLocalizationGen.h"
#include "uLang/Common/Text/FilePathUtils.h"
#include "uLang/Diagnostics/Diagnostics.h"
#include "uLang/Semantics/SemanticProgram.h"
#include "uLang/Syntax/VstNode.h"
#include "uLang/Toolchain/CommandLine.h"
#include <cmath>
#include <cstdio>
namespace {
namespace Private {
using namespace uLang;
struct FImpl : SAstVisitor
{
const CSemanticProgram& Program;
CDiagnostics& Diagnostics;
TArray<FSolLocalizationInfo>& LocalizationInfo;
TArray<FSolLocalizationInfo>& StringInfo;
explicit FImpl(const CSemanticProgram& Program,
CDiagnostics& Diagnostics,
TArray<FSolLocalizationInfo>& LocalizationInfo,
TArray<FSolLocalizationInfo>& StringInfo)
: Program(Program)
, Diagnostics(Diagnostics)
, LocalizationInfo(LocalizationInfo)
, StringInfo(StringInfo)
{
}
void ScrapeProgram()
{
Visit(*Program._AstProject);
}
//-------------------------------------------------------------------------------------------------
template<typename... ResultArgsType>
void AppendGlitch(const CAstNode& AstNode, ResultArgsType&&... ResultArgs)
{
SGlitchResult Glitch(ForwardArg<ResultArgsType>(ResultArgs)...);
if (AstNode.GetNodeType() == Cases<EAstNodeType::Context_Package, EAstNodeType::Definition_Module>
&& (!AstNode.GetMappedVstNode() || !AstNode.GetMappedVstNode()->Whence().IsValid()))
{
Diagnostics.AppendGlitch(Move(Glitch), SGlitchLocus());
}
else
{
ULANG_ASSERTF(
AstNode.GetMappedVstNode() && AstNode.GetMappedVstNode()->Whence().IsValid(),
"Expected valid whence for node used as glitch locus on %s id:%i - %s",
AstNode.GetErrorDesc().AsCString(),
GetDiagnosticInfo(Glitch._Id).ReferenceCode,
Glitch._Message.AsCString());
Diagnostics.AppendGlitch(Move(Glitch), SGlitchLocus(&AstNode));
}
}
//-------------------------------------------------------------------------------------------------
void ScrapeString(const CExprString& StringAst)
{
SGlitchLocus GlitchLocus(&StringAst);
StringInfo.Emplace(StringAst._String, GlitchLocus.AsFormattedString());
}
//-------------------------------------------------------------------------------------------------
void ScrapeLocalization(const CExprDefinition& DefinitionAst)
{
TSPtr<CExpressionBase> Value = DefinitionAst.Value();
if (!Value)
{
/* We allowed this in an earlier version of the compiler in the case of a localization in an
* abstract class, and will allow it now until we have support for breaking changes for
* to-be-published projects, without breaking already published projects (SOL-5053).
AppendGlitch(
DefinitionAst,
EDiagnostic::ErrSyntax_InternalError,
"Value is missing for localization.");
*/
return;
}
if (Value->GetNodeType() == EAstNodeType::External)
{ // We are in a digest, and there is nothing to see here
return;
}
if (Value->GetNodeType() != EAstNodeType::Invoke_Type)
{
AppendGlitch(
DefinitionAst,
EDiagnostic::ErrSyntax_InternalError,
"Expected a type invocation here.");
return;
}
Value = static_cast<CExprInvokeType&>(*Value)._Argument;
if (Value->GetNodeType() != EAstNodeType::Invoke_Invocation)
{
AppendGlitch(
DefinitionAst,
EDiagnostic::ErrSyntax_InternalError,
"Expected an invocation for localization.");
return;
}
CExprInvocation& Invocation = static_cast<CExprInvocation&>(*Value);
if (!Invocation.GetArgument())
{
AppendGlitch(
DefinitionAst,
EDiagnostic::ErrSyntax_InternalError,
"No arguments for localization.");
return;
}
if (Invocation.GetArgument()->GetNodeType() != EAstNodeType::Invoke_MakeTuple)
{
AppendGlitch(
DefinitionAst,
EDiagnostic::ErrSyntax_InternalError,
"Expected a tuple for localization,");
return;
}
CExprMakeTuple& MakeTuple = static_cast<CExprMakeTuple&>(*Invocation.GetArgument());
if (MakeTuple.SubExprNum() < 2)
{
AppendGlitch(
DefinitionAst,
EDiagnostic::ErrSyntax_InternalError,
"Too few arguments for localization");
return;
}
if (!MakeTuple.GetSubExprs()[0])
{
AppendGlitch(
DefinitionAst,
EDiagnostic::ErrSyntax_InternalError,
"No path for localization.");
return;
}
if (MakeTuple.GetSubExprs()[0]->GetNodeType() != EAstNodeType::Literal_String)
{
AppendGlitch(
DefinitionAst,
EDiagnostic::ErrSyntax_InternalError,
"Localization path must be a string at this point.");
return;
}
const CUTF8String& Path = static_cast<CExprString&>(*MakeTuple.GetSubExprs()[0])._String;
if (!MakeTuple.GetSubExprs()[0])
{
AppendGlitch(
DefinitionAst,
EDiagnostic::ErrSyntax_InternalError,
"No default for localization.");
return;
}
if (MakeTuple.GetSubExprs()[1]->GetNodeType() != EAstNodeType::Literal_String)
{
AppendGlitch(
DefinitionAst,
EDiagnostic::ErrSyntax_InternalError,
"Localization default must be a string.");
return;
}
const CUTF8String& Default = static_cast<CExprString&>(*MakeTuple.GetSubExprs()[1])._String;
SGlitchLocus GlitchLocus(&DefinitionAst);
LocalizationInfo.Emplace(Path, Default, GlitchLocus.AsFormattedString());
}
// SAstVisitor interface
void Visit(CAstNode& Node)
{
const EAstNodeType NodeType = Node.GetNodeType();
if (NodeType == EAstNodeType::Definition_Function)
{
CExprFunctionDefinition& Function = static_cast<CExprFunctionDefinition&>(Node);
if (Function._Function->HasAttributeClass(Program._localizes, Program))
{
ScrapeLocalization(Function);
}
}
else if (NodeType == EAstNodeType::Definition_Data)
{
CExprDataDefinition& DataDefAst = static_cast<CExprDataDefinition&>(Node);
if (DataDefAst._DataMember->HasAttributeClass(Program._localizes, Program))
{
ScrapeLocalization(DataDefAst);
}
}
else if (NodeType == EAstNodeType::Literal_String)
{
ScrapeString(static_cast<CExprString&>(Node));
}
Node.VisitChildren(*this);
}
virtual void Visit(const char* FieldName, CAstNode& AstNode) override { Visit(AstNode); }
virtual void VisitElement(CAstNode& AstNode) override { Visit(AstNode); }
};
}
}
namespace uLang
{
void FVerseLocalizationGen::operator()(const CSemanticProgram& Program,
CDiagnostics& Diagnostics,
TArray<FSolLocalizationInfo>& LocalizationInfo,
TArray<FSolLocalizationInfo>& StringInfo) const
{
::Private::FImpl Impl(Program, Diagnostics, LocalizationInfo, StringInfo);
Impl.ScrapeProgram();
}
}