diff options
author | rtk0c <[email protected]> | 2022-05-30 15:52:19 -0700 |
---|---|---|
committer | rtk0c <[email protected]> | 2022-05-30 15:52:19 -0700 |
commit | 7d8bca09b3c4bf1418e758bd3bd0d6f85672153e (patch) | |
tree | f6e411e1ec949187889d268f2993e38cddb74d53 /buildtools | |
parent | ce9559e8c2b69d46cff064241bd9a04c014af44f (diff) |
Changeset: 52 Add support for namespaced enums
Diffstat (limited to 'buildtools')
-rw-r--r-- | buildtools/codegen/CodegenDecl.hpp | 9 | ||||
-rw-r--r-- | buildtools/codegen/CodegenInput.inl | 35 | ||||
-rw-r--r-- | buildtools/codegen/CodegenUtils.inl | 33 | ||||
-rw-r--r-- | buildtools/codegen/main.cpp | 189 | ||||
-rw-r--r-- | buildtools/codegen/tests/examples/TestEnum.hpp.txt | 22 |
5 files changed, 241 insertions, 47 deletions
diff --git a/buildtools/codegen/CodegenDecl.hpp b/buildtools/codegen/CodegenDecl.hpp index b7c1ee4..32d5445 100644 --- a/buildtools/codegen/CodegenDecl.hpp +++ b/buildtools/codegen/CodegenDecl.hpp @@ -3,8 +3,15 @@ #include <string> #include <vector> +struct DeclNamespace { + DeclNamespace* container = nullptr; + std::string name; + std::string_view fullname; // View into storage map key +}; + // Structs or classes struct DeclStruct { + DeclNamespace* container = nullptr; std::string name; }; @@ -40,6 +47,7 @@ struct DeclEnumElement { }; struct DeclEnum { + DeclNamespace* container = nullptr; std::string name; std::vector<DeclEnumElement> elements; EnumUnderlyingType underlyingType; @@ -56,6 +64,7 @@ struct DeclFunctionArgument { }; struct DeclFunction { + DeclNamespace* container = nullptr; // Things like extern, static, etc. that gets written before the function return type std::string prefix; std::string name; diff --git a/buildtools/codegen/CodegenInput.inl b/buildtools/codegen/CodegenInput.inl index 80a39d0..0809e7f 100644 --- a/buildtools/codegen/CodegenInput.inl +++ b/buildtools/codegen/CodegenInput.inl @@ -3,20 +3,26 @@ #include "CodegenConfig.hpp" #include "CodegenDecl.hpp" +#include "CodegenUtils.inl" + #include <Utils.hpp> #include <robin_hood.h> #include <cinttypes> #include <string> +#include <string_view> #include <vector> +using namespace std::literals; + class CodegenInput { private: std::vector<DeclEnum> mEnums; - robin_hood::unordered_map<std::string, size_t, StringHash, StringEqual> mDeclByName; + robin_hood::unordered_flat_map<std::string, size_t, StringHash, StringEqual> mDeclByName; + robin_hood::unordered_node_map<std::string, DeclNamespace, StringHash, StringEqual> mNamespaces; public: - void AddEnum(DeclEnum decl) { + void AddEnum(std::string fullname, DeclEnum decl) { #if CODEGEN_DEBUG_PRINT printf("Committed enum '%s'\n", decl.name.c_str()); for (auto& elm : decl.elements) { @@ -24,10 +30,20 @@ public: } #endif - mDeclByName.try_emplace(std::string(decl.name), mEnums.size()); + mDeclByName.try_emplace(std::move(fullname), mEnums.size()); mEnums.push_back(std::move(decl)); } + DeclNamespace* AddNamespace(DeclNamespace ns) { + auto path = Utils::MakeFullName(""sv, &ns); + auto [iter, success] = mNamespaces.try_emplace(std::move(path), std::move(ns)); + auto& nsRef = iter->second; + if (success) { + nsRef.fullname = iter->first; + } + return &nsRef; + } + const DeclEnum* FindEnumByName(std::string_view name) const { // TODO handle multiple kinds of decl auto iter = mDeclByName.find(name); @@ -37,4 +53,17 @@ public: return nullptr; } } + + const DeclNamespace* FindNamespace(std::string_view fullname) const { + auto iter = mNamespaces.find(fullname); + if (iter != mNamespaces.end()) { + return &iter->second; + } else { + return nullptr; + } + } + + DeclNamespace* FindNamespace(std::string_view name) { + return const_cast<DeclNamespace*>(const_cast<const CodegenInput*>(this)->FindNamespace(name)); + } }; diff --git a/buildtools/codegen/CodegenUtils.inl b/buildtools/codegen/CodegenUtils.inl index ea46ac1..f9d913e 100644 --- a/buildtools/codegen/CodegenUtils.inl +++ b/buildtools/codegen/CodegenUtils.inl @@ -46,6 +46,34 @@ bool WriteOutputFile(const CodegenOutput& output, std::string_view dir, std::str return true; } +std::string MakeFullName(std::string_view name, DeclNamespace* ns = nullptr) { + size_t length = 0; + std::vector<std::string_view> components; + if (!name.empty()) { + components.push_back(name); + length += name.length(); + } + + DeclNamespace* currentNamespace = ns; + while (currentNamespace) { + components.push_back(currentNamespace->name); + length += currentNamespace->name.size() + /*::*/ 2; + currentNamespace = currentNamespace->container; + } + + std::string fullname; + fullname.reserve(length); + for (auto it = components.rbegin(); it != components.rend(); ++it) { + fullname += *it; + fullname += "::"; + } + // Get rid of the last "::" + fullname.pop_back(); + fullname.pop_back(); + + return fullname; +} + void ProduceGeneratedHeaderFileHeader(CodegenOutput& output) { output.AddOutputThing(CodegenOutputThing{ .text = &R"""( @@ -53,6 +81,9 @@ void ProduceGeneratedHeaderFileHeader(CodegenOutput& output) { #pragma once #include <MetadataBase.hpp> + +#include <cstddef> +#include <cstdint> )"""[1], }); } @@ -63,6 +94,8 @@ void ProduceGeneratedSourceFileHeader(CodegenOutput& output) { // This file is generated. Any changes will be overidden when building. #include "GeneratedCode.hpp" +#include <cstddef> +#include <cstdint> #include <frozen/string.h> #include <frozen/unordered_map.h> using namespace std::literals; diff --git a/buildtools/codegen/main.cpp b/buildtools/codegen/main.cpp index 3a3e443..2c259a4 100644 --- a/buildtools/codegen/main.cpp +++ b/buildtools/codegen/main.cpp @@ -75,6 +75,7 @@ STR_LUT_DECL(ClexNames, CLEX_eof, CLEX_ext_COUNT) { } enum CppKeyword { + CKw_Namespace, CKw_Struct, CKw_Class, CKw_Enum, @@ -83,6 +84,7 @@ enum CppKeyword { BSTR_LUT_DECL(CppKeyword, 0, CKw_COUNT) { BSTR_LUT_MAP_FOR(CppKeyword); + BSTR_LUT_MAP(CKw_Namespace, "namespace"); BSTR_LUT_MAP(CKw_Struct, "struct"); BSTR_LUT_MAP(CKw_Class, "class"); BSTR_LUT_MAP(CKw_Enum, "enum"); @@ -277,7 +279,27 @@ std::string GenerateEnumStringMap(CodegenOutput& out, const DeclEnum& decl, bool } void GenerateForEnum(CodegenOutput& headerOut, CodegenOutput& sourceOut, const DeclEnum& decl, EnumFlags<EnumMetaGenOptions> options) { - bool useExcludeHeuristics = options.IsSet(EMGO_ExcludeUseHeuristics); + char enumName[2048]; + if (decl.container) { + snprintf(enumName, sizeof(enumName), "%.*s::%s", PRINTF_STRING_VIEW(decl.container->fullname), decl.name.c_str()); + } else { + strncpy(enumName, decl.name.c_str(), sizeof(enumName)); + } + + auto useExcludeHeuristics = options.IsSet(EMGO_ExcludeUseHeuristics); + auto filteredElements = [&]() { + if (useExcludeHeuristics) { + decltype(decl.elements) result; + for (auto& elm : decl.elements) { + if (elm.name.ends_with("COUNT")) continue; + + result.push_back(elm); + } + return result; + } else { + return decl.elements; + } + }(); if (options.IsSet(EMGO_ToString)) { // Generate value -> string lookup table and function @@ -285,23 +307,18 @@ void GenerateForEnum(CodegenOutput& headerOut, CodegenOutput& sourceOut, const D switch (decl.GetPattern()) { case EVP_Continuous: { auto arrayName = GenerateEnumStringArray(sourceOut, decl, useExcludeHeuristics); - int minVal = decl.elements.front().value; - int maxVal = decl.elements.back().value; - if (useExcludeHeuristics && - decl.elements.back().name.ends_with("COUNT") && - decl.elements.size() >= 2) - { - // Skip the last *_COUNT element if instructed to use heuristics - maxVal = decl.elements[decl.elements.size() - 2].value; - } + int minVal = filteredElements.empty() ? 0 : filteredElements.front().value; + int maxVal = filteredElements.empty() ? 0 : filteredElements.back().value; CodegenOutputThing lookupFunctionDef; - auto& o2 = lookupFunctionDef.text; - APPEND_LIT_LN(o2, "template <>"); - APPEND_FMT_LN(o2, "std::string_view Metadata::EnumToString<%s>(%s value) {", decl.name.c_str(), decl.name.c_str()); - APPEND_FMT_LN(o2, " if (value < 0 || value >= %d) return {};", maxVal); - APPEND_FMT_LN(o2, " return %s[value - %d];", arrayName.c_str(), minVal); - APPEND_LIT_LN(o2, "}"); + { + auto& o = lookupFunctionDef.text; + APPEND_LIT_LN(o, "template <>"); + APPEND_FMT_LN(o, "std::string_view Metadata::EnumToString<%s>(%s value) {", enumName, enumName); + APPEND_FMT_LN(o, " if (value < %d || value > %d) return {};", minVal, maxVal); + APPEND_FMT_LN(o, " return %s[value - %d];", arrayName.c_str(), minVal); + APPEND_LIT_LN(o, "}"); + } sourceOut.AddOutputThing(std::move(lookupFunctionDef)); } break; @@ -323,28 +340,34 @@ void GenerateForEnum(CodegenOutput& headerOut, CodegenOutput& sourceOut, const D if (options.IsSet(EMGO_FromString)) { // Generate string -> value lookup table char mapName[1024]; + // TODO mangle to prevent name conflicts of enum in different namespaces snprintf(mapName, sizeof(mapName), "gCG_%s_Str2Val", decl.name.c_str()); CodegenOutputThing lookupTable; - auto& o1 = lookupTable.text; - APPEND_FMT_LN(o1, "constinit frozen::unordered_map<frozen::string, %s, %" PRId64 "> %s = {", decl.name.c_str(), decl.elements.size(), mapName); - for (auto& elm : decl.elements) { - APPEND_FMT_LN(o1, "{\"%s\", %s::%s},", elm.name.c_str(), decl.name.c_str(), elm.name.c_str()); + { + auto& o = lookupTable.text; + // TODO use correct underlying type + APPEND_FMT_LN(o, "constinit frozen::unordered_map<frozen::string, uint64_t, %" PRId64 "> %s = {", filteredElements.size(), mapName); + for (auto& elm : filteredElements) { + APPEND_FMT_LN(o, "{\"%s\", %" PRId64 "},", elm.name.c_str(), elm.value); + } + APPEND_LIT_LN(o, "};"); } - APPEND_LIT_LN(o1, "};"); // Generate lookup function CodegenOutputThing lookupFunctionDef; - auto& o3 = lookupFunctionDef.text; - APPEND_LIT_LN(o3, "template <>"); - APPEND_FMT_LN(o3, "std::optional<%s> Metadata::EnumFromString<%s>(std::string_view value) {", decl.name.c_str(), decl.name.c_str()); - APPEND_FMT_LN(o3, " auto iter = %s.find(value);", mapName); - APPEND_FMT_LN(o3, " if (iter != %s.end()) {", mapName); - APPEND_LIT_LN(o3, " return iter->second;"); - APPEND_LIT_LN(o3, " } else {"); - APPEND_LIT_LN(o3, " return {};"); - APPEND_LIT_LN(o3, " }"); - APPEND_LIT_LN(o3, "}"); + { + auto& o = lookupFunctionDef.text; + APPEND_LIT_LN(o, "template <>"); + APPEND_FMT_LN(o, "std::optional<%s> Metadata::EnumFromString<%s>(std::string_view value) {", enumName, enumName); + APPEND_FMT_LN(o, " auto iter = %s.find(value);", mapName); + APPEND_FMT_LN(o, " if (iter != %s.end()) {", mapName); + APPEND_FMT_LN(o, " return (%s)iter->second;", enumName); + APPEND_LIT_LN(o, " } else {"); + APPEND_LIT_LN(o, " return {};"); + APPEND_LIT_LN(o, " }"); + APPEND_LIT_LN(o, "}"); + } sourceOut.AddOutputThing(std::move(lookupTable)); sourceOut.AddOutputThing(std::move(lookupFunctionDef)); @@ -369,7 +392,32 @@ void HandleInputFile(AppState& state, std::string_view filenameStem, std::string CodegenOutput cgSourceOutput; Utils::ProduceGeneratedSourceFileHeader(cgSourceOutput); - int bracePairDepth = 0; + int currentBraceDepth = 0; + // The current effective namespace, see example + DeclNamespace* currentNamespace = nullptr; + + 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; + }; + std::vector<NamespaceStackframe> nsStack; + + // 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 */ + // } + // } + while (idx < tokens.size()) { auto& token = tokens[idx]; @@ -388,6 +436,40 @@ void HandleInputFile(AppState& state, std::string_view filenameStem, std::string } } switch (keyword) { + case CKw_Namespace: { + ++idx; + incrementTokenIdx = false; + + while (true) { + if (tokens[idx].type != CLEX_id) { + // TODO better error recovery + printf("[ERROR] invalid syntax for namespace\n"); + break; + } + + currentNamespace = cgInput.AddNamespace(DeclNamespace{ + .container = currentNamespace, + .name = tokens[idx].text, + }); + + if (tokens[idx + 1].text[0] == ':' && + tokens[idx + 2].text[0] == ':') + { + // Skip the two ':' tokens, try parse the next identifier + idx += 3; + } else { + break; + } + } + + nsStack.push_back(NamespaceStackframe{ + .ns = currentNamespace, + .depth = currentBraceDepth, + }); + + goto endIdenCase; + } + case CKw_Struct: case CKw_Class: { auto& idenTok = tokens[idx + 1]; // TODO handle end of list @@ -401,6 +483,7 @@ void HandleInputFile(AppState& state, std::string_view filenameStem, std::string incrementTokenIdx = false; DeclEnum enumDecl; + enumDecl.container = currentNamespace; enumDecl.underlyingType = EUT_Int32; // TODO if (tokens[idx].text == "class") { @@ -452,7 +535,8 @@ void HandleInputFile(AppState& state, std::string_view filenameStem, std::string ++idx; } - cgInput.AddEnum(std::move(enumDecl)); + auto fullname = Utils::MakeFullName(enumDecl.name, currentNamespace); + cgInput.AddEnum(std::move(fullname), std::move(enumDecl)); goto endIdenCase; } @@ -488,7 +572,7 @@ void HandleInputFile(AppState& state, std::string_view filenameStem, std::string } auto& enumName = argList[0][0]->text; - auto enumDecl = cgInput.FindEnumByName(enumName); + auto enumDecl = cgInput.FindEnumByName(Utils::MakeFullName(enumName, currentNamespace)); if (!enumDecl) { printf("[ERROR] BRUSSEL_ENUM: referring to non-existent enum '%s'\n", enumName.c_str()); break; @@ -519,15 +603,32 @@ void HandleInputFile(AppState& state, std::string_view filenameStem, std::string break; } - case '{': { - bracePairDepth++; - CheckBraceDepth(bracePairDepth); - } break; + case CLEX_ext_single_char: + switch (token.text[0]) { + case '{': { + currentBraceDepth++; + CheckBraceDepth(currentBraceDepth); + } break; - case '}': { - bracePairDepth--; - CheckBraceDepth(bracePairDepth); - } break; + case '}': { + currentBraceDepth--; + CheckBraceDepth(currentBraceDepth); + + if (!nsStack.empty()) { + auto& ns = nsStack.back(); + if (ns.depth == currentBraceDepth) { + nsStack.pop_back(); + + if (!nsStack.empty()) { + currentNamespace = nsStack.back().ns; + } else { + currentNamespace = nullptr; + } + } + } + } break; + } + break; } if (incrementTokenIdx) { @@ -535,7 +636,7 @@ void HandleInputFile(AppState& state, std::string_view filenameStem, std::string } } - if (bracePairDepth != 0) { + if (currentBraceDepth != 0) { printf("[WARNING] unbalanced brace at end of file."); } @@ -632,7 +733,7 @@ where <output path>: the directory to write generated contents to. This will N } state.outputDir = std::string_view(argv[1]); - DEBUG_PRINTF("Outputting to directory %s.\n", outputDir); + DEBUG_PRINTF("Outputting to directory %.*s.\n", PRINTF_STRING_VIEW(state.outputDir)); for (int i = 2; i < argc; ++i) { const char* argRaw = argv[i]; diff --git a/buildtools/codegen/tests/examples/TestEnum.hpp.txt b/buildtools/codegen/tests/examples/TestEnum.hpp.txt index e596c5e..441d97c 100644 --- a/buildtools/codegen/tests/examples/TestEnum.hpp.txt +++ b/buildtools/codegen/tests/examples/TestEnum.hpp.txt @@ -19,3 +19,25 @@ enum CountedEnum { CE_COUNT, }; BRUSSEL_ENUM(CountedEnum, ToString FromString ExcludeHeuristics); + +namespace MyNamespace { +enum MyNamespacedEnum { + MNE_Foo, + MNE_Bar, +}; +BRUSSEL_ENUM(MyNamespacedEnum, ToString FromString ExcludeHeuristics); + +namespace details { + enum MyNamespacedEnum { + MNE_Foo, + MNE_Bar, + }; + BRUSSEL_ENUM(MyNamespacedEnum, ToString FromString ExcludeHeuristics); +} +} + +namespace foo::details { +enum Enum { +}; +BRUSSEL_ENUM(Enum, ToString FromString ExcludeHeuristics); +} |