Files
2025-05-18 13:04:45 +08:00

272 lines
12 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "uLang/Common/Containers/SharedPointerArray.h"
#include "uLang/Common/Misc/Optional.h"
#include "uLang/Common/Misc/EnumUtils.h" // for ULANG_ENUM_BIT_FLAGS()
#include "uLang/CompilerPasses/IParserPass.h"
#include "uLang/CompilerPasses/IPostVstFilter.h"
#include "uLang/CompilerPasses/ISemanticAnalyzerPass.h"
#include "uLang/CompilerPasses/IPostSemanticAnalysisFilter.h"
#include "uLang/CompilerPasses/IPostIrFilter.h"
#include "uLang/CompilerPasses/IIRGeneratorPass.h"
#include "uLang/CompilerPasses/IAssemblerPass.h"
#include "uLang/CompilerPasses/ApiLayerInjections.h"
#include "uLang/Semantics/SemanticProgram.h"
#include "uLang/Syntax/VstNode.h" // for Vst::Node
#include "uLang/SourceProject/SourceProject.h"
#include "uLang/SourceProject/VerseVersion.h"
#include "uLang/VerseLocalizationGen.h"
#define UE_API VERSECOMPILER_API
namespace uLang
{
// Forward declarations
class CToolchain;
class CUTF8StringView;
struct SToolchainParams
{
/**
* --- Parser --------------------------------
* The parser is responsible for ingesting a source file, parsing and tokenizing it, and
* generating an abstract syntax tree (Vst) for the rest of the compiler to consume. It
* should not need to worry about semantics, just syntax
*/
TOptional<TSRef<IParserPass>> Parser;
/**
* --- Post Vst Filters ----------------------
* The post Vst filters take an Vst, and transform it in any way it deems fit. Applications
* of this stage can include optimizers (operation reduction, constant folding, etc.), or
* metadata stripping or remapping. There can be as many filters as needed, and are called
* in array order, starting at 0. It is the loader's responsibility for establishing that
* order.
*/
TSRefArray<IPostVstFilter> PostVstFilters;
/**
* --- Semantic Analyzer ------------------
* The semantic analyzer consumes the generated and optimized VST, transforms it to an AST,
* and semantically analyzes the AST, annotating the AST with inferred types and other
* analysis products.
*/
TOptional<TSRef<ISemanticAnalyzerPass>> SemanticAnalyzer;
/**
* --- Post Semantic Analysis Filters ---------------
* This stage takes the semantically analyzed AST, and performs operations on the result
* before passing along to the IR generator.
*/
TSRefArray<IPostSemanticAnalysisFilter> PostSemanticAnalysisFilters;
/**
* --- Post IR generation Filters ---------------
* This stage takes the generated IR, and performs operations on the result
* before passing along to the assembler.
*/
TSRefArray<IPostIrFilter> PostIrFilters;
/**
* --- IR (Intermediate Representation) -------
* The IrGenerator creates an intermediate representation intended for lenient analysis and code generation.
*/
TOptional<TSRef<IIrGeneratorPass>> IrGenerator;
/**
* --- Assembler -----------------------------
* The assembler is responsible both for code-gen and linking (resolving any symbols between
* language objects). Note that there may be runtime bindings that can't be resolved at this
* stage, but all uLang internal bindings should be validated and linked here.
*/
TOptional<TSRef<IAssemblerPass>> Assembler;
/**
* --- API Layer Injections ---------------
*/
SToolchainInjections LayerInjections;
};
VERSECOMPILER_API TSRef<CToolchain> CreateToolchain(const SToolchainParams& Params);
enum class ECompilerResult : uint32_t
{
Compile_NoOp = (0x00),
Compile_RanSyntaxPass = (1<<0),
Compile_RanSemanticPass = (1<<1),
Compile_RanLocalizationPass = (1<<2),
Compile_RanIrPass = (1<<3),
Compile_RanCodeGenPass = (1<<4),
Compile_SkippedByInjection = (1<<5),
Compile_SkippedByEmptyPass = (1<<6),
Compile_SyntaxError = (1<<7),
Compile_SemanticError = (1<<8),
Compile_IrError = (1<<9),
Compile_LocalizationError = (1<<10),
Compile_CodeGenError = (1<<11),
CompileMask_Failure = (Compile_SyntaxError | Compile_SemanticError | Compile_IrError | Compile_LocalizationError | Compile_CodeGenError),
CompileMask_Skipped = (Compile_SkippedByInjection | Compile_SkippedByEmptyPass),
CompileMask_Aborted = (CompileMask_Failure | CompileMask_Skipped),
};
ULANG_ENUM_BIT_FLAGS(ECompilerResult, inline);
ULANG_FORCEINLINE bool IsCompileFailure(ECompilerResult E) { return Enum_HasAnyFlags(E, ECompilerResult::CompileMask_Failure); }
ULANG_FORCEINLINE bool IsAbortedCompile(ECompilerResult E) { return Enum_HasAnyFlags(E, ECompilerResult::CompileMask_Aborted); }
ULANG_FORCEINLINE bool IsCompileIncomplete(ECompilerResult E) { return !E || IsAbortedCompile(E); }
ULANG_FORCEINLINE bool IsCompileComplete(ECompilerResult E) { return !IsCompileIncomplete(E); }
struct SBuildResults
{
VERSECOMPILER_API bool HasFailure() const;
SBuildResults& operator |= (const SBuildResults& Other);
SBuildStatistics _Statistics = {};
bool _IOErrorsFound = false;
ECompilerResult _CompilerResult = ECompilerResult::Compile_NoOp;
ELinkerResult _LinkerResult = ELinkerResult::Link_Skipped;
};
/* Compiler+Linker toolchain
**************************************************************************/
/**
* The compiler toolchain, which has five stages of compilation. It's structured as a layered,
* multi-stage compiler API. Each stage is interchangeable, which means the frontend and the
* backend are retargetable.
*
* This class needs to be assembled by the Toolchain Loader, which uses the Modular Features
* to find a module or modules that implement the five stages of the compiler. This also lets
* the user mix in various optimizing passes (post-Vst filters), or bytecode packing
* (post-expression filters) from any source.
*/
class CToolchain : public CSharedMix
{
public:
/**
* Compile and link all text snippets in the given project
*/
UE_API SBuildResults BuildProject(const CSourceProject& SourceProject, const SBuildContext& BuildContext, const SProgramContext& ProgramContext);
/**
* Parse a snippet of text and return the resulting Vst snippet
* Steps:
* 1. Pre parse injections (from both toolchain and build context)
* 2. Parse
* 3. Post Vst filters
* 4. Post parse injections (from both toolchain and build context)
*/
UE_API ECompilerResult ParseSnippet(const uLang::TSRef<Verse::Vst::Snippet>& OutVst,
const CUTF8StringView& TextSnippet,
const SBuildContext& BuildContext,
const uint32_t VerseVersion = Verse::Version::Default,
const uint32_t UploadedAtFNVersion = VerseFN::UploadedAtFNVersion::Latest);
/**
* Run SemanticAnalyzeVst and AssembleProgram on an Vst.
*/
UE_API ECompilerResult CompileVst(const TSRef<Verse::Vst::Project>& Vst, const SBuildContext& BuildContext, const SProgramContext& ProgramContext);
/**
* Run semantic analysis on a given Vst snippet
* Steps:
* 1. Pre semantic analysis injections (from both toolchain and build context)
* 2. Semantic analysis
* 3. Post expression filters
* 4. Post semantic analysis injections (from both toolchain and build context)
*/
UE_API ECompilerResult SemanticAnalyzeVst(TOptional<TSRef<CSemanticProgram>>& OutProgram, const TSRef<Verse::Vst::Project>& Vst, const SBuildContext& BuildContext, const SProgramContext& ProgramContext);
/**
* Extract localization information.
*/
UE_API ECompilerResult ExtractLocalization(const TSRef<CSemanticProgram>& Program, const SBuildContext& BuildContext, const SProgramContext& ProgramContext);
/**
* Run IR generation on a given Ast snippet.
* Steps:
* 1. IR generation
* 2. (future lenient analysis)
*/
UE_API ECompilerResult IrGenerateProgram(const TSRef<CSemanticProgram>& Program, const SBuildContext& BuildContext, const SProgramContext& ProgramContext);
/**
* Run semantic analysis and code generation on a given Vst snippet
* Steps:
* 1. Pre translate injections
* 2. Assembler (code generation)
*/
UE_API ECompilerResult AssembleProgram(const TSRef<CSemanticProgram>& Program, const SBuildContext& BuildContext, const SProgramContext& ProgramContext);
/**
* Run linker (not currently used)
*/
UE_API ELinkerResult Link(const SBuildContext& BuildContext, const SProgramContext& ProgramContext);
/**
* Accessors for the various stages of the compiler. Most users will never actually need
* to grab these stages individual, but in the cases where you need to do partial compilation,
* they can be useful to manually run
*/
const TOptional<TSRef<IParserPass>>& GetParser() const { return _Params.Parser; }
const TSRefArray<IPostVstFilter>& GetPostVstFilters() const { return _Params.PostVstFilters; }
const TOptional<TSRef<ISemanticAnalyzerPass>>& GetSemanticAnalyzer() const { return _Params.SemanticAnalyzer; }
const TSRefArray<IPostSemanticAnalysisFilter>& GetPostSemanticAnalysisFilters() const { return _Params.PostSemanticAnalysisFilters; }
const TSRefArray<IPostIrFilter>& GetPostIrFilters() const { return _Params.PostIrFilters; }
const TOptional<TSRef<IAssemblerPass>>& GetAssembler() const { return _Params.Assembler; }
const TSPtr<Verse::Vst::Project>& GetProjectVst() const { return _ProjectVst; }
/**
* Directly sets the cached VST project to the new project specified.
*
* Warning: doing this will cause the previously cached VST project's destructor to be called,
* which means that any AST nodes that still hold references to the VST nodes within will now
* have those references be invalidated. The only time you should use this is if you don't care
* about the previous VST project anymore and its accompanying AST either.
*
* @param NewProject The new project to store.
*/
void SetProjectVst(const TSRef<Verse::Vst::Project>& NewProject)
{
this->_ProjectVst = NewProject;
}
// Take localization information, i.e, it removes it from this object.
UE_API TArray<FSolLocalizationInfo> TakeLocalizationInfo();
// Take string information, i.e, it removes it from this object.
UE_API TArray<FSolLocalizationInfo> TakeStringInfo();
private:
friend VERSECOMPILER_API TSRef<CToolchain> CreateToolchain(const SToolchainParams& Params);
friend TSRef<CToolchain>;
UE_API CToolchain(const SToolchainParams& Params); // use CreateToolchain() to construct
struct SOrderedPackage
{
const CSourcePackage* _Package = nullptr;
int32_t _DependencyDepth = -1;
};
// Build a list of packages ordered by dependency depth
// Returns true if successful, or false if glitches were encountered
UE_API bool BuildOrderedPackageList(const CSourceProject& SourceProject, const SBuildContext& BuildContext, TArray<SOrderedPackage>& OutOrderedPackageList) const;
SToolchainParams _Params;
TSPtr<Verse::Vst::Project> _ProjectVst;
// Localization and string information stored here.
TArray<FSolLocalizationInfo> _LocalizationInfo;
TArray<FSolLocalizationInfo> _StringInfo;
};
}
#undef UE_API