diff options
-rw-r--r-- | source/20-codegen-compiler/CodegenDecl.hpp | 12 | ||||
-rw-r--r-- | source/20-codegen-compiler/CodegenModel.cpp | 106 | ||||
-rw-r--r-- | source/20-codegen-compiler/CodegenModelArchive.hpp | 1 | ||||
-rw-r--r-- | source/20-codegen-compiler/main.cpp | 33 | ||||
-rw-r--r-- | source/20-codegen-compiler/test/examples/TestEnum.hpp.txt | 24 |
5 files changed, 132 insertions, 44 deletions
diff --git a/source/20-codegen-compiler/CodegenDecl.hpp b/source/20-codegen-compiler/CodegenDecl.hpp index 739dbe4..00fc18e 100644 --- a/source/20-codegen-compiler/CodegenDecl.hpp +++ b/source/20-codegen-compiler/CodegenDecl.hpp @@ -5,7 +5,16 @@ // TODO replace std::string name with std::string_view into the token storage? +struct SourceFile { + std::string_view filename; // View into storage map key + bool header = false; + /// Whether this file is being reprocessed in this invocation of codegen.exe or not. + bool reprocessing = false; +}; + struct DeclNamespace { + // NOTE: namespace doesn't have a source file field, because the same namespace can be "reopened" in multipled files + DeclNamespace* container = nullptr; std::string name; std::string_view fullname; // View into storage map key @@ -26,6 +35,7 @@ struct DeclMemberFunction { // Structs or classes struct DeclStruct { + SourceFile* sourceFile = nullptr; DeclNamespace* container = nullptr; std::vector<const DeclStruct*> baseClasses; std::vector<DeclMemberVariable> memberVariables; @@ -72,6 +82,7 @@ struct DeclEnumElement { }; struct DeclEnum { + SourceFile* sourceFile = nullptr; DeclNamespace* container = nullptr; std::string name; std::string_view fullname; @@ -101,6 +112,7 @@ struct DeclFunctionArgument { }; struct DeclFunction { + SourceFile* sourceFile = nullptr; DeclNamespace* container = nullptr; // Things like extern, static, etc. that gets written before the function return type std::string prefix; diff --git a/source/20-codegen-compiler/CodegenModel.cpp b/source/20-codegen-compiler/CodegenModel.cpp index 08323bc..4a9a8f8 100644 --- a/source/20-codegen-compiler/CodegenModel.cpp +++ b/source/20-codegen-compiler/CodegenModel.cpp @@ -31,7 +31,7 @@ public: // A number for `PRAGMA user_vesrion`, representing the current database version. Increment when the table format changes. #define CURRENT_DATABASE_VERSION 1 -constexpr int64_t kGlobalNamespaceRowId = 1; +constexpr int64_t kGlobalNamespaceId = 1; struct SQLiteDatabase { sqlite3* database = nullptr; @@ -49,6 +49,11 @@ struct SQLiteDatabase { struct SQLiteStatement { sqlite3_stmt* stmt = nullptr; + SQLiteStatement(const SQLiteStatement&) = delete; + SQLiteStatement& operator=(const SQLiteStatement&) = delete; + + SQLiteStatement() = default; + ~SQLiteStatement() { // NOTE: calling with NULL is a harmless no-op // NOTE: we don't care about the error code, because they are returned if the statement has errored in the most recent execution @@ -97,6 +102,8 @@ public: SQLiteStatement findOrStoreNamespaceStmt; SQLiteStatement storeEnumStmt; SQLiteStatement storeEnumElmStmt; + SQLiteStatement deleteStructDeclByFilenameStmt; + SQLiteStatement deleteEnumDeclByFilenameStmt; void InitializeCoreStatements() { char* errMsg = nullptr; @@ -108,34 +115,45 @@ public: result = sqlite3_exec(database, R"""( BEGIN TRANSACTION; CREATE TABLE Namespaces( - ParentNamespaceRowId INTEGER REFERENCES Namespaces(rowid), + -- NOTE: SQLite forbids foreign keys referencing the implicit `rowid` column, we have to create an alias for it + Id INTEGER PRIMARY KEY, + ParentNamespaceId INTEGER REFERENCES Namespaces(Id), Name TEXT, - UNIQUE (ParentNamespaceRowId, Name) + UNIQUE (ParentNamespaceId, Name) ); CREATE TABLE DeclStructs( - NamespaceRowId INTEGER REFERENCES Namespaces(rowid), - ParentStructRowId INTEGER REFERENCES DeclStructs(rowid), + Id INTEGER PRIMARY KEY, + NamespaceId INTEGER REFERENCES Namespaces(Id), + ParentStructId INTEGER REFERENCES DeclStructs(Id), Name TEXT, - UNIQUE (NamespaceRowId, Name) + FileName TEXT, + UNIQUE (NamespaceId, Name) ); CREATE TABLE DeclEnums( - NamespaceRowId INTEGER REFERENCES Namespaces(rowid), + Id INTEGER PRIMARY KEY, + NamespaceId INTEGER REFERENCES Namespaces(Id), Name TEXT, UnderlyingType TEXT, - UNIQUE (NamespaceRowId, Name) + FileName TEXT, + UNIQUE (NamespaceId, Name) ); CREATE TABLE DeclEnumElements( - EnumRowId INTEGER REFERENCES DeclEnums(rowid), + EnumId INTEGER REFERENCES DeclEnums(Id) ON DELETE CASCADE, Name TEXT, Value INTEGER, - UNIQUE (EnumRowId, Name) + UNIQUE (EnumId, Name) ); -INSERT INTO Namespaces(rowid, ParentNamespaceRowId, Name) +CREATE INDEX Index_DeclStructs_FileName + ON DeclStructs(FileName); +CREATE INDEX Index_DeclEnums_FileName + ON DeclEnums(FileName); + +INSERT INTO Namespaces(Id, ParentNamespaceId, Name) VALUES - -- Special global namespace that has no parent, and rowid should always be 1 + -- Special global namespace that has no parent, and Id should always be 1 (1, NULL, "<global namespace>"); COMMIT TRANSACTION; @@ -168,7 +186,7 @@ COMMIT TRANSACTION; /// \return Row ID of the namespace, or 0 if it currently doesn't exist. int64_t FindNamespace(const DeclNamespace* ns) { if (!ns) { - return kGlobalNamespaceRowId; + return kGlobalNamespaceId; } InitializeStmt_findNamespaceStmt(); @@ -178,7 +196,7 @@ COMMIT TRANSACTION; /// \return Row ID of the namespace. int64_t FindOrStoreNamespace(const DeclNamespace* ns) { if (!ns) { - return kGlobalNamespaceRowId; + return kGlobalNamespaceId; } InitializeStmt_findNamespaceStmt(); @@ -189,15 +207,15 @@ COMMIT TRANSACTION; private: void InitializeStmt_findNamespaceStmt() { findNamespaceStmt.InitializeLazily(database, R"""( -SELECT rowid FROM Namespaces WHERE ParentNamespaceRowId = ?1 AND Name = ?2; +SELECT Id FROM Namespaces WHERE ParentNamespaceId = ?1 AND Name = ?2; )"""sv); } void InitializeStmt_findOrStoreNamespaceStmt() { findOrStoreNamespaceStmt.InitializeLazily(database, R"""( -INSERT INTO Namespaces(ParentNamespaceRowId, Name) +INSERT INTO Namespaces(ParentNamespaceId, Name) VALUES (?1, ?2) - RETURNING rowid; + RETURNING Id; )"""sv); } @@ -210,7 +228,7 @@ INSERT INTO Namespaces(ParentNamespaceRowId, Name) return 0; } } else { - parentNsRowId = kGlobalNamespaceRowId; + parentNsRowId = kGlobalNamespaceId; } return FindNamespaceImpl(ns, parentNsRowId); @@ -239,7 +257,7 @@ INSERT INTO Namespaces(ParentNamespaceRowId, Name) sqlite3_stmt* stmt = findOrStoreNamespaceStmt; int64_t parentRowId = ns.container ? FindOrStoreNamespaceImpl(*ns.container) - : kGlobalNamespaceRowId; + : kGlobalNamespaceId; sqlite3_bind_int64(stmt, 1, parentRowId); sqlite3_bind_text(stmt, 2, ns.name.c_str(), ns.name.size(), nullptr); int result = sqlite3_step(stmt); @@ -367,8 +385,10 @@ CodegenModelArchive::CodegenModelArchive(std::string_view dbPath) } } - // This database is used for a buildsystem and can be regenerated at any time. We don't care for the slightest about data integrity, we just want fast updates // NOTE: These pragmas are not persistent, so we need to set them every time + // SQLite3 as of 2022-06-24 defaults to foreign_keys = OFF, we need this to be on for ON DELETE CASCADE and etc. to work + sqlite3_exec(m->database, "PRAGMA foreign_keys = ON", nullptr, nullptr, nullptr); + // This database is used for a buildsystem and can be regenerated at any time. We don't care for the slightest about data integrity, we just want fast updates sqlite3_exec(m->database, "PRAGMA synchronous = OFF", nullptr, nullptr, nullptr); sqlite3_exec(m->database, "PRAGMA journal_mode = MEMORY", nullptr, nullptr, nullptr); @@ -381,6 +401,27 @@ CodegenModelArchive::~CodegenModelArchive() { delete m; } +void CodegenModelArchive::DeleteDeclsRelatedToFile(std::string_view filename) { + // -Argument- -Description- + // ?1 The filename to delete + m->deleteStructDeclByFilenameStmt.InitializeLazily(m->database, "DELETE FROM DeclStructs WHERE FileName = ?1;"sv); + m->deleteEnumDeclByFilenameStmt.InitializeLazily(m->database, "DELETE FROM DeclEnums WHERE FileName = ?1;"sv); + + m->BeginTransaction(); + auto stmtList = { + m->deleteStructDeclByFilenameStmt.stmt, + m->deleteEnumDeclByFilenameStmt.stmt, + }; + for (auto& stmt : stmtList) { + sqlite3_bind_text(stmt, 1, filename.data(), filename.size(), nullptr); + int result = sqlite3_step(stmt); + assert(result == SQLITE_DONE); + sqlite3_reset(stmt); + sqlite3_clear_bindings(stmt); + } + m->CommitTransaction(); +} + void CodegenModelArchive::Store(const CodegenModel& cgInput) { auto& cgm = cgInput.GetPimpl(); @@ -425,35 +466,42 @@ void CodegenModelArchive::StoreEnum(const DeclEnum& decl) { // ?1 Namespace ID // ?2 Enum name // ?3 Enum underlying type + // ?4 File containing the enum m->storeEnumStmt.InitializeLazily(m->database, R"""( -INSERT INTO DeclEnums(NamespaceRowId, Name, UnderlyingType) - VALUES (?1, ?2, ?3) - ON CONFLICT DO UPDATE SET UnderlyingType=?3 - RETURNING rowid; +INSERT INTO DeclEnums(NamespaceId, Name, UnderlyingType, FileName) + VALUES (?1, ?2, ?3, ?4) + ON CONFLICT DO UPDATE SET + UnderlyingType = ?3, + FileName = ?4 + RETURNING Id; )"""sv); // -Argument- -Description- - // ?1 Container enum's rowid + // ?1 Container enum's id // ?2 Enum element name // ?3 Enum element value m->storeEnumElmStmt.InitializeLazily(m->database, R"""( -INSERT INTO DeclEnumElements(EnumRowId, Name, Value) +INSERT INTO DeclEnumElements(EnumId, Name, Value) VALUES (?1, ?2, ?3) ON CONFLICT DO UPDATE SET Value=?3; )"""sv); - // TODO delete non-existent enums sqlite3_bind_int(m->storeEnumStmt, 1, m->FindNamespace(decl.container)); sqlite3_bind_text(m->storeEnumStmt, 2, decl.name.c_str(), decl.name.size(), nullptr); sqlite3_bind_text(m->storeEnumStmt, 3, decl.underlyingTypeStr.c_str(), decl.underlyingTypeStr.size(), nullptr); + if (decl.sourceFile) { + sqlite3_bind_text(m->storeEnumStmt, 4, decl.sourceFile->filename.data(), decl.sourceFile->filename.size(), nullptr); + } else { + sqlite3_bind_text(m->storeEnumElmStmt, 4, "", 0, nullptr); + } int result = sqlite3_step(m->storeEnumStmt); assert(result == SQLITE_ROW); - auto enumRowId = sqlite3_column_int64(m->storeEnumStmt, 0); + auto EnumId = sqlite3_column_int64(m->storeEnumStmt, 0); sqlite3_reset(m->storeEnumStmt); sqlite3_clear_bindings(m->storeEnumStmt); for (auto& elm : decl.elements) { - sqlite3_bind_int64(m->storeEnumElmStmt, 1, enumRowId); + sqlite3_bind_int64(m->storeEnumElmStmt, 1, EnumId); sqlite3_bind_text(m->storeEnumElmStmt, 2, elm.name.c_str(), elm.name.size(), nullptr); sqlite3_bind_int64(m->storeEnumElmStmt, 3, elm.value); int result = sqlite3_step(m->storeEnumElmStmt); diff --git a/source/20-codegen-compiler/CodegenModelArchive.hpp b/source/20-codegen-compiler/CodegenModelArchive.hpp index 0e0e5ad..21cc32c 100644 --- a/source/20-codegen-compiler/CodegenModelArchive.hpp +++ b/source/20-codegen-compiler/CodegenModelArchive.hpp @@ -18,6 +18,7 @@ public: // Implementation detail helper, don't use outside Private& GetPimpl() const { return *m; } + void DeleteDeclsRelatedToFile(std::string_view filename); void Store(const CodegenModel& cgInput); void StoreStruct(const DeclStruct& decl); void StoreFunction(const DeclFunction& decl); diff --git a/source/20-codegen-compiler/main.cpp b/source/20-codegen-compiler/main.cpp index 5a59b5d..c393fb0 100644 --- a/source/20-codegen-compiler/main.cpp +++ b/source/20-codegen-compiler/main.cpp @@ -30,8 +30,22 @@ namespace fs = std::filesystem; struct AppState { /*nullable*/ CodegenModelArchive* modelArchive = nullptr; + robin_hood::unordered_map<std::string, SourceFile, StringHash, StringEqual> sourceFiles; std::string_view outputDir; std::string_view databaseFilePath; + + SourceFile& GetOrCreateSourceFile(std::string_view filename) { + auto iter = sourceFiles.find(filename); + if (iter != sourceFiles.end()) { + return iter->second; + } else { + auto [iter, success] = sourceFiles.try_emplace(std::string(filename), SourceFile{}); + auto& filename = iter->first; // NOTE: shadows the parameter `filename` + auto& sourceFile = iter->second; + sourceFile.filename = filename; + return sourceFile; + } + } }; FSTR_LUT_DECL(ClexNames, CLEX_eof, CLEX_ext_COUNT) { @@ -85,21 +99,28 @@ 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_Int16, "short int"); RSTR_LUT_MAP(EUT_Uint16, "unsigned short"); + RSTR_LUT_MAP(EUT_Uint16, "unsigned short int"); 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_Int32, "long int"); RSTR_LUT_MAP(EUT_Uint32, "unsigned long"); + RSTR_LUT_MAP(EUT_Uint32, "unsigned long int"); #else RSTR_LUT_MAP(EUT_Int64, "long"); + RSTR_LUT_MAP(EUT_Int64, "long int"); RSTR_LUT_MAP(EUT_Uint64, "unsigned long"); + RSTR_LUT_MAP(EUT_Uint64, "unsigned long int"); #endif RSTR_LUT_MAP(EUT_Int64, "long long"); + RSTR_LUT_MAP(EUT_Int64, "long long int"); RSTR_LUT_MAP(EUT_Uint64, "unsigned long long"); + RSTR_LUT_MAP(EUT_Uint64, "unsigned long long int"); // Sized types RSTR_LUT_MAP(EUT_Int8, "int8_t"); @@ -279,7 +300,6 @@ found: } enum StructMetaGenOptions { - // TODO how tf do we implement this one: needs full source scanning SMGO_InheritanceHiearchy, SMGO_COUNT, }; @@ -618,6 +638,10 @@ void HandleInputFile(AppState& as, std::string_view filenameStem, std::string_vi printf("END tokens\n"); #endif + auto& sourceFile = as.GetOrCreateSourceFile(filenameStem); + sourceFile.header = true; + sourceFile.reprocessing = true; + ParserState ps; { INPLACE_FMT(hpp, "%.*s.gh.inl", PRINTF_STRING_VIEW(filenameStem)); @@ -707,6 +731,7 @@ void HandleInputFile(AppState& as, std::string_view filenameStem, std::string_vi auto& name = idenTok.text; auto fullname = Utils::MakeFullName(name, ps.currentNamespace); DeclStruct structDecl; + structDecl.sourceFile = &sourceFile; structDecl.container = ps.currentNamespace; structDecl.name = name; @@ -781,6 +806,7 @@ void HandleInputFile(AppState& as, std::string_view filenameStem, std::string_vi auto& name = tokens[idx].text; auto fullname = Utils::MakeFullName(name, ps.currentNamespace); DeclEnum enumDecl; + enumDecl.sourceFile = &sourceFile; enumDecl.container = ps.currentNamespace; enumDecl.underlyingType = EUT_Int32; // TODO enumDecl.underlyingTypeStr = "int"; @@ -1070,6 +1096,7 @@ void HandleInputFile(AppState& as, std::string_view filenameStem, std::string_vi Utils::WriteOutputFile(ps.standaloneSourceOutput, generatedCppName); if (as.modelArchive) { + as.modelArchive->DeleteDeclsRelatedToFile(filenameStem); as.modelArchive->Store(ps.model); } } @@ -1189,6 +1216,7 @@ where --output-dir=<path>: the *directory* to write generated contents to. Thi <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 + "fileList" read <input path> as a text file, and process each line as a separate file path )"""[1]); return -1; } @@ -1227,7 +1255,6 @@ where --output-dir=<path>: the *directory* to write generated contents to. Thi DEBUG_PRINTF("Outputting to directory %.*s.\n", PRINTF_STRING_VIEW(as.outputDir)); DEBUG_PRINTF("Databse file: %.*s.\n", PRINTF_STRING_VIEW(as.databaseFilePath)); - // TODO model archive is broken right now, see each TODO in CodegenModel.cpp CodegenModelArchive archive(as.databaseFilePath); as.modelArchive = &archive; diff --git a/source/20-codegen-compiler/test/examples/TestEnum.hpp.txt b/source/20-codegen-compiler/test/examples/TestEnum.hpp.txt index fea1f6d..428c47f 100644 --- a/source/20-codegen-compiler/test/examples/TestEnum.hpp.txt +++ b/source/20-codegen-compiler/test/examples/TestEnum.hpp.txt @@ -22,23 +22,23 @@ enum CountedEnum { }; namespace MyNamespace { -enum class MyNamespacedEnum { - BRUSSEL_ENUM(ToString, FromString, ExcludeHeuristics) - MNE_Foo, - MNE_Bar, -}; - -namespace details { - enum MyNamespacedEnum { + enum class MyNamespacedEnum { BRUSSEL_ENUM(ToString, FromString, ExcludeHeuristics) MNE_Foo, MNE_Bar, }; -} + + namespace details { + enum MyNamespacedEnum { + BRUSSEL_ENUM(ToString, FromString, ExcludeHeuristics) + MNE_Foo, + MNE_Bar, + }; + } } namespace foo::details { -enum Enum { - BRUSSEL_ENUM(ToString, FromString, ExcludeHeuristics) -}; + enum Enum { + BRUSSEL_ENUM(ToString, FromString, ExcludeHeuristics) + }; } |