aboutsummaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/20-codegen-compiler/CodegenModel.cpp177
-rw-r--r--source/20-codegen-compiler/CodegenModelArchive.hpp8
-rw-r--r--source/20-codegen-compiler/main.cpp27
3 files changed, 194 insertions, 18 deletions
diff --git a/source/20-codegen-compiler/CodegenModel.cpp b/source/20-codegen-compiler/CodegenModel.cpp
index 0017b62..88439e8 100644
--- a/source/20-codegen-compiler/CodegenModel.cpp
+++ b/source/20-codegen-compiler/CodegenModel.cpp
@@ -16,6 +16,8 @@
using namespace std::literals;
+// TODO only delete unused records from model instead of regenerating all records every time
+
struct SomeDecl {
std::variant<DeclStruct, DeclFunction, DeclEnum> v;
};
@@ -55,6 +57,10 @@ struct SQLiteStatement {
SQLiteStatement() = default;
+ SQLiteStatement(sqlite3* database, std::string_view sql) {
+ Initialize(database, sql);
+ }
+
~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
@@ -98,9 +104,10 @@ public:
SQLiteStatement beginTransactionStmt;
SQLiteStatement commitTransactionStmt;
SQLiteStatement rollbackTransactionStmt;
- SQLiteStatement storeFileStmt;
SQLiteStatement findFileStmt;
+ SQLiteStatement storeFileStmt;
SQLiteStatement findNamespaceStmt;
+ SQLiteStatement getNamespaceStmt;
SQLiteStatement storeNamespaceStmt;
/* Component Statements, initalized on demand */
SQLiteStatement storeStructStmt;
@@ -112,6 +119,8 @@ public:
SQLiteStatement deleteFunctionDeclByFilenameStmt;
SQLiteStatement deleteStructDeclByFilenameStmt;
SQLiteStatement deleteEnumDeclByFilenameStmt;
+ // TODO
+ // SQLiteStatement getRootClassStmt;
void InitializeDatabase() {
char* errMsg = nullptr;
@@ -120,7 +129,6 @@ public:
PrintErrMsgIfPresent(errMsg);
assert(result == SQLITE_OK);
- // TODO create a table of file names
// TODO unique with overloading, and container structs
result = sqlite3_exec(database, R"""(
BEGIN TRANSACTION;
@@ -140,7 +148,7 @@ CREATE TABLE Namespaces(
CREATE TABLE DeclFunctions(
Id INTEGER PRIMARY KEY,
- FileId INTEGER REFERENCES Files(Id),
+ FileId INTEGER REFERENCES Files(Id) ON DELETE CASCADE,
NamespaceId INTEGER REFERENCES Namespaces(Id),
Name TEXT
);
@@ -153,7 +161,7 @@ CREATE TABLE DeclFunctionParameters(
CREATE TABLE DeclStructs(
Id INTEGER PRIMARY KEY,
- FileId INTEGER REFERENCES Files(Id),
+ FileId INTEGER REFERENCES Files(Id) ON DELETE CASCADE,
NamespaceId INTEGER REFERENCES Namespaces(Id),
Name TEXT,
IsMetadataMarked INTEGER
@@ -193,7 +201,7 @@ CREATE TABLE DeclStructMethodParameters(
CREATE TABLE DeclEnums(
Id INTEGER PRIMARY KEY,
- FileId INTEGER REFERENCES Files(Id),
+FileId INTEGER REFERENCES Files(Id) ON DELETE CASCADE,
NamespaceId INTEGER REFERENCES Namespaces(Id),
Name TEXT,
UnderlyingType TEXT
@@ -286,6 +294,55 @@ COMMIT TRANSACTION;
return nsId;
}
+ // TODO maybe merge with Utils::MakeFullName?
+ std::string GetNamespaceFullName(int64_t nsId) {
+ std::vector<std::string> namespaceNames;
+ size_t fullnameLength = 0;
+
+ sqlite3_stmt* stmt = getNamespaceStmt;
+ int64_t currentNsId = nsId;
+ while (true) {
+ sqlite3_bind_int64(stmt, 1, currentNsId);
+
+ int result = sqlite3_step(stmt);
+ DEFER {
+ sqlite3_reset(stmt);
+ sqlite3_clear_bindings(stmt);
+ };
+ assert(result == SQLITE_ROW);
+
+ /* 0th column Id */
+ int64_t parentNamespaceId = sqlite3_column_int64(stmt, 1);
+ auto nameCstr = (const char*)sqlite3_column_text(stmt, 2);
+
+ currentNsId = parentNamespaceId;
+ std::string name(nameCstr);
+ fullnameLength += name.size() + 2;
+ namespaceNames.push_back(std::move(name));
+
+ if (parentNamespaceId == kGlobalNamespaceId) {
+ break;
+ }
+ }
+ fullnameLength -= 2;
+
+ std::string fullname;
+ fullname.reserve(fullnameLength);
+
+ for (auto it = namespaceNames.rbegin(); it != namespaceNames.rend(); ++it) {
+ fullname.append(*it);
+ if (std::next(it) != namespaceNames.rend()) {
+ fullname.append("::");
+ }
+ }
+
+ return fullname;
+ }
+
+ std::string GetDeclFullName(int64_t nsId, std::string_view declName) {
+ return GetNamespaceFullName(nsId).append(declName);
+ }
+
/// \return Row ID of the file, or 0 if it currently doesn't exist.
int64_t FindFile(std::string_view filename) {
sqlite3_stmt* stmt = findFileStmt;
@@ -498,6 +555,7 @@ CodegenModelArchive::CodegenModelArchive(std::string_view dbPath)
m->findFileStmt.Initialize(m->database, "SELECT Id FROM Files WHERE FileName = ?1");
m->storeFileStmt.Initialize(m->database, "INSERT INTO Files(FileName) VALUES (?1) RETURNING Id");
m->findNamespaceStmt.Initialize(m->database, "SELECT Id FROM Namespaces WHERE ParentNamespaceId = ?1 AND Name = ?2");
+ m->getNamespaceStmt.Initialize(m->database, "SELECT * FROM Namespaces WHERE Id = ?1"sv);
m->storeNamespaceStmt.Initialize(m->database, "INSERT INTO Namespaces(ParentNamespaceId, Name) VALUES (?1, ?2) RETURNING Id");
}
@@ -528,8 +586,8 @@ void CodegenModelArchive::DeleteDeclsRelatedToFile(std::string_view filename) {
m->CommitTransaction();
}
-void CodegenModelArchive::Store(const CodegenModel& cgInput) {
- auto& cgm = cgInput.GetPimpl();
+void CodegenModelArchive::Store(const CodegenModel& cgModel) {
+ auto& cgm = cgModel.GetPimpl();
struct Visiter {
CodegenModelArchive* self;
@@ -551,6 +609,7 @@ void CodegenModelArchive::Store(const CodegenModel& cgInput) {
// TODO why are some namepsaces not found in StoreStruct or StoreEnum calls, if we don't use FindOrStoreNamespace there?
// since we store all currently scanned namespaces into the model here, does that mean the parser is not correctly picking some of those up?
// but if the parser is failing, than the DeclThing objects should have a null `container` field?
+ // [2022-07-15] I think this bug is fixed, it was caused by FindOrStoreNamespace() not calling sqlite3_reset(), causing a stmt execution state mixup between different namespaces
for (auto&& [DISCARD, ns] : cgm.namespaces) {
// This will insert the namespace if it doesn't exist, or no-op (fetches data) if it already exists
m->FindOrStoreNamespace(&ns);
@@ -562,6 +621,110 @@ void CodegenModelArchive::Store(const CodegenModel& cgInput) {
m->CommitTransaction();
}
+void CodegenModelArchive::LoadInto(CodegenModel& model) const {
+ // TODO
+}
+
+CodegenModel CodegenModelArchive::Load() const {
+ CodegenModel cgModel;
+
+ // TODO files
+ // TODO namespaces
+
+ robin_hood::unordered_map<int64_t, DeclStruct*> structsById;
+ robin_hood::unordered_map<int64_t, DeclMemberVariable*> propertiesById;
+ robin_hood::unordered_map<int64_t, DeclMemberFunction*> methodsById;
+
+ // NOTE: this is basically { initializer; while(true) { body; } } in a short form
+ // Load structs
+ for (SQLiteStatement stmt(m->database, "SELECT * FROM DeclStructs"sv);;) {
+ int result = sqlite3_step(stmt);
+ if (result == SQLITE_DONE) {
+ break;
+ }
+
+ if (result == SQLITE_ROW) {
+ int64_t id = sqlite3_column_int64(stmt, 9);
+ int64_t fileId = sqlite3_column_int64(stmt, 1);
+ int64_t nsId = sqlite3_column_int64(stmt, 2);
+ std::string_view name((const char*)sqlite3_column_text(stmt, 3));
+
+ auto decl = cgModel.AddStruct(m->GetDeclFullName(nsId, name), DeclStruct{});
+ structsById.try_emplace(id, decl);
+
+ continue;
+ }
+
+ // Executing the statement resulted in an error
+ assert(false);
+ }
+ // Load struct's base classes
+ for (SQLiteStatement stmt(m->database, "SELECT * FROM DeclStructBaseClassRelations");;) {
+ int result = sqlite3_step(stmt);
+ if (result == SQLITE_DONE) {
+ break;
+ }
+
+ if (result == SQLITE_ROW) {
+ int64_t structId = sqlite3_column_int64(stmt, 0);
+ int64_t parentStructNsId = sqlite3_column_int64(stmt, 1);
+ std::string_view parentStructName((const char*)sqlite3_column_text(stmt, 2));
+
+ auto declThis = structsById.at(structId);
+ auto declParent = cgModel.FindStruct(parentStructName); // TODO namespace
+ declThis->baseClasses.push_back(declParent);
+
+ continue;
+ }
+
+ assert(false);
+ }
+ // Load struct properties
+ for (SQLiteStatement stmt(m->database, "SELECT * FROM DeclStructProperties"sv);;) {
+ int result = sqlite3_step(stmt);
+ if (result == SQLITE_DONE) {
+ break;
+ }
+
+ if (result == SQLITE_ROW) {
+ // TODO
+ continue;
+ }
+
+ assert(false);
+ }
+ // Load struct methods
+ for (SQLiteStatement stmt(m->database, "SELECT * FROM DeclStructMethods"sv);;) {
+ int result = sqlite3_step(stmt);
+ if (result == SQLITE_DONE) {
+ break;
+ }
+
+ if (result == SQLITE_ROW) {
+ // TODO
+ continue;
+ }
+
+ assert(false);
+ }
+ // Load method params
+ for (SQLiteStatement stmt(m->database, "SELECT * FROM DeclStructMethodParameters"sv);;) {
+ int result = sqlite3_step(stmt);
+ if (result == SQLITE_DONE) {
+ break;
+ }
+
+ if (result == SQLITE_ROW) {
+ // TODO
+ continue;
+ }
+
+ assert(false);
+ }
+
+ return cgModel;
+}
+
void CodegenModelArchive::StoreStruct(const DeclStruct& decl) {
// -Argument- -Description-
// ?1 Namespace ID
diff --git a/source/20-codegen-compiler/CodegenModelArchive.hpp b/source/20-codegen-compiler/CodegenModelArchive.hpp
index f548048..e67f4d9 100644
--- a/source/20-codegen-compiler/CodegenModelArchive.hpp
+++ b/source/20-codegen-compiler/CodegenModelArchive.hpp
@@ -4,8 +4,8 @@
#include "CodegenModel.hpp"
#include <sqlite3.h>
-#include <string_view>
#include <cstdint>
+#include <string_view>
class CodegenModelArchive {
private:
@@ -20,7 +20,11 @@ public:
Private& GetPimpl() const { return *m; }
void DeleteDeclsRelatedToFile(std::string_view filename);
- void Store(const CodegenModel& cgInput);
+
+ void Store(const CodegenModel& model);
+ void LoadInto(CodegenModel& model) const;
+ CodegenModel Load() const;
+
void StoreStruct(const DeclStruct& decl);
void StoreFunction(const DeclFunction& decl);
void StoreEnum(const DeclEnum& decl);
diff --git a/source/20-codegen-compiler/main.cpp b/source/20-codegen-compiler/main.cpp
index dcec0d3..81b5d23 100644
--- a/source/20-codegen-compiler/main.cpp
+++ b/source/20-codegen-compiler/main.cpp
@@ -30,6 +30,7 @@ namespace fs = std::filesystem;
// TOOD maybe switch to libclang, maintaining this parser is just too painful
struct AppState {
+ CodegenModel* model;
CodegenModelArchive* modelArchive;
robin_hood::unordered_map<std::string, SourceFile, StringHash, StringEqual> sourceFiles;
std::string_view outputDir;
@@ -1192,10 +1193,8 @@ void ParseInputFileAndGenerate(AppState& as, CodegenLexer& /*lexingState*/ ls, s
INPLACE_FMT(generatedCppName, "%.*s/%.*s.g.cpp", PRINTF_STRING_VIEW(as.outputDir), PRINTF_STRING_VIEW(filenameStem));
Utils::WriteOutputFile(po.standaloneSourceOutput, generatedCppName);
- if (as.modelArchive) {
- as.modelArchive->DeleteDeclsRelatedToFile(filenameStem);
- as.modelArchive->Store(po.model);
- }
+ as.modelArchive->DeleteDeclsRelatedToFile(filenameStem);
+ as.modelArchive->Store(po.model);
}
void HandleInputFile(AppState& as, const fs::path& path) {
@@ -1348,13 +1347,23 @@ 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));
-#define BRUSSEL_ENABLE_CODEGEN_ARCHIVE 1
-#if BRUSSEL_ENABLE_CODEGEN_ARCHIVE
+ // TODO make every input command share the same CodegenModel
+ // TODO move the actual output logic after processing all input commands, based on SQLite batabase model instead of the in-memory CodegenModel model
+ // this allows better consistency between direct in-file entities (like enums) vs. multi-file entities (like struct inheritance hierarchy)
+ // this would also mean almost rewriting the whole codegen logic, to work on a changelist fetched from SQLite database instead of being embedded inside the parser loop
+ // TODO how do we detect the case of
+ // 1. has: Foo.hpp Bar.hpp
+ // 2. struct Foo; struct Bar : Foo;
+ // 3. struct Foo is removed from Foo.hpp, but our parser only recieves Foo.hpp as file changed--and can't figure out that there is still a reference to Foo in Bar.hpp
+ // possible solutions
+ // - use some kind of database scanner to review all references to a class when removing (e.g. detect for logic error on foreign key linked columns)
+ // - follow the file links in database, and propagate parsing to those files in the hierarchy
+ // - pretty much defeats the purpose of using an incremental parser: some classes like GameObject will have links throughout a very large portion of the project code
+ CodegenModel model;
CodegenModelArchive archive(as.databaseFilePath);
+
+ as.model = &model;
as.modelArchive = &archive;
-#else
- as.modelArchive = nullptr;
-#endif
// Positional argument pass
for (int i = 1; i < argc; ++i) {