From 66b63ae887f553e1cac813546a6b827a9c85d80c Mon Sep 17 00:00:00 2001 From: rtk0c Date: Sun, 29 May 2022 16:14:26 -0700 Subject: Changeset: 43 Add tostring code gen for enums --- buildtools/codegen/CodegenDecl.cpp | 48 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 buildtools/codegen/CodegenDecl.cpp (limited to 'buildtools/codegen/CodegenDecl.cpp') diff --git a/buildtools/codegen/CodegenDecl.cpp b/buildtools/codegen/CodegenDecl.cpp new file mode 100644 index 0000000..ac6bb01 --- /dev/null +++ b/buildtools/codegen/CodegenDecl.cpp @@ -0,0 +1,48 @@ +#include "CodegenDecl.hpp" + +#include + +static EnumValuePattern NextPattern(EnumValuePattern val) { + return (EnumValuePattern)(val + 1); +} + +EnumValuePattern DeclEnum::CalcPattern() const { + if (elements.empty()) return EVP_Continuous; + + auto pattern = EVP_Continuous; +restart: + auto lastVal = elements[0].value; + for (size_t i = 1; i < elements.size(); ++i) { + auto currVal = elements[i].value; + switch (pattern) { + case EVP_Continuous: { + bool satisfy = lastVal + 1 == currVal; + if (!satisfy) { + pattern = NextPattern(pattern); + goto restart; + } + } break; + + case EVP_Bits: { + bool satisfy = (lastVal << 1) == currVal; + if (!satisfy) { + pattern = NextPattern(pattern); + goto restart; + } + } break; + + // A random pattern can match anything + case EVP_Random: + case EVP_COUNT: break; + } + lastVal = currVal; + } + + return pattern; +} + +void DeclEnum::CalcPatternIfNecessary() { + if (pattern == EVP_COUNT) { + CalcPattern(); + } +} -- cgit v1.2.3-70-g09d2 From 5112858f9a4adcf76240bcddad19179a5c56d4f3 Mon Sep 17 00:00:00 2001 From: rtk0c Date: Sun, 29 May 2022 18:30:03 -0700 Subject: Changeset: 47 Add fromstring codegen for enums --- buildtools/codegen/CodegenDecl.cpp | 5 +- buildtools/codegen/CodegenDecl.hpp | 4 +- buildtools/codegen/CodegenMacros.hpp | 30 ++++++++ buildtools/codegen/CodegenOutput.inl | 25 +++++-- buildtools/codegen/main.cpp | 98 +++++++++++++++++++------- buildtools/codegen/tests/examples/TestEnum.hpp | 6 +- 6 files changed, 130 insertions(+), 38 deletions(-) create mode 100644 buildtools/codegen/CodegenMacros.hpp (limited to 'buildtools/codegen/CodegenDecl.cpp') diff --git a/buildtools/codegen/CodegenDecl.cpp b/buildtools/codegen/CodegenDecl.cpp index ac6bb01..7cf21ce 100644 --- a/buildtools/codegen/CodegenDecl.cpp +++ b/buildtools/codegen/CodegenDecl.cpp @@ -41,8 +41,9 @@ restart: return pattern; } -void DeclEnum::CalcPatternIfNecessary() { +EnumValuePattern DeclEnum::GetPattern() const { if (pattern == EVP_COUNT) { - CalcPattern(); + pattern = CalcPattern(); } + return pattern; } diff --git a/buildtools/codegen/CodegenDecl.hpp b/buildtools/codegen/CodegenDecl.hpp index c2d8dbb..b7c1ee4 100644 --- a/buildtools/codegen/CodegenDecl.hpp +++ b/buildtools/codegen/CodegenDecl.hpp @@ -44,10 +44,10 @@ struct DeclEnum { std::vector elements; EnumUnderlyingType underlyingType; // Start with invalid value, calculate on demand - EnumValuePattern pattern = EVP_COUNT; + mutable EnumValuePattern pattern = EVP_COUNT; EnumValuePattern CalcPattern() const; - void CalcPatternIfNecessary(); + EnumValuePattern GetPattern() const; }; struct DeclFunctionArgument { diff --git a/buildtools/codegen/CodegenMacros.hpp b/buildtools/codegen/CodegenMacros.hpp new file mode 100644 index 0000000..84c9d09 --- /dev/null +++ b/buildtools/codegen/CodegenMacros.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include + +// I give up, hopefully nothing overflows this buffer +// TODO handle buffer sizing properly + +#define APPEND_LIT(out, str) out += str + +#define APPEND_FMT(out, format, ...) \ + { \ + char buffer[65536]; \ + snprintf(buffer, sizeof(buffer), format, __VA_ARGS__); \ + out += buffer; \ + } + +#define WRITE_LIT(file, str) fwrite(str, sizeof(char), sizeof(str) - 1, file) + +// NOTE: snprintf() returns the size written (given an infinite buffer) not including \0 +#define WRITE_FMT(file, format, ...) \ + { \ + char buffer[65536]; \ + int size = snprintf(buffer, sizeof(buffer), format, __VA_ARGS__); \ + fwrite(buffer, sizeof(char), std::min(size, sizeof(buffer)), file); \ + } + +#define APPEND_LIT_LN(out, str) APPEND_LIT(out, (str "\n")) +#define APPEND_FMT_LN(out, format, ...) APPEND_FMT(out, (format "\n"), __VA_ARGS__) +#define WRITE_LIT_LN(out, str) WRITE_LIT(out, (str "\n")) +#define WRITE_FMT_LN(out, format, ...) WRITE_FMT(out, (format "\n"), __VA_ARGS__) diff --git a/buildtools/codegen/CodegenOutput.inl b/buildtools/codegen/CodegenOutput.inl index 752682c..d1f2e50 100644 --- a/buildtools/codegen/CodegenOutput.inl +++ b/buildtools/codegen/CodegenOutput.inl @@ -1,7 +1,9 @@ #pragma once #include "CodegenDecl.hpp" +#include "CodegenMacros.hpp" +#include #include #include #include @@ -31,15 +33,30 @@ public: void MergeContents(CodegenOutput other) { std::move(other.mOutThings.begin(), other.mOutThings.end(), std::back_inserter(this->mOutThings)); + std::move(other.mOutStructs.begin(), other.mOutStructs.end(), std::back_inserter(this->mOutStructs)); + std::move(other.mOutEnums.begin(), other.mOutEnums.end(), std::back_inserter(this->mOutEnums)); + std::move(other.mOutFunctions.begin(), other.mOutFunctions.end(), std::back_inserter(this->mOutFunctions)); } void Write(FILE* file) { -#define WRITE_LITERAL(str) fwrite(str, sizeof(char), sizeof(str) - 1, file) for (auto& thing : mOutThings) { - WRITE_LITERAL("// Output thing\n"); + WRITE_LIT(file, "// Output thing\n"); fwrite(thing.text.c_str(), sizeof(char), thing.text.size(), file); - WRITE_LITERAL("\n"); + WRITE_LIT(file, "\n"); + } + + for (auto& declStruct : mOutStructs) { + WRITE_FMT(file, "struct %s {\n", declStruct.name.c_str()); + // TODO + WRITE_LIT(file, "}\n"); + } + + for (auto& declEnum : mOutEnums) { + // TODO + } + + for (auto& declFunc : mOutFunctions) { + // TODO } -#undef WRITE_LITERAL } }; diff --git a/buildtools/codegen/main.cpp b/buildtools/codegen/main.cpp index 334b195..df52b66 100644 --- a/buildtools/codegen/main.cpp +++ b/buildtools/codegen/main.cpp @@ -1,5 +1,6 @@ #include "CodegenConfig.hpp" #include "CodegenDecl.hpp" +#include "CodegenMacros.hpp" #include "CodegenInput.inl" #include "CodegenOutput.inl" @@ -10,6 +11,8 @@ #include #include +#include +#include #include #include #include @@ -23,8 +26,11 @@ using namespace std::literals; namespace fs = std::filesystem; +// TODO handle namespace + struct AppState { - CodegenOutput mainOutput; + CodegenOutput headerOutput; + CodegenOutput sourceOutput; }; enum { @@ -229,6 +235,7 @@ BSTR_LUT_DECL(StructMetaGenOptions, 0, SMGO_COUNT) { enum EnumMetaGenOptions { EMGO_ToString, + EMGO_FromString, EMGO_ExcludeUseHeuristics, EMGO_COUNT, }; @@ -236,31 +243,24 @@ enum EnumMetaGenOptions { BSTR_LUT_DECL(EnumMetaGenOptions, 0, EMGO_COUNT) { BSTR_LUT_MAP_FOR(EnumMetaGenOptions); BSTR_LUT_MAP(EMGO_ToString, "ToString"); + BSTR_LUT_MAP(EMGO_FromString, "FromString"); BSTR_LUT_MAP(EMGO_ExcludeUseHeuristics, "ExcludeHeuristics"); } -// I give up, hopefully nothing overflows this buffer -#define APPEND_SPRINTF(out, format, ...) \ - { \ - char buffer[65536]; \ - snprintf(buffer, sizeof(buffer), format, __VA_ARGS__); \ - out += buffer; \ - } - std::string GenerateEnumStringArray(CodegenOutput& out, const DeclEnum& decl, bool useHeruistics) { std::string arrayName; - APPEND_SPRINTF(arrayName, "gCG_%s_StringTable", decl.name.c_str()); + APPEND_FMT(arrayName, "gCG_%s_Val2Str", decl.name.c_str()); CodegenOutputThing thing; - APPEND_SPRINTF(thing.text, "const char* %s[] = {\n", arrayName.c_str()); + APPEND_FMT_LN(thing.text, "const char* %s[] = {", arrayName.c_str()); for (auto& elm : decl.elements) { if (useHeruistics && elm.name.ends_with("COUNT")) { continue; } - APPEND_SPRINTF(thing.text, "\"%s\",\n", elm.name.c_str()); + APPEND_FMT_LN(thing.text, "\"%s\",", elm.name.c_str()); } - thing.text += "};\n"; + APPEND_LIT_LN(thing.text, "};"); out.AddOutputThing(std::move(thing)); return arrayName; @@ -277,9 +277,9 @@ void GenerateForEnum(CodegenOutput& out, const DeclEnum& decl, EnumFlags string lookup table and function - switch (decl.CalcPattern()) { + switch (decl.GetPattern()) { case EVP_Continuous: { auto arrayName = GenerateEnumStringArray(out, decl, useExcludeHeuristics); int minVal = decl.elements.front().value; @@ -293,25 +293,57 @@ void GenerateForEnum(CodegenOutput& out, const DeclEnum& decl, EnumFlags= %d) return {};\n", maxVal); - APPEND_SPRINTF(lookupFunction.text, " return %s[value - %d];\n", arrayName.c_str(), minVal); - lookupFunction.text += "}\n"; + auto& o = lookupFunction.text; + APPEND_FMT_LN(o, "std::string_view EnumToString_%s(%s value) {", decl.name.c_str(), decl.name.c_str()); + APPEND_FMT_LN(o, " if (value < 0 || value >= %d) return {};", maxVal); + APPEND_FMT_LN(o, " return %s[value - %d];", arrayName.c_str(), minVal); + APPEND_LIT_LN(o, "}"); out.AddOutputThing(std::move(lookupFunction)); } break; case EVP_Bits: { auto arrayName = GenerateEnumStringArray(out, decl, useExcludeHeuristics); + // TODO } break; case EVP_Random: { auto mapName = GenerateEnumStringMap(out, decl, useExcludeHeuristics); + // TODO } break; case EVP_COUNT: break; } } + + if (options.IsSet(EMGO_FromString)) { + // Generate string -> value lookup table + char mapName[1024]; + 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()); + } + APPEND_LIT_LN(o1, "};"); + + // Generate lookup function + CodegenOutputThing lookupFunction; + auto& o2 = lookupFunction.text; + APPEND_FMT_LN(o2, "std::optional<%s> EnumFromString_%s(std::string_view value) {", decl.name.c_str(), decl.name.c_str()); + APPEND_FMT_LN(o2, " auto iter = %s.find(value);", mapName); + APPEND_FMT_LN(o2, " if (iter != %s.end()) {", mapName); + APPEND_LIT_LN(o2, " return iter->second;"); + APPEND_LIT_LN(o2, " } else {"); + APPEND_LIT_LN(o2, " return {};"); + APPEND_LIT_LN(o2, " }"); + APPEND_LIT_LN(o2, "}"); + + out.AddOutputThing(std::move(lookupTable)); + out.AddOutputThing(std::move(lookupFunction)); + } } void HandleInputFile(AppState& state, std::string_view source) { @@ -499,7 +531,7 @@ void HandleInputFile(AppState& state, std::string_view source) { printf("[WARNING] unbalanced brace at end of file."); } - state.mainOutput.MergeContents(std::move(fileOutput)); + state.sourceOutput.MergeContents(std::move(fileOutput)); } std::string ReadFileAtOnce(const fs::path& path) { @@ -577,17 +609,29 @@ int main(int argc, char* argv[]) { AppState state; + state.sourceOutput.AddOutputThing(CodegenOutputThing{ + .text = &R"""( +// This file is generated. Any changes will be overidden when building. + +#include +#include +#include +#include +using namespace std::literals; + )"""[1], + }); + // 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 < 2) { // NOTE: keep in sync with various enum options and parser code printf(&R"""( - USAGE: codegen.exe [:]... - where : the _file_ to write generated contents to - is one of: - "single" process this file only - "rec" starting at the given directory , recursively process all .h .c .hpp .cpp files - )"""[1]); +USAGE: codegen.exe [:]... +where : the _file_ to write generated contents to + is one of: + "single" process this file only + "rec" starting at the given directory , recursively process all .h .c .hpp .cpp files +)"""[1]); return -1; } @@ -615,7 +659,7 @@ int main(int argc, char* argv[]) { return -1; } DEFER { fclose(outputFile); }; - state.mainOutput.Write(outputFile); + state.sourceOutput.Write(outputFile); } return 0; diff --git a/buildtools/codegen/tests/examples/TestEnum.hpp b/buildtools/codegen/tests/examples/TestEnum.hpp index c498cd9..6b4ab33 100644 --- a/buildtools/codegen/tests/examples/TestEnum.hpp +++ b/buildtools/codegen/tests/examples/TestEnum.hpp @@ -5,14 +5,14 @@ enum MyEnum { EnumElement2, EnumElement3, }; -BRUSSEL_ENUM(MyEnum, ToString); +BRUSSEL_ENUM(MyEnum, ToString FromString); enum CountedEnumAll { CEA_Foo, CEA_Bar, CEA_COUNT, }; -BRUSSEL_ENUM(CountedEnumAll, ToString); +BRUSSEL_ENUM(CountedEnumAll, ToString FromString); enum CountedEnum { CE_Foo, @@ -20,4 +20,4 @@ enum CountedEnum { CE_FooBar, CE_COUNT, }; -BRUSSEL_ENUM(CountedEnum, ToString ExcludeHeuristics); +BRUSSEL_ENUM(CountedEnum, ToString FromString ExcludeHeuristics); -- cgit v1.2.3-70-g09d2