From cb489a3fba3ed81163ee47366437d6b30a6c9e3d Mon Sep 17 00:00:00 2001 From: Andreas Drewke Date: Sat, 28 Dec 2024 15:48:17 +0100 Subject: [PATCH] Validations: refactored Validations out of MinitScript.cpp, its just too fat --- Makefile | 3 +- Makefile.nmake | 3 +- src/minitscript/minitscript/MinitScript.cpp | 427 ++---------------- src/minitscript/minitscript/MinitScript.h | 71 +-- src/minitscript/minitscript/Validations.cpp | 387 ++++++++++++++++ src/minitscript/minitscript/Validations.h | 124 +++++ src/minitscript/minitscript/fwd-minitscript.h | 3 +- 7 files changed, 555 insertions(+), 463 deletions(-) create mode 100644 src/minitscript/minitscript/Validations.cpp create mode 100644 src/minitscript/minitscript/Validations.h diff --git a/Makefile b/Makefile index 47d2365..642130d 100644 --- a/Makefile +++ b/Makefile @@ -109,8 +109,9 @@ SRCS = \ src/minitscript/minitscript/StringMethods.cpp \ src/minitscript/minitscript/TimeMethods.cpp \ src/minitscript/minitscript/Transpiler.cpp \ - src/minitscript/minitscript/XMLMethods.cpp \ + src/minitscript/minitscript/Validations.cpp \ src/minitscript/minitscript/Version.cpp \ + src/minitscript/minitscript/XMLMethods.cpp \ src/minitscript/network/httpclient/HTTPClient.cpp \ src/minitscript/network/httpclient/HTTPClientException.cpp \ src/minitscript/network/httpclient/HTTPDownloadClient.cpp \ diff --git a/Makefile.nmake b/Makefile.nmake index 5a55e63..8392f07 100644 --- a/Makefile.nmake +++ b/Makefile.nmake @@ -52,8 +52,9 @@ SRCS = \ src/minitscript/minitscript/StringMethods.cpp \ src/minitscript/minitscript/TimeMethods.cpp \ src/minitscript/minitscript/Transpiler.cpp \ - src/minitscript/minitscript/XMLMethods.cpp \ + src/minitscript/minitscript/Validations.cpp \ src/minitscript/minitscript/Version.cpp \ + src/minitscript/minitscript/XMLMethods.cpp \ src/minitscript/network/httpclient/HTTPClient.cpp \ src/minitscript/network/httpclient/HTTPClientException.cpp \ src/minitscript/network/httpclient/HTTPDownloadClient.cpp \ diff --git a/src/minitscript/minitscript/MinitScript.cpp b/src/minitscript/minitscript/MinitScript.cpp index c73ec9e..8d3725d 100644 --- a/src/minitscript/minitscript/MinitScript.cpp +++ b/src/minitscript/minitscript/MinitScript.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -91,6 +92,7 @@ using minitscript::minitscript::ScriptMethods; using minitscript::minitscript::SetMethods; using minitscript::minitscript::StringMethods; using minitscript::minitscript::TimeMethods; +using minitscript::minitscript::Validations; using minitscript::minitscript::XMLMethods; // TODO: we can remove the _ here again, as MinitScript.cpp is not a transpilation unit anymore @@ -934,6 +936,31 @@ bool MinitScript::createStatementSyntaxTree(const string& scriptFileName, int sc return false; } +int MinitScript::getStackletScopeScriptIdx(int scriptIdx) { + if (scriptIdx < 0 || scriptIdx >= scripts.size() || + scripts[scriptIdx].type != MinitScript::Script::TYPE_STACKLET) { + return MinitScript::SCRIPTIDX_NONE; + } + // + const auto& stackletScript = scripts[scriptIdx]; + const auto& stackletScopeName = stackletScript.arguments.size() == 1?stackletScript.arguments[0].name:string(); + if (stackletScopeName.empty() == true) { + return MinitScript::SCRIPTIDX_NONE; + } + // + for (auto i = 0; i < scripts.size(); i++) { + if (i == scriptIdx) continue; + const auto& scriptCandidate = scripts[i]; + if (scriptCandidate.type != MinitScript::Script::TYPE_FUNCTION && scriptCandidate.type != MinitScript::Script::TYPE_STACKLET) continue; + if (scriptCandidate.condition == stackletScopeName) { + if (scriptCandidate.type == MinitScript::Script::TYPE_STACKLET) return getStackletScopeScriptIdx(i); else return i; + } + } + // + return MinitScript::SCRIPTIDX_NONE; +} + + bool MinitScript::setupFunctionAndStackletScriptIndices(int scriptIdx) { // auto& script = scripts[scriptIdx]; @@ -1119,400 +1146,6 @@ bool MinitScript::setupFunctionAndStackletScriptIndices(Variable& variable, cons return true; } -int MinitScript::getStackletScopeScriptIdx(int scriptIdx) { - if (scriptIdx < 0 || scriptIdx >= scripts.size() || - scripts[scriptIdx].type != MinitScript::Script::TYPE_STACKLET) { - return MinitScript::SCRIPTIDX_NONE; - } - // - const auto& stackletScript = scripts[scriptIdx]; - const auto& stackletScopeName = stackletScript.arguments.size() == 1?stackletScript.arguments[0].name:string(); - if (stackletScopeName.empty() == true) { - return MinitScript::SCRIPTIDX_NONE; - } - // - for (auto i = 0; i < scripts.size(); i++) { - if (i == scriptIdx) continue; - const auto& scriptCandidate = scripts[i]; - if (scriptCandidate.type != MinitScript::Script::TYPE_FUNCTION && scriptCandidate.type != MinitScript::Script::TYPE_STACKLET) continue; - if (scriptCandidate.condition == stackletScopeName) { - if (scriptCandidate.type == MinitScript::Script::TYPE_STACKLET) return getStackletScopeScriptIdx(i); else return i; - } - } - // - return MinitScript::SCRIPTIDX_NONE; -} - -bool MinitScript::validateStacklets(int scriptIdx) { - // - const auto& script = scripts[scriptIdx]; - auto statementIdx = STATEMENTIDX_FIRST; - // - for (const auto& syntaxTreeNode: script.syntaxTree) { - const auto& statement = script.statements[statementIdx++]; - // - if (validateStacklets(script.type == Script::TYPE_FUNCTION?scriptIdx:SCRIPTIDX_NONE, syntaxTreeNode, statement) == false) { - // - return false; - } - } - // - return true; -} - -bool MinitScript::validateStacklets(const string& function, int scopeScriptIdx) { - auto functionScriptIdx = getFunctionScriptIdx(function); - if (functionScriptIdx == SCRIPTIDX_NONE) { - _Console::printLine("MinitScript::validateStacklet(): function not found: " + function); - return false; - } - // - const auto& script = scripts[functionScriptIdx]; - auto statementIdx = STATEMENTIDX_FIRST; - // - for (const auto& syntaxTreeNode: script.syntaxTree) { - const auto& statement = script.statements[statementIdx++]; - // - if (validateStacklets(scopeScriptIdx == MinitScript::SCRIPTIDX_NONE?functionScriptIdx:scopeScriptIdx, syntaxTreeNode, statement) == false) { - // - return false; - } - } - // - return true; -} - -bool MinitScript::validateStacklets(int scopeScriptIdx, const SyntaxTreeNode& syntaxTreeNode, const Statement& statement) { - switch (syntaxTreeNode.type) { - case SyntaxTreeNode::SCRIPTSYNTAXTREENODE_LITERAL: - { - // TODO: improve me! This is actually litaral only, which can be also set as variable and be reused later - // basically we forbid here to create a stacklet assignment variable with wrong scope in a given scope - if (syntaxTreeNode.value.getType() == MinitScript::TYPE_STACKLET_ASSIGNMENT) { - // we only allow assignments of stacklets with a correct scope, means - string stackletName; - auto stackletScriptIdx = SCRIPTIDX_NONE; - if (syntaxTreeNode.value.getStackletValue(stackletName, stackletScriptIdx) == false || - (stackletScriptIdx = getFunctionScriptIdx(stackletName)) == SCRIPTIDX_NONE) { - // - _Console::printLine( - getStatementInformation(statement, syntaxTreeNode.subLineIdx) + - ": " + - syntaxTreeNode.value.getValueAsString() + - ": Stacklet not found" - ); - // - parseErrors.push_back( - getStatementInformation(statement, syntaxTreeNode.subLineIdx) + - ": " + - syntaxTreeNode.value.getValueAsString() + - ": Stacklet not found" - ); - // - return false; - } - // - int stackletScopeScriptIdx = getStackletScopeScriptIdx(stackletScriptIdx); - if (stackletScopeScriptIdx != scopeScriptIdx) { - // construct scope error - string scopeErrorMessage; - if (stackletScopeScriptIdx == SCRIPTIDX_NONE) { - scopeErrorMessage = "Stacklet requires root scope"; - } else { - scopeErrorMessage = "Stacklet requires scope of " + scripts[stackletScopeScriptIdx].condition + "()"; - } - scopeErrorMessage+= ", but has scope of "; - if (scopeScriptIdx == SCRIPTIDX_NONE) { - scopeErrorMessage+= "root scope"; - } else { - scopeErrorMessage+= scripts[scopeScriptIdx].condition + "()"; - } - // - _Console::printLine( - getStatementInformation(statement, syntaxTreeNode.subLineIdx) + - ": " + - syntaxTreeNode.value.getValueAsString() + - ": Stacklet scope invalid: " + - scopeErrorMessage - ); - // - parseErrors.push_back( - getStatementInformation(statement, syntaxTreeNode.subLineIdx) + - ": " + - syntaxTreeNode.value.getValueAsString() + - ": Stacklet scope invalid" + - scopeErrorMessage - ); - // - return false; - } - // check stacklet itself for stacklet litarals - if (validateStacklets(stackletName, scopeScriptIdx) == false) return false; - } - // - break; - } - case SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_METHOD: - { - for (const auto& argument: syntaxTreeNode.arguments) { - if (validateStacklets(scopeScriptIdx, argument, statement) == false) return false; - } - // - break; - } - case SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_FUNCTION: - { - for (const auto& argument: syntaxTreeNode.arguments) { - if (validateStacklets(scopeScriptIdx, argument, statement) == false) return false; - } - // - if (getFunctionScriptIdx(syntaxTreeNode.value.getValueAsString()) == scopeScriptIdx) { - // recursion - } else { - validateStacklets(syntaxTreeNode.value.getValueAsString()); - } - // - break; - } - case SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_STACKLET: - { - // - string stackletName = syntaxTreeNode.value.getValueAsString(); - auto stackletScriptIdx = syntaxTreeNode.getScriptIdx(); - if (stackletName.empty() == true || stackletScriptIdx == SCRIPTIDX_NONE) { - // - _Console::printLine( - getStatementInformation(statement, syntaxTreeNode.subLineIdx) + - ": " + - syntaxTreeNode.value.getValueAsString() + - ": Stacklet not found" - ); - // - parseErrors.push_back( - getStatementInformation(statement, syntaxTreeNode.subLineIdx) + - ": " + - syntaxTreeNode.value.getValueAsString() + - ": Stacklet not found" - ); - // - return false; - } - // - int stackletScopeScriptIdx = getStackletScopeScriptIdx(stackletScriptIdx); - if (stackletScopeScriptIdx != scopeScriptIdx) { - // construct scope error - string scopeErrorMessage; - if (stackletScopeScriptIdx == SCRIPTIDX_NONE) { - scopeErrorMessage = "Stacklet requires root scope"; - } else { - scopeErrorMessage = "Stacklet requires scope of " + scripts[stackletScopeScriptIdx].condition + "()"; - } - scopeErrorMessage+= ", but has scope of "; - if (scopeScriptIdx == SCRIPTIDX_NONE) { - scopeErrorMessage+= "root scope"; - } else { - scopeErrorMessage+= scripts[scopeScriptIdx].condition + "()"; - } - // - _Console::printLine( - getStatementInformation(statement, syntaxTreeNode.subLineIdx) + - ": " + - syntaxTreeNode.value.getValueAsString() + - ": Stacklet scope invalid: " + - scopeErrorMessage - ); - // - parseErrors.push_back( - getStatementInformation(statement, syntaxTreeNode.subLineIdx) + - ": " + - syntaxTreeNode.value.getValueAsString() + - ": Stacklet scope invalid" + - scopeErrorMessage - ); - // - return false; - } - // - validateStacklets(syntaxTreeNode.value.getValueAsString(), scopeScriptIdx); - // - break; - } - default: - break; - } - // - return true; -} - -bool MinitScript::validateCallable(const string& function) { - auto functionScriptIdx = getFunctionScriptIdx(function); - if (functionScriptIdx == SCRIPTIDX_NONE) { - _Console::printLine("MinitScript::validateCallable(): function not found: " + function); - return false; - } - // - const auto& script = scripts[functionScriptIdx]; - auto statementIdx = STATEMENTIDX_FIRST; - // - for (const auto& syntaxTreeNode: script.syntaxTree) { - const auto& statement = script.statements[statementIdx++]; - // - if (validateCallable(syntaxTreeNode, statement) == false) { - // - return false; - } - } - // - return true; -} - -bool MinitScript::validateCallable(const SyntaxTreeNode& syntaxTreeNode, const Statement& statement) { - // - switch (syntaxTreeNode.type) { - case SyntaxTreeNode::SCRIPTSYNTAXTREENODE_LITERAL: - { - break; - } - case SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_METHOD: - { - const auto& contextFunctions = syntaxTreeNode.getMethod()->getContextFunctions(); - if (contextFunctions.empty() == false) { - // - _Console::printLine( - getStatementInformation(statement, syntaxTreeNode.subLineIdx) + - ": Method " + - syntaxTreeNode.getMethod()->getMethodName() + "() can not be called within a callable function" - ); - // - parseErrors.push_back( - getStatementInformation(statement, syntaxTreeNode.subLineIdx) + - ": Method " + - syntaxTreeNode.getMethod()->getMethodName() + "() can not be called within a callable function" - ); - // - return false; - } - } - break; - case SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_FUNCTION: - { - for (const auto& argument: syntaxTreeNode.arguments) { - if (validateCallable(argument, statement) == false) return false; - } - // - validateCallable(syntaxTreeNode.value.getValueAsString()); - // - break; - } - case SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_STACKLET: - { - validateCallable(syntaxTreeNode.value.getValueAsString()); - // - break; - } - default: - break; - } - // - return true; -} - -bool MinitScript::validateContextFunctions(const string& function, vector& functionStack) { - auto functionScriptIdx = getFunctionScriptIdx(function); - if (functionScriptIdx == SCRIPTIDX_NONE) { - _Console::printLine("MinitScript::validateContextFunctions(): Function not found: " + function); - return false; - } - // - const auto& script = scripts[functionScriptIdx]; - auto statementIdx = STATEMENTIDX_FIRST; - // - functionStack.push_back(script.condition); - // - for (const auto& syntaxTreeNode: script.syntaxTree) { - const auto& statement = script.statements[statementIdx++]; - // - if (validateContextFunctions(syntaxTreeNode, functionStack, statement) == false) { - // - return false; - } - } - // - functionStack.erase(functionStack.begin() + functionStack.size() - 1); - // - return true; -} - -bool MinitScript::validateContextFunctions(const SyntaxTreeNode& syntaxTreeNode, vector& functionStack, const Statement& statement) { - // - switch (syntaxTreeNode.type) { - case SyntaxTreeNode::SCRIPTSYNTAXTREENODE_LITERAL: - { - break; - } - case SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_METHOD: - { - const auto& contextFunctions = syntaxTreeNode.getMethod()->getContextFunctions(); - if (contextFunctions.empty() == false) { - // - string contextFunctionsString; - for (const auto &contextFunction: contextFunctions) { - if (contextFunctionsString.empty() == false) contextFunctionsString+= ", "; - contextFunctionsString+= contextFunction + "()"; - } - // - const auto& functionStackFunction = functionStack[0]; - if (find(contextFunctions.begin(), contextFunctions.end(), functionStackFunction) == contextFunctions.end()) { - // - string contextFunctionsString; - for (const auto &contextFunction: contextFunctions) { - if (contextFunctionsString.empty() == false) contextFunctionsString+= ", "; - contextFunctionsString+= contextFunction + "()"; - } - // - _Console::printLine( - getStatementInformation(statement, syntaxTreeNode.subLineIdx) + - ": Method " + - syntaxTreeNode.getMethod()->getMethodName() + "() can only be called within the following functions: " + - contextFunctionsString + - ", but was called from " + - functionStackFunction + "()" - ); - // - parseErrors.push_back( - getStatementInformation(statement, syntaxTreeNode.subLineIdx) + - ": Method " + - syntaxTreeNode.getMethod()->getMethodName() + "() can only be called within the following functions: " + - contextFunctionsString + - ", but was called from " + - functionStackFunction + "()" - ); - // - return false; - } - } - } - break; - case SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_FUNCTION: - case SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_STACKLET: - { - for (const auto& argument: syntaxTreeNode.arguments) { - if (validateContextFunctions(argument, functionStack, statement) == false) return false; - } - // - if (functionStack.back() != syntaxTreeNode.value.getValueAsString()) { - validateContextFunctions(syntaxTreeNode.value.getValueAsString(), functionStack); - } - // - break; - } - default: - break; - } - // - return true; -} - void MinitScript::emit(const string& condition) { // defer emit if a function/stacklet is still running if (isFunctionRunning() == true) { @@ -2988,7 +2621,7 @@ void MinitScript::parseScript(const string& pathName, const string& fileName, bo script.type == MinitScript::Script::TYPE_ON || script.type == MinitScript::Script::TYPE_ONENABLED) { // - if (validateStacklets(scriptIdx) == false) { + if (Validations::validateStacklets(this, scriptIdx, parseErrors) == false) { scriptValid = false; return; } @@ -2998,7 +2631,7 @@ void MinitScript::parseScript(const string& pathName, const string& fileName, bo // if (script.callable == true) { // - if (validateCallable(script.condition) == false) { + if (Validations::validateCallable(this, script.condition, parseErrors) == false) { scriptValid = false; return; } @@ -3006,7 +2639,7 @@ void MinitScript::parseScript(const string& pathName, const string& fileName, bo // vector functionStack; // - if (validateContextFunctions(script.condition, functionStack) == false) { + if (Validations::validateContextFunctions(this, script.condition, functionStack, parseErrors) == false) { scriptValid = false; return; } diff --git a/src/minitscript/minitscript/MinitScript.h b/src/minitscript/minitscript/MinitScript.h index eb79654..011cb9d 100644 --- a/src/minitscript/minitscript/MinitScript.h +++ b/src/minitscript/minitscript/MinitScript.h @@ -73,6 +73,7 @@ class minitscript::minitscript::MinitScript { friend class MathMethods; friend class ScriptMethods; friend class Transpiler; + friend class Validations; public: enum Operator { @@ -3853,6 +3854,13 @@ class minitscript::minitscript::MinitScript { */ bool createStatementSyntaxTree(const string& scriptFileName, int scriptIdx, const string_view& methodName, const vector& arguments, const Statement& statement, SyntaxTreeNode& syntaxTree, int subLineIdx = 0); + /** + * Return stacklet scope script index + * @param scriptIdx stacklet script index + * @return stacklet scope script index + */ + int getStackletScopeScriptIdx(int scriptIdx); + /** * Setup function and stacket script indices * @param scriptIdx script index @@ -3875,69 +3883,6 @@ class minitscript::minitscript::MinitScript { */ bool setupFunctionAndStackletScriptIndices(Variable& variable, const Statement& statement, int subLineIdx); - /** - * Return stacklet scope script index - * @param scriptIdx stacklet script index - * @return stacklet scope script index - */ - int getStackletScopeScriptIdx(int scriptIdx); - - /** - * Validate stacklets - * @param scriptIdx script index - * @return success - */ - bool validateStacklets(int scriptIdx); - - /** - * Validate stacklets - * @param function function - * @param scopeScriptIdx scope script index or MinitScript::SCRIPTIDX_NONE for the function itself - * @return success - */ - bool validateStacklets(const string& function, int scopeScriptIdx = MinitScript::SCRIPTIDX_NONE); - - /** - * Validate stacklets - * @param scopeScriptIdx scope script index - * @param syntaxTreeNode syntax tree node - * @param statement statement - * @return success - */ - bool validateStacklets(int scopeScriptIdx, const SyntaxTreeNode& syntaxTreeNode, const Statement& statement); - - /** - * Validate callabe - * @param function function - * @return success - */ - bool validateCallable(const string& function); - - /** - * Validate callable - * @param syntaxTreeNode syntax tree node - * @param statement statement - * @return success - */ - bool validateCallable(const SyntaxTreeNode& syntaxTreeNode, const Statement& statement); - - /** - * Validate context functions - * @param function function - * @param functionStack function stack - * @return success - */ - bool validateContextFunctions(const string& function, vector& functionStack); - - /** - * Validate context functions - * @param syntaxTreeNode syntax tree node - * @param functionStack function stack - * @param statement statement - * @return success - */ - bool validateContextFunctions(const SyntaxTreeNode& syntaxTreeNode, vector& functionStack, const Statement& statement); - /** * Returns if char is operator char * @param c char to test diff --git a/src/minitscript/minitscript/Validations.cpp b/src/minitscript/minitscript/Validations.cpp new file mode 100644 index 0000000..3d20c9f --- /dev/null +++ b/src/minitscript/minitscript/Validations.cpp @@ -0,0 +1,387 @@ +#include +#include + +#include +#include +#include + +using std::string; +using std::vector; + +using minitscript::minitscript::MinitScript; +using minitscript::minitscript::Validations; + +bool Validations::validateStacklets(MinitScript* minitScript, int scriptIdx, vector& parseErrors) { + // + const auto& scripts = minitScript->getScripts(); + const auto& script = scripts[scriptIdx]; + auto statementIdx = MinitScript::STATEMENTIDX_FIRST; + // + for (const auto& syntaxTreeNode: script.syntaxTree) { + const auto& statement = script.statements[statementIdx++]; + // + if (validateStacklets(minitScript, script.type == MinitScript::Script::TYPE_FUNCTION?scriptIdx:MinitScript::SCRIPTIDX_NONE, syntaxTreeNode, statement, parseErrors) == false) { + // + return false; + } + } + // + return true; +} + +bool Validations::validateStacklets(MinitScript* minitScript, const string& function, vector& parseErrors, int scopeScriptIdx) { + auto functionScriptIdx = minitScript->getFunctionScriptIdx(function); + if (functionScriptIdx == MinitScript::SCRIPTIDX_NONE) { + _Console::printLine("MinitScript::validateStacklet(): function not found: " + function); + return false; + } + // + const auto& scripts = minitScript->getScripts(); + const auto& script = scripts[functionScriptIdx]; + auto statementIdx = MinitScript::STATEMENTIDX_FIRST; + // + for (const auto& syntaxTreeNode: script.syntaxTree) { + const auto& statement = script.statements[statementIdx++]; + // + if (validateStacklets(minitScript, scopeScriptIdx == MinitScript::SCRIPTIDX_NONE?functionScriptIdx:scopeScriptIdx, syntaxTreeNode, statement, parseErrors) == false) { + // + return false; + } + } + // + return true; +} + +bool Validations::validateStacklets(MinitScript* minitScript, int scopeScriptIdx, const MinitScript::SyntaxTreeNode& syntaxTreeNode, const MinitScript::Statement& statement, vector& parseErrors) { + const auto& scripts = minitScript->getScripts(); + switch (syntaxTreeNode.type) { + case MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_LITERAL: + { + // TODO: improve me! This is actually litaral only, which can be also set as variable and be reused later + // basically we forbid here to create a stacklet assignment variable with wrong scope in a given scope + if (syntaxTreeNode.value.getType() == MinitScript::TYPE_STACKLET_ASSIGNMENT) { + // we only allow assignments of stacklets with a correct scope, means + string stackletName; + auto stackletScriptIdx = MinitScript::SCRIPTIDX_NONE; + if (syntaxTreeNode.value.getStackletValue(stackletName, stackletScriptIdx) == false || + (stackletScriptIdx = minitScript->getFunctionScriptIdx(stackletName)) == MinitScript::SCRIPTIDX_NONE) { + // + _Console::printLine( + minitScript->getStatementInformation(statement, syntaxTreeNode.subLineIdx) + + ": " + + syntaxTreeNode.value.getValueAsString() + + ": Stacklet not found" + ); + // + parseErrors.push_back( + minitScript->getStatementInformation(statement, syntaxTreeNode.subLineIdx) + + ": " + + syntaxTreeNode.value.getValueAsString() + + ": Stacklet not found" + ); + // + return false; + } + // + int stackletScopeScriptIdx = minitScript->getStackletScopeScriptIdx(stackletScriptIdx); + if (stackletScopeScriptIdx != scopeScriptIdx) { + // construct scope error + string scopeErrorMessage; + if (stackletScopeScriptIdx == MinitScript::SCRIPTIDX_NONE) { + scopeErrorMessage = "Stacklet requires root scope"; + } else { + scopeErrorMessage = "Stacklet requires scope of " + scripts[stackletScopeScriptIdx].condition + "()"; + } + scopeErrorMessage+= ", but has scope of "; + if (scopeScriptIdx == MinitScript::SCRIPTIDX_NONE) { + scopeErrorMessage+= "root scope"; + } else { + scopeErrorMessage+= scripts[scopeScriptIdx].condition + "()"; + } + // + _Console::printLine( + minitScript->getStatementInformation(statement, syntaxTreeNode.subLineIdx) + + ": " + + syntaxTreeNode.value.getValueAsString() + + ": Stacklet scope invalid: " + + scopeErrorMessage + ); + // + parseErrors.push_back( + minitScript->getStatementInformation(statement, syntaxTreeNode.subLineIdx) + + ": " + + syntaxTreeNode.value.getValueAsString() + + ": Stacklet scope invalid" + + scopeErrorMessage + ); + // + return false; + } + // check stacklet itself for stacklet litarals + if (validateStacklets(minitScript, stackletName, parseErrors, scopeScriptIdx) == false) return false; + } + // + break; + } + case MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_METHOD: + { + for (const auto& argument: syntaxTreeNode.arguments) { + if (validateStacklets(minitScript, scopeScriptIdx, argument, statement, parseErrors) == false) return false; + } + // + break; + } + case MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_FUNCTION: + { + for (const auto& argument: syntaxTreeNode.arguments) { + if (validateStacklets(minitScript, scopeScriptIdx, argument, statement, parseErrors) == false) return false; + } + // + if (minitScript->getFunctionScriptIdx(syntaxTreeNode.value.getValueAsString()) == scopeScriptIdx) { + // recursion + } else { + validateStacklets(minitScript, syntaxTreeNode.value.getValueAsString(), parseErrors); + } + // + break; + } + case MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_STACKLET: + { + // + string stackletName = syntaxTreeNode.value.getValueAsString(); + auto stackletScriptIdx = syntaxTreeNode.getScriptIdx(); + if (stackletName.empty() == true || stackletScriptIdx == MinitScript::SCRIPTIDX_NONE) { + // + _Console::printLine( + minitScript->getStatementInformation(statement, syntaxTreeNode.subLineIdx) + + ": " + + syntaxTreeNode.value.getValueAsString() + + ": Stacklet not found" + ); + // + parseErrors.push_back( + minitScript->getStatementInformation(statement, syntaxTreeNode.subLineIdx) + + ": " + + syntaxTreeNode.value.getValueAsString() + + ": Stacklet not found" + ); + // + return false; + } + // + int stackletScopeScriptIdx = minitScript->getStackletScopeScriptIdx(stackletScriptIdx); + if (stackletScopeScriptIdx != scopeScriptIdx) { + // construct scope error + string scopeErrorMessage; + if (stackletScopeScriptIdx == MinitScript::SCRIPTIDX_NONE) { + scopeErrorMessage = "Stacklet requires root scope"; + } else { + scopeErrorMessage = "Stacklet requires scope of " + scripts[stackletScopeScriptIdx].condition + "()"; + } + scopeErrorMessage+= ", but has scope of "; + if (scopeScriptIdx == MinitScript::SCRIPTIDX_NONE) { + scopeErrorMessage+= "root scope"; + } else { + scopeErrorMessage+= scripts[scopeScriptIdx].condition + "()"; + } + // + _Console::printLine( + minitScript->getStatementInformation(statement, syntaxTreeNode.subLineIdx) + + ": " + + syntaxTreeNode.value.getValueAsString() + + ": Stacklet scope invalid: " + + scopeErrorMessage + ); + // + parseErrors.push_back( + minitScript->getStatementInformation(statement, syntaxTreeNode.subLineIdx) + + ": " + + syntaxTreeNode.value.getValueAsString() + + ": Stacklet scope invalid" + + scopeErrorMessage + ); + // + return false; + } + // + validateStacklets(minitScript, syntaxTreeNode.value.getValueAsString(), parseErrors, scopeScriptIdx); + // + break; + } + default: + break; + } + // + return true; +} + +bool Validations::validateCallable(MinitScript* minitScript, const string& function, vector& parseErrors) { + auto functionScriptIdx = minitScript->getFunctionScriptIdx(function); + if (functionScriptIdx == MinitScript::SCRIPTIDX_NONE) { + _Console::printLine("MinitScript::validateCallable(): function not found: " + function); + return false; + } + // + const auto& scripts = minitScript->getScripts(); + const auto& script = scripts[functionScriptIdx]; + auto statementIdx = MinitScript::STATEMENTIDX_FIRST; + // + for (const auto& syntaxTreeNode: script.syntaxTree) { + const auto& statement = script.statements[statementIdx++]; + // + if (validateCallable(minitScript, syntaxTreeNode, statement, parseErrors) == false) { + // + return false; + } + } + // + return true; +} + +bool Validations::validateCallable(MinitScript* minitScript, const MinitScript::SyntaxTreeNode& syntaxTreeNode, const MinitScript::Statement& statement, vector& parseErrors) { + // + switch (syntaxTreeNode.type) { + case MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_LITERAL: + { + break; + } + case MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_METHOD: + { + const auto& contextFunctions = syntaxTreeNode.getMethod()->getContextFunctions(); + if (contextFunctions.empty() == false) { + // + _Console::printLine( + minitScript->getStatementInformation(statement, syntaxTreeNode.subLineIdx) + + ": Method " + + syntaxTreeNode.getMethod()->getMethodName() + "() can not be called within a callable function" + ); + // + parseErrors.push_back( + minitScript->getStatementInformation(statement, syntaxTreeNode.subLineIdx) + + ": Method " + + syntaxTreeNode.getMethod()->getMethodName() + "() can not be called within a callable function" + ); + // + return false; + } + } + break; + case MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_FUNCTION: + { + for (const auto& argument: syntaxTreeNode.arguments) { + if (validateCallable(minitScript, argument, statement, parseErrors) == false) return false; + } + // + validateCallable(minitScript, syntaxTreeNode.value.getValueAsString(), parseErrors); + // + break; + } + case MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_STACKLET: + { + validateCallable(minitScript, syntaxTreeNode.value.getValueAsString(), parseErrors); + // + break; + } + default: + break; + } + // + return true; +} + +bool Validations::validateContextFunctions(MinitScript* minitScript, const string& function, vector& functionStack, vector& parseErrors) { + auto functionScriptIdx = minitScript->getFunctionScriptIdx(function); + if (functionScriptIdx == MinitScript::SCRIPTIDX_NONE) { + _Console::printLine("MinitScript::validateContextFunctions(): Function not found: " + function); + return false; + } + // + const auto& scripts = minitScript->getScripts(); + const auto& script = scripts[functionScriptIdx]; + auto statementIdx = MinitScript::STATEMENTIDX_FIRST; + // + functionStack.push_back(script.condition); + // + for (const auto& syntaxTreeNode: script.syntaxTree) { + const auto& statement = script.statements[statementIdx++]; + // + if (validateContextFunctions(minitScript, syntaxTreeNode, functionStack, statement, parseErrors) == false) { + // + return false; + } + } + // + functionStack.erase(functionStack.begin() + functionStack.size() - 1); + // + return true; +} + +bool Validations::validateContextFunctions(MinitScript* minitScript, const MinitScript::SyntaxTreeNode& syntaxTreeNode, vector& functionStack, const MinitScript::Statement& statement, vector& parseErrors) { + // + switch (syntaxTreeNode.type) { + case MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_LITERAL: + { + break; + } + case MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_METHOD: + { + const auto& contextFunctions = syntaxTreeNode.getMethod()->getContextFunctions(); + if (contextFunctions.empty() == false) { + // + string contextFunctionsString; + for (const auto &contextFunction: contextFunctions) { + if (contextFunctionsString.empty() == false) contextFunctionsString+= ", "; + contextFunctionsString+= contextFunction + "()"; + } + // + const auto& functionStackFunction = functionStack[0]; + if (find(contextFunctions.begin(), contextFunctions.end(), functionStackFunction) == contextFunctions.end()) { + // + string contextFunctionsString; + for (const auto &contextFunction: contextFunctions) { + if (contextFunctionsString.empty() == false) contextFunctionsString+= ", "; + contextFunctionsString+= contextFunction + "()"; + } + // + _Console::printLine( + minitScript->getStatementInformation(statement, syntaxTreeNode.subLineIdx) + + ": Method " + + syntaxTreeNode.getMethod()->getMethodName() + "() can only be called within the following functions: " + + contextFunctionsString + + ", but was called from " + + functionStackFunction + "()" + ); + // + parseErrors.push_back( + minitScript->getStatementInformation(statement, syntaxTreeNode.subLineIdx) + + ": Method " + + syntaxTreeNode.getMethod()->getMethodName() + "() can only be called within the following functions: " + + contextFunctionsString + + ", but was called from " + + functionStackFunction + "()" + ); + // + return false; + } + } + } + break; + case MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_FUNCTION: + case MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_STACKLET: + { + for (const auto& argument: syntaxTreeNode.arguments) { + if (validateContextFunctions(minitScript, argument, functionStack, statement, parseErrors) == false) return false; + } + // + if (functionStack.back() != syntaxTreeNode.value.getValueAsString()) { + validateContextFunctions(minitScript, syntaxTreeNode.value.getValueAsString(), functionStack, parseErrors); + } + // + break; + } + default: + break; + } + // + return true; +} diff --git a/src/minitscript/minitscript/Validations.h b/src/minitscript/minitscript/Validations.h new file mode 100644 index 0000000..ed80573 --- /dev/null +++ b/src/minitscript/minitscript/Validations.h @@ -0,0 +1,124 @@ +#pragma once + +#include +#include + +#include +#include + +using minitscript::minitscript::MinitScript; + +using std::string; +using std::vector; + +/** + * MinitScript validations methods + * @author Andreas Drewke + */ +class minitscript::minitscript::Validations { +public: + /** + * Validate stacklets + * @param minitScript MinitScript instance + * @param scriptIdx script index + * @param parseErrors parse errors + * @return success + */ + static bool validateStacklets( + MinitScript* minitScript, + int scriptIdx, + vector& parseErrors + ); + + /** + * Validate stacklets + * @param minitScript MinitScript instance + * @param function function + * @param parseErrors parse errors + * @param scopeScriptIdx scope script index or MinitScript::SCRIPTIDX_NONE for the function itself + * @return success + */ + static bool validateStacklets( + MinitScript* minitScript, + const string& function, + vector& parseErrors, + int scopeScriptIdx = MinitScript::SCRIPTIDX_NONE + ); + + /** + * Validate stacklets + * @param minitScript MinitScript instance + * @param scopeScriptIdx scope script index + * @param syntaxTreeNode syntax tree node + * @param statement statement + * @param parseErrors parse errors + * @return success + */ + static bool validateStacklets( + MinitScript* minitScript, + int scopeScriptIdx, + const MinitScript::SyntaxTreeNode& syntaxTreeNode, + const MinitScript::Statement& statement, + vector& parseErrors + ); + + /** + * Validate callabe + * @param minitScript MinitScript instance + * @param function function + * @param parseErrors parse errors + * @return success + */ + static bool validateCallable( + MinitScript* minitScript, + const string& function, + vector& parseErrors + ); + + /** + * Validate callable + * @param minitScript MinitScript instance + * @param syntaxTreeNode syntax tree node + * @param statement statement + * @param parseErrors parse errors + * @return success + */ + static bool validateCallable( + MinitScript* minitScript, + const MinitScript::SyntaxTreeNode& syntaxTreeNode, + const MinitScript::Statement& statement, + vector& parseErrors + ); + + /** + * Validate context functions + * @param minitScript MinitScript instance + * @param function function + * @param functionStack function stack + * @param parseErrors parse errors + * @return success + */ + static bool validateContextFunctions( + MinitScript* minitScript, + const string& function, + vector& functionStack, + vector& parseErrors + ); + + /** + * Validate context functions + * @param minitScript MinitScript instance + * @param syntaxTreeNode syntax tree node + * @param functionStack function stack + * @param statement statement + * @param parseErrors parse errors + * @return success + */ + static bool validateContextFunctions( + MinitScript* minitScript, + const MinitScript::SyntaxTreeNode& syntaxTreeNode, + vector& functionStack, + const MinitScript::Statement& statement, + vector& parseErrors + ); +}; diff --git a/src/minitscript/minitscript/fwd-minitscript.h b/src/minitscript/minitscript/fwd-minitscript.h index 7645021..6f9c403 100644 --- a/src/minitscript/minitscript/fwd-minitscript.h +++ b/src/minitscript/minitscript/fwd-minitscript.h @@ -27,7 +27,8 @@ namespace minitscript { class StringMethods; class TimeMethods; class Transpiler; - class XMLMethods; + class Validations; class Version; + class XMLMethods; } // namespace minitscript } // namespace minitscript