diff options
Diffstat (limited to 'source/CodegenCompiler/main.cpp')
-rw-r--r-- | source/CodegenCompiler/main.cpp | 1112 |
1 files changed, 0 insertions, 1112 deletions
diff --git a/source/CodegenCompiler/main.cpp b/source/CodegenCompiler/main.cpp deleted file mode 100644 index 5e052a3..0000000 --- a/source/CodegenCompiler/main.cpp +++ /dev/null @@ -1,1112 +0,0 @@ -#include "CodegenConfig.hpp" -#include "CodegenDecl.hpp" -#include "CodegenInput.hpp" -#include "CodegenLexer.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 - -struct AppState { - std::string_view outputDir; -}; - -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 - // TODO all of these can be suffixde with "int" - RSTR_LUT_MAP(EUT_Int16, "short"); - RSTR_LUT_MAP(EUT_Uint16, "unsigned short"); - 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_Uint32, "unsigned long"); -#else - RSTR_LUT_MAP(EUT_Int64, "long"); - RSTR_LUT_MAP(EUT_Uint64, "unsigned long"); -#endif - RSTR_LUT_MAP(EUT_Int64, "long long"); - RSTR_LUT_MAP(EUT_Uint64, "unsigned long long"); - - // 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_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"); -} - -enum CodegenDirective { - CD_Class, - CD_ClassProperty, - CD_ClassMethod, - CD_Enum, - 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"); -} - -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; -} - -std::vector<const StbLexerToken*>* -GetDirectiveArgument(std::vector<std::vector<const StbLexerToken*>>& list, size_t idx, const char* errMsg = nullptr) { - if (idx < list.size()) { - if (errMsg) { - printf("%s", errMsg); - } - return &list[idx]; - } - return nullptr; -} - -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; -} - -enum StructMetaGenOptions { - // TODO how tf do we implement this one: needs full source scanning - 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_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"); -} - -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, EnumFlags<EnumMetaGenOptions> options) { - 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)); - } - - // TODO mangle to prevent name conflicts of enum in different namespaces - auto& declIdName = decl.name; - - 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 - INPLACE_FMT(val2StrName, "gCG_%s_Val2Str", declIdName.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, enumName); - } - - CodegenOutputThing lookupFunctionDef; - { - 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, " 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 (options.IsSet(EMGO_FromString)) { - // Generate string -> value lookup table - INPLACE_FMT(str2ValName, "gCG_%s_Str2Val", declIdName.c_str()); - - CodegenOutputThing lookupTable; - { - 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(), 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, enumName); - } - - 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, enumName); - 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); - 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) { - // TODO -} - -void GenerateClassFunction(CodegenOutput& headerOutput, CodegenOutput& sourceOutput) { - // TODO -} - -void GenerateForClassMetadata( - CodegenOutput& headerOutput, - CodegenOutput& sourceOutput, - const DeclStruct& decl) // -{ - // TODO mangle - auto declIdName = decl.name.c_str(); - - CodegenOutputThing data; - // TODO generate type id, this needs global scanning - APPEND_FMT_LN(data.text, "const TypeInfo* const gCGtype_%s_BaseClasses[] = {", declIdName); - for (auto& baseClass : decl.baseClasses) { - // TODO get ptr to TypeInfo, this needs global scanning for non-file local classes - } - APPEND_LIT_LN(data.text, "};"); - APPEND_FMT_LN(data.text, "const TypePropertyInfo gCGtype_%s_Properties[] = {", declIdName); - 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, "};"); - APPEND_FMT_LN(data.text, "const TypeInfo gCGtype_%s_TypeInfo = {", declIdName); - APPEND_FMT_LN(data.text, ".name = \"%s\"sv,", declIdName); - APPEND_FMT_LN(data.text, ".parents = gCGtype_%s_BaseClasses,", declIdName); - APPEND_FMT_LN(data.text, ".properties = gCGtype_%s_Properties};", declIdName); - - CodegenOutputThing queryFunc; - APPEND_FMT(queryFunc.text, - "template <>\n" - "const TypeInfo* Metadata::GetTypeInfo<%.*s>() {\n" - " return &gCGtype_%s_TypeInfo;\n" - "}\n", - PRINTF_STRING_VIEW(decl.fullname), - declIdName); - - sourceOutput.AddOutputThing(std::move(data)); - sourceOutput.AddOutputThing(std::move(queryFunc)); -} - -void HandleInputFile(AppState& state, std::string_view filenameStem, std::string_view source) { - CodegenLexer lexer; - lexer.InitializeFrom(source); - -#if CODEGEN_DEBUG_PRINT - printf("BEGIN tokens\n"); - for (auto& token : lexer.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 - - CodegenInput cgInput; - CodegenOutput cgHeaderOutput; - CodegenOutput cgSourceOutput; - { - INPLACE_FMT(hpp, "%.*s.gh.inl", PRINTF_STRING_VIEW(filenameStem)); - INPLACE_FMT(cpp, "%.*s.gs.inl", PRINTF_STRING_VIEW(filenameStem)); - Utils::ProduceGeneratedHeader(hpp, cgHeaderOutput, cpp, cgSourceOutput); - } - CodegenOutput cgStandaloneSourceOutput; - - int currentBraceDepth = 0; - // The current effective namespace, see example - DeclNamespace* currentNamespace = nullptr; - DeclStruct* currentStruct = nullptr; - int currentStructBraceDepth = 0; - - 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 */ - // } - // } - - auto& tokens = lexer.tokens; - auto& idx = lexer.idx; - while (lexer.idx < lexer.tokens.size()) { - auto& token = lexer.Current(); - - bool incrementTokenIdx = true; - - // Reamalgamate token type and single char tokens; - int tokenKey; - if (token.type == CLEX_ext_single_char) { - tokenKey = token.text[0]; - } else { - tokenKey = token.type; - } - - switch (tokenKey) { - 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; - } - - currentNamespace = cgInput.AddNamespace(DeclNamespace{ - .container = currentNamespace, - .name = tokens[idx].text, - }); - - // Consume the identifier token - ++idx; - - if (tokens[idx].type == CLEX_ext_double_colon) { - // Consume the "::" token - ++idx; - } else { - break; - } - } - - nsStack.push_back(NamespaceStackframe{ - .ns = currentNamespace, - .depth = currentBraceDepth, - }); - - goto endCaseCLEX_id; - } - - case CKw_Struct: - case CKw_Class: { - // Consume the 'class' or 'struct' keyword - ++idx; - incrementTokenIdx = false; - - 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, currentNamespace); - DeclStruct structDecl; - structDecl.container = currentNamespace; - structDecl.name = name; - - // Consume the identifier token - ++idx; - - if (lexer.TryConsumeSingleCharToken(':')) { - while (true) { - // Public, protected, etc. - TryConsumeAnyKeyword(lexer); - - 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, currentNamespace); - auto baseClassDecl = cgInput.FindStruct(baseClassFullname); - if (baseClassDecl) { - // 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 (lexer.TryConsumeSingleCharToken('{')) { - // End of base class list - --idx; // Give the '{' token back to the main loop - break; - } else if (!lexer.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 = cgInput.AddStruct(std::move(fullname), std::move(structDecl)); - currentStruct = decl; - currentStructBraceDepth = currentBraceDepth; - } - - endCase: - goto endCaseCLEX_id; - } - - case CKw_Enum: { - // 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()); - } - - DeclEnum enumDecl; - enumDecl.container = currentNamespace; - enumDecl.underlyingType = EUT_Int32; // TODO - enumDecl.name = tokens[idx].text; - - // Consume the enum name identifier - ++idx; - - int enumClosingBraceCount = 0; - int enumBraceDepth = 0; - while (enumClosingBraceCount == 0 && idx < tokens.size()) { - auto& token = tokens[idx]; - switch (token.type) { - case CLEX_id: { - 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 CLEX_ext_single_char: { - switch (token.text[0]) { - case '{': { - ++enumBraceDepth; - } break; - - case '}': { - --enumBraceDepth; - ++enumClosingBraceCount; - } break; - } - } break; - } - - ++idx; - } - - auto fullname = Utils::MakeFullName(enumDecl.name, currentNamespace); - cgInput.AddEnum(std::move(fullname), std::move(enumDecl)); - goto endCaseCLEX_id; - } - - // 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 (!currentStruct) { - printf("[ERROR] BRUSSEL_CLASS must be used within a class or struct\n"); - break; - } - - // Always-on option - currentStruct->generating = true; - - auto argList = TryConsumeDirectiveArgumentList(lexer); - 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: currentStruct->generatingInheritanceHiearchy = true; break; - case SMGO_COUNT: break; - } - } - - goto endCaseCLEX_id; - } - - case CD_ClassProperty: { - // Consume the directive - ++idx; - incrementTokenIdx = false; - - if (!currentStruct || - !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(lexer); - auto declOpt = TryConsumeMemberVariable(lexer); - if (!declOpt.has_value()) { - printf("[ERROR] a member variable must immediately follow a BRUSSEL_PROPERTY\n"); - break; - } - auto& decl = declOpt.value(); - - // 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: { - // TODO I'm too lazy to write error checks, just let the codegen crash - auto& getterName = arg.at(1)->text; - if (getterName == "auto") { - // NOTE: intentionally shadowing - INPLACE_FMT(getterName, "Get%s", GetPascalCasedName().c_str()); - - // TODO generate getter function - - decl.getterName = getterName; - } else { - decl.getterName = getterName; - } - } break; - - case SPO_Setter: { - // TODO - auto& setterName = arg.at(1)->text; - if (setterName == "auto") { - // NOTE: intentionally shadowing - INPLACE_FMT(setterName, "Set%s", GetPascalCasedName().c_str()); - - // TODO generate setter function - - decl.setterName = setterName; - } else { - decl.setterName = setterName; - } - } break; - - case SPO_COUNT: break; - } - } - - currentStruct->memberVariables.push_back(std::move(decl)); - - goto endCaseCLEX_id; - } - - case CD_ClassMethod: { - // Consume the directive - ++idx; - incrementTokenIdx = false; - - goto endCaseCLEX_id; - } - - case CD_Enum: { - // Consume the directive - ++idx; - incrementTokenIdx = false; - - auto& optionsStrMap = RSTR_LUT(EnumMetaGenOptions); - auto argList = TryConsumeDirectiveArgumentList(lexer); - - if (argList.size() < 1) { - printf("[ERROR] invalid syntax for BRUSSEL_ENUM\n"); - break; - } - - auto& enumName = argList[0][0]->text; - auto enumDecl = cgInput.FindEnum(Utils::MakeFullName(enumName, currentNamespace)); - if (!enumDecl) { - printf("[ERROR] BRUSSEL_ENUM: referring to non-existent enum '%s'\n", enumName.c_str()); - break; - } - - auto& directiveOptions = argList[1]; - EnumFlags<EnumMetaGenOptions> options; - for (auto optionTok : directiveOptions) { - auto iter = optionsStrMap.find(optionTok->text); - if (iter != optionsStrMap.end()) { - options |= iter->second; - } else { - printf("[ERROR] BRUSSEL_ENUM: invalid option '%s'\n", optionTok->text.c_str()); - } - } - - GenerateForEnum(cgHeaderOutput, cgSourceOutput, *enumDecl, options); - - goto endCaseCLEX_id; - } - - case CD_COUNT: break; - } - - endCaseCLEX_id:; - } break; - - case '{': { - currentBraceDepth++; - if (currentBraceDepth < 0) { - printf("[WARNING] unbalanced brace\n"); - } - } break; - - case '}': { - currentBraceDepth--; - if (currentBraceDepth < 0) { - printf("[WARNING] unbalanced brace\n"); - } - - if (!nsStack.empty()) { - auto& ns = nsStack.back(); - if (ns.depth == currentBraceDepth) { - nsStack.pop_back(); - - if (!nsStack.empty()) { - currentNamespace = nsStack.back().ns; - } else { - currentNamespace = nullptr; - } - } - } - - if (currentStruct && - currentBraceDepth == currentStructBraceDepth) - { - // Exit struct - - if (currentStruct->generating) { - GenerateForClassMetadata(cgHeaderOutput, cgSourceOutput, *currentStruct); - } - if (currentStruct->generatingInheritanceHiearchy) { - // NOTE: this option is transitive to all child classes (as long as they have the basic annotation) - // TODO - } - - currentStruct = nullptr; - currentStructBraceDepth = 0; - } - } break; - } - - if (incrementTokenIdx) { - ++idx; - } - } - - if (currentBraceDepth != 0) { - printf("[WARNING] unbalanced brace at end of file."); - } - - INPLACE_FMT(generatedHeaderInlName, "%.*s/%.*s.gh.inl", PRINTF_STRING_VIEW(state.outputDir), PRINTF_STRING_VIEW(filenameStem)); - Utils::WriteOutputFile(cgHeaderOutput, generatedHeaderInlName); - INPLACE_FMT(generatedSourceInlName, "%.*s/%.*s.gs.inl", PRINTF_STRING_VIEW(state.outputDir), PRINTF_STRING_VIEW(filenameStem)); - Utils::WriteOutputFile(cgSourceOutput, generatedSourceInlName); - INPLACE_FMT(generatedCppName, "%.*s/%.*s.g.cpp", PRINTF_STRING_VIEW(state.outputDir), PRINTF_STRING_VIEW(filenameStem)); - Utils::WriteOutputFile(cgStandaloneSourceOutput, generatedCppName); -} - -enum InputOpcode { - IOP_ProcessSingleFile, - IOP_ProcessRecursively, - IOP_COUNT, -}; - -void HandleArgument(AppState& state, InputOpcode opcode, std::string_view operand) { - switch (opcode) { - case IOP_ProcessSingleFile: { - DEBUG_PRINTF("Processing single file %.*s\n", PRINTF_STRING_VIEW(operand)); - - fs::path path(operand); - auto filenameStem = path.stem().string(); - auto source = Utils::ReadFileAsString(path); - HandleInputFile(state, filenameStem, source); - } 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(); - auto pathExt = path.extension(); - auto pathStem = path.stem(); - if (pathExt != ".h" && - pathExt != ".hpp") - { - continue; - } - - DEBUG_PRINTF("Processing subfile %s\n", path.string().c_str()); - - auto filenameStem = pathStem.string(); - auto source = Utils::ReadFileAsString(path); - HandleInputFile(state, filenameStem, source); - } - } 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 { - DEBUG_PRINTF("Unknown input opcode %s\n", text.data()); - throw std::runtime_error("Unknown input opcode"); - } -} - -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 state; - - // 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 <output path> [<opcode>:<input path>]... -where <output path>: the directory to write generated contents to. This will NOT automatically create the directory. - <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 -)"""[1]); - return -1; - } - - state.outputDir = std::string_view(argv[1]); - DEBUG_PRINTF("Outputting to directory %.*s.\n", PRINTF_STRING_VIEW(state.outputDir)); - - for (int i = 2; i < argc; ++i) { - const char* argRaw = argv[i]; - std::string_view arg(argRaw); - DEBUG_PRINTF("Processing input command %s\n", argRaw); - - 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(state, opcode, operand); - } - } - - return 0; -} |