From 369cb617ce0fa165b44748df2ab5efbac0a41dbe Mon Sep 17 00:00:00 2001 From: Kubat <mael.martin31@gmail.com> Date: Mon, 16 Aug 2021 14:28:24 +0200 Subject: [PATCH] [WIP] SCRIPT: Begin the module validation process --- src/Lib/Script/CRTPLuaScriptObject.cc | 90 ++++++++++++++++++++------- src/Lib/Script/CRTPLuaScriptObject.hh | 2 + src/Lib/Script/LuaContext.cc | 35 ++++++++++- src/Lib/Script/LuaContext.hh | 10 ++- src/Lib/Script/ScriptStore.cc | 6 ++ src/Lib/Script/ScriptStore.hh | 2 + src/VivyCli.cc | 1 + utils/lua/sample-spec.module | 20 +++--- 8 files changed, 131 insertions(+), 35 deletions(-) diff --git a/src/Lib/Script/CRTPLuaScriptObject.cc b/src/Lib/Script/CRTPLuaScriptObject.cc index 21e83d1e..522adad5 100644 --- a/src/Lib/Script/CRTPLuaScriptObject.cc +++ b/src/Lib/Script/CRTPLuaScriptObject.cc @@ -1,5 +1,7 @@ #include "CRTPLuaScriptObject.hh" #include "lua.hpp" +#include <QFileInfo> +#include <QString> using namespace Vivy::Script; @@ -63,64 +65,52 @@ ModuleDeclaration::setImplementationFile(lua_State *const L) noexcept int ModuleDeclaration::setImports(lua_State *const L) noexcept { - auto *const self = ModuleDeclaration::CHECK(L, 1); - auto *const context = LuaContext::getContext(L); - IterateOverArray(L, 2, [&context, self, L]() noexcept -> bool { + auto *const self = ModuleDeclaration::CHECK(L, 1); + IterateOverArray(L, 2, [self, L]() noexcept -> bool { std::string import(CHECK_STRING_VIEW(L, -1)); Utils::trim(import); self->importNames.emplace_back(import); - err(context) << "Add import " << import << " for module " << self->moduleName << "\n"; return false; // Value was not popped from stack! }); - err(context) << "Imports done!\n"; LUA_RETURN_NOTHING(L); } int ModuleDeclaration::setOptions(lua_State *const L) noexcept { - auto *const self = ModuleDeclaration::CHECK(L, 1); - auto *const context = LuaContext::getContext(L); - IterateOverArray(L, 2, [&context, self, L]() noexcept -> bool { + auto *const self = ModuleDeclaration::CHECK(L, 1); + IterateOverArray(L, 2, [self, L]() noexcept -> bool { OptionDeclaration *const opt = OptionDeclaration::CHECK(L, -1); self->moduleOptions.emplace_back(opt); - err(context) << "Add option for module " << self->moduleName << "\n"; self->addUserValue(L, 1); // Tie the opt to the module return true; // The value was popped from the stack }); - err(context) << "Options done!\n"; LUA_RETURN_NOTHING(L); } int ModuleDeclaration::setFunctions(lua_State *const L) noexcept { - auto *const self = ModuleDeclaration::CHECK(L, 1); - auto *const context = LuaContext::getContext(L); - IterateOverArray(L, 2, [&context, self, L]() noexcept -> bool { + auto *const self = ModuleDeclaration::CHECK(L, 1); + IterateOverArray(L, 2, [self, L]() noexcept -> bool { FunctionDeclaration *const fun = FunctionDeclaration::CHECK(L, -1); self->moduleFunctions.emplace_back(fun); - err(context) << "Add function " << fun->name << " to module " << self->moduleName << "\n"; self->addUserValue(L, 1); // Tie the function to the module return true; // The value was popped from the stack }); - err(context) << "Functions done!\n"; LUA_RETURN_NOTHING(L); } int ModuleDeclaration::setJobs(lua_State *const L) noexcept { - auto *const self = ModuleDeclaration::CHECK(L, 1); - auto *const context = LuaContext::getContext(L); - IterateOverArray(L, 2, [&context, self, L]() noexcept -> bool { + auto *const self = ModuleDeclaration::CHECK(L, 1); + IterateOverArray(L, 2, [self, L]() noexcept -> bool { JobDeclaration *const job = JobDeclaration::CHECK(L, -1); self->moduleJobs.emplace_back(job); - err(context) << "Add job " << job->name << " to module " << self->moduleName << "\n"; self->addUserValue(L, 1); // Tie the job to the module return true; // The value was popped from the stack }); - err(context) << "Jobs done!\n"; LUA_RETURN_NOTHING(L); } @@ -129,12 +119,66 @@ ModuleDeclaration::pushToRuntime(lua_State *const L) noexcept { ModuleDeclaration *const self = CHECK(L, 1); auto *const context = LuaContext::getContext(L); - err(context) << "Register module \"" << self->moduleName << "\" in the runtime!\n"; - lua_settop(L, 1); - context->registerModuleDeclaration(self); + + Utils::uniqAndSort<std::string>(self->importNames); + if (self->validateModule(L)) { + err(context) << "Register module \"" << self->moduleName << "\" in the runtime!\n"; + lua_settop(L, 1); + + if (context->registerModuleDeclaration(self) == LuaContext::Code::Error) + context->setFailed("Failed to register module " + self->moduleName); + } + + else + context->setFailed("Module " + self->moduleName + " is invalid"); + LUA_RETURN_NOTHING(L); } +bool +ModuleDeclaration::validateModule(lua_State *const L) const noexcept +{ + auto *const context = LuaContext::getContext(L); + + // Minimal module file + if (moduleName.empty() || authorName.empty() || implementationFile.empty()) { + context->setFailed("The module does not have the minimal required information"); + return false; + } + + // Implementation file exists, relative to the .module file + { + QString moduleFile = context->getCurrentLuaFile().c_str(); + QFileInfo moduleInfo(moduleFile); + QFileInfo fileInfo(moduleInfo.absoluteDir(), implementationFile.c_str()); + if (!fileInfo.exists()) { + context->setFailed("The module " + moduleName + + " does not have a valid implementation file"); + return false; + } + } + + // Imports + { + std::vector<std::string> usedImports{}; + const auto getUsedImports = [&usedImports](auto vectorOfPtr) noexcept -> void { + for (const auto *ptr : vectorOfPtr) { + if (!ptr->parentScript.empty()) + usedImports.emplace_back(ptr->parentScript); + } + }; + + getUsedImports(moduleJobs); + getUsedImports(moduleFunctions); + + Utils::uniqAndSort<std::string>(usedImports); + std::vector<std::string> unusedImports = + Utils::sortedSetDifference(usedImports, importNames); + } + + return true; +} + // OptionDeclaration int diff --git a/src/Lib/Script/CRTPLuaScriptObject.hh b/src/Lib/Script/CRTPLuaScriptObject.hh index f255335c..5f717f7b 100644 --- a/src/Lib/Script/CRTPLuaScriptObject.hh +++ b/src/Lib/Script/CRTPLuaScriptObject.hh @@ -251,6 +251,8 @@ script_class (ModuleDeclaration) { // Will be resolved later std::vector<std::string> importNames{}; + bool validateModule(lua_State *const) const noexcept; + public: std::string moduleName{}; std::string authorName{}; diff --git a/src/Lib/Script/LuaContext.cc b/src/Lib/Script/LuaContext.cc index 554d104d..bc298d9c 100644 --- a/src/Lib/Script/LuaContext.cc +++ b/src/Lib/Script/LuaContext.cc @@ -41,10 +41,12 @@ LuaContext::~LuaContext() noexcept void LuaContext::loadPackagedFile(const QString &url) noexcept { + currentFile = url.toStdString(); QFile libVivy(url); if (!libVivy.open(QIODevice::ReadOnly | QIODevice::Text)) qFatal("FATA -> failed to find packaged Vivy file %s", url.toStdString().c_str()); loadString(libVivy.readAll().toStdString().c_str()); + currentFile = ""; } LuaContext * @@ -95,11 +97,15 @@ LuaContext::loadString(const char *str) noexcept LuaContext::Code LuaContext::loadFile(const char *file) noexcept { + currentFile = file; if (luaL_loadfile(L, file) != LUA_OK) { err(this) << "Error loading file " << file << ": " << getLastError().toStdString() << "\n"; + currentFile = ""; return Code::Error; } - return exec(); + auto rc = exec(); + currentFile = ""; + return rc; } QString @@ -166,3 +172,30 @@ LuaContext::getAllModuleName() const noexcept moduleNames.emplace_back(name); return moduleNames; } + +void +LuaContext::setFailed(const std::string &msg) noexcept +{ + failureString = msg; + streamErr << "LuaContext is now in failure state: " << msg; +} + +bool +LuaContext::isFailed() const noexcept +{ + return failureString.empty(); +} + +const std::string & +LuaContext::getFailureDescription() const noexcept +{ + return failureString; +} + +const std::string & +LuaContext::getCurrentLuaFile() const +{ + if (currentFile.empty()) + throw std::logic_error("Not called when a file is being evaluated"); + return currentFile; +} diff --git a/src/Lib/Script/LuaContext.hh b/src/Lib/Script/LuaContext.hh index 52b8a175..2e165626 100644 --- a/src/Lib/Script/LuaContext.hh +++ b/src/Lib/Script/LuaContext.hh @@ -23,6 +23,8 @@ namespace Vivy::Script // New Lua script instance class LuaContext final { lua_State *L{ nullptr }; + std::string failureString{}; + std::string currentFile{}; std::map<const std::string_view, const int> modules = {}; static inline std::map<const lua_State *const, LuaContext *const> contextList = {}; @@ -55,9 +57,15 @@ public: // Returns the module or `nullptr` if not found. const ModuleDeclaration *getModule(const std::string_view) const noexcept; - const std::vector<std::string_view> getAllModuleName() const noexcept; + void setFailed(const std::string &) noexcept; + bool isFailed() const noexcept; + const std::string &getFailureDescription() const noexcept; + + // Makes only sens when called from within loadFile and loadPackagedFile + const std::string &getCurrentLuaFile() const; + private: // Exec all loaded strings and files Code exec() noexcept; diff --git a/src/Lib/Script/ScriptStore.cc b/src/Lib/Script/ScriptStore.cc index 411ebb0b..ad8e16e5 100644 --- a/src/Lib/Script/ScriptStore.cc +++ b/src/Lib/Script/ScriptStore.cc @@ -64,3 +64,9 @@ ScriptStore::getLoadedModules() const noexcept else return {}; } + +const Script::ModuleDeclaration * +ScriptStore::getModule(const std::string_view str) const noexcept +{ + return luaContext->getModule(str); +} diff --git a/src/Lib/Script/ScriptStore.hh b/src/Lib/Script/ScriptStore.hh index c091ad74..9dd345e9 100644 --- a/src/Lib/Script/ScriptStore.hh +++ b/src/Lib/Script/ScriptStore.hh @@ -7,6 +7,7 @@ namespace Vivy::Script { class LuaContext; +class ModuleDeclaration; } namespace Vivy @@ -30,6 +31,7 @@ public: // Get modules from scripts const std::vector<std::string_view> getLoadedModules() const noexcept; + const Script::ModuleDeclaration *getModule(const std::string_view) const noexcept; private: std::unique_ptr<Script::LuaContext> luaContext{ nullptr }; diff --git a/src/VivyCli.cc b/src/VivyCli.cc index 42e1c2f1..0b7017e8 100644 --- a/src/VivyCli.cc +++ b/src/VivyCli.cc @@ -18,6 +18,7 @@ VivyCli::exec() noexcept scriptStore.executeScript(selectedDoc->getUuid()); for (const auto &str : scriptStore.getLoadedModules()) { std::cout << "Module " << str << " was loaded!\n"; + const auto *mod = scriptStore.getModule(str); } return 0; } diff --git a/utils/lua/sample-spec.module b/utils/lua/sample-spec.module index a6886f8d..c65d82e2 100644 --- a/utils/lua/sample-spec.module +++ b/utils/lua/sample-spec.module @@ -3,14 +3,6 @@ custom_opt = DeclareOption { option1 = false } time_opt = DeclareOption { preTime = -900, postTime = 300, } color_opt = DeclareOption { color1 = Vivy:newColor("#314159") } -job1 = DeclareJob { "set_retime", options = { time_opt } } -job2 = DeclareJob { "demultiply_syllabes" } -job3 = ImportJob { "utils", "color_after_read_3", options = { color_opt } } - -fun1 = DeclareFunction { "tripleCopySyl" } -fun2 = DeclareFunction { "printFoo" } -fun3 = ImportFunction { "utils", "prettyPrint" } - DeclareModule { name = "sample-spec", description = "Sample script used for the specification proposition", @@ -19,6 +11,14 @@ DeclareModule { revision = "rev-1254", imports = { "utils" }, options = { custom_opt, time_opt, color_opt }, - functions = { fun1, fun2, fun3 }, - jobs = { job1, job2, job3 }, + functions = { + DeclareFunction { "tripleCopySyl" }, + DeclareFunction { "printFoo" }, + ImportFunction { "utils", "prettyPrint" }, + }, + jobs = { + DeclareJob { "set_retime", options = { time_opt } }, + DeclareJob { "demultiply_syllabes" }, + ImportJob { "utils", "color_after_read_3", options = { color_opt } }, + }, } -- GitLab