diff options
Diffstat (limited to 'buildtools/codegen/main.cpp')
-rw-r--r-- | buildtools/codegen/main.cpp | 212 |
1 files changed, 179 insertions, 33 deletions
diff --git a/buildtools/codegen/main.cpp b/buildtools/codegen/main.cpp index 74acd1c..ed8cbbe 100644 --- a/buildtools/codegen/main.cpp +++ b/buildtools/codegen/main.cpp @@ -76,6 +76,7 @@ BSTR_LUT_DECL(CppKeyword, 0, CKw_COUNT) { BSTR_LUT_MAP_FOR(CppKeyword); BSTR_LUT_MAP(CKw_Struct, "struct"); BSTR_LUT_MAP(CKw_Class, "class"); + BSTR_LUT_MAP(CKw_Enum, "enum"); } enum CodegenDirective { @@ -161,6 +162,10 @@ PeekDirectiveArgumentList(const std::vector<StbLexerToken>& tokens, size_t curre } } + if (!currentArg.empty()) { + result.push_back(std::move(currentArg)); + } + return { result, i }; } @@ -223,21 +228,95 @@ BSTR_LUT_DECL(StructMetaGenOptions, 0, SMGO_COUNT) { } enum EnumMetaGenOptions { - EMGO_Basic, + EMGO_ToString, + EMGO_ExcludeUseHeuristics, EMGO_COUNT, }; BSTR_LUT_DECL(EnumMetaGenOptions, 0, EMGO_COUNT) { BSTR_LUT_MAP_FOR(EnumMetaGenOptions); - BSTR_LUT_MAP(EMGO_Basic, "GenBasic"); + BSTR_LUT_MAP(EMGO_ToString, "ToString"); + 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()); + + CodegenOutputThing thing; + APPEND_SPRINTF(thing.text, "const char* %s[] = {\n", 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()); + } + thing.text += "};\n"; + out.AddOutputThing(std::move(thing)); + + return arrayName; +} + +std::string GenerateEnumStringMap(CodegenOutput& out, const DeclEnum& decl, bool useHeruistics) { + std::string mapName; + // TODO + + return mapName; } void GenerateForEnum(CodegenOutput& out, const DeclEnum& decl, EnumFlags<EnumMetaGenOptions> options) { + bool useExcludeHeuristics = options.IsSet(EMGO_ExcludeUseHeuristics); + + if (options.IsSet(EMGO_ToString)) { + // Generate name lookup table + + switch (decl.CalcPattern()) { + case EVP_Continuous: { + auto arrayName = GenerateEnumStringArray(out, 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; + } + + CodegenOutputThing lookupFunction; + APPEND_SPRINTF(lookupFunction.text, "std::string_view Stringify%s(%s value) {\n", decl.name.c_str(), decl.name.c_str()); + APPEND_SPRINTF(lookupFunction.text, " if (value < 0 || value >= %d) return {};\n", maxVal); + APPEND_SPRINTF(lookupFunction.text, " return %s[value - %d];\n", arrayName.c_str(), minVal); + lookupFunction.text += "}\n"; + + out.AddOutputThing(std::move(lookupFunction)); + } break; + + case EVP_Bits: { + auto arrayName = GenerateEnumStringArray(out, decl, useExcludeHeuristics); + } break; + + case EVP_Random: { + auto mapName = GenerateEnumStringMap(out, decl, useExcludeHeuristics); + } break; + + case EVP_COUNT: break; + } + } } void HandleInputFile(AppState& state, std::string_view source) { auto tokens = RecordTokens(source); - size_t tokenIdx = 0; + size_t idx = 0; #if CODEGEN_DEBUG_PRINT printf("BEGIN tokens\n"); @@ -247,12 +326,12 @@ void HandleInputFile(AppState& state, std::string_view source) { printf("END tokens\n"); #endif - CodegenInput input; - CodegenOutput output; + CodegenInput fileInput; + CodegenOutput fileOutput; int bracePairDepth = 0; - while (tokenIdx < tokens.size()) { - auto& token = tokens[tokenIdx]; + while (idx < tokens.size()) { + auto& token = tokens[idx]; bool incrementTokenIdx = true; @@ -265,25 +344,77 @@ void HandleInputFile(AppState& state, std::string_view source) { if (iter != map.end()) { keyword = iter->second; } else { - break; + keyword = CKw_COUNT; // Skip keyword section } } switch (keyword) { case CKw_Struct: case CKw_Class: { - auto& idenTok = tokens[tokenIdx + 1]; // TODO handle end of list + auto& idenTok = tokens[idx + 1]; // TODO handle end of list DEBUG_PRINTF("[DEBUG] found struct named %s\n", idenTok.text.c_str()); - } break; + goto endIdenCase; + } case CKw_Enum: { - StbLexerToken* idenTok = &token + 1; // TODO handle end of list - if (idenTok->text == "class") { - idenTok += 1; - DEBUG_PRINTF("[DEBUG] found enum class named %s\n", idenTok->text.c_str()); + // Consume the "enum" keyword + ++idx; + incrementTokenIdx = false; + + DeclEnum enumDecl; + enumDecl.underlyingType = EUT_Int32; // TODO + + if (tokens[idx].text == "class") { + // Consume the "class" keyword + ++idx; + DEBUG_PRINTF("[DEBUG] found enum class named %s\n", tokens[idx].text.c_str()); } else { - DEBUG_PRINTF("[DEBUG] found enum named %s\n", idenTok->text.c_str()); + DEBUG_PRINTF("[DEBUG] found enum named %s\n", tokens[idx].text.c_str()); + } + + // Consume the enum name identifier + enumDecl.name = tokens[idx].text; + ++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: { + + } break; + + case CLEX_ext_single_char: { + switch (token.text[0]) { + case '{': { + ++enumBraceDepth; + } break; + + case '}': { + --enumBraceDepth; + ++enumClosingBraceCount; + } break; + } + } break; + } + + ++idx; } - } break; + + fileInput.AddEnum(std::move(enumDecl)); + goto endIdenCase; + } case CKw_COUNT: break; } @@ -295,24 +426,33 @@ void HandleInputFile(AppState& state, std::string_view source) { if (iter != map.end()) { directive = iter->second; } else { - break; + directive = CD_COUNT; // Skip directive section } } switch (directive) { case CD_ClassInfo: { // TODO - } break; + goto endIdenCase; + } case CD_EnumInfo: { + // Consume the directive + ++idx; + incrementTokenIdx = false; + auto& optionsStrMap = BSTR_LUT_S2V(EnumMetaGenOptions); - auto [argList, newIdx] = PeekDirectiveArgumentList(tokens, tokenIdx); + auto [argList, newIdx] = PeekDirectiveArgumentList(tokens, idx); if (argList.size() < 1) { printf("[ERROR] invalid syntax for BRUSSEL_ENUM\n"); - break; + break; // TODO handle this error case gracefully (advance to semicolon?) } auto& enumName = argList[0][0]->text; - auto enumDecl = input.FindEnumByName(enumName); + auto enumDecl = fileInput.FindEnumByName(enumName); + if (!enumDecl) { + printf("[ERROR] BRUSSEL_ENUM: referring to non-existent enum '%s'\n", enumName.c_str()); + break; + } auto& directiveOptions = argList[1]; EnumFlags<EnumMetaGenOptions> options; @@ -321,19 +461,23 @@ void HandleInputFile(AppState& state, std::string_view source) { if (iter != optionsStrMap.end()) { options |= iter->second; } else { - printf("[ERROR] invalid option '%s' for BRUSSEL_ENUM", optionTok->text.c_str()); + printf("[ERROR] BRUSSEL_ENUM: invalid option '%s'\n", optionTok->text.c_str()); } } - GenerateForEnum(output, *enumDecl, options); + GenerateForEnum(fileOutput, *enumDecl, options); - tokenIdx = newIdx; + idx = newIdx; incrementTokenIdx = false; - } break; + goto endIdenCase; + } case CD_COUNT: break; } - } break; + + endIdenCase: + break; + } case '{': { bracePairDepth++; @@ -347,13 +491,15 @@ void HandleInputFile(AppState& state, std::string_view source) { } if (incrementTokenIdx) { - ++tokenIdx; + ++idx; } } if (bracePairDepth != 0) { printf("[WARNING] unbalanced brace at end of file."); } + + state.mainOutput.MergeContents(std::move(fileOutput)); } std::string ReadFileAtOnce(const fs::path& path) { @@ -436,12 +582,12 @@ int main(int argc, char* argv[]) { 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 _file_ to write generated contents to - <opcode> is one of: - "single" process this <input path> file only - "rec" starting at the given directory <input path>, recursively process all .h .c .hpp .cpp files -)"""[1]); + USAGE: codegen.exe <output path> [<opcode>:<input path>]... + where <output path>: the _file_ to write generated contents to + <opcode> is one of: + "single" process this <input path> file only + "rec" starting at the given directory <input path>, recursively process all .h .c .hpp .cpp files + )"""[1]); return -1; } |