aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrtk0c <[email protected]>2022-05-30 15:52:19 -0700
committerrtk0c <[email protected]>2022-05-30 15:52:19 -0700
commit7d8bca09b3c4bf1418e758bd3bd0d6f85672153e (patch)
treef6e411e1ec949187889d268f2993e38cddb74d53
parentce9559e8c2b69d46cff064241bd9a04c014af44f (diff)
Changeset: 52 Add support for namespaced enums
-rw-r--r--buildtools/codegen/CodegenDecl.hpp9
-rw-r--r--buildtools/codegen/CodegenInput.inl35
-rw-r--r--buildtools/codegen/CodegenUtils.inl33
-rw-r--r--buildtools/codegen/main.cpp189
-rw-r--r--buildtools/codegen/tests/examples/TestEnum.hpp.txt22
-rw-r--r--source/GraphicsTags.hpp11
-rw-r--r--source/main.cpp5
7 files changed, 244 insertions, 60 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);
+}
diff --git a/source/GraphicsTags.hpp b/source/GraphicsTags.hpp
index 09e62bf..f83b99c 100644
--- a/source/GraphicsTags.hpp
+++ b/source/GraphicsTags.hpp
@@ -7,14 +7,6 @@
#include <MacrosCodegen.hpp>
-enum TestEnum {
- TE_Position,
- TE_Color,
- TE_TexCoord,
- TE_COUNT,
-};
-BRUSSEL_ENUM(TestEnum, ToString FromString ExcludeHeuristics);
-
namespace Tags {
/// Vertex element semantics, used to identify the meaning of vertex buffer contents
enum VertexElementSemantic {
@@ -42,6 +34,7 @@ enum VertexElementSemantic {
VES_Generic,
VES_COUNT,
};
+BRUSSEL_ENUM(VertexElementSemantic, ToString FromString ExcludeHeuristics);
std::string_view NameOf(VertexElementSemantic semantic);
VertexElementSemantic FindVertexElementSemantic(std::string_view name);
@@ -82,6 +75,8 @@ enum VertexElementType {
VET_Ushort4Norm,
VET_NORM_END = VET_Ushort4Norm,
};
+// TODO this enum isn't continuous, not supported yet
+// BRUSSEL_ENUM(VertexElementType, ToString FromString ExcludeHeuristics);
int SizeOf(VertexElementType type);
int VectorLenOf(VertexElementType type);
diff --git a/source/main.cpp b/source/main.cpp
index 353746b..c49fc0b 100644
--- a/source/main.cpp
+++ b/source/main.cpp
@@ -3,11 +3,9 @@
#include "AppConfig.hpp"
#include "CommonVertexIndex.hpp"
#include "EditorGuizmo.hpp"
-#include "GraphicsTags.hpp"
#include "Ires.hpp"
#include "Level.hpp"
#include "Material.hpp"
-#include "Metadata.hpp"
#include "Shader.hpp"
#define GLFW_INCLUDE_NONE
@@ -104,9 +102,6 @@ fs::path GetEnvVar(const char* name, const char* backup) {
int main(int argc, char* argv[]) {
using namespace Tags;
- auto str = Metadata::EnumToString(TE_Color);
- printf("%.*s", PRINTF_STRING_VIEW(str));
-
constexpr auto kImGuiBackend = "imgui-backend";
constexpr auto kGameDataDir = "game-data-directory";
constexpr auto kGameAssetDir = "game-asset-directory";