diff options
Diffstat (limited to 'source/20-codegen-compiler/main.cpp')
-rw-r--r-- | source/20-codegen-compiler/main.cpp | 1443 |
1 files changed, 0 insertions, 1443 deletions
diff --git a/source/20-codegen-compiler/main.cpp b/source/20-codegen-compiler/main.cpp deleted file mode 100644 index a2e50f5..0000000 --- a/source/20-codegen-compiler/main.cpp +++ /dev/null @@ -1,1443 +0,0 @@ -#include "CodegenConfig.hpp" -#include "CodegenDecl.hpp" -#include "CodegenLexer.hpp" -#include "CodegenModel.hpp" -#include "CodegenOutput.hpp" -#include "CodegenUtils.hpp" - -#include <Enum.hpp> -#include <LookupTable.hpp> -#include <Macros.hpp> -#include <ScopeGuard.hpp> -#include <Utils.hpp> - -#include <robin_hood.h> -#include <stb_c_lexer.h> -#include <cinttypes> -#include <cstdlib> -#include <filesystem> -#include <memory> -#include <optional> -#include <span> -#include <string> -#include <string_view> - -using namespace std::literals; -namespace fs = std::filesystem; - -// TODO support codegen target in .cpp files -// TOOD maybe switch to libclang, maintaining this parser is just too painful - -struct AppState { - CodegenRuntimeModel* runtimeModel; - CodegenArchiveModel* archiveModel; - // NOTE: decl objects reference the SourceFile objects by pointer - robin_hood::unordered_node_map<std::string, SourceFile, StringHash, StringEqual> sourceFiles; - std::vector<DeclEnum*> enumsToRevisit; - std::vector<DeclStruct*> structsToRevisit; - std::string_view outputDir; - std::string_view databaseFilePath; - - SourceFile& GetOrCreateSourceFile(std::string_view filename) { - auto iter = sourceFiles.find(filename); - if (iter != sourceFiles.end()) { - return iter->second; - } else { - auto [iter, success] = sourceFiles.try_emplace(std::string(filename), SourceFile{}); - // NOTE: "persistent" means pointer stable below - auto& persistentFilename = iter->first; - auto& persistentSourceFile = iter->second; - persistentSourceFile.filename = persistentFilename; - return persistentSourceFile; - } - } -}; - -FSTR_LUT_DECL(ClexNames, CLEX_eof, CLEX_ext_COUNT) { - FSTR_LUT_MAP_FOR(ClexNames); - FSTR_LUT_MAP_ENUM(CLEX_intlit); - FSTR_LUT_MAP_ENUM(CLEX_floatlit); - FSTR_LUT_MAP_ENUM(CLEX_id); - FSTR_LUT_MAP_ENUM(CLEX_dqstring); - FSTR_LUT_MAP_ENUM(CLEX_sqstring); - FSTR_LUT_MAP_ENUM(CLEX_charlit); - FSTR_LUT_MAP_ENUM(CLEX_eq); - FSTR_LUT_MAP_ENUM(CLEX_noteq); - FSTR_LUT_MAP_ENUM(CLEX_lesseq); - FSTR_LUT_MAP_ENUM(CLEX_greatereq); - FSTR_LUT_MAP_ENUM(CLEX_andand); - FSTR_LUT_MAP_ENUM(CLEX_oror); - FSTR_LUT_MAP_ENUM(CLEX_shl); - FSTR_LUT_MAP_ENUM(CLEX_shr); - FSTR_LUT_MAP_ENUM(CLEX_plusplus); - FSTR_LUT_MAP_ENUM(CLEX_minusminus); - FSTR_LUT_MAP_ENUM(CLEX_pluseq); - FSTR_LUT_MAP_ENUM(CLEX_minuseq); - FSTR_LUT_MAP_ENUM(CLEX_muleq); - FSTR_LUT_MAP_ENUM(CLEX_diveq); - FSTR_LUT_MAP_ENUM(CLEX_modeq); - FSTR_LUT_MAP_ENUM(CLEX_andeq); - FSTR_LUT_MAP_ENUM(CLEX_oreq); - FSTR_LUT_MAP_ENUM(CLEX_xoreq); - FSTR_LUT_MAP_ENUM(CLEX_arrow); - FSTR_LUT_MAP_ENUM(CLEX_eqarrow); - FSTR_LUT_MAP_ENUM(CLEX_shleq); - FSTR_LUT_MAP_ENUM(CLEX_shreq); - FSTR_LUT_MAP_ENUM(CLEX_ext_single_char); - FSTR_LUT_MAP_ENUM(CLEX_ext_double_colon); - FSTR_LUT_MAP_ENUM(CLEX_ext_dot_dot_dot); -} - -FSTR_LUT_DECL(EnumUnderlyingType, 0, EUT_COUNT) { - FSTR_LUT_MAP_FOR(EnumUnderlyingType); - FSTR_LUT_MAP(EUT_Int8, "int8_t"); - FSTR_LUT_MAP(EUT_Int16, "int16_t"); - FSTR_LUT_MAP(EUT_Int32, "int32_t"); - FSTR_LUT_MAP(EUT_Int64, "int64_t"); - FSTR_LUT_MAP(EUT_Uint8, "uint8_t"); - FSTR_LUT_MAP(EUT_Uint16, "uint16_t"); - FSTR_LUT_MAP(EUT_Uint32, "uint32_t"); - FSTR_LUT_MAP(EUT_Uint64, "uint64_t"); -} - -RSTR_LUT_DECL(EnumUnderlyingType, 0, EUT_COUNT) { - RSTR_LUT_MAP_FOR(EnumUnderlyingType); - - // Platform-dependent types - RSTR_LUT_MAP(EUT_Int16, "short"); - RSTR_LUT_MAP(EUT_Int16, "short int"); - RSTR_LUT_MAP(EUT_Uint16, "unsigned short"); - RSTR_LUT_MAP(EUT_Uint16, "unsigned short int"); - RSTR_LUT_MAP(EUT_Int32, "int"); - RSTR_LUT_MAP(EUT_Uint32, "unsigned"); - RSTR_LUT_MAP(EUT_Uint32, "unsigned int"); -#ifdef _WIN32 - RSTR_LUT_MAP(EUT_Int32, "long"); - RSTR_LUT_MAP(EUT_Int32, "long int"); - RSTR_LUT_MAP(EUT_Uint32, "unsigned long"); - RSTR_LUT_MAP(EUT_Uint32, "unsigned long int"); -#else - RSTR_LUT_MAP(EUT_Int64, "long"); - RSTR_LUT_MAP(EUT_Int64, "long int"); - RSTR_LUT_MAP(EUT_Uint64, "unsigned long"); - RSTR_LUT_MAP(EUT_Uint64, "unsigned long int"); -#endif - RSTR_LUT_MAP(EUT_Int64, "long long"); - RSTR_LUT_MAP(EUT_Int64, "long long int"); - RSTR_LUT_MAP(EUT_Uint64, "unsigned long long"); - RSTR_LUT_MAP(EUT_Uint64, "unsigned long long int"); - - // Sized types - RSTR_LUT_MAP(EUT_Int8, "int8_t"); - RSTR_LUT_MAP(EUT_Int16, "int16_t"); - RSTR_LUT_MAP(EUT_Int32, "int32_t"); - RSTR_LUT_MAP(EUT_Int64, "int64_t"); - RSTR_LUT_MAP(EUT_Uint8, "uint8_t"); - RSTR_LUT_MAP(EUT_Uint16, "uint16_t"); - RSTR_LUT_MAP(EUT_Uint32, "uint32_t"); - RSTR_LUT_MAP(EUT_Uint64, "uint64_t"); -} - -FSTR_LUT_DECL(EnumValuePattern, 0, EVP_COUNT) { - FSTR_LUT_MAP_FOR(EnumValuePattern); - FSTR_LUT_MAP_ENUM(EVP_Continuous); - FSTR_LUT_MAP_ENUM(EVP_Bits); - FSTR_LUT_MAP_ENUM(EVP_Random); -} - -enum CppKeyword { - CKw_Namespace, - CKw_Struct, - CKw_Class, - CKw_Enum, - CKw_Public, - CKw_Protected, - CKw_Private, - CKw_Virtual, - CKw_Using, - CKw_Template, - CKw_COUNT, -}; - -RSTR_LUT_DECL(CppKeyword, 0, CKw_COUNT) { - RSTR_LUT_MAP_FOR(CppKeyword); - RSTR_LUT_MAP(CKw_Namespace, "namespace"); - RSTR_LUT_MAP(CKw_Struct, "struct"); - RSTR_LUT_MAP(CKw_Class, "class"); - RSTR_LUT_MAP(CKw_Enum, "enum"); - RSTR_LUT_MAP(CKw_Public, "public"); - RSTR_LUT_MAP(CKw_Protected, "protected"); - RSTR_LUT_MAP(CKw_Private, "private"); - RSTR_LUT_MAP(CKw_Virtual, "virtual"); - RSTR_LUT_MAP(CKw_Using, "using"); - RSTR_LUT_MAP(CKw_Template, "template"); -} - -enum CodegenDirective { - CD_Class, - CD_ClassProperty, - CD_ClassMethod, - CD_Enum, - CD_XGlobalVar, - CD_XGlobalVarCtor, - CD_XGlobalVarDtor, - CD_COUNT, -}; - -RSTR_LUT_DECL(CodegenDirective, 0, CD_COUNT) { - RSTR_LUT_MAP_FOR(CodegenDirective); - RSTR_LUT_MAP(CD_Class, "BRUSSEL_CLASS"); - RSTR_LUT_MAP(CD_ClassProperty, "BRUSSEL_PROPERTY"); - RSTR_LUT_MAP(CD_ClassMethod, "BRUSSEL_METHOD"); - RSTR_LUT_MAP(CD_Enum, "BRUSSEL_ENUM"); - RSTR_LUT_MAP(CD_XGlobalVar, "BRUSSEL_GLOBAL_DECL"); - RSTR_LUT_MAP(CD_XGlobalVarCtor, "BRUSSEL_GLOBAL_CTOR"); - RSTR_LUT_MAP(CD_XGlobalVarDtor, "BRUSSEL_GLOBAL_DTOR"); -} - -std::vector<std::vector<const StbLexerToken*>> -TryConsumeDirectiveArgumentList(CodegenLexer& lexer) { - std::vector<std::vector<const StbLexerToken*>> result; - decltype(result)::value_type currentArg; - - size_t i = lexer.idx; - int parenDepth = 0; - for (; i < lexer.tokens.size(); ++i) { - auto& token = lexer.tokens[i]; - if (token.text[0] == '(') { - if (parenDepth > 0) { - currentArg.push_back(&token); - } - ++parenDepth; - } else if (token.text[0] == ')') { - --parenDepth; - if (parenDepth == 0) { - // End of argument list - ++i; // Consume the ')' token - break; - } - } else if (parenDepth > 0) { - // Parse these only if we are inside the argument list - if (token.text[0] == ',') { - result.push_back(std::move(currentArg)); - currentArg = {}; - } else { - currentArg.push_back(&token); - } - } - } - - if (!currentArg.empty()) { - result.push_back(std::move(currentArg)); - } - - lexer.idx = i; - return result; -} - -bool TryConsumeKeyword(CodegenLexer& lexer, CppKeyword keyword) { - auto& token = lexer.Current(); - if (token.type == CLEX_id) { - auto iter = RSTR_LUT(CppKeyword).find(token.text); - if (iter != RSTR_LUT(CppKeyword).end()) { - ++lexer.idx; - return true; - } - } - return false; -} - -bool TryConsumeAnyKeyword(CodegenLexer& lexer) { - auto& token = lexer.Current(); - if (token.type == CLEX_id && - RSTR_LUT(CppKeyword).contains(token.text)) - { - ++lexer.idx; - return true; - } - return false; -} - -std::optional<DeclMemberVariable> -TryConsumeMemberVariable(CodegenLexer& lexer) { - // The identifier/name will always be one single token, right before the 1st '=' (if has initializer) or ';' (no initializer) - // NOTE: we assume there is no (a == b) stuff in the templates - - auto& tokens = lexer.tokens; - auto& idx = lexer.idx; - - size_t idenTokIdx; - size_t typeStart = idx; - size_t typeEnd; - for (; idx < tokens.size(); ++idx) { - auto& token = tokens[idx]; - if (token.type == CLEX_ext_single_char) { - if (token.text[0] == '=') { - typeEnd = idx - 1; - idenTokIdx = idx - 1; - lexer.SkipUntilTokenSingleChar(';'); - goto found; - } else if (token.text[0] == ';') { - typeEnd = idx - 1; - idenTokIdx = idx - 1; - goto found; - } - } - } - // We reached end of input but still no end of statement - return {}; - -found: - if (tokens[idenTokIdx].type != CLEX_id) { - // Expected identifier, found something else - return {}; - } - - DeclMemberVariable result; - result.name = tokens[idenTokIdx].text; - result.type = CombineTokens(std::span(&tokens[typeStart], &tokens[typeEnd])); - - // Consume the '=' or ';' token - ++idx; - - return result; -} - -EnumUnderlyingType TryConsumeEnumUnderlyingType(CodegenLexer& lexer) { - // Try 1, 2, 3, 4 tokens from the current position - // NOTE: see the FSTR map initialization code for reference that there is max 4 tokens - for (int i = 4; i >= 1; --i) { - auto text = CombineTokens(std::span(&lexer.Current(), i), " "sv); - auto iter = RSTR_LUT(EnumUnderlyingType).find(text); - if (iter != RSTR_LUT(EnumUnderlyingType).end()) { - lexer.idx += i; - return iter->second; - } - } - return EUT_COUNT; -} - -// Also includes the ':' token in the front -EnumUnderlyingType TryConsumeEnumUnderlyingTypeClause(CodegenLexer& lexer) { - if (lexer.Current().text != ":") { - return EUT_COUNT; - } - - ++lexer.idx; - return TryConsumeEnumUnderlyingType(lexer); -} - -enum StructMetaGenOptions { - SMGO_InheritanceHiearchy, - SMGO_COUNT, -}; - -RSTR_LUT_DECL(StructMetaGenOptions, 0, SMGO_COUNT) { - RSTR_LUT_MAP_FOR(StructMetaGenOptions); - RSTR_LUT_MAP(SMGO_InheritanceHiearchy, "InheritanceHiearchy"); -} - -enum StructPropertyOptions { - SPO_Getter, - SPO_Setter, - SPO_COUNT, -}; - -RSTR_LUT_DECL(StructPropertyOptions, 0, SPO_COUNT) { - RSTR_LUT_MAP_FOR(StructPropertyOptions); - RSTR_LUT_MAP(SPO_Getter, "GETTER"); - RSTR_LUT_MAP(SPO_Setter, "SETTER"); -} - -enum EnumMetaGenOptions { - EMGO_ToString, - EMGO_FromString, - EMGO_ExcludeUseHeuristics, - EMGO_RemovePrefix, - EMGO_AddPrefix, - EMGO_COUNT, -}; - -RSTR_LUT_DECL(EnumMetaGenOptions, 0, EMGO_COUNT) { - RSTR_LUT_MAP_FOR(EnumMetaGenOptions); - RSTR_LUT_MAP(EMGO_ToString, "ToString"); - RSTR_LUT_MAP(EMGO_FromString, "FromString"); - RSTR_LUT_MAP(EMGO_ExcludeUseHeuristics, "ExcludeHeuristics"); - RSTR_LUT_MAP(EMGO_RemovePrefix, "RemovePrefix"); - RSTR_LUT_MAP(EMGO_AddPrefix, "AddPrefix"); -} - -void GenerateEnumStringArray(CodegenOutput& out, const DeclEnum& decl, const char* arrayName, const std::vector<DeclEnumElement>& filteredElements) { - CodegenOutputThing thing; - APPEND_FMT_LN(thing.text, "const char* %s[] = {", arrayName); - for (auto& elm : filteredElements) { - APPEND_FMT_LN(thing.text, "\"%s\",", elm.name.c_str()); - } - APPEND_LIT_LN(thing.text, "};"); - out.AddOutputThing(std::move(thing)); -} - -void GenerateEnumStringMap(CodegenOutput& out, const DeclEnum& decl, const char* mapName, const std::vector<DeclEnumElement>& filteredElements) { - CodegenOutputThing thing; - // TODO - out.AddOutputThing(std::move(thing)); -} - -void GenerateForEnum(CodegenOutput& headerOut, CodegenOutput& sourceOut, const DeclEnum& decl) { - auto& enumName = decl.GetFullName(); - auto& mangledName = decl.GetMangledName(); - - auto useExcludeHeuristics = decl.generateExcludeUseHeuristics; - auto filteredElements = [&]() { - if (useExcludeHeuristics) { - decltype(decl.elements) result; - for (auto& elm : decl.elements) { - if (elm.name.ends_with("COUNT")) continue; - - std::string_view trimmedName = elm.name; - if (!decl.generateRemovingPrefix.empty() && - elm.name.starts_with(decl.generateRemovingPrefix)) - { - trimmedName = trimmedName.substr(decl.generateRemovingPrefix.size()); - } - - result.push_back(DeclEnumElement{ - .name = decl.generatingAddingPrefix + std::string(trimmedName), - .value = elm.value, - }); - } - return result; - } else { - return decl.elements; - } - }(); - - if (decl.generateToString) { - // Generate value -> string lookup table and function - INPLACE_FMT(val2StrName, "gCG_%s_Val2Str", mangledName.c_str()); - - switch (decl.GetPattern()) { - case EVP_Continuous: { - GenerateEnumStringArray(sourceOut, decl, val2StrName, filteredElements); - int minVal = filteredElements.empty() ? 0 : filteredElements.front().value; - int maxVal = filteredElements.empty() ? 0 : filteredElements.back().value; - - CodegenOutputThing lookupFunctionDecl; - { - auto& o = lookupFunctionDecl.text; - APPEND_LIT_LN(o, "template <>"); - APPEND_FMT_LN(o, "std::string_view Metadata::EnumToString<%s>(%s value);", enumName.c_str(), enumName.c_str()); - } - - CodegenOutputThing lookupFunctionDef; - { - auto& o = lookupFunctionDef.text; - APPEND_LIT_LN(o, "template <>"); - APPEND_FMT_LN(o, "std::string_view Metadata::EnumToString<%s>(%s value) {", enumName.c_str(), enumName.c_str()); - APPEND_FMT_LN(o, " auto intVal = (%s)value;", FSTR_LUT_LOOKUP(EnumUnderlyingType, decl.underlyingType)); - APPEND_FMT_LN(o, " if (intVal < %d || intVal > %d) return {};", minVal, maxVal); - APPEND_FMT_LN(o, " return %s[intVal - %d];", val2StrName, minVal); - APPEND_LIT_LN(o, "}"); - } - - headerOut.AddOutputThing(std::move(lookupFunctionDecl)); - sourceOut.AddOutputThing(std::move(lookupFunctionDef)); - } break; - - case EVP_Bits: { - GenerateEnumStringArray(sourceOut, decl, val2StrName, filteredElements); - // TODO - } break; - - case EVP_Random: { - GenerateEnumStringMap(sourceOut, decl, val2StrName, filteredElements); - // TODO - } break; - - case EVP_COUNT: break; - } - } - - if (decl.generateFromString) { - // Generate string -> value lookup table - INPLACE_FMT(str2ValName, "gCG_%s_Str2Val", mangledName.c_str()); - - CodegenOutputThing lookupTable; - { - auto& o = lookupTable.text; - // TODO use correct underlying type - APPEND_FMT_LN(o, "constinit frozen::unordered_map<frozen::string, %s, %" PRId64 "> %s = {", FSTR_LUT_LOOKUP(EnumUnderlyingType, decl.underlyingType), filteredElements.size(), str2ValName); - for (auto& elm : filteredElements) { - APPEND_FMT_LN(o, "{\"%s\", %" PRId64 "},", elm.name.c_str(), elm.value); - } - APPEND_LIT_LN(o, "};"); - } - - // Generate lookup function - CodegenOutputThing lookupFunctionDecl; - { - auto& o = lookupFunctionDecl.text; - APPEND_LIT_LN(o, "template <>"); - APPEND_FMT_LN(o, "std::optional<%s> Metadata::EnumFromString<%s>(std::string_view value);", enumName.c_str(), enumName.c_str()); - } - - CodegenOutputThing lookupFunctionDef; - { - auto& o = lookupFunctionDef.text; - APPEND_LIT_LN(o, "template <>"); - APPEND_FMT_LN(o, "std::optional<%s> Metadata::EnumFromString<%s>(std::string_view value) {", enumName.c_str(), enumName.c_str()); - APPEND_FMT_LN(o, " auto iter = %s.find(value);", str2ValName); - APPEND_FMT_LN(o, " if (iter != %s.end()) {", str2ValName); - APPEND_FMT_LN(o, " return (%s)iter->second;", enumName.c_str()); - APPEND_LIT_LN(o, " } else {"); - APPEND_LIT_LN(o, " return {};"); - APPEND_LIT_LN(o, " }"); - APPEND_LIT_LN(o, "}"); - } - - sourceOut.AddOutputThing(std::move(lookupTable)); - headerOut.AddOutputThing(std::move(lookupFunctionDecl)); - sourceOut.AddOutputThing(std::move(lookupFunctionDef)); - } -} - -void GenerateClassProperty(CodegenOutput& headerOutput, CodegenOutput& sourceOutput) { - CodegenOutputThing thing; - APPEND_FMT_LN(thing.text, "TypePropertyInfo gCGtype_%s_%s_Property = {", "TODO", "TODO"); - APPEND_LIT_LN(thing.text, "};"); - - sourceOutput.AddOutputThing(std::move(thing)); -} - -void GenerateClassFunction(CodegenOutput& headerOutput, CodegenOutput& sourceOutput) { - // TODO -} - -void GenerateForClassMetadata( - CodegenOutput& headerOutput, - CodegenOutput& sourceOutput, - const DeclStruct& decl // -) { - auto& mangedName = decl.GetMangledName(); - auto mangedNameCstr = mangedName.c_str(); - - CodegenOutputThing data; - // TODO generate type id, this needs global scanning - - if (!decl.baseClasses.empty()) { - // Forward declare the variables (which may appear before this section, after this section, or in another TU) - for (auto& baseClass : decl.baseClasses) { - auto baseClassIdName = baseClass->name.c_str(); - APPEND_FMT_LN(data.text, "extern const TypeInfo gCGtype_%s_TypeInfo;", baseClassIdName); - } - APPEND_FMT_LN(data.text, "const TypeInfo* const gCGtype_%s_BaseClasses[] = {", mangedNameCstr); - for (auto& baseClass : decl.baseClasses) { - auto baseClassIdName = baseClass->name.c_str(); - APPEND_FMT_LN(data.text, "gCGtype_%s_TypeInfo,", baseClassIdName); - } - APPEND_LIT_LN(data.text, "};"); - } - - if (!decl.memberVariables.empty()) { - APPEND_FMT_LN(data.text, "const TypePropertyInfo gCGtype_%s_Properties[] = {", mangedNameCstr); - for (auto& property : decl.memberVariables) { - APPEND_FMT_LN(data.text, "{.name=\"%s\"sv, .getterName=\"%s\"sv, .setterName=\"%s\"sv},", property.name.c_str(), property.getterName.c_str(), property.setterName.c_str()); - } - APPEND_LIT_LN(data.text, "};"); - } - - if (!decl.memberFunctions.empty()) { - APPEND_FMT_LN(data.text, "const TypeMethodInfo gCGtype_%s_Methods[] = {", mangedNameCstr); - for (auto& method : decl.memberFunctions) { - // TODO - } - APPEND_LIT_LN(data.text, "};"); - } - - APPEND_FMT_LN(data.text, "const TypeInfo gCGtype_%s_TypeInfo = {", mangedNameCstr); - APPEND_FMT_LN(data.text, ".name = \"%s\"sv,", mangedNameCstr); - if (!decl.baseClasses.empty()) APPEND_FMT_LN(data.text, ".parents = gCGtype_%s_BaseClasses,", mangedNameCstr); - if (!decl.memberVariables.empty()) APPEND_FMT_LN(data.text, ".properties = gCGtype_%s_Properties,", mangedNameCstr); - if (!decl.memberFunctions.empty()) APPEND_FMT_LN(data.text, ".methods = gCGtype_%s_Methods,", mangedNameCstr); - APPEND_LIT_LN(data.text, "};"); - - CodegenOutputThing queryFunc; - APPEND_FMT(queryFunc.text, - "template <>\n" - "const TypeInfo* Metadata::GetTypeInfo<%s>() {\n" - " return &gCGtype_%s_TypeInfo;\n" - "}\n", - decl.fullname->c_str(), - mangedNameCstr); - - sourceOutput.AddOutputThing(std::move(data)); - sourceOutput.AddOutputThing(std::move(queryFunc)); -} - -struct NamespaceStackframe { - // The current namespace that owns the brace level, see example - DeclNamespace* ns = nullptr; - // Brace depth `ns` was created at (e.g. [std::details].depth == 0) - int depth = 0; -}; - -struct ParserState { - // TODO -}; - -struct ParserOutput { - // Example: - // namespace std::details { - // /* [stack top].ns = std::details */ - // /* [stack top].depth = std */ - // } - // namespace foo { - // /* [stack top].ns = foo */ - // /* [stack top].depth = foo */ - // namespace details { - // /* [stack top].ns = foo::details */ - // /* [stack top].depth = foo::details */ - // } - // } - std::vector<NamespaceStackframe> nsStack; - // The current effective namespace, see example - DeclNamespace* currentNamespace = nullptr; - - DeclStruct* currentStruct = nullptr; - DeclEnum* currentEnum = nullptr; - int currentBraceDepth = 0; - int currentStructBraceDepth = -1; - int currentEnumBraceDepth = -1; -}; - -void HandleDirectiveEnum(AppState& as, ParserOutput& ps, CodegenLexer& lexer) { - // Consume the directive - ++lexer.idx; - - if (!ps.currentEnum) { - printf("[ERROR] BRUSSEL_ENUM must be used within a enum\n"); - return; - } - - auto argList = TryConsumeDirectiveArgumentList(lexer); - auto& lut = RSTR_LUT(EnumMetaGenOptions); - for (auto& arg : argList) { - if (arg.empty()) { - printf("[ERROR] empty argument is invalid in BRUSSEL_ENUM\n"); - continue; - } - - auto& optionDirective = arg[0]->text; - auto iter = lut.find(optionDirective); - if (iter == lut.end()) { - printf("[ERROR] BRUSSEL_ENUM: invalid option '%s'\n", optionDirective.c_str()); - } - - auto option = iter->second; - switch (option) { - case EMGO_ToString: ps.currentEnum->generateToString = true; break; - case EMGO_FromString: ps.currentEnum->generateFromString = true; break; - case EMGO_ExcludeUseHeuristics: ps.currentEnum->generateExcludeUseHeuristics = true; break; - - case EMGO_RemovePrefix: { - if (argList.size() <= 1) { - printf("[ERROR] missing argument for RemovePrefix"); - break; - } - ps.currentEnum->generateRemovingPrefix = arg[1]->text; - } break; - case EMGO_AddPrefix: { - if (argList.size() <= 1) { - printf("[ERROR] missing argument for AddPrefix"); - break; - } - ps.currentEnum->generatingAddingPrefix = arg[1]->text; - } break; - - case EMGO_COUNT: break; - } - } - - ps.currentEnum->generating = true; -} - -CodegenLexer LexInputFile(AppState& as, std::string_view source) { - CodegenLexer result; - result.InitializeFrom(source); - return result; -} - -void ParseInputFileAndGenerate(AppState& as, CodegenLexer& /*lexingState*/ ls, std::string_view filenameStem) { -#if CODEGEN_DEBUG_PRINT - printf("BEGIN tokens\n"); - for (auto& token : ls.tokens) { - switch (token.type) { - case CLEX_intlit: { - printf(" token %-32s = %ld\n", FSTR_LUT_LOOKUP(ClexNames, token.type), token.lexerIntNumber); - } break; - - case CLEX_floatlit: { - printf(" token %-32s = %f\n", FSTR_LUT_LOOKUP(ClexNames, token.type), token.lexerRealNumber); - } break; - - default: { - printf(" token %-32s '%s'\n", FSTR_LUT_LOOKUP(ClexNames, token.type), token.text.c_str()); - } break; - } - } - printf("END tokens\n"); -#endif - - auto& sourceFile = as.GetOrCreateSourceFile(filenameStem); - sourceFile.header = true; - sourceFile.reprocessing = true; - - // TODO move lexedTokens and consumption related functions to ParserState struct - - ParserOutput po; - - auto& tokens = ls.tokens; - auto& idx = ls.idx; - while (ls.idx < ls.tokens.size()) { - auto& token = ls.Current(); - - bool incrementTokenIdx = true; - switch (token.Reamalgamate()) { - case CLEX_id: { - CppKeyword keyword; - { - auto& map = RSTR_LUT(CppKeyword); - auto iter = map.find(token.text); - if (iter != map.end()) { - keyword = iter->second; - } else { - keyword = CKw_COUNT; // Skip keyword section - } - } - switch (keyword) { - case CKw_Namespace: { - ++idx; - incrementTokenIdx = false; - - int nestingCount = 0; - while (true) { - if (tokens[idx].type != CLEX_id) { - // TODO better error recovery - // TODO handle annoymous namespaces - printf("[ERROR] invalid syntax for namespace\n"); - break; - } - - po.currentNamespace = as.runtimeModel->AddNamespace(DeclNamespace{ - .container = po.currentNamespace, - .name = tokens[idx].text, - }); - - // Consume the identifier token - ++idx; - - if (tokens[idx].type == CLEX_ext_double_colon) { - // Consume the "::" token - ++idx; - } else { - break; - } - } - - po.nsStack.push_back(NamespaceStackframe{ - .ns = po.currentNamespace, - .depth = po.currentBraceDepth, - }); - - goto endCaseCLEX_id; - } - - case CKw_Struct: - case CKw_Class: { - // Consume the 'class' or 'struct' keyword - ++idx; - incrementTokenIdx = false; - - // For forward declarations, there are always 2 tokens after `class`: an identifier, and the ';' token - // Example: - // class MyClass; - if (tokens[idx + 0].type == CLEX_id && - tokens[idx + 1].text == ";") - { - // Skip class forward declarations - idx += 2; - break; - } - - auto& idenTok = tokens[idx]; - if (idenTok.type != CLEX_id) { - printf("[ERROR] invalid syntax for struct or class\n"); - break; - } - - DEBUG_PRINTF("[DEBUG] found struct named %s\n", idenTok.text.c_str()); - - auto& name = idenTok.text; - auto fullname = Utils::MakeFullName(name, po.currentNamespace); - DeclStruct structDecl; - structDecl.sourceFile = &sourceFile; - structDecl.container = po.currentNamespace; - structDecl.name = name; - - // Consume the identifier token - ++idx; - - if (ls.TryConsumeSingleCharToken(':')) { - while (true) { - // Public, protected, etc. - TryConsumeAnyKeyword(ls); - - auto& idenTok = tokens[idx]; - if (idenTok.type != CLEX_id) { - printf("[ERROR] invalid syntax for class inheritance list\n"); - goto endCase; - } - - // TODO support namespace qualified names - auto baseClassFullname = Utils::MakeFullName(idenTok.text, po.currentNamespace); - auto baseClassDecl = as.runtimeModel->FindStruct(baseClassFullname); - if (baseClassDecl) { - // TODO retreive class from database - // ---- Or just silent create it, and assume the code was valid? - // We silently ignore a non-existent base class, because they may reside in a file that we didn't scan - structDecl.baseClasses.push_back(baseClassDecl); - } - - // Consume the identifier token - ++idx; - - if (ls.TryConsumeSingleCharToken('{')) { - // End of base class list - --idx; // Give the '{' token back to the main loop - break; - } else if (!ls.TryConsumeSingleCharToken(',')) { - // If the list didn't end, we expect a comma (then followed by more entries) - printf("[ERROR] invalid syntax for class inheritance list\n"); - goto endCase; - } - - // NOTE: we currently only scan one base class to workaround some code inherits from template classes after their initial base class - // TODO remove this hack - break; - } - } - - { - // Get a pointer to the decl inside CodegenInput's storage - auto decl = as.runtimeModel->AddStruct(std::move(fullname), std::move(structDecl)); - po.currentStruct = decl; - po.currentStructBraceDepth = po.currentBraceDepth; - } - - endCase: - goto endCaseCLEX_id; - } - - case CKw_Enum: { - if (po.currentStruct) { - // TODO parsing enums inside classes is currently broken (1. model database is not modeled for this 2. codegen logic is not modeled) - break; - } - - // Consume the "enum" keyword - ++idx; - incrementTokenIdx = false; - - StbLexerToken* idenTok; - if (tokens[idx].text == "class") { - // Consume the "class" keyword - ++idx; - idenTok = &tokens[idx]; - DEBUG_PRINTF("[DEBUG] found enum class named %s\n", idenTok->text.c_str()); - } else { - idenTok = &tokens[idx]; - DEBUG_PRINTF("[DEBUG] found enum named %s\n", idenTok->text.c_str()); - } - - auto& name = tokens[idx].text; - auto fullname = Utils::MakeFullName(name, po.currentNamespace); - DeclEnum enumDecl; - enumDecl.sourceFile = &sourceFile; - enumDecl.container = po.currentNamespace; - enumDecl.name = name; - // Setting underlying type: see below - - // Temporarily bind the pointers to local variable, HandleDirectiveEnum() and other functions expect these to the set - po.currentEnum = &enumDecl; - po.currentEnumBraceDepth = po.currentBraceDepth; - - // Consume the enum name identifier - ++idx; - - // Setting underlying type - if (auto eut = TryConsumeEnumUnderlyingTypeClause(ls); - eut != EUT_COUNT) - { - enumDecl.underlyingType = eut; - } else { - enumDecl.underlyingType = EUT_Int32; - } - - int enumClosingBraceCount = 0; - int enumBraceDepth = 0; - while (enumClosingBraceCount == 0 && idx < tokens.size()) { - auto& token = tokens[idx]; - switch (token.Reamalgamate()) { - case CLEX_id: { - if (token.text == "BRUSSEL_ENUM") { - // Consume the argument list and skip advancing index: this function already consumed all the tokens about BRUSSEL_ENUM - HandleDirectiveEnum(as, po, ls); - continue; - } else { - auto& vec = enumDecl.elements; - // Set to the previous enum element's value + 1, or starting from 0 if this is the first - // Also overridden in the CLEX_intlit branch - auto value = vec.empty() ? 0 : vec.back().value + 1; - vec.push_back(DeclEnumElement{ - .name = token.text, - .value = value, - }); - } - } break; - - case CLEX_intlit: { - auto& vec = enumDecl.elements; - if (!vec.empty()) { - auto& lastElm = vec.back(); - lastElm.value = token.lexerIntNumber; - } - } break; - - case '{': { - ++enumBraceDepth; - } break; - - case '}': { - --enumBraceDepth; - ++enumClosingBraceCount; - } break; - } - - ++idx; - } - - { - auto decl = as.runtimeModel->AddEnum(std::move(fullname), std::move(enumDecl)); - // Fix pointers - po.currentEnum = decl; - po.currentEnumBraceDepth = po.currentBraceDepth; - } - - if (po.currentEnum->generating) { - as.enumsToRevisit.push_back(po.currentEnum); - } - - // NOTE: we parse the whole enum at once (above code), the enum ends right here after the closing brace '}' - po.currentEnum = nullptr; - po.currentEnumBraceDepth = -1; - - goto endCaseCLEX_id; - } - - // Consume the whole statement, because this statement may contain `enum` or `class` keywords that will pollute the parser - case CKw_Template: { - // `template` is either a template list which we don't care about, or a part of a type which we don't care about, - // unless it's a part of a function declaration, where the tokens are handled inside CG_ClassMethod parsing - // TODO handle nested templates or operator> inside template expression - ls.SkipUntilTokenSingleChar('>'); - } break; - case CKw_Using: { - // `using` indicates a type alias or namespace import which we don't care about - ls.SkipUntilTokenSingleChar(';'); - } break; - - // We don't care about these keywords - case CKw_Public: - case CKw_Protected: - case CKw_Private: - case CKw_Virtual: - case CKw_COUNT: break; - } - - CodegenDirective directive; - { - auto& map = RSTR_LUT(CodegenDirective); - auto iter = map.find(token.text); - if (iter != map.end()) { - directive = iter->second; - } else { - directive = CD_COUNT; // Skip directive section - } - } - switch (directive) { - case CD_Class: { - // Consume the directive - ++idx; - incrementTokenIdx = false; - - if (!po.currentStruct) { - printf("[ERROR] BRUSSEL_CLASS must be used within a class or struct\n"); - break; - } - - // Always-on option - po.currentStruct->generating = true; - - auto argList = TryConsumeDirectiveArgumentList(ls); - auto& lut = RSTR_LUT(StructMetaGenOptions); - for (auto& arg : argList) { - if (arg.empty()) { - printf("[ERROR] empty argument is invalid in BRUSSEL_CLASS\n"); - continue; - } - - auto& optionDirective = arg[0]->text; - auto iter = lut.find(optionDirective); - if (iter == lut.end()) continue; - switch (iter->second) { - case SMGO_InheritanceHiearchy: po.currentStruct->generatingInheritanceHiearchy = true; break; - case SMGO_COUNT: break; - } - } - - goto endCaseCLEX_id; - } - - case CD_ClassProperty: { - // Consume the directive - ++idx; - incrementTokenIdx = false; - - if (!po.currentStruct || - !po.currentStruct->generating) - { - printf("[ERROR] BRUSSEL_PROPERTY must be used within a class or struct, that has the BRUSSEL_CLASS directive\n"); - break; - } - - auto argList = TryConsumeDirectiveArgumentList(ls); - auto declOpt = TryConsumeMemberVariable(ls); - if (!declOpt.has_value()) { - printf("[ERROR] a member variable must immediately follow a BRUSSEL_PROPERTY\n"); - break; - } - auto& decl = declOpt.value(); - decl.containerStruct = po.currentStruct; - - // Different option's common logic - std::string pascalCaseName; - auto GetPascalCasedName = [&]() -> const std::string& { - if (pascalCaseName.empty()) { - pascalCaseName = Utils::MakePascalCase(decl.name); - } - return pascalCaseName; - }; - - auto& lut = RSTR_LUT(StructPropertyOptions); - for (auto& arg : argList) { - if (arg.empty()) { - printf("[ERROR] empty argument is invalid in BRUSSEL_PROPERTY\n"); - continue; - } - - auto& optionDirective = arg[0]->text; - auto iter = lut.find(optionDirective); - if (iter == lut.end()) continue; - switch (iter->second) { - case SPO_Getter: { - // NOTE: I'm too lazy to write error checks, just let the codegen crash if syntax is invalid - auto& getterName = arg.at(1)->text; - if (getterName == "auto") { - // NOTE: intentionally shadowing - INPLACE_FMT(getterName, "Get%s", GetPascalCasedName().c_str()); - - decl.getterName = getterName; - decl.isGetterGenerated = true; - } else { - decl.getterName = getterName; - } - } break; - - case SPO_Setter: { - // NOTE: I'm too lazy to write error checks, just let the codegen crash if syntax is invalid - auto& setterName = arg.at(1)->text; - if (setterName == "auto") { - // NOTE: intentionally shadowing - INPLACE_FMT(setterName, "Set%s", GetPascalCasedName().c_str()); - - decl.setterName = setterName; - decl.isSetterGenerated = true; - } else { - decl.setterName = setterName; - } - } break; - - case SPO_COUNT: break; - } - } - - po.currentStruct->memberVariables.push_back(std::move(decl)); - - goto endCaseCLEX_id; - } - - case CD_ClassMethod: { - // Consume the directive - ++idx; - incrementTokenIdx = false; - - goto endCaseCLEX_id; - } - - case CD_XGlobalVar: { - // TODO - goto endCaseCLEX_id; - } - - case CD_XGlobalVarCtor: { - // TODO - goto endCaseCLEX_id; - } - - case CD_XGlobalVarDtor: { - // TODO - goto endCaseCLEX_id; - } - - // This directive always appear inside a enum{} block, which is handled above in the keywords section - case CD_Enum: - case CD_COUNT: break; - } - - endCaseCLEX_id:; - } break; - - case '{': { - po.currentBraceDepth++; - if (po.currentBraceDepth < 0) { - printf("[WARNING] unbalanced brace\n"); - } - } break; - - case '}': { - po.currentBraceDepth--; - if (po.currentBraceDepth < 0) { - printf("[WARNING] unbalanced brace\n"); - } - - if (!po.nsStack.empty()) { - auto& ns = po.nsStack.back(); - if (ns.depth == po.currentBraceDepth) { - po.nsStack.pop_back(); - - if (!po.nsStack.empty()) { - po.currentNamespace = po.nsStack.back().ns; - } else { - po.currentNamespace = nullptr; - } - } - } - - if (po.currentStruct && po.currentBraceDepth == po.currentStructBraceDepth) { - // Exit struct - - if (po.currentStruct->generating) { - as.structsToRevisit.push_back(po.currentStruct); - } - - po.currentStruct = nullptr; - po.currentStructBraceDepth = -1; - } - if (po.currentEnum && po.currentBraceDepth == po.currentEnumBraceDepth) { - // Exit enum - - // TODO this is unused currently, see CKw_Enum branch - if (po.currentEnum->generating) { - as.enumsToRevisit.push_back(po.currentEnum); - } - - po.currentEnum = nullptr; - po.currentEnumBraceDepth = -1; - } - } break; - } - - if (incrementTokenIdx) { - ++idx; - } - } - - if (po.currentBraceDepth != 0) { - printf("[WARNING] unbalanced brace at end of file\n"); - } - - as.archiveModel->DeleteDeclsRelatedToFile(filenameStem); - // as.modelArchive->Store(po.model); -} - -void HandleInputFile(AppState& as, const fs::path& path) { - auto filenameStem = path.stem().string(); - auto lexingState = LexInputFile(as, Utils::ReadFileAsString(path)); - ParseInputFileAndGenerate(as, lexingState, filenameStem); -} - -enum InputOpcode { - IOP_ProcessSingleFile, - IOP_ProcessRecursively, - IOP_ProcessFileList, - IOP_COUNT, -}; - -void HandleArgument(AppState& as, InputOpcode opcode, std::string_view operand) { - switch (opcode) { - case IOP_ProcessSingleFile: { - DEBUG_PRINTF("Processing single file %.*s\n", PRINTF_STRING_VIEW(operand)); - HandleInputFile(as, fs::path(operand)); - } break; - - case IOP_ProcessRecursively: { - DEBUG_PRINTF("Recursively processing folder %.*s\n", PRINTF_STRING_VIEW(operand)); - - fs::path startPath(operand); - for (auto& item : fs::recursive_directory_iterator(startPath)) { - if (!item.is_regular_file()) { - continue; - } - - auto& path = item.path(); - if (auto pathExt = path.extension(); - pathExt != ".h" && - pathExt != ".hpp") - { - continue; - } - - DEBUG_PRINTF("Processing subfile %s\n", path.string().c_str()); - HandleInputFile(as, path); - } - } break; - - case IOP_ProcessFileList: { - DEBUG_PRINTF("Processing file list %.*s\n", PRINTF_STRING_VIEW(operand)); - - fs::path fileListPath(operand); - auto fileList = Utils::OpenCstdioFile(fileListPath, Utils::Read); - if (!fileList) { - // NOTE: need this because our dirty-file-list generation algorithm in CMakeLists.txt doesn't produce a file when nothing is changed - DEBUG_PRINTF("File-list file does not exist, silently skipping.\n"); - break; - } - DEFER { - fclose(fileList); - }; - - std::string line; - while (Utils::ReadCstdioLine(fileList, line)) { - // Remove '\n' - line.pop_back(); - - DEBUG_PRINTF("Processing file in list %.*s\n", line.c_str()); - HandleInputFile(as, fs::path(line)); - } - } break; - - case IOP_COUNT: break; - } -} - -InputOpcode ParseInputOpcode(std::string_view text) { - if (text == "single"sv) { - return IOP_ProcessSingleFile; - } else if (text == "rec"sv) { - return IOP_ProcessRecursively; - } else if (text == "fileList"sv) { - return IOP_ProcessFileList; - } else { - INPLACE_FMT(msg, "Unknown input opcode %s\n", text.data()); - throw std::runtime_error(msg); - } -} - -int main(int argc, char* argv[]) { - FSTR_LUT_INIT(ClexNames); - FSTR_LUT_INIT(EnumUnderlyingType); - RSTR_LUT_INIT(EnumUnderlyingType); - FSTR_LUT_INIT(EnumValuePattern); - RSTR_LUT_INIT(CppKeyword); - RSTR_LUT_INIT(CodegenDirective); - RSTR_LUT_INIT(StructMetaGenOptions); - RSTR_LUT_INIT(StructPropertyOptions); - RSTR_LUT_INIT(EnumMetaGenOptions); - - // TODO better arg parser - // option 1: use cxxopts and positional arguments - // option 2: take one argument only, being a json objecet - - AppState as; - - // If no cli is provided (argv[0] conventionally but not mandatorily the cli), this will do thing - // Otherwise, start with the 2nd element in the array, which is the 1st actual argument - if (argc <= 1) { - // NOTE: keep in sync with various enum options and parser code - printf(&R"""( -USAGE: codegen.exe --output-dir=<path> [--database=<path>] [<opcode>:<input path>]... -where --output-dir=<path>: the *directory* to write generated contents to. This will NOT automatically create the directory. - --database=<path>: the *file* to use for the code model database. - <opcode> is one of: - "single" process this <input path> file only - "rec" starting at the given directory <input path>, recursively process all .h .hpp files - "fileList" read <input path> as a text file, and process each line as a separate file path -)"""[1]); - return -1; - } - - // Named argument pass - robin_hood::unordered_map<std::string_view, std::string_view*> namedArguments{ - { "output-dir"sv, &as.outputDir }, - { "database"sv, &as.databaseFilePath }, - }; - for (int i = 1; i < argc; ++i) { - std::string_view arg(argv[i]); - if (!arg.starts_with("--")) { - // Convention: a "--" argument indicates everything afterwords are positional arguments - if (arg.size() == 2) { - break; - } else { - continue; - } - } - - size_t equalLoc = arg.find('='); - auto oper = arg.substr(/*--*/ 2, equalLoc - 2); - auto iter = namedArguments.find(oper); - if (iter != namedArguments.end()) { - auto storage = iter->second; - if (storage) { - if (equalLoc == std::string_view::npos) { - *storage = ""sv; - } else { - *storage = arg.substr(equalLoc + 1); - } - } - } - } - - DEBUG_PRINTF("Outputting to directory %.*s.\n", PRINTF_STRING_VIEW(as.outputDir)); - DEBUG_PRINTF("Databse file: %.*s.\n", PRINTF_STRING_VIEW(as.databaseFilePath)); - - // TODO move the actual output logic after processing all input commands, based on SQLite batabase model instead of the in-memory CodegenModel model - // this allows better consistency between direct in-file entities (like enums) vs. multi-file entities (like struct inheritance hierarchy) - // this would also mean almost rewriting the whole codegen logic, to work on a changelist fetched from SQLite database instead of being embedded inside the parser loop - // TODO how do we detect the case of - // 1. has: Foo.hpp Bar.hpp - // 2. struct Foo; struct Bar : Foo; - // 3. struct Foo is removed from Foo.hpp, but our parser only recieves Foo.hpp as file changed--and can't figure out that there is still a reference to Foo in Bar.hpp - // possible solutions - // - use some kind of database scanner to review all references to a class when removing (e.g. detect for logic error on foreign key linked columns) - // - follow the file links in database, and propagate parsing to those files in the hierarchy - // - pretty much defeats the purpose of using an incremental parser: some classes like GameObject will have links throughout a very large portion of the project code - // - [x] out of parser generation - // - [ ] database readback - // - [ ] full database based generation (tentative) - CodegenRuntimeModel runtimeModel; - CodegenArchiveModel archiveModel(as.databaseFilePath); - - as.runtimeModel = &runtimeModel; - as.archiveModel = &archiveModel; - - // Positional argument pass - for (int i = 1; i < argc; ++i) { - std::string_view arg(argv[i]); - if (arg.starts_with("--")) { - continue; - } - - DEBUG_PRINTF("Processing input command %s\n", argv[i]); - - auto separatorLoc = arg.find(':'); - if (separatorLoc != std::string_view::npos) { - auto opcodeString = arg.substr(0, separatorLoc); - auto opcode = ParseInputOpcode(opcodeString); - auto operand = arg.substr(separatorLoc + 1); - - HandleArgument(as, opcode, operand); - } - } - - for (auto decl : as.enumsToRevisit) { - if (!decl->generating) { - continue; - } - - auto& headerOutput = decl->sourceFile->postHeaderOutput; - auto& sourceOutput = decl->sourceFile->postSourceOutput; - GenerateForEnum(headerOutput, sourceOutput, *decl); - } - for (auto decl : as.structsToRevisit) { - if (!decl->generating) { - continue; - } - - auto& headerOutput = decl->sourceFile->postHeaderOutput; - auto& sourceOutput = decl->sourceFile->postSourceOutput; - - // Always-on metdata - GenerateForClassMetadata(headerOutput, sourceOutput, *decl); - - if (decl->generatingInheritanceHiearchy) { - // TODO - } - - for (auto& property : decl->memberVariables) { - if (property.isGetterGenerated) { - // TODO work with pass-by-value vs pass-by-reference - // this probably needs libclang to detect the size and existance of trivial copy-ctors - CodegenOutputThing data; - APPEND_FMT_LN(data.text, "const %s& %s::%s() const {", property.type.c_str(), property.containerStruct->fullname->c_str(), property.getterName.c_str()); - APPEND_FMT_LN(data.text, " return %s;", property.name.c_str()); - APPEND_LIT_LN(data.text, "}"); - - sourceOutput.AddOutputThing(std::move(data)); - } - if (property.isSetterGenerated) { - CodegenOutputThing data; - APPEND_FMT_LN(data.text, "void %s::%s(const %s& value) const {", property.containerStruct->fullname->c_str(), property.setterName.c_str(), property.type.c_str()); - APPEND_FMT_LN(data.text, " this->%s = value;", property.name.c_str()); - APPEND_LIT_LN(data.text, "}"); - - sourceOutput.AddOutputThing(std::move(data)); - } - } - for (auto& method : decl->memberFunctions) { - // TODO - } - } - - archiveModel.Store(runtimeModel); - - // Write output files - for (auto&& [_, sourceFile] : as.sourceFiles) { - INPLACE_FMT(hpp, "%.*s.gh.inl", PRINTF_STRING_VIEW(sourceFile.filename)); - INPLACE_FMT(cpp, "%.*s.gs.inl", PRINTF_STRING_VIEW(sourceFile.filename)); - Utils::ProduceGeneratedHeader(hpp, sourceFile.postHeaderOutput, cpp, sourceFile.postSourceOutput); - - INPLACE_FMT(generatedHeaderInlName, "%.*s/%s", PRINTF_STRING_VIEW(as.outputDir), hpp); - Utils::WriteOutputFile(sourceFile.postHeaderOutput, generatedHeaderInlName); - INPLACE_FMT(generatedSourceInlName, "%.*s/%s", PRINTF_STRING_VIEW(as.outputDir), cpp); - Utils::WriteOutputFile(sourceFile.postSourceOutput, generatedSourceInlName); - INPLACE_FMT(generatedCppName, "%.*s/%.*s.g.cpp", PRINTF_STRING_VIEW(as.outputDir), PRINTF_STRING_VIEW(sourceFile.filename)); - Utils::WriteOutputFile(sourceFile.tuOutput, generatedCppName); - } - - return 0; -} - -// TODO move this function to CodegenDecl.cpp, after making LUT able to cross TUs -std::string_view DeclEnum::GetUnderlyingTypeName() const { - return FSTR_LUT_LOOKUP(EnumUnderlyingType, underlyingType); -} |