aboutsummaryrefslogtreecommitdiff
path: root/source/20-codegen-compiler/main.cpp
diff options
context:
space:
mode:
authorrtk0c <[email protected]>2023-10-19 22:50:07 -0700
committerrtk0c <[email protected]>2023-10-19 22:50:07 -0700
commit2c92e07f337e42cf58970443f9de678f85a9b2a4 (patch)
tree075d5407e1e12a9d35cbee6e4c20ad34e0765c42 /source/20-codegen-compiler/main.cpp
parent615809c036f604bce4582cea8ad49c64693f4f45 (diff)
The great renaming: switch to "module style"
Diffstat (limited to 'source/20-codegen-compiler/main.cpp')
-rw-r--r--source/20-codegen-compiler/main.cpp1443
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);
-}