diff --git a/resources/tests/scripts/class-test.tscript b/resources/tests/scripts/class-test.tscript new file mode 100644 index 000000000..1c71061e2 --- /dev/null +++ b/resources/tests/scripts/class-test.tscript @@ -0,0 +1,53 @@ +# initialize +on: initialize + console.log("------------------------") + console.log("class-test: Initialize") + console.log("------------------------") + console.log() +end + +function: printABC(=$this) + console.log( + "printABC(): " + + "a: " + $this.a + ", " + + "b: " + $this.b + ", " + + "c: " + $this.c + ", " + + "a*b*c: " + ($this.a * $this.b * $this.c) + ) + $this.c = $this.c + 1 +end + +# if no condition is met, nothing will be executed, lol :D +on: nothing + console.log("---------------------") + console.log("class-test: Nothing") + console.log("---------------------") + console.log() + # + $class = { + # member variables + a: 1, + b: 2, + c: 3, + # member methods + printABC: () -> printABC + }; + console.dump($class) + # + $i = 0 + forCondition($i < 5) + $class->printABC() + ++$i + end + # + script.stop() +end + +# an error has occurred +on: error + console.log("------------------") + console.log("class-test: Error") + console.log("------------------") + console.log("An error occurred") + script.wait(1000) +end \ No newline at end of file diff --git a/src/tdme/utilities/MiniScript.cpp b/src/tdme/utilities/MiniScript.cpp index 48ca8862d..827903646 100644 --- a/src/tdme/utilities/MiniScript.cpp +++ b/src/tdme/utilities/MiniScript.cpp @@ -461,7 +461,6 @@ bool MiniScript::parseScriptStatement(const string_view& executableStatement, st evaluateMemberAccessArguments.push_back(string_view(&accessObjectMemberStatement.data()[idx], accessObjectMemberStatement.size() - idx)); } accessObjectMemberStatement+= ")"; - // set up new results methodName = evaluateMemberAccessMethodName; arguments = evaluateMemberAccessArguments; @@ -1207,6 +1206,7 @@ const string MiniScript::determineNextStatement(const string& scriptCode, int& i statementCodeLines.emplace_back(); auto quote = '\0'; auto expectBracket = false; + auto emptyBrackets = false; auto bracketCount = 0; auto squareBracketCount = 0; auto curlyBracketCount = 0; @@ -1214,6 +1214,7 @@ const string MiniScript::determineNextStatement(const string& scriptCode, int& i auto lc = '\0'; for (; i < scriptCode.size(); i++) { auto c = scriptCode[i]; + auto nc = i + 1 < scriptCode.size()?scriptCode[i + 1]:'\0'; // handle quotes if ((c == '"' || c == '\'') && lc != '\\') { if (quote == '\0') { @@ -1224,6 +1225,8 @@ const string MiniScript::determineNextStatement(const string& scriptCode, int& i } // add char to script line statementCodeLines[statementCodeLines.size() - 1] += c; + // + emptyBrackets = false; } else if (quote != '\0') { // no op @@ -1231,34 +1234,55 @@ const string MiniScript::determineNextStatement(const string& scriptCode, int& i } else // brackets if (c == '(') { + emptyBrackets = false; + // bracketCount++; expectBracket = false; // add char to script line statementCodeLines[statementCodeLines.size() - 1] += c; } else if (c == ')') { + // + emptyBrackets = false; + // bracketCount--; // add char to script line statementCodeLines[statementCodeLines.size() - 1] += c; + // + if (lc == '(') { + emptyBrackets = true; + } } else // square brackets if (c == '[') { + // + emptyBrackets = false; + // squareBracketCount++; // add char to script line statementCodeLines[statementCodeLines.size() - 1] += c; } else if (c == ']') { + // + emptyBrackets = false; + // squareBracketCount--; // add char to script line statementCodeLines[statementCodeLines.size() - 1] += c; } else // curly brackets if (c == '{') { + // + emptyBrackets = false; + // curlyBracketCount++; // add char to script line statementCodeLines[statementCodeLines.size() - 1] += c; } else if (c == '}') { + // + emptyBrackets = false; + // curlyBracketCount--; // add char to script line statementCodeLines[statementCodeLines.size() - 1] += c; @@ -1276,23 +1300,25 @@ const string MiniScript::determineNextStatement(const string& scriptCode, int& i } else if (lc == '-' && c == '>') { // we expect a bracket now - expectBracket = true; + if (emptyBrackets == false) expectBracket = true; // add char to script line statementCodeLines[statementCodeLines.size() - 1] += c; } else - if (((c == '\n' && ++line) || (hash == false && c == ';')) && (c == ';' || hash == true || (expectBracket == false && bracketCount == 0 && squareBracketCount == 0 && curlyBracketCount == 0))) { - // break condition - bracketCount = 0; - squareBracketCount = 0; - curlyBracketCount = 0; - hash = false; + if ((c == '\n' && ++line) || (hash == false && c == ';')) { // break here and process script line - break; - } else - if (c == '\n') { - // ignore \n but create a new script code line for this statement - statementCodeLines.emplace_back(); + if (expectBracket == false && bracketCount == 0 && squareBracketCount == 0 && curlyBracketCount == 0) break; + // unset hash after newline + if (c == '\n') { + // + hash = false; + // + statementCodeLines.emplace_back(); + } else { + // + emptyBrackets = false; + } } else { + if (Character::isSpace(c) == false && c != '-' && nc != '>') emptyBrackets = false; // add char to script line statementCodeLines[statementCodeLines.size() - 1] += c; } @@ -1301,7 +1327,11 @@ const string MiniScript::determineNextStatement(const string& scriptCode, int& i } // - for (const auto& line: statementCodeLines) statementCode+= StringTools::trim(line); + for (const auto& line: statementCodeLines) { + auto trimmedLine = StringTools::trim(line); + if (trimmedLine.empty() == true || StringTools::startsWith(trimmedLine, "#") == true) continue; + statementCode+= trimmedLine; + } // add last line index if (i == scriptCode.size() && scriptCode[scriptCode.size() - 1] != '\n') ++line; @@ -1387,7 +1417,7 @@ void MiniScript::parseScript(const string& pathName, const string& fileName) { // add last line index if (i == scriptCode.size() && scriptCode[scriptCode.size() - 1] != '\n') ++lineIdx; // - if (StringTools::startsWith(statementCode, "#") == true || statementCode.empty() == true) { + if (statementCode.empty() == true) { continue; } @@ -2754,14 +2784,28 @@ void MiniScript::registerMethods() { Console::println(getMethodName() + "(): " + miniScript->getStatementInformation(statement) + ": argument mismatch: expected arguments: " + miniScript->getArgumentInformation(getMethodName())); miniScript->startErrorScript(); } else { + // check if map, if so fetch function assignment of member property + auto functionIdx = MiniScript::SCRIPTIDX_NONE; + if (argumentValues[1].getType() == TYPE_MAP) { + string function; + auto mapValue = argumentValues[1].getMapValue(member); + if (mapValue.getType() == MiniScript::TYPE_FUNCTION_ASSIGNMENT && mapValue.getStringValue(function) == true) { + functionIdx = miniScript->getFunctionScriptIdx(function); + } + } + // const auto& className = ScriptVariable::getClassName(argumentValues[1].getType()); - if (className.empty() == false) { - #if defined(__MINISCRIPT_TRANSPILATION__) - auto method = evaluateMemberAccessArrays[static_cast(argumentValues[1].getType()) - static_cast(MiniScript::TYPE_STRING)][EVALUATEMEMBERACCESS_MEMBER]; - #else - auto method = miniScript->getMethod(className + "." + member); - #endif - if (method != nullptr) { + // + if (className.empty() == false || functionIdx != MiniScript::SCRIPTIDX_NONE) { + ScriptMethod* method { nullptr }; + if (functionIdx == MiniScript::SCRIPTIDX_NONE) { + #if defined(__MINISCRIPT_TRANSPILATION__) + method = evaluateMemberAccessArrays[static_cast(argumentValues[1].getType()) - static_cast(MiniScript::TYPE_STRING)][EVALUATEMEMBERACCESS_MEMBER]; + #else + method = miniScript->getMethod(className + "." + member); + #endif + } + if (method != nullptr || functionIdx != MiniScript::SCRIPTIDX_NONE) { // create method call arguments vector callArgumentValues(1 + (argumentValues.size() - 3) / 2); // this @@ -2775,16 +2819,18 @@ void MiniScript::registerMethods() { } } span callArgumentValuesSpan(callArgumentValues); - method->executeMethod(callArgumentValuesSpan, returnValue, statement); + if (method != nullptr) { + method->executeMethod(callArgumentValuesSpan, returnValue, statement); + } else + if (functionIdx != MiniScript::SCRIPTIDX_NONE) { + miniScript->call(functionIdx, callArgumentValuesSpan, returnValue); + } // assign back variables { auto argumentIdx = 0; - for (const auto& argumentType: method->getArgumentTypes()) { - if (argumentType.assignBack == false) { - argumentIdx++; - continue; - } - // + + // + auto assignBack = [&]() { if (argumentIdx == 0) { if (isVariableAccess(variable) == true) { miniScript->setVariable(variable, callArgumentValuesSpan[0], &statement); @@ -2805,8 +2851,33 @@ void MiniScript::registerMethods() { } } } - // - argumentIdx++; + }; + + // method + if (method != nullptr) { + for (const auto& argument: method->getArgumentTypes()) { + if (argument.assignBack == false) { + argumentIdx++; + continue; + } + // + assignBack(); + // + argumentIdx++; + } + } else + if (functionIdx != MiniScript::SCRIPTIDX_NONE) { + const auto& script = miniScript->getScripts()[functionIdx]; + for (const auto& argument: script.arguments) { + if (argument.assignBack == false) { + argumentIdx++; + continue; + } + // + assignBack(); + // + argumentIdx++; + } } } // write back arguments from call arguments @@ -8876,10 +8947,13 @@ const MiniScript::ScriptVariable MiniScript::initializeMapSet(const string_view& } else // possible function call if (c == '(') { + // bracketCount++; } else if (c == ')') { bracketCount--; + // function assignment + if (lc == '(' && bracketCount == 0 && mapValueStart == string::npos) mapValueStart = i - 1; } else // map/set initializer if (c == '{' && squareBracketCount == 0 && bracketCount == 0) { diff --git a/src/tdme/utilities/MiniScript.h b/src/tdme/utilities/MiniScript.h index 87b2371f0..1985889be 100644 --- a/src/tdme/utilities/MiniScript.h +++ b/src/tdme/utilities/MiniScript.h @@ -161,6 +161,7 @@ class tdme::utilities::MiniScript { TYPE_MAP, TYPE_SET, TYPE_FUNCTION_CALL, + TYPE_FUNCTION_ASSIGNMENT, TYPE_PSEUDO_NUMBER, TYPE_PSEUDO_MIXED, }; @@ -543,6 +544,11 @@ class tdme::utilities::MiniScript { } // break; + case TYPE_FUNCTION_ASSIGNMENT: + setFunctionAssignment(scriptVariable.getStringValueReference()); + break; + // pseudo ... + default: break; } } @@ -634,6 +640,11 @@ class tdme::utilities::MiniScript { } // break; + case TYPE_FUNCTION_ASSIGNMENT: + setFunctionAssignment(scriptVariable.getStringValueReference()); + break; + // pseudo ... + default: break; } return *this; } @@ -791,6 +802,7 @@ class tdme::utilities::MiniScript { case TYPE_FLOAT: break; case TYPE_STRING: + case TYPE_FUNCTION_ASSIGNMENT: delete static_cast((void*)valuePtr); break; case TYPE_VECTOR2: @@ -843,6 +855,7 @@ class tdme::utilities::MiniScript { case TYPE_FLOAT: break; case TYPE_STRING: + case TYPE_FUNCTION_ASSIGNMENT: valuePtr = (uint64_t)(new string()); break; case TYPE_VECTOR2: @@ -1011,6 +1024,7 @@ class tdme::utilities::MiniScript { value = to_string(getFloatValueReference()); return true; case TYPE_STRING: + case TYPE_FUNCTION_ASSIGNMENT: value = getStringValueReference(); return true; default: @@ -1529,6 +1543,15 @@ class tdme::utilities::MiniScript { */ void setFunctionCallStatement(const string& initializerStatement, MiniScript* miniScript, const ScriptStatement& statement); + /** + * Set function assignment from given value into variable + * @param value value + */ + inline void setFunctionAssignment(const string& value) { + setType(TYPE_FUNCTION_ASSIGNMENT); + getStringValueReference() = value; + } + /** * Set implicit typed value given by value string * @param value value @@ -1536,6 +1559,8 @@ class tdme::utilities::MiniScript { * @param statement statement */ inline void setImplicitTypedValue(const string& value, MiniScript* miniScript, const ScriptStatement& statement) { + string function; + // if (value == "null") { setNullValue(); } else @@ -1559,6 +1584,9 @@ class tdme::utilities::MiniScript { StringTools::endsWith(value, "]") == true) { *this = initializeArray(string_view(value), miniScript, statement); } else + if (isFunctionAssignment(value, function) == true) { + setFunctionAssignment(function); + } else // function call if (value.find('(') != string::npos && value.find(')') != string::npos) { @@ -1579,6 +1607,8 @@ class tdme::utilities::MiniScript { * @param statement statement */ inline void setImplicitTypedValueFromStringView(const string_view& value, MiniScript* miniScript, const ScriptStatement& statement) { + string function; + // if (value == "null") { setNullValue(); } else @@ -1602,6 +1632,9 @@ class tdme::utilities::MiniScript { StringTools::viewEndsWith(value, "]") == true) { *this = initializeArray(value, miniScript, statement); } else + if (viewIsFunctionAssignment(value, function) == true) { + setFunctionAssignment(function); + } else // function call if (value.find('(') != string::npos && value.find(')') != string::npos) { @@ -1974,6 +2007,9 @@ class tdme::utilities::MiniScript { break; } case TYPE_FUNCTION_CALL: + result+= "{" + getStringValueReference() + "}"; + break; + case TYPE_FUNCTION_ASSIGNMENT: result+= "() -> " + getStringValueReference(); break; case TYPE_PSEUDO_NUMBER: @@ -3498,17 +3534,99 @@ class tdme::utilities::MiniScript { */ void registerMethod(ScriptMethod* scriptMethod); + /** + * Returns if a given string is a function assignment + * @param candidate candidate + * @param function function + * @return if candidate is a function assignment + */ + inline static bool isFunctionAssignment(const string& candidate, string& function) { + if (candidate.size() == 0) return false; + // + auto i = 0; + // ( + if (candidate[i++] != '(') return false; + // + if (i >= candidate.size()) return false; + // ) + if (candidate[i++] != ')') return false; + // spaces + for (; i < candidate.size() && Character::isSpace(candidate[i]) == true; i++); if (i >= candidate.size()) return false; + // - + if (candidate[i++] != '-') return false; + // + if (i >= candidate.size()) return false; + // > + if (candidate[i++] != '>') return false; + // spaces + for (; i < candidate.size() && Character::isSpace(candidate[i]) == true; i++); if (i >= candidate.size()) return false; + // + string _function; + for (; i < candidate.size(); i++) { + auto c = candidate[i]; + if (Character::isAlphaNumeric(c) == false && c != '_') { + return false; + } + _function+= c; + } + // + function = _function; + // + return true; + } + + /** + * Returns if a given string is a function assignment + * @param candidate candidate + * @param function function + * @return if candidate is a function assignment + */ + inline static bool viewIsFunctionAssignment(const string_view& candidate, string& function) { + if (candidate.size() == 0) return false; + // + auto i = 0; + // ( + if (candidate[i++] != '(') return false; + // + if (i >= candidate.size()) return false; + // ) + if (candidate[i++] != ')') return false; + // spaces + for (; i < candidate.size() && Character::isSpace(candidate[i]) == true; i++); if (i >= candidate.size()) return false; + // - + if (candidate[i++] != '-') return false; + // + if (i >= candidate.size()) return false; + // > + if (candidate[i++] != '>') return false; + // spaces + for (; i < candidate.size() && Character::isSpace(candidate[i]) == true; i++); if (i >= candidate.size()) return false; + // + string _function; + for (; i < candidate.size(); i++) { + auto c = candidate[i]; + if (Character::isAlphaNumeric(c) == false && c != '_') { + return false; + } + _function+= c; + } + // + function = _function; + // + return true; + } + /** * Returns if a given string is a variable name - * @param string string + * @param candidate candidate * @return if string is a variable name */ - inline static bool isVariableAccess(const string& string) { - if (string.size() == 0) return false; - if (string[0] != '$') return false; + inline static bool isVariableAccess(const string& candidate) { + if (candidate.size() == 0) return false; + if (candidate[0] != '$') return false; auto squareBracketCount = 0; - for (auto i = 1; i < string.size(); i++) { - auto c = string[i]; + for (auto i = 1; i < candidate.size(); i++) { + auto c = candidate[i]; if (c == '[') { squareBracketCount++; } else @@ -3524,15 +3642,15 @@ class tdme::utilities::MiniScript { /** * Returns if a given string is a variable name - * @param string string + * @param candidate candidate * @return if string is a variable name */ - inline static bool viewIsVariableAccess(const string_view& string) { - if (string.size() == 0) return false; - if (string[0] != '$') return false; + inline static bool viewIsVariableAccess(const string_view& candidate) { + if (candidate.size() == 0) return false; + if (candidate[0] != '$') return false; auto squareBracketCount = 0; - for (auto i = 1; i < string.size(); i++) { - auto c = string[i]; + for (auto i = 1; i < candidate.size(); i++) { + auto c = candidate[i]; if (c == '[') { squareBracketCount++; } else @@ -3548,14 +3666,14 @@ class tdme::utilities::MiniScript { /** * Returns if a given string is a valid map key name - * @param string string + * @param candidate candidate * @return if string is a valid map key name */ - inline static bool viewIsKey(const string_view& string) { - if (string.size() == 0) return false; - if (string[0] == '$') return false; - for (auto i = 0; i < string.size(); i++) { - auto c = string[i]; + inline static bool viewIsKey(const string_view& candidate) { + if (candidate.size() == 0) return false; + if (candidate[0] == '$') return false; + for (auto i = 0; i < candidate.size(); i++) { + auto c = candidate[i]; if (Character::isAlphaNumeric(c) == false && c != '_') return false; } return true; @@ -3750,7 +3868,6 @@ class tdme::utilities::MiniScript { // lookup function auto scriptFunctionsIt = scriptFunctions.find(function); if (scriptFunctionsIt == scriptFunctions.end()) { - Console::println("MiniScript::getFunctionScriptIdx(): Script user function not found: " + function); return SCRIPTIDX_NONE; } //