From 7d8bca09b3c4bf1418e758bd3bd0d6f85672153e Mon Sep 17 00:00:00 2001 From: rtk0c Date: Mon, 30 May 2022 15:52:19 -0700 Subject: Changeset: 52 Add support for namespaced enums --- buildtools/codegen/main.cpp | 191 +++++++++++++++++++++++++++++++++----------- 1 file changed, 146 insertions(+), 45 deletions(-) (limited to 'buildtools/codegen/main.cpp') 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 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 %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 %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 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 '}': { - bracePairDepth--; - CheckBraceDepth(bracePairDepth); - } break; + case CLEX_ext_single_char: + switch (token.text[0]) { + case '{': { + currentBraceDepth++; + CheckBraceDepth(currentBraceDepth); + } 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 : 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]; -- cgit v1.2.3-70-g09d2