#include "CodegenLookupTable.h" #include "CodegenOutput.hpp" #include "Macros.hpp" #include "ScopeGuard.hpp" #include "Utils.hpp" #include #include #include #include #include #include #include #include using namespace std::literals; namespace fs = std::filesystem; enum InputOpcode { IOP_ProcessSingleFile, IOP_ProcessRecursively, IOP_COUNT, }; enum CodegenDirectives { CD_ClassInfo, // BRUSSEL_CLASS CD_EnumInfo, // BRUSSEL_ENUM // TODO implement CD_GlobalSequencer, // BRUSSEL_INIT CD_COUNT, }; enum EnumUnderlyingType { EUT_Int8, EUT_Int16, EUT_Int32, EUT_Int64, EUT_Uint8, EUT_Uint16, EUT_Uint32, EUT_Uint64, EUT_COUNT, }; InputOpcode ParseInputOpcode(std::string_view text) { if (text == "single"sv) { return IOP_ProcessSingleFile; } else if (text == "rec"sv) { return IOP_ProcessRecursively; } return IOP_COUNT; } struct InputDefinitionStruct { std::string name; }; struct InputDefinitionEnumElement { std::string name; uint64_t value; }; struct InputDefinitionEnum { std::string name; std::vector elements; EnumUnderlyingType underlyingType; }; struct StbLexerToken { std::string text; // Can either be CLEX_* values, or just chars for single character tokens int type; }; void CheckBraceDepth(int braceDpeth) { if (braceDpeth < 0) { printf("[WARNING] unbalanced brace"); } } void HandleInputFile(std::string_view source) { stb_lexer lexer; char stringStorage[65536]; const char* srcBegin = source.data(); const char* srcEnd = srcBegin + source.length(); stb_c_lexer_init(&lexer, srcBegin, srcEnd, stringStorage, sizeof(stringStorage)); std::vector tokens; std::vector foundStructs; InputDefinitionStruct currStruct; std::vector foundEnums; InputDefinitionEnum currEnum; auto PushFoundStruct = [&]() { foundStructs.push_back(std::move(currStruct)); currStruct = {}; }; auto PushFoundEnum = [&]() { foundEnums.push_back(std::move(currEnum)); currEnum = {}; }; enum NextMatchingConstruct { NMC_None, NMC_Enum, NMC_StructClass, } matchingConstruct = NMC_None; bool matchingConstructInBody = false; bool matchingDirectiveParams = false; int bracePairDepth = 0; while (true) { // See stb_c_lexer.h's comments, here are a few additinos that aren't made clear in the file: // - `lexer->token` (noted as "token" below) after calling stb_c_lexer_get_token() contains either: // 1. 0 <= token < 256: an ASCII character (more precisely a single char that the lexer ate; technically can be an incomplete code unit) // 2. token < 0: an unknown token // 3. One of the `CLEX_*` enums: a special, recognized token such as an operator int stbToken = stb_c_lexer_get_token(&lexer); if (stbToken == 0) { // EOF break; } // TODO needed? // StbLexerToken token; // token.type = lexer.token; // token.text = std::string(lexer.string, lexer.string_len); // tokens.push_back(token); switch (lexer.token) { case CLEX_id: { // WORKAROUND: stb_c_lexer doens't set string_len properly when parsing identifiers std::string_view idenText(lexer.string); // std::string_view idenText(lexer.string, lexer.string_len); switch (matchingConstruct) { case NMC_StructClass: { if (matchingConstructInBody) { // TODO } } break; case NMC_Enum: { if (matchingConstructInBody) { printf("[DEBUG] found enum element '%s'\n", lexer.string); currEnum.elements.push_back(InputDefinitionEnumElement{ .name = std::string(idenText), .value = 0, // TODO parse }); } else { currEnum.name = std::string(idenText); } } break; default: { if (idenText == "struct"sv || idenText == "class"sv) { printf("[DEBUG] found struct named\n"); matchingConstruct = NMC_StructClass; } else if (idenText == "enum"sv) { printf("[DEBUG] found enum\n"); matchingConstruct = NMC_Enum; } else if (idenText == "BRUSSEL_CLASS"sv) { // TODO printf("[DEBUG] found BRUSSEL_CLASS\n"); } else if (idenText == "BRUSSEL_ENUM"sv) { matchingDirectiveParams = true; printf("[DEBUG] found BRUSSEL_ENUM\n"); } if (matchingDirectiveParams) { for (auto& foundEnum : foundEnums) { if (foundEnum.name == idenText) { // TODO generate data break; } } matchingDirectiveParams = false; } } break; } } break; case CLEX_intlit: case CLEX_floatlit: case CLEX_dqstring: case CLEX_sqstring: case CLEX_charlit: case CLEX_eq: case CLEX_noteq: case CLEX_lesseq: case CLEX_greatereq: case CLEX_andand: case CLEX_oror: case CLEX_shl: case CLEX_shr: case CLEX_plusplus: case CLEX_minusminus: case CLEX_pluseq: case CLEX_minuseq: case CLEX_muleq: case CLEX_diveq: case CLEX_modeq: case CLEX_andeq: case CLEX_oreq: case CLEX_xoreq: case CLEX_arrow: case CLEX_eqarrow: case CLEX_shleq: case CLEX_shreq: { } break; case '{': { bracePairDepth++; CheckBraceDepth(bracePairDepth); switch (matchingConstruct) { case NMC_StructClass: case NMC_Enum: { matchingConstructInBody = true; } break; default: break; } } break; case '}': { bracePairDepth--; CheckBraceDepth(bracePairDepth); switch (matchingConstruct) { case NMC_StructClass: { matchingConstruct = NMC_None; matchingConstructInBody = false; } break; case NMC_Enum: { printf("[DEBUG] committed enum '%s'\n", currEnum.name.c_str()); for (auto& elm : currEnum.elements) { printf(" - element %s = %" PRId64 "\n", elm.name.c_str(), elm.value); } matchingConstruct = NMC_None; matchingConstructInBody = false; PushFoundEnum(); } break; default: break; } } break; case CLEX_parse_error: { fprintf(stderr, "[ERROR] stb_c_lexer countered a parse error."); // TODO how to handle? } break; } } if (bracePairDepth != 0) { printf("[WARNING] unbalanced brace at end of file."); } } std::string ReadFileAtOnce(const fs::path& path) { auto file = Utils::OpenCstdioFile(path, Utils::Read); if (!file) throw std::runtime_error("Failed to open source file."); DEFER { fclose(file); }; fseek(file, 0, SEEK_END); auto fileSize = ftell(file); rewind(file); std::string result(fileSize, '\0'); fread(result.data(), fileSize, 1, file); return result; } void HandleArgument(InputOpcode opcode, std::string_view operand) { switch (opcode) { case IOP_ProcessSingleFile: { fs::path filePath(operand); auto source = ReadFileAtOnce(filePath); HandleInputFile(source); } break; case IOP_ProcessRecursively: { fs::path startPath(operand); for (auto& item : fs::recursive_directory_iterator(startPath)) { if (!item.is_regular_file()) { continue; } auto& path = item.path(); auto filename = path.filename().string(); if (filename != ".c" || filename != ".cpp") { continue; } auto source = ReadFileAtOnce(path); HandleInputFile(source); } } break; case IOP_COUNT: break; } } int main(int argc, char* argv[]) { // TODO better arg parser // option 1: use cxxopts and positional arguments // option 1: take one argument only, being a json objecet // If no cli is provided (argv[0]), this loop will do nothing // Otherwise, start with the 2nd element which is the 1st argument for (int i = 1; i < argc; ++i) { std::string_view arg(argv[i]); auto separatorLoc = arg.find(':'); if (separatorLoc != std::string_view::npos) { auto opcode = ParseInputOpcode(arg.substr(0, separatorLoc)); auto operand = arg.substr(separatorLoc + 1); HandleArgument(opcode, operand); } } return 0; }