TDME2 1.9.121
MiniScript.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4#include <map>
5#include <stack>
6#include <string>
7#include <string_view>
8#include <unordered_map>
9#include <unordered_set>
10#include <vector>
11
12#include <tdme/tdme.h>
20#include <tdme/math/Math.h>
21#include <tdme/math/Vector3.h>
30#include <tdme/utilities/Time.h>
31
32using std::find;
33using std::map;
34using std::remove;
35using std::sort;
36using std::stack;
37using std::string;
38using std::string_view;
39using std::to_string;
40using std::unordered_map;
41using std::unordered_set;
42using std::vector;
43
62
63string MiniScript::OPERATOR_CHARS = "!*/%+-<>&|=";
64
65MiniScript::MiniScript() {
66 setNative(false);
67}
68
70 for (auto& scriptMethodIt: scriptMethods) delete scriptMethodIt.second;
71 for (auto& scriptStateMachineStateIt: scriptStateMachineStates) delete scriptStateMachineStateIt.second;
72 for (auto& scriptVariableIt: scriptState.variables) delete scriptVariableIt.second;
73}
74
76 auto scriptStateMachineStateIt = scriptStateMachineStates.find(state->getId());
77 if (scriptStateMachineStateIt != scriptStateMachineStates.end()) {
78 Console::println("MiniScript::registerStateMachineState(): '" + scriptFileName + "': state with id + " + to_string(state->getId()) + ", name '" + state->getName() + "' already registered.");
79 return;
80 }
81 scriptStateMachineStates[state->getId()] = state;
82}
83
85}
86
88 auto scriptMethodsIt = scriptMethods.find(method->getMethodName());
89 if (scriptMethodsIt != scriptMethods.end()) {
90 Console::println("MiniScript::registerMethod(): '" + scriptFileName + "': method with name '" + method->getMethodName() + "' already registered.");
91 return;
92 }
93 scriptMethods[method->getMethodName()] = method;
94}
95
97 if (scriptState.scriptIdx == -1 || scriptState.statementIdx == -1 || scriptState.running == false) return;
98 auto& script = scripts[scriptState.scriptIdx];
99 if (script.statements.empty() == true) return;
100 auto& statement = script.statements[scriptState.statementIdx];
101 if (VERBOSE == true) Console::println("MiniScript::executeScriptLine(): '" + scriptFileName + "': @" + to_string(statement.line) + ": '" + statement.statement + "'");
102
104 if (scriptState.statementIdx >= script.statements.size()) {
108 }
109
110 string_view method;
111 vector<string_view> arguments;
112 if (parseScriptStatement(statement.statement, method, arguments) == true) {
113 auto returnValue = executeScriptStatement(method, arguments, statement);
114 } else {
115 Console::println("MiniScript::executeScriptLine(): '" + scriptFileName + "': @" + to_string(statement.line) + ": '" + statement.statement + "': parse error");
117 }
118}
119
120bool MiniScript::parseScriptStatement(const string_view& statement, string_view& method, vector<string_view>& arguments) {
121 if (VERBOSE == true) Console::println("MiniScript::parseScriptStatement(): '" + scriptFileName + "': '" + string(statement) + "'");
122 auto bracketCount = 0;
123 auto quote = false;
124 auto methodStart = string::npos;
125 auto methodEnd = string::npos;
126 auto argumentStart = string::npos;
127 auto argumentEnd = string::npos;
128 auto quotedArgumentStart = string::npos;
129 auto quotedArgumentEnd = string::npos;
130 for (auto i = 0; i < statement.size(); i++) {
131 auto c = statement[i];
132 if (c == '"') {
133 if (bracketCount == 1) {
134 if (quote == false) {
135 quotedArgumentStart = i;
136 } else {
137 quotedArgumentEnd = i;
138 }
139 } else {
140 if (quote == false) {
141 if (argumentStart == string::npos) {
142 argumentStart = i;
143 }
144 } else {
145 argumentEnd = i;
146 }
147 }
148 quote = quote == false?true:false;
149 } else
150 if (quote == true) {
151 if (bracketCount == 1) {
152 quotedArgumentEnd = i;
153 } else {
154 if (argumentStart == string::npos) {
155 argumentStart = i;
156 } else {
157 argumentEnd = i;
158 }
159 }
160 } else
161 if (quote == false) {
162 if (c == '(') {
163 bracketCount++;
164 if (bracketCount > 1) {
165 if (argumentStart == string::npos) {
166 argumentStart = i + 1;
167 } else {
168 argumentEnd = i;
169 }
170 }
171 } else
172 if (c == ')') {
173 bracketCount--;
174 if (bracketCount == 0) {
175 if (quotedArgumentStart != string::npos) {
176 if (quotedArgumentEnd == string::npos) quotedArgumentEnd = i - 1;
177 auto argumentLength = quotedArgumentEnd - quotedArgumentStart + 1;
178 if (argumentLength > 0) arguments.push_back(StringTools::viewTrim(string_view(&statement[quotedArgumentStart], argumentLength)));
179 quotedArgumentStart = string::npos;
180 quotedArgumentEnd = string::npos;
181 } else
182 if (argumentStart != string::npos) {
183 if (argumentEnd == string::npos) argumentEnd = i - 1;
184 auto argumentLength = argumentEnd - argumentStart + 1;
185 if (argumentLength > 0) arguments.push_back(StringTools::viewTrim(string_view(&statement[argumentStart], argumentLength)));
186 argumentStart = string::npos;
187 argumentEnd = string::npos;
188 }
189 } else {
190 if (argumentStart == string::npos) {
191 argumentStart = i + 1;
192 } else {
193 argumentEnd = i;
194 }
195 }
196 } else
197 if (c == ',') {
198 if (bracketCount == 1) {
199 if (quotedArgumentStart != string::npos) {
200 if (quotedArgumentEnd == string::npos) quotedArgumentEnd = i - 1;
201 auto argumentLength = quotedArgumentEnd - quotedArgumentStart + 1;
202 if (argumentLength > 0) arguments.push_back(StringTools::viewTrim(string_view(&statement[quotedArgumentStart], argumentLength)));
203 quotedArgumentStart = string::npos;
204 quotedArgumentEnd = string::npos;
205 } else
206 if (argumentStart != string::npos) {
207 if (argumentEnd == string::npos) argumentEnd = i - 1;
208 auto argumentLength = argumentEnd - argumentStart + 1;
209 if (argumentLength > 0) arguments.push_back(StringTools::viewTrim(string_view(&statement[argumentStart], argumentEnd - argumentStart + 1)));
210 argumentStart = string::npos;
211 argumentEnd = string::npos;
212 }
213 } else {
214 if (argumentStart == string::npos) {
215 argumentStart = i + 1;
216 } else {
217 argumentEnd = i;
218 }
219 }
220 } else
221 if (bracketCount == 0) {
222 if (methodStart == string::npos) methodStart = i; else methodEnd = i;
223 } else {
224 if (argumentStart == string::npos) {
225 if (Character::isSpace(c) == false) {
226 argumentStart = i;
227 }
228 } else {
229 argumentEnd = i;
230 }
231 }
232 }
233 }
234 if (methodStart != string::npos && methodEnd != string::npos) {
235 method = StringTools::viewTrim(string_view(&statement[methodStart], methodEnd - methodStart + 1));
236 }
237 if (VERBOSE == true) {
238 Console::print("MiniScript::parseScriptStatement(): '" + scriptFileName + "': method: '" + string(method) + "', arguments: ");
239 int variableIdx = 0;
240 for (auto& argument: arguments) {
241 if (variableIdx > 0) Console::print(", ");
242 Console::print("'" + string(argument) + "'");
243 variableIdx++;
244 }
246 }
247 if (bracketCount > 0) {
248 Console::println("MiniScript::parseScriptStatement(): '" + scriptFileName + "': '" + string(statement) + "': unbalanced bracket count: " + to_string(bracketCount) + " still open");
249 return false;
250 }
251 return true;
252}
253
254MiniScript::ScriptVariable MiniScript::executeScriptStatement(const string_view& method, const vector<string_view>& arguments, const ScriptStatement& statement) {
255 if (VERBOSE == true) Console::println("MiniScript::executeScriptStatement(): '" + scriptFileName + "': @" + to_string(statement.line) + ": '" + statement.statement + "': string arguments: " + string(method) + "(" + getArgumentsAsString(arguments) + ")");
256 vector<ScriptVariable> argumentValues;
257 ScriptVariable returnValue;
258 // check if argument is a method calls return value
259 for (auto& argument: arguments) {
260 if (argument.empty() == false &&
261 StringTools::viewStartsWith(argument, "\"") == false &&
262 StringTools::viewEndsWith(argument, "\"") == false &&
263 argument.find('(') != string::npos &&
264 argument.find(')') != string::npos) {
265 // method call, call method and put its return value into argument value
266 string_view subMethod;
267 vector<string_view> subArguments;
268 if (parseScriptStatement(argument, subMethod, subArguments) == true) {
269 auto argumentValue = executeScriptStatement(subMethod, subArguments, statement);
270 argumentValues.push_back(argumentValue);
271 } else {
272 Console::println("MiniScript::executeScriptStatement(): parseScriptStatement(): '" + scriptFileName + "': @" + to_string(statement.line) + ": '" + statement.statement + "': '" + string(argument) + "': parse error");
274 }
275 } else
276 // variable
277 if (StringTools::viewStartsWith(argument, "$") == true) {
278 auto variableIt = scriptState.variables.find(string(argument));
279 if (variableIt == scriptState.variables.end()) {
280 Console::println("MiniScript::executeScriptStatement(): '" + scriptFileName + "': @" + to_string(statement.line) + ": '" + statement.statement + "': " + string(method) + "(" + string(argument) + "): variable: '" + string(argument) + "' does not exist");
281 argumentValues.push_back(ScriptVariable());
282 } else {
283 auto argumentValue = *variableIt->second;
284 argumentValues.push_back(argumentValue);
285 }
286 } else {
287 // literal
288 ScriptVariable argumentValue;
289 if (StringTools::viewStartsWith(argument, "\"") == true &&
290 StringTools::viewEndsWith(argument, "\"") == true) {
291 argumentValue.setValue(string(StringTools::viewSubstring(argument, 1, argument.size() - 1)));
292 } else {
293 argumentValue.setImplicitTypedValueFromStringView(argument);
294 }
295 argumentValues.push_back(argumentValue);
296 }
297 }
298 if (VERBOSE == true) {
299 string argumentValuesString;
300 for (auto& argumentValue: argumentValues) argumentValuesString+= (argumentValuesString.empty() == false?",":"") + argumentValue.getAsString();
301 Console::println("MiniScript::executeScriptStatement(): '" + scriptFileName + "': @" + to_string(statement.line) + ": '" + statement.statement + "': " + string(method) + "(" + argumentValuesString + ")");
302 }
303 auto scriptMethodsIt = scriptMethods.find(string(method));
304 if (scriptMethodsIt != scriptMethods.end()) {
305 auto scriptMethod = scriptMethodsIt->second;
306 auto argumentIdx = 0;
307 if (scriptMethod->isVariadic() == false) {
308 for (auto& argumentType: scriptMethod->getArgumentTypes()) {
309 auto argumentOk = true;
310 switch(argumentType.type) {
311 case TYPE_VOID:
312 break;
313 case TYPE_BOOLEAN:
314 {
315 bool booleanValue;
316 argumentOk = getBooleanValue(argumentValues, argumentIdx, booleanValue, argumentType.optional);
317 }
318 break;
319 case TYPE_INTEGER:
320 {
321 int64_t integerValue;
322 argumentOk = getIntegerValue(argumentValues, argumentIdx, integerValue, argumentType.optional);
323 }
324 break;
325 case TYPE_FLOAT:
326 {
327 float floatValue;
328 argumentOk = getFloatValue(argumentValues, argumentIdx, floatValue, argumentType.optional);
329 }
330 break;
331 case TYPE_STRING:
332 {
333 string stringValue;
334 argumentOk = getStringValue(argumentValues, argumentIdx, stringValue, argumentType.optional);
335 }
336 break;
337 case TYPE_VECTOR3:
338 {
339 Vector3 vector3Value;
340 argumentOk = getVector3Value(argumentValues, argumentIdx, vector3Value, argumentType.optional);
341 break;
342 }
344 {
345 Transformations transformationsValue;
346 argumentOk = getTransformationsValue(argumentValues, argumentIdx, transformationsValue, argumentType.optional);
347 break;
348 }
349 }
350 if (argumentOk == false) {
352 string("MiniScript::executeScriptStatement(): ") +
353 "'" + scriptFileName + "': " +
354 "@" + to_string(statement.line) +
355 ": '" + statement.statement + "'" +
356 ": method '" + string(method) + "'" +
357 ": argument value @ " + to_string(argumentIdx) + ": expected " + ScriptVariable::getTypeAsString(argumentType.type) + ", but got: " + (argumentIdx < argumentValues.size()?argumentValues[argumentIdx].getAsString():"nothing"));
358 }
359 argumentIdx++;
360 }
361 if (argumentValues.size() > scriptMethod->getArgumentTypes().size()) {
363 string("MiniScript::executeScriptStatement(): ") +
364 "'" + scriptFileName + "': " +
365 "@" + to_string(statement.line) +
366 ": '" + statement.statement + "'" +
367 ": method '" + string(method) + "'" +
368 ": too many arguments: expected: " + to_string(scriptMethod->getArgumentTypes().size()) + ", got " + to_string(argumentValues.size()));
369 }
370 }
371 scriptMethod->executeMethod(argumentValues, returnValue, statement);
372 if (scriptMethod->isMixedReturnValue() == false && returnValue.getType() != scriptMethod->getReturnValueType()) {
374 string("MiniScript::executeScriptStatement(): ") +
375 "'" + scriptFileName + "': " +
376 "@" + to_string(statement.line) +
377 ": '" + statement.statement + "'" +
378 ": method '" + string(method) + "'" +
379 ": return value: expected " + ScriptVariable::getTypeAsString(scriptMethod->getReturnValueType()) + ", but got: " + ScriptVariable::getTypeAsString(returnValue.getType()));
380 }
381 return returnValue;
382 } else {
383 Console::println("MiniScript::executeScriptStatement(): '" + scriptFileName + "': unknown method @" + to_string(statement.line) + ": '" + statement.statement + "': " + string(method) + "(" + getArgumentsAsString(arguments) + ")");
385 }
386 return returnValue;
387}
388
389void MiniScript::emit(const string& condition) {
390 if (VERBOSE == true) Console::println("MiniScript::emit(): '" + scriptFileName + "': " + condition);
391 auto scriptIdxToStart = 0;
392 for (auto& script: scripts) {
393 auto conditionMet = true;
394 if (script.name.empty() == false && script.name == condition) {
395 break;
396 } else
397 if (script.condition == condition) {
398 break;
399 } else {
400 scriptIdxToStart++;
401 }
402 }
403 if (scriptIdxToStart == scripts.size()) {
404 scriptIdxToStart = -1;
406 return;
407 }
408 if (scriptState.scriptIdx == scriptIdxToStart) {
409 Console::println("MiniScript::emit(): '" + scriptFileName + "': script already running: " + condition);
410 return;
411 }
412 //
414}
415
417 while (true == true) {
418 // determine state machine state if it did change
422 auto scriptStateMachineStateIt = scriptStateMachineStates.find(scriptState.state.state);
423 if (scriptStateMachineStateIt != scriptStateMachineStates.end()) {
424 scriptState.state.lastStateMachineState = scriptStateMachineStateIt->second;
425 }
426 }
427
428 // execute state machine
429 if (scriptState.state.lastStateMachineState != nullptr) {
431 // ignore STATE_NEXT_STATEMENT on native
432 } else {
434 }
435 } else {
436 Console::println("MiniScript::execute(): '" + scriptFileName + "': unknown state with id: " + to_string(scriptState.state.state));
437 break;
438 }
439
440 //
441 if (native == true) {
442 // check named conditions
443 auto now = Time::getCurrentMillis();
444 if (scriptState.enabledNamedConditions.empty() == false &&
446 auto scriptIdxToStart = determineNamedScriptIdxToStart();
447 if (scriptIdxToStart != -1 && scriptIdxToStart != scriptState.scriptIdx) {
448 //
450 }
452 }
453 // stop here
454 break;
455 } else {
456 // break if no next statement but other state machine state or not running
458 }
459 }
460}
461
463 if (scriptState.state.state == STATE_NONE) return;
464
465 // check named conditions
466 auto now = Time::getCurrentMillis();
467 if (scriptState.enabledNamedConditions.empty() == false &&
469 auto scriptIdxToStart = determineNamedScriptIdxToStart();
470 if (scriptIdxToStart != -1 && scriptIdxToStart != scriptState.scriptIdx) {
471 //
473 }
475 }
476
477 // execute while having statements to be processed
479}
480
481void MiniScript::loadScript(const string& pathName, const string& fileName) {
482 //
483 scriptValid = true;
484 scriptPathName = pathName;
485 scriptFileName = fileName;
486
487 // shutdown methods and machine states
488 for (auto& scriptMethodIt: scriptMethods) delete scriptMethodIt.second;
489 for (auto& scriptStateMachineStateIt: scriptStateMachineStates) delete scriptStateMachineStateIt.second;
490 for (auto& scriptVariableIt: scriptState.variables) delete scriptVariableIt.second;
491 scriptMethods.clear();
493 scriptState.variables.clear();
494
495 // shutdown script state
497
498 //
500
501 //
502 vector<string> scriptLines;
503 try {
504 FileSystem::getInstance()->getContentAsStringArray(pathName, fileName, scriptLines);
505 } catch (FileSystemException& fse) {
506 Console::println("MiniScript::loadScript(): " + pathName + "/" + fileName + ": an error occurred: " + fse.what());
507 }
508
509 //
510 {
511 string scriptAsString;
512 for (auto& scriptLine: scriptLines) scriptAsString+= scriptLine + "\n";
513 auto scriptHash = SHA256::encode(scriptAsString);
514 if (native == true) {
515 if (scriptHash == hash) {
518 startScript();
519 return;
520 } else {
521 Console::println("MiniScript::loadScript(): " + pathName + "/" + fileName + ": Script has changed. Script will be run in interpreted mode. Retranspile and recompile your script if you want to run it natively.");
522 native = false;
523 }
524 }
525 hash = scriptHash;
526 }
527
528 //
531
532 //
533 auto haveCondition = false;
534 auto line = 1;
535 auto statementIdx = 1;
536 enum GotoStatementType { GOTOSTATEMENTTYPE_FOR, GOTOSTATEMENTTYPE_IF, GOTOSTATEMENTTYPE_ELSE, GOTOSTATEMENTTYPE_ELSEIF };
537 struct GotoStatementStruct {
538 GotoStatementType type;
539 int statementIdx;
540 };
541 stack<GotoStatementStruct> gotoStatementStack;
542 for (auto scriptLine: scriptLines) {
543 scriptLine = StringTools::trim(scriptLine);
544 if (StringTools::startsWith(scriptLine, "#") == true || scriptLine.empty() == true) {
545 line++;
546 continue;
547 }
548 if (haveCondition == false) {
549 if (StringTools::startsWith(scriptLine, "on:") == true || StringTools::startsWith(scriptLine, "on-enabled:") == true) {
550 haveCondition = true;
552 scriptLine =
553 conditionType == Script::CONDITIONTYPE_ONENABLED?
554 StringTools::trim(StringTools::substring(scriptLine, string("on-enabled:").size())):
555 StringTools::trim(StringTools::substring(scriptLine, string("on:").size()));
556 string name;
557 auto scriptLineNameSeparatorIdx = scriptLine.rfind(":=");
558 if (scriptLineNameSeparatorIdx != string::npos) {
559 name = StringTools::trim(StringTools::substring(scriptLine, scriptLineNameSeparatorIdx + 2));
560 scriptLine = StringTools::trim(StringTools::substring(scriptLine, 0, scriptLineNameSeparatorIdx));
561 }
562 auto condition = doStatementPreProcessing(StringTools::trim(scriptLine));
563 auto emitCondition = StringTools::regexMatch(condition, "[a-zA-Z0-9]+");
564 statementIdx = 0;
565 scripts.push_back(
566 {
567 .conditionType = conditionType,
568 .line = line,
569 .condition = condition,
570 .name = name,
571 .emitCondition = emitCondition,
572 .statements = {},
573 }
574 );
575 } else {
576 Console::println("MiniScript::MiniScript(): '" + scriptFileName + "': @" + to_string(line) + ": expecting 'on:' or 'on-enabled:' script condition");
577 scriptValid = false;
578 }
579 } else {
580 if (StringTools::startsWith(scriptLine, "on:") == true) {
581 Console::println("MiniScript::loadScript(): '" + scriptFileName + ": unbalanced forXXX/if/elseif/else/end");
582 scriptValid = false;
583 } else
584 if (scriptLine == "end") {
585 if (gotoStatementStack.empty() == false) {
586 auto gotoStatementStackElement = gotoStatementStack.top();
587 gotoStatementStack.pop();
588 switch(gotoStatementStackElement.type) {
589 case GOTOSTATEMENTTYPE_FOR:
590 {
591 scripts[scripts.size() - 1].statements.push_back({ .line = line, .statementIdx = statementIdx, .statement = scriptLine, .gotoStatementIdx = gotoStatementStackElement.statementIdx });
592 scripts[scripts.size() - 1].statements[gotoStatementStackElement.statementIdx].gotoStatementIdx = scripts[scripts.size() - 1].statements.size();
593 }
594 break;
595 case GOTOSTATEMENTTYPE_IF:
596 {
597 scripts[scripts.size() - 1].statements[gotoStatementStackElement.statementIdx].gotoStatementIdx = scripts[scripts.size() - 1].statements.size();
598 scripts[scripts.size() - 1].statements.push_back({ .line = line, .statementIdx = statementIdx, .statement = scriptLine, .gotoStatementIdx = -1 });
599 }
600 break;
601 case GOTOSTATEMENTTYPE_ELSE:
602 {
603 scripts[scripts.size() - 1].statements[gotoStatementStackElement.statementIdx].gotoStatementIdx = scripts[scripts.size() - 1].statements.size();
604 scripts[scripts.size() - 1].statements.push_back({ .line = line, .statementIdx = statementIdx, .statement = scriptLine, .gotoStatementIdx = -1 });
605 }
606 break;
607 case GOTOSTATEMENTTYPE_ELSEIF:
608 {
609 scripts[scripts.size() - 1].statements[gotoStatementStackElement.statementIdx].gotoStatementIdx = scripts[scripts.size() - 1].statements.size();
610 scripts[scripts.size() - 1].statements.push_back({ .line = line, .statementIdx = statementIdx, .statement = scriptLine, .gotoStatementIdx = -1 });
611 }
612 break;
613 }
614 } else{
615 scripts[scripts.size() - 1].statements.push_back({ .line = line, .statementIdx = statementIdx, .statement = "end", .gotoStatementIdx = -1 });
616 haveCondition = false;
617 }
618 } else
619 if (scriptLine == "else") {
620 if (gotoStatementStack.empty() == false) {
621 auto gotoStatementStackElement = gotoStatementStack.top();
622 gotoStatementStack.pop();
623 switch(gotoStatementStackElement.type) {
624 case GOTOSTATEMENTTYPE_IF:
625 {
626 scripts[scripts.size() - 1].statements[gotoStatementStackElement.statementIdx].gotoStatementIdx = scripts[scripts.size() - 1].statements.size();
627 scripts[scripts.size() - 1].statements.push_back({ .line = line, .statementIdx = statementIdx, .statement = scriptLine, .gotoStatementIdx = -1 });
628 }
629 break;
630 case GOTOSTATEMENTTYPE_ELSEIF:
631 {
632 scripts[scripts.size() - 1].statements[gotoStatementStackElement.statementIdx].gotoStatementIdx = scripts[scripts.size() - 1].statements.size();
633 scripts[scripts.size() - 1].statements.push_back({ .line = line, .statementIdx = statementIdx, .statement = scriptLine, .gotoStatementIdx = -1 });
634 }
635 break;
636 default:
637 Console::println("MiniScript::MiniScript(): '" + scriptFileName + ": @" + to_string(line) + ": else without if/elseif");
638 scriptValid = false;
639 break;
640 }
641 gotoStatementStack.push(
642 {
643 .type = GOTOSTATEMENTTYPE_ELSE,
644 .statementIdx = statementIdx
645 }
646 );
647 } else {
648 Console::println("MiniScript::MiniScript(): '" + scriptFileName + ": @" + to_string(line) + ": else without if");
649 scriptValid = false;
650 }
651 } else
652 if (StringTools::regexMatch(scriptLine, "^elseif[\\s]*\\(.*\\)$") == true) {
653 scriptLine = doStatementPreProcessing(scriptLine);
654 if (gotoStatementStack.empty() == false) {
655 auto gotoStatementStackElement = gotoStatementStack.top();
656 gotoStatementStack.pop();
657 switch(gotoStatementStackElement.type) {
658 case GOTOSTATEMENTTYPE_IF:
659 {
660 scripts[scripts.size() - 1].statements[gotoStatementStackElement.statementIdx].gotoStatementIdx = scripts[scripts.size() - 1].statements.size();
661 scripts[scripts.size() - 1].statements.push_back({ .line = line, .statementIdx = statementIdx, .statement = scriptLine, .gotoStatementIdx = -1 });
662 }
663 break;
664 case GOTOSTATEMENTTYPE_ELSEIF:
665 {
666 scripts[scripts.size() - 1].statements[gotoStatementStackElement.statementIdx].gotoStatementIdx = scripts[scripts.size() - 1].statements.size();
667 scripts[scripts.size() - 1].statements.push_back({ .line = line, .statementIdx = statementIdx, .statement = scriptLine, .gotoStatementIdx = -1 });
668 }
669 break;
670 default:
671 Console::println("MiniScript::MiniScript(): '" + scriptFileName + ": @" + to_string(line) + ": elseif without if");
672 scriptValid = false;
673 break;
674 }
675 gotoStatementStack.push(
676 {
677 .type = GOTOSTATEMENTTYPE_ELSEIF,
678 .statementIdx = statementIdx
679 }
680 );
681 } else {
682 Console::println("MiniScript::MiniScript(): '" + scriptFileName + ": @" + to_string(line) + ": elseif without if");
683 }
684 } else {
685 scriptLine = doStatementPreProcessing(scriptLine);
686 if (StringTools::regexMatch(scriptLine, "^forTime[\\s]*\\(.*\\)$") == true ||
687 StringTools::regexMatch(scriptLine, "^forCondition[\\s]*\\(.*\\)$") == true) {
688 gotoStatementStack.push(
689 {
690 .type = GOTOSTATEMENTTYPE_FOR,
691 .statementIdx = statementIdx
692 }
693 );
694 } else
695 if (StringTools::regexMatch(scriptLine, "^if[\\s]*\\(.*\\)$") == true) {
696 gotoStatementStack.push(
697 {
698 .type = GOTOSTATEMENTTYPE_IF,
699 .statementIdx = statementIdx
700 }
701 );
702 }
703 scripts[scripts.size() - 1].statements.push_back({ .line = line, .statementIdx = statementIdx, .statement = scriptLine, .gotoStatementIdx = -1 });
704 }
705 statementIdx++;
706 }
707 line++;
708 }
709
710 // check for unbalanced forXXX/if/elseif/else/end
711 if (scriptValid == true && gotoStatementStack.empty() == false) {
712 scriptValid = false;
713 // TODO: give some more info about line and statement of not closed condition
714 Console::println("MiniScript::loadScript(): '" + scriptFileName + ": unbalanced forXXX/if/elseif/else/end");
715 return;
716 }
717
718 // check for initialize and error condition
719 auto haveInitializeScript = false;
720 auto haveErrorScript = false;
721 for (auto& script: scripts) {
722 if (script.conditionType == Script::CONDITIONTYPE_ONENABLED) {
723 // no op
724 } else
725 if (script.condition == "initialize") {
726 haveInitializeScript = true;
727 } else
728 if (script.condition == "error") {
729 haveErrorScript = true;
730 }
731 }
732 if (haveInitializeScript == false || haveErrorScript == false) {
733 scriptValid = false;
734 Console::println("MiniScript::loadScript(): '" + scriptFileName + ": script needs to define a initialize and error condition");
735 return;
736
737 }
738
739 //
740 startScript();
741}
742
744 if (VERBOSE == true) Console::println("MiniScript::startScript(): '" + scriptFileName + ": starting script.");
745 if (scriptValid == false) {
746 Console::println("MiniScript::startScript(): '" + scriptFileName + "': script not valid: not starting");
747 return;
748 }
749 for (auto& scriptVariableIt: scriptState.variables) delete scriptVariableIt.second;
750 scriptState.variables.clear();
751 scriptState.running = true;
753 emit("initialize");
754}
755
757 if (VERBOSE == true) Console::println("MiniScript::determineScriptIdxToStart()");
758 auto currentScriptStateScript = scriptState.state;
759 auto nothingScriptIdx = -1;
760 auto scriptIdx = 0;
761 for (auto& script: scripts) {
762 if (script.conditionType == Script::CONDITIONTYPE_ONENABLED) {
763 // no op
764 } else
765 if (script.emitCondition == true && script.condition == "nothing") {
766 nothingScriptIdx = scriptIdx;
767 // no op
768 } else
769 if (script.emitCondition == true) {
770 // emit condition
771 } else {
772 auto conditionMet = true;
773 auto& condition = script.condition;
774 string_view method;
775 vector<string_view> arguments;
776 if (parseScriptStatement(condition, method, arguments) == true) {
777 auto returnValue = executeScriptStatement(
778 method,
779 arguments,
780 {
781 .line = script.line,
782 .statementIdx = 0,
783 .statement = condition,
784 .gotoStatementIdx = -1
785 }
786 );
787 auto returnValueBoolValue = false;
788 if (returnValue.getBooleanValue(returnValueBoolValue, false) == false) {
789 Console::println("MiniScript::determineScriptIdxToStart(): '" + condition + "': expecting boolean return value, but got: " + returnValue.getAsString());
790 conditionMet = false;
791 } else
792 if (returnValueBoolValue == false) {
793 conditionMet = false;
794 }
795 } else {
796 Console::println("MiniScript::determineScriptIdxToStart(): '" + scriptFileName + "': @" + to_string(script.line) + ": '" + condition + "': parse error");
797 }
798 if (conditionMet == false) {
799 if (VERBOSE == true) {
800 Console::print("MiniScript::determineScriptIdxToStart(): " + condition + ": FAILED");
801 }
802 } else {
803 if (VERBOSE == true) {
804 Console::print("MiniScript::determineScriptIdxToStart(): " + condition + ": OK");
805 }
806 scriptState.state = currentScriptStateScript;
807 return scriptIdx;
808 }
809 }
810 scriptIdx++;
811 }
812 scriptState.state = currentScriptStateScript;
813 return nothingScriptIdx;
814}
815
817 if (VERBOSE == true) Console::println("MiniScript::determineNamedScriptIdxToStart()");
818 auto currentScriptStateState = scriptState.state;
819 // TODO: we could have a hash map here to speed up enabledConditionName -> script lookup
820 for (auto& enabledConditionName: scriptState.enabledNamedConditions) {
821 auto scriptIdx = 0;
822 for (auto& script: scripts) {
823 if (script.conditionType != Script::CONDITIONTYPE_ONENABLED ||
824 script.name != enabledConditionName) {
825 // no op
826 } else {
827 auto conditionMet = true;
828 auto& condition = script.condition;
829 string_view method;
830 vector<string_view> arguments;
831 if (parseScriptStatement(condition, method, arguments) == true) {
832 auto returnValue = executeScriptStatement(
833 method,
834 arguments,
835 {
836 .line = script.line,
837 .statementIdx = 0,
838 .statement = condition,
839 .gotoStatementIdx = -1
840 }
841 );
842 auto returnValueBoolValue = false;
843 if (returnValue.getBooleanValue(returnValueBoolValue, false) == false) {
844 Console::println("MiniScript::determineNamedScriptIdxToStart(): '" + condition + "': expecting boolean return value, but got: " + returnValue.getAsString());
845 conditionMet = false;
846 } else
847 if (returnValueBoolValue == false) {
848 conditionMet = false;
849 }
850 } else {
851 Console::println("MiniScript::determineNamedScriptIdxToStart(): '" + scriptFileName + "': @" + to_string(script.line) + ": '" + condition + "': parse error");
852 }
853 if (conditionMet == false) {
854 if (VERBOSE == true) {
855 Console::print("MiniScript::determineNamedScriptIdxToStart(): " + condition + ": FAILED");
856 }
857 } else {
858 if (VERBOSE == true) {
859 Console::print("MiniScript::determineNamedScriptIdxToStart(): " + condition + ": OK");
860 }
861 scriptState.state = currentScriptStateState;
862 return scriptIdx;
863 }
864 }
865 scriptIdx++;
866 }
867 }
868 scriptState.state = currentScriptStateState;
869 return -1;
870}
871
873 //
874 auto bracketCount = 0;
875 auto quote = false;
876 for (auto i = 0; i < statement.size(); i++) {
877 auto c = statement[i];
878 if (c == '"') {
879 quote = quote == false?true:false;
880 } else
881 if (quote == false) {
882 if (c == '(') {
883 bracketCount++;
884 } else
885 if (c == ')') {
886 bracketCount--;
887 } else {
888 for (int j = OPERATOR_NONE + 1; j < OPERATOR_MAX; j++) {
889 auto priorizedOperator = static_cast<ScriptOperator>(j);
890 string operatorCandidate;
891 auto operatorString = getOperatorAsString(priorizedOperator);
892 if (operatorString.size() == 1) operatorCandidate+= statement[i];
893 if (operatorString.size() == 2 && i + 1 < statement.size()) {
894 operatorCandidate+= statement[i];
895 operatorCandidate+= statement[i + 1];
896 }
897 if (operatorString == operatorCandidate && (nextOperator.idx == -1 || priorizedOperator > nextOperator.scriptOperator)) {
898 if (i > 0 && isOperatorChar(statement[i - 1]) == true) {
899 continue;
900 }
901 if (operatorString.size() == 2 && i + 2 < statement.size() && isOperatorChar(statement[i + 2]) == true) {
902 continue;
903 } else
904 if (operatorString.size() == 1 && i + 1 < statement.size() && isOperatorChar(statement[i + 1]) == true) {
905 continue;
906 }
907 if (priorizedOperator == OPERATOR_SUBTRACTION) {
908 auto leftArgumentLeft = 0;
909 auto leftArgument = findLeftArgument(statement, i - 1, leftArgumentLeft);
910 if (leftArgument.length() == 0) continue;
911 }
912 nextOperator.idx = i;
913 nextOperator.scriptOperator = priorizedOperator;
914 }
915 }
916 }
917 }
918 }
919
920 //
921 if (bracketCount > 0) {
922 Console::println("MiniScript::getNextStatementOperator(): '" + scriptFileName + "': '" + statement + "': unbalanced bracket count: " + to_string(bracketCount) + " still open");
923 return false;
924 }
925 //
926 return nextOperator.idx != -1;
927}
928
929const string MiniScript::trimArgument(const string& argument) {
930 auto processedArgument = StringTools::trim(argument);
931 if (StringTools::startsWith(processedArgument, "(") == true && StringTools::endsWith(processedArgument, ")") == true) {
932 processedArgument = StringTools::substring(processedArgument, 1, processedArgument.size() - 1);
933 }
934 return processedArgument;
935}
936
937const string MiniScript::findRightArgument(const string statement, int position, int& length) {
938 //
939 auto bracketCount = 0;
940 auto quote = false;
941 string argument;
942 length = 0;
943 for (auto i = position; i < statement.size(); i++) {
944 auto c = statement[i];
945 if (c == '"') {
946 quote = quote == false?true:false;
947 argument+= c;
948 } else
949 if (quote == false) {
950 if (c == '(') {
951 bracketCount++;
952 argument+= c;
953 } else
954 if (c == ')') {
955 bracketCount--;
956 if (bracketCount < 0) return trimArgument(argument);
957 argument+= c;
958 } else
959 if (c == ',') {
960 if (bracketCount == 0) return trimArgument(argument);
961 argument+= c;
962 } else {
963 argument+= c;
964 }
965 } else
966 if (quote == true) {
967 argument+= c;
968 }
969 length++;
970 }
971 return trimArgument(argument);
972}
973
974const string MiniScript::findLeftArgument(const string statement, int position, int& length) {
975 //
976 auto bracketCount = 0;
977 auto quote = false;
978 string argument;
979 length = 0;
980 for (int i = position; i >= 0; i--) {
981 auto c = statement[i];
982 if (c == '"') {
983 quote = quote == false?true:false;
984 argument = c + argument;
985 } else
986 if (quote == false) {
987 if (c == ')') {
988 bracketCount++;
989 argument = c + argument;
990 } else
991 if (c == '(') {
992 bracketCount--;
993 if (bracketCount < 0) return trimArgument(argument);
994 argument = c + argument;
995 } else
996 if (c == ',') {
997 if (bracketCount == 0) return trimArgument(argument);
998 argument = c + argument;
999 } else {
1000 argument = c + argument;
1001 }
1002 } else
1003 if (quote == true) {
1004 argument = c + argument;
1005 }
1006 length++;
1007 }
1008 return trimArgument(argument);
1009}
1010
1011const string MiniScript::doStatementPreProcessing(const string& statement) {
1012 auto preprocessedStatement = statement;
1013 ScriptStatementOperator nextOperators;
1014 while (getNextStatementOperator(preprocessedStatement, nextOperators) == true) {
1015 auto methodIt = scriptOperators.find(nextOperators.scriptOperator);
1016 if (methodIt == scriptOperators.end()) {
1017 Console::println("MiniScript::doStatementPreProcessing(): operator found in: '" + preprocessedStatement + "'@" + to_string(nextOperators.idx) + ": no method found");
1018 // TODO: error handling
1019 return preprocessedStatement;
1020 }
1021 auto method = methodIt->second;
1022 if (method->getArgumentTypes().size() == 1) {
1023 // find the single argument right
1024 auto operatorString = getOperatorAsString(nextOperators.scriptOperator);
1025 int rightArgumentLength = 0;
1026 auto rightArgument = findRightArgument(preprocessedStatement, nextOperators.idx + operatorString.size(), rightArgumentLength);
1027 // substitute with method call
1028 preprocessedStatement =
1029 StringTools::substring(preprocessedStatement, 0, nextOperators.idx) +
1030 method->getMethodName() + "(" + rightArgument + ")" +
1031 StringTools::substring(preprocessedStatement, nextOperators.idx + operatorString.size() + rightArgumentLength, preprocessedStatement.size());
1032 } else
1033 if (method->isVariadic() == true ||
1034 method->getArgumentTypes().size() == 2) {
1035 auto operatorString = getOperatorAsString(nextOperators.scriptOperator);
1036 // find the first argument left
1037 int leftArgumentLength = 0;
1038 auto leftArgument = findLeftArgument(preprocessedStatement, nextOperators.idx - 1, leftArgumentLength);
1039 // find the first argument right
1040 int rightArgumentLength = 0;
1041 auto rightArgument = findRightArgument(preprocessedStatement, nextOperators.idx + operatorString.size(), rightArgumentLength);
1042 //
1043 if (nextOperators.scriptOperator == OPERATOR_SET) {
1044 leftArgument = "\"" + leftArgument + "\"";
1045 }
1046 // substitute with method call
1047 preprocessedStatement =
1048 StringTools::substring(preprocessedStatement, 0, nextOperators.idx - leftArgumentLength) +
1049 method->getMethodName() + "(" + leftArgument + ", " + rightArgument + ")" +
1050 StringTools::substring(preprocessedStatement, nextOperators.idx + operatorString.size() + rightArgumentLength, preprocessedStatement.size());
1051 }
1052 nextOperators = ScriptStatementOperator();
1053 }
1054 return preprocessedStatement;
1055}
1056
1058 string result;
1059 result+= "Script: " + scriptPathName + "/" + scriptFileName + " (runs " + (native == true?"natively":"interpreted") + ")" + "\n\n";
1060 for (auto& script: scripts) {
1061 switch(script.conditionType) {
1062 case Script::CONDITIONTYPE_ON: result+= "on: "; break;
1063 case Script::CONDITIONTYPE_ONENABLED: result+= "on-enabled: "; break;
1064 }
1065 if (script.condition.empty() == false)
1066 result+= script.condition + "; ";
1067 if (script.name.empty() == false) {
1068 result+= "name = '" + script.name + "';\n";
1069 } else {
1070 result+= "\n";
1071 }
1072 for (auto& scriptStatement: script.statements) {
1073 result+= "\t" + to_string(scriptStatement.statementIdx) + ": " + scriptStatement.statement + (scriptStatement.gotoStatementIdx != -1?" (gotoStatement " + to_string(scriptStatement.gotoStatementIdx) + ")":"") + "\n";
1074 }
1075 result+= "\n";
1076 }
1077
1078 //
1079 result+="State Machine States:\n";
1080 {
1081 vector<string> states;
1082 for (auto& scriptStateMachineStateIt: scriptStateMachineStates) {
1083 string state;
1084 state = scriptStateMachineStateIt.second->getName() + "(" + to_string(scriptStateMachineStateIt.second->getId()) + ")";
1085 states.push_back(state);
1086 }
1087 sort(states.begin(), states.end());
1088 for (auto& state: states) result+= state+ "\n";
1089 }
1090 result+= "\n";
1091
1092 //
1093 if (native == false) {
1094 //
1095 result+= "Methods:\n";
1096 {
1097 vector<string> methods;
1098 for (auto& scriptMethodIt: scriptMethods) {
1099 auto scriptMethod = scriptMethodIt.second;
1100 string method;
1101 method+= scriptMethod->getMethodName();
1102 method+= "(";
1103 auto argumentIdx = 0;
1104 if (scriptMethod->isVariadic() == true) {
1105 method+="...";
1106 } else {
1107 for (auto& argumentType: scriptMethod->getArgumentTypes()) {
1108 if (argumentIdx > 0) method+= ", ";
1109 method+= argumentType.name + ": " + ScriptVariable::getTypeAsString(argumentType.type);
1110 if (argumentType.optional == true) {
1111 method+= "(OPTIONAL)";
1112 }
1113 argumentIdx++;
1114 }
1115 }
1116 method+= "): ";
1117 method+= scriptMethod->isMixedReturnValue() == true?"Mixed":ScriptVariable::getTypeAsString(scriptMethod->getReturnValueType());
1118 methods.push_back(method);
1119 }
1120 sort(methods.begin(), methods.end());
1121 for (auto& method: methods) result+= method + "\n";
1122 }
1123 result+= "\n";
1124
1125 //
1126 result+= "Operators:\n";
1127 {
1128 vector<string> operators;
1129 for (auto& scriptOperatorIt: scriptOperators) {
1130 auto method = scriptOperatorIt.second;
1131 string operatorString;
1132 operatorString+= getOperatorAsString(method->getOperator());
1133 operatorString+= " --> ";
1134 operatorString+= method->getMethodName();
1135 operatorString+= "(";
1136 auto argumentIdx = 0;
1137 if (method->isVariadic() == true) {
1138 operatorString+="...";
1139 } else {
1140 for (auto& argumentType: method->getArgumentTypes()) {
1141 if (argumentIdx > 0) operatorString+= ", ";
1142 operatorString+= argumentType.name + ": " + ScriptVariable::getTypeAsString(argumentType.type);
1143 if (argumentType.optional == true) {
1144 operatorString+= "(OPTIONAL)";
1145 }
1146 argumentIdx++;
1147 }
1148 }
1149 operatorString+= "): ";
1150 operatorString+= method->isMixedReturnValue() == true?"Mixed":ScriptVariable::getTypeAsString(method->getReturnValueType());
1151 operators.push_back(operatorString);
1152 }
1153 sort(operators.begin(), operators.end());
1154 for (auto& method: operators) result+= method + "\n";
1155 }
1156 result+= "\n";
1157 } else {
1158 result+= "Methods:\n\trunning natively\n\n";
1159 result+= "Operators:\n\trunning natively\n\n";
1160 }
1161
1162 //
1163 result+= "Variables:\n";
1164 {
1165 vector<string> variables;
1166 for (auto& scriptVariableIt: scriptState.variables) {
1167 string variable;
1168 auto& scriptVariable = *scriptVariableIt.second;
1169 variable+= scriptVariableIt.first + " = " + scriptVariable.getAsString();
1170 variables.push_back(variable);
1171 }
1172 sort(variables.begin(), variables.end());
1173 for (auto& variable: variables) result+= variable + "\n";
1174 }
1175
1176 //
1177 return result;
1178}
1179
1181 // base
1182 if (native == false) {
1183 //
1184 class ScriptStateNextStatement: public ScriptStateMachineState {
1185 private:
1186 MiniScript* miniScript { nullptr };
1187 public:
1188 ScriptStateNextStatement(MiniScript* miniScript): ScriptStateMachineState(), miniScript(miniScript) {}
1189 virtual const string getName() override {
1190 return "STATE_NEXT_STATEMENT";
1191 }
1192 virtual int getId() override {
1193 return STATE_NEXT_STATEMENT;
1194 }
1195 virtual void execute() override {
1196 if (miniScript->scriptState.statementIdx == -1) {
1197 miniScript->scriptState.enabledNamedConditions.clear();
1200 return;
1201 }
1202 if (miniScript->native == false) miniScript->executeScriptLine();
1203 }
1204 };
1205 registerStateMachineState(new ScriptStateNextStatement(this));
1206 }
1207 {
1208 //
1209 class ScriptStateWait: public ScriptStateMachineState {
1210 private:
1211 MiniScript* miniScript { nullptr };
1212 public:
1213 ScriptStateWait(MiniScript* miniScript): ScriptStateMachineState(), miniScript(miniScript) {}
1214 virtual const string getName() override {
1215 return "STATE_WAIT";
1216 }
1217 virtual int getId() override {
1218 return STATE_WAIT;
1219 }
1220 virtual void execute() override {
1221 auto now = Time::getCurrentMillis();
1222 if (now > miniScript->scriptState.timeWaitStarted + miniScript->scriptState.timeWaitTime) {
1224 }
1225 }
1226 };
1227 registerStateMachineState(new ScriptStateWait(this));
1228 }
1229 {
1230 //
1231 class ScriptStateWaitForCondition: public ScriptStateMachineState {
1232 private:
1233 MiniScript* miniScript { nullptr };
1234 public:
1235 ScriptStateWaitForCondition(MiniScript* miniScript): ScriptStateMachineState(), miniScript(miniScript) {}
1236 virtual const string getName() override {
1237 return "STATE_WAIT_FOR_CONDITION";
1238 }
1239 virtual int getId() override {
1241 }
1242 virtual void execute() override {
1243 auto now = Time::getCurrentMillis();
1244 if (now < miniScript->scriptState.timeWaitStarted + miniScript->scriptState.timeWaitTime) {
1245 return;
1246 }
1247 auto scriptIdxToStart = miniScript->determineScriptIdxToStart();
1248 if (scriptIdxToStart == -1) {
1249 miniScript->scriptState.timeWaitStarted = now;
1250 miniScript->scriptState.timeWaitTime = 100LL;
1251 return;
1252 }
1253 miniScript->resetScriptExecutationState(scriptIdxToStart, STATE_NEXT_STATEMENT);
1254 }
1255 };
1256 registerStateMachineState(new ScriptStateWaitForCondition(this));
1257 }
1258}
1259
1261 // script base methods
1262 {
1263 //
1264 class ScriptMethodEnd: public ScriptMethod {
1265 private:
1266 MiniScript* miniScript { nullptr };
1267 public:
1268 ScriptMethodEnd(MiniScript* miniScript): ScriptMethod(), miniScript(miniScript) {}
1269 const string getMethodName() override {
1270 return "end";
1271 }
1272 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
1273 if (miniScript->scriptState.endTypeStack.empty() == true) {
1274 if (miniScript->scriptState.statementIdx < miniScript->scripts[miniScript->scriptState.scriptIdx].statements.size() - 1) {
1275 Console::println("ScriptMethodEnd::executeMethod(): end without forXXX/if");
1276 miniScript->startErrorScript();
1277 }
1278 } else {
1279 auto endType = miniScript->scriptState.endTypeStack.top();
1280 miniScript->scriptState.endTypeStack.pop();
1281 switch(endType) {
1283 // no op
1284 break;
1286 miniScript->scriptState.conditionStack.pop();
1287 break;
1288 }
1289 if (statement.gotoStatementIdx != STATE_NONE) {
1291 miniScript->gotoStatementGoto(statement);
1292 }
1293 }
1294 }
1295 };
1296 registerMethod(new ScriptMethodEnd(this));
1297 }
1298 {
1299 //
1300 class ScriptMethodForTime: public ScriptMethod {
1301 private:
1302 MiniScript* miniScript { nullptr };
1303 public:
1304 ScriptMethodForTime(MiniScript* miniScript):
1306 {
1307 {.type = ScriptVariableType::TYPE_INTEGER, .name = "time", .optional = false }
1308 }
1309 ),
1310 miniScript(miniScript) {}
1311 const string getMethodName() override {
1312 return "forTime";
1313 }
1314 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
1315 int64_t time;
1316 if (miniScript->getIntegerValue(argumentValues, 0, time) == false) {
1317 Console::println("ScriptMethodForTime::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: integer expected");
1318 miniScript->startErrorScript();
1319 } else {
1320 //
1321 auto now = Time::getCurrentMillis();
1322 auto timeWaitStarted = now;
1323 auto forTimeStartedIt = miniScript->scriptState.forTimeStarted.find(statement.line);
1324 if (forTimeStartedIt == miniScript->scriptState.forTimeStarted.end()) {
1325 miniScript->scriptState.forTimeStarted[statement.line] = timeWaitStarted;
1326 } else {
1327 timeWaitStarted = forTimeStartedIt->second;
1328 }
1329 //
1330 if (Time::getCurrentMillis() > timeWaitStarted + time) {
1331 miniScript->scriptState.forTimeStarted.erase(statement.line);
1333 miniScript->gotoStatementGoto(statement);
1334 } else {
1336 }
1337 }
1338 }
1339 };
1340 registerMethod(new ScriptMethodForTime(this));
1341 }
1342 {
1343 //
1344 class ScriptMethodForCondition: public ScriptMethod {
1345 private:
1346 MiniScript* miniScript { nullptr };
1347 public:
1348 ScriptMethodForCondition(MiniScript* miniScript):
1350 {
1351 {.type = ScriptVariableType::TYPE_BOOLEAN, .name = "condition", .optional = false }
1352 }
1353 ),
1354 miniScript(miniScript) {}
1355 const string getMethodName() override {
1356 return "forCondition";
1357 }
1358 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
1359 bool booleanValue;
1360 if (miniScript->getBooleanValue(argumentValues, 0, booleanValue, false) == false) {
1361 Console::println("ScriptMethodForCondition::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: boolean expected");
1362 miniScript->startErrorScript();
1363 } else {
1364 //
1365 auto now = Time::getCurrentMillis();
1366 if (booleanValue == false) {
1368 miniScript->gotoStatementGoto(statement);
1369 } else {
1371 }
1372 }
1373 }
1374 };
1375 registerMethod(new ScriptMethodForCondition(this));
1376 }
1377 {
1378 //
1379 class ScriptMethodIfCondition: public ScriptMethod {
1380 private:
1381 MiniScript* miniScript { nullptr };
1382 public:
1383 ScriptMethodIfCondition(MiniScript* miniScript):
1385 {
1386 {.type = ScriptVariableType::TYPE_BOOLEAN, .name = "condition", .optional = false }
1387 }
1388 ),
1389 miniScript(miniScript) {}
1390 const string getMethodName() override {
1391 return "if";
1392 }
1393 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
1394 bool booleanValue;
1395 if (miniScript->getBooleanValue(argumentValues, 0, booleanValue, false) == false) {
1396 Console::println("ScriptMethodIfCondition::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: boolean expected");
1397 miniScript->startErrorScript();
1398 } else {
1399 //
1401 //
1402 miniScript->scriptState.conditionStack.push(booleanValue);
1403 if (booleanValue == false) {
1405 miniScript->gotoStatementGoto(statement);
1406 }
1407 }
1408 }
1409 };
1410 registerMethod(new ScriptMethodIfCondition(this));
1411 }
1412 {
1413 //
1414 class ScriptMethodElseIfCondition: public ScriptMethod {
1415 private:
1416 MiniScript* miniScript { nullptr };
1417 public:
1418 ScriptMethodElseIfCondition(MiniScript* miniScript):
1420 {
1421 {.type = ScriptVariableType::TYPE_BOOLEAN, .name = "condition", .optional = false }
1422 }
1423 ),
1424 miniScript(miniScript) {}
1425 const string getMethodName() override {
1426 return "elseif";
1427 }
1428 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
1429 bool booleanValue;
1430 if (miniScript->getBooleanValue(argumentValues, 0, booleanValue, false) == false) {
1431 Console::println("ScriptMethodElseIfCondition::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: boolean expected");
1432 miniScript->startErrorScript();
1433 } else
1434 if (miniScript->scriptState.conditionStack.empty() == true) {
1435 Console::println("ScriptMethodElseIfCondition::executeMethod(): elseif without if");
1436 miniScript->startErrorScript();
1437 } else {
1438 //
1439 auto conditionStackElement = miniScript->scriptState.conditionStack.top();
1440 if (conditionStackElement == false) {
1441 miniScript->scriptState.conditionStack.pop();
1442 miniScript->scriptState.conditionStack.push(booleanValue);
1443 }
1444 if (conditionStackElement == true || booleanValue == false) {
1446 miniScript->gotoStatementGoto(statement);
1447 }
1448 }
1449 }
1450 };
1451 registerMethod(new ScriptMethodElseIfCondition(this));
1452 }
1453 {
1454 //
1455 class ScriptMethodElse: public ScriptMethod {
1456 private:
1457 MiniScript* miniScript { nullptr };
1458 public:
1459 ScriptMethodElse(MiniScript* miniScript): ScriptMethod(), miniScript(miniScript) {}
1460 const string getMethodName() override {
1461 return "else";
1462 }
1463 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
1464 if (miniScript->scriptState.conditionStack.empty() == true) {
1465 Console::println("ScriptMethodElse::executeMethod(): else without if");
1466 miniScript->startErrorScript();
1467 } else {
1468 auto conditionStackElement = miniScript->scriptState.conditionStack.top();
1469 if (conditionStackElement == true) {
1471 miniScript->gotoStatementGoto(statement);
1472 }
1473 }
1474 }
1475 };
1476 registerMethod(new ScriptMethodElse(this));
1477 }
1478 {
1479 //
1480 class ScriptMethodScriptWaitForCondition: public ScriptMethod {
1481 private:
1482 MiniScript* miniScript { nullptr };
1483 public:
1484 ScriptMethodScriptWaitForCondition(MiniScript* miniScript): miniScript(miniScript) {}
1485 const string getMethodName() override {
1486 return "script.waitForCondition";
1487 }
1488 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
1489 // script bindings
1491 miniScript->scriptState.timeWaitTime = 100LL;
1493 }
1494 };
1495 registerMethod(new ScriptMethodScriptWaitForCondition(this));
1496 }
1497 {
1498 //
1499 class ScriptMethodScriptWait: public ScriptMethod {
1500 private:
1501 MiniScript* miniScript { nullptr };
1502 public:
1503 ScriptMethodScriptWait(MiniScript* miniScript):
1504 ScriptMethod({
1505 {.type = ScriptVariableType::TYPE_INTEGER, .name = "time", .optional = false }
1506 }),
1507 miniScript(miniScript) {}
1508 const string getMethodName() override {
1509 return "script.wait";
1510 }
1511 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
1512 int64_t time;
1513 if (miniScript->getIntegerValue(argumentValues, 0, time) == true) {
1515 miniScript->scriptState.timeWaitTime = time;
1516 miniScript->setScriptState(STATE_WAIT);
1517 } else {
1518 Console::println("ScriptMethodScriptWait::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: integer expected");
1519 }
1520 }
1521 };
1522 registerMethod(new ScriptMethodScriptWait(this));
1523 }
1524 {
1525 //
1526 class ScriptMethodScriptEmit: public ScriptMethod {
1527 private:
1528 MiniScript* miniScript { nullptr };
1529 public:
1530 ScriptMethodScriptEmit(MiniScript* miniScript):
1532 {
1533 {.type = ScriptVariableType::TYPE_STRING, .name = "condition", .optional = false }
1534 }
1535 ),
1536 miniScript(miniScript) {}
1537 const string getMethodName() override {
1538 return "script.emit";
1539 }
1540 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
1541 string condition;
1542 if (MiniScript::getStringValue(argumentValues, 0, condition, false) == true) {
1543 miniScript->emit(condition);
1544 } else {
1545 Console::println("ScriptMethodScriptWait::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: string expected");
1546 miniScript->startErrorScript();
1547 }
1548 }
1549 };
1550 registerMethod(new ScriptMethodScriptEmit(this));
1551 }
1552 {
1553 //
1554 class ScriptMethodScriptEnableNamedCondition: public ScriptMethod {
1555 private:
1556 MiniScript* miniScript { nullptr };
1557 public:
1558 ScriptMethodScriptEnableNamedCondition(MiniScript* miniScript):
1560 {
1561 {.type = ScriptVariableType::TYPE_STRING, .name = "name", .optional = false }
1562 }
1563 ),
1564 miniScript(miniScript) {}
1565 const string getMethodName() override {
1566 return "script.enableNamedCondition";
1567 }
1568 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
1569 string name;
1570 if (MiniScript::getStringValue(argumentValues, 0, name, false) == true) {
1571 miniScript->scriptState.enabledNamedConditions.erase(
1572 remove(
1573 miniScript->scriptState.enabledNamedConditions.begin(),
1574 miniScript->scriptState.enabledNamedConditions.end(),
1575 name
1576 ),
1577 miniScript->scriptState.enabledNamedConditions.end()
1578 );
1579 miniScript->scriptState.enabledNamedConditions.push_back(name);
1580 } else {
1581 Console::println("ScriptMethodScriptWait::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: string expected");
1582 miniScript->startErrorScript();
1583 }
1584 }
1585 };
1586 registerMethod(new ScriptMethodScriptEnableNamedCondition(this));
1587 }
1588 {
1589 //
1590 class ScriptMethodScriptDisableNamedCondition: public ScriptMethod {
1591 private:
1592 MiniScript* miniScript { nullptr };
1593 public:
1594 ScriptMethodScriptDisableNamedCondition(MiniScript* miniScript):
1596 {
1597 {.type = ScriptVariableType::TYPE_STRING, .name = "name", .optional = false }
1598 }
1599 ),
1600 miniScript(miniScript) {}
1601 const string getMethodName() override {
1602 return "script.disableNamedCondition";
1603 }
1604 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
1605 string name;
1606 if (MiniScript::getStringValue(argumentValues, 0, name, false) == true) {
1607 miniScript->scriptState.enabledNamedConditions.erase(
1608 remove(
1609 miniScript->scriptState.enabledNamedConditions.begin(),
1610 miniScript->scriptState.enabledNamedConditions.end(),
1611 name
1612 ),
1613 miniScript->scriptState.enabledNamedConditions.end()
1614 );
1615 } else {
1616 Console::println("ScriptMethodScriptWait::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: string expected");
1617 miniScript->startErrorScript();
1618 }
1619 }
1620 };
1621 registerMethod(new ScriptMethodScriptDisableNamedCondition(this));
1622 }
1623 {
1624 //
1625 class ScriptMethodScriptGetNamedConditions: public ScriptMethod {
1626 private:
1627 MiniScript* miniScript { nullptr };
1628 public:
1629 ScriptMethodScriptGetNamedConditions(MiniScript* miniScript):
1630 ScriptMethod({}, ScriptVariableType::TYPE_STRING),
1631 miniScript(miniScript) {}
1632 const string getMethodName() override {
1633 return "script.getNamedConditions";
1634 }
1635 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
1636 string result;
1637 for (auto& namedCondition: miniScript->scriptState.enabledNamedConditions) {
1638 result+= result.empty() == false?",":namedCondition;
1639 }
1640 returnValue.setValue(result);
1641 }
1642 };
1643 registerMethod(new ScriptMethodScriptGetNamedConditions(this));
1644 }
1645 {
1646 //
1647 class ScriptMethodConsoleLog: public ScriptMethod {
1648 private:
1649 MiniScript* miniScript { nullptr };
1650 public:
1651 ScriptMethodConsoleLog(MiniScript* miniScript): ScriptMethod(), miniScript(miniScript) {}
1652 const string getMethodName() override {
1653 return "console.log";
1654 }
1655 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
1656 for (auto& argumentValue: argumentValues) {
1657 Console::print(argumentValue.getValueString());
1658 }
1660 }
1661 bool isVariadic() override {
1662 return true;
1663 }
1664 };
1665 registerMethod(new ScriptMethodConsoleLog(this));
1666 }
1667 {
1668 //
1669 class ScriptMethodStringStop: public ScriptMethod {
1670 private:
1671 MiniScript* miniScript { nullptr };
1672 public:
1673 ScriptMethodStringStop(MiniScript* miniScript): ScriptMethod(), miniScript(miniScript) {}
1674 const string getMethodName() override {
1675 return "script.stop";
1676 }
1677 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
1678 //
1679 miniScript->stopScriptExecutation();
1680 }
1681 };
1682 registerMethod(new ScriptMethodStringStop(this));
1683 }
1684 // equality
1685 {
1686 //
1687 class ScriptMethodEquals: public ScriptMethod {
1688 private:
1689 MiniScript* miniScript { nullptr };
1690 public:
1691 ScriptMethodEquals(MiniScript* miniScript): ScriptMethod({}, ScriptVariableType::TYPE_BOOLEAN), miniScript(miniScript) {}
1692 const string getMethodName() override {
1693 return "equals";
1694 }
1695 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
1696 returnValue.setValue(true);
1697 for (auto i = 1; i < argumentValues.size(); i++) {
1698 if (argumentValues[0].getValueString() != argumentValues[i].getValueString()) {
1699 returnValue.setValue(false);
1700 break;
1701 }
1702 }
1703 }
1704 bool isVariadic() override {
1705 return true;
1706 }
1707 ScriptOperator getOperator() override {
1708 return OPERATOR_EQUALS;
1709 }
1710 };
1711 registerMethod(new ScriptMethodEquals(this));
1712 }
1713 {
1714 //
1715 class ScriptMethodNotEqual: public ScriptMethod {
1716 private:
1717 MiniScript* miniScript { nullptr };
1718 public:
1719 ScriptMethodNotEqual(MiniScript* miniScript): ScriptMethod({}, ScriptVariableType::TYPE_BOOLEAN), miniScript(miniScript) {}
1720 const string getMethodName() override {
1721 return "notequal";
1722 }
1723 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
1724 returnValue.setValue(true);
1725 for (auto i = 1; i < argumentValues.size(); i++) {
1726 if (argumentValues[0].getValueString() == argumentValues[i].getValueString()) {
1727 returnValue.setValue(false);
1728 break;
1729 }
1730 }
1731 }
1732 bool isVariadic() override {
1733 return true;
1734 }
1735 ScriptOperator getOperator() override {
1736 return OPERATOR_NOTEQUAL;
1737 }
1738 };
1739 registerMethod(new ScriptMethodNotEqual(this));
1740 }
1741 // int methods
1742 {
1743 //
1744 class ScriptMethodInt: public ScriptMethod {
1745 private:
1746 MiniScript* miniScript { nullptr };
1747 public:
1748 ScriptMethodInt(MiniScript* miniScript):
1750 {
1751 {.type = ScriptVariableType::TYPE_INTEGER, .name = "int", .optional = false }
1752 },
1753 ScriptVariableType::TYPE_INTEGER
1754 ),
1755 miniScript(miniScript) {}
1756 const string getMethodName() override {
1757 return "int";
1758 }
1759 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
1760 int64_t integerValue;
1761 if (MiniScript::getIntegerValue(argumentValues, 0, integerValue, false) == true) {
1762 returnValue.setValue(integerValue);
1763 } else {
1764 Console::println("ScriptMethodInt::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: integer expected");
1765 miniScript->startErrorScript();
1766 }
1767 }
1768 };
1769 registerMethod(new ScriptMethodInt(this));
1770 }
1771 // float methods
1772 {
1773 //
1774 class ScriptMethodFloat: public ScriptMethod {
1775 private:
1776 MiniScript* miniScript { nullptr };
1777 public:
1778 ScriptMethodFloat(MiniScript* miniScript):
1780 {
1781 {.type = ScriptVariableType::TYPE_FLOAT, .name = "float", .optional = false }
1782 },
1783 ScriptVariableType::TYPE_FLOAT
1784 ),
1785 miniScript(miniScript) {}
1786 const string getMethodName() override {
1787 return "float";
1788 }
1789 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
1790 float floatValue;
1791 if (MiniScript::getFloatValue(argumentValues, 0, floatValue, false) == true) {
1792 returnValue.setValue(floatValue);
1793 } else {
1794 Console::println("ScriptMethodFloat::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: float expected");
1795 miniScript->startErrorScript();
1796 }
1797 }
1798 };
1799 registerMethod(new ScriptMethodFloat(this));
1800 }
1801 //
1802 {
1803 //
1804 class ScriptMethodGreater: public ScriptMethod {
1805 private:
1806 MiniScript* miniScript { nullptr };
1807 public:
1808 ScriptMethodGreater(MiniScript* miniScript):
1810 {
1811 {.type = ScriptVariableType::TYPE_FLOAT, .name = "a", .optional = false },
1812 {.type = ScriptVariableType::TYPE_FLOAT, .name = "b", .optional = false }
1813 },
1814 ScriptVariableType::TYPE_BOOLEAN),
1815 miniScript(miniScript) {}
1816 const string getMethodName() override {
1817 return "greater";
1818 }
1819 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
1820 float floatValueA;
1821 float floatValueB;
1822 if (MiniScript::getFloatValue(argumentValues, 0, floatValueA, false) == true &&
1823 MiniScript::getFloatValue(argumentValues, 1, floatValueB, false) == true) {
1824 returnValue.setValue(floatValueA > floatValueB);
1825 } else {
1826 Console::println("ScriptMethodFGreater::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: float expected, @ argument 1: float expected");
1827 miniScript->startErrorScript();
1828 }
1829 }
1830 ScriptOperator getOperator() override {
1831 return OPERATOR_GREATER;
1832 }
1833 };
1834 registerMethod(new ScriptMethodGreater(this));
1835 }
1836 {
1837 //
1838 class ScriptMethodGreaterEquals: public ScriptMethod {
1839 private:
1840 MiniScript* miniScript { nullptr };
1841 public:
1842 ScriptMethodGreaterEquals(MiniScript* miniScript):
1844 {
1845 {.type = ScriptVariableType::TYPE_FLOAT, .name = "a", .optional = false },
1846 {.type = ScriptVariableType::TYPE_FLOAT, .name = "b", .optional = false }
1847 },
1848 ScriptVariableType::TYPE_BOOLEAN),
1849 miniScript(miniScript) {}
1850 const string getMethodName() override {
1851 return "greaterequals";
1852 }
1853 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
1854 float floatValueA;
1855 float floatValueB;
1856 if (MiniScript::getFloatValue(argumentValues, 0, floatValueA, false) == true &&
1857 MiniScript::getFloatValue(argumentValues, 1, floatValueB, false) == true) {
1858 returnValue.setValue(floatValueA >= floatValueB);
1859 } else {
1860 Console::println("ScriptMethodFGreater::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: float expected, @ argument 1: float expected");
1861 miniScript->startErrorScript();
1862 }
1863 }
1864 ScriptOperator getOperator() override {
1866 }
1867 };
1868 registerMethod(new ScriptMethodGreaterEquals(this));
1869 }
1870 {
1871 //
1872 class ScriptMethodLesser: public ScriptMethod {
1873 private:
1874 MiniScript* miniScript { nullptr };
1875 public:
1876 ScriptMethodLesser(MiniScript* miniScript):
1878 {
1879 {.type = ScriptVariableType::TYPE_FLOAT, .name = "a", .optional = false },
1880 {.type = ScriptVariableType::TYPE_FLOAT, .name = "b", .optional = false }
1881 },
1882 ScriptVariableType::TYPE_BOOLEAN),
1883 miniScript(miniScript) {}
1884 const string getMethodName() override {
1885 return "lesser";
1886 }
1887 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
1888 float floatValueA;
1889 float floatValueB;
1890 if (MiniScript::getFloatValue(argumentValues, 0, floatValueA, false) == true &&
1891 MiniScript::getFloatValue(argumentValues, 1, floatValueB, false) == true) {
1892 returnValue.setValue(floatValueA < floatValueB);
1893 } else {
1894 Console::println("ScriptMethodFLesser::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: float expected, @ argument 1: float expected");
1895 miniScript->startErrorScript();
1896 }
1897 }
1898 ScriptOperator getOperator() override {
1899 return OPERATOR_LESSER;
1900 }
1901 };
1902 registerMethod(new ScriptMethodLesser(this));
1903 }
1904 {
1905 //
1906 class ScriptMethodLesserEquals: public ScriptMethod {
1907 private:
1908 MiniScript* miniScript { nullptr };
1909 public:
1910 ScriptMethodLesserEquals(MiniScript* miniScript):
1912 {
1913 {.type = ScriptVariableType::TYPE_FLOAT, .name = "a", .optional = false },
1914 {.type = ScriptVariableType::TYPE_FLOAT, .name = "b", .optional = false }
1915 },
1916 ScriptVariableType::TYPE_BOOLEAN),
1917 miniScript(miniScript) {}
1918 const string getMethodName() override {
1919 return "lesserequals";
1920 }
1921 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
1922 float floatValueA;
1923 float floatValueB;
1924 if (MiniScript::getFloatValue(argumentValues, 0, floatValueA, false) == true &&
1925 MiniScript::getFloatValue(argumentValues, 1, floatValueB, false) == true) {
1926 returnValue.setValue(floatValueA <= floatValueB);
1927 } else {
1928 Console::println("ScriptMethodFLesser::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: float expected, @ argument 1: float expected");
1929 miniScript->startErrorScript();
1930 }
1931 }
1932 ScriptOperator getOperator() override {
1933 return OPERATOR_LESSEREQUALS;
1934 }
1935 };
1936 registerMethod(new ScriptMethodLesserEquals(this));
1937 }
1938 {
1939 //
1940 class ScriptMethodAdd: public ScriptMethod {
1941 private:
1942 MiniScript* miniScript { nullptr };
1943 public:
1944 ScriptMethodAdd(MiniScript* miniScript): ScriptMethod({}, ScriptVariableType::TYPE_VOID), miniScript(miniScript) {}
1945 const string getMethodName() override {
1946 return "add";
1947 }
1948 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
1949 if (MiniScript::hasType(argumentValues, MiniScript::TYPE_STRING) == true) {
1950 string result;
1951 for (auto i = 0; i < argumentValues.size(); i++) {
1952 string stringValue;
1953 if (MiniScript::getStringValue(argumentValues, i, stringValue, false) == true) {
1954 result+= stringValue;
1955 } else {
1956 Console::println("ScriptMethodAdd::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument " + to_string(i) + ": string expected");
1957 miniScript->startErrorScript();
1958 }
1959 }
1960 returnValue.setValue(result);
1961 } else
1962 if (MiniScript::hasType(argumentValues, MiniScript::TYPE_VECTOR3) == true) {
1963 Vector3 result;
1964 for (auto i = 0; i < argumentValues.size(); i++) {
1965 if (argumentValues[i].getType() == MiniScript::TYPE_VECTOR3) {
1966 Vector3 vec3Value;
1967 if (MiniScript::getVector3Value(argumentValues, i, vec3Value, false) == true) {
1968 result+= vec3Value;
1969 } else {
1970 Console::println("ScriptMethodAdd::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument " + to_string(i) + ": vector3 expected");
1971 miniScript->startErrorScript();
1972 }
1973 } else {
1974 float floatValue;
1975 if (MiniScript::getFloatValue(argumentValues, i, floatValue, false) == true) {
1976 result+= Vector3(floatValue, floatValue, floatValue);
1977 } else {
1978 Console::println("ScriptMethodAdd::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument " + to_string(i) + ": float expected");
1979 miniScript->startErrorScript();
1980 }
1981 }
1982 }
1983 returnValue.setValue(result);
1984 } else
1985 if (MiniScript::hasType(argumentValues, MiniScript::TYPE_FLOAT) == true) {
1986 float result = 0.0f;
1987 for (auto i = 0; i < argumentValues.size(); i++) {
1988 float floatValue;
1989 if (MiniScript::getFloatValue(argumentValues, i, floatValue, false) == true) {
1990 result+= floatValue;
1991 } else {
1992 Console::println("ScriptMethodAdd::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument " + to_string(i) + ": float expected");
1993 miniScript->startErrorScript();
1994 }
1995 }
1996 returnValue.setValue(result);
1997 } else {
1998 int64_t result = 0.0f;
1999 for (auto i = 0; i < argumentValues.size(); i++) {
2000 int64_t intValue;
2001 if (MiniScript::getIntegerValue(argumentValues, i, intValue, false) == true) {
2002 result+= intValue;
2003 } else {
2004 Console::println("ScriptMethodAdd::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument " + to_string(i) + ": integer expected");
2005 miniScript->startErrorScript();
2006 }
2007 }
2008 returnValue.setValue(result);
2009 }
2010 }
2011 bool isVariadic() override {
2012 return true;
2013 }
2014 bool isMixedReturnValue() override {
2015 return true;
2016 }
2017 ScriptOperator getOperator() override {
2018 return OPERATOR_ADDITION;
2019 }
2020 };
2021 registerMethod(new ScriptMethodAdd(this));
2022 }
2023 {
2024 //
2025 class ScriptMethodSub: public ScriptMethod {
2026 private:
2027 MiniScript* miniScript { nullptr };
2028 public:
2029 ScriptMethodSub(MiniScript* miniScript): ScriptMethod({}, ScriptVariableType::TYPE_VOID), miniScript(miniScript) {}
2030 const string getMethodName() override {
2031 return "sub";
2032 }
2033 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
2034 if (MiniScript::hasType(argumentValues, MiniScript::TYPE_VECTOR3) == true) {
2035 Vector3 result;
2036 if (argumentValues[0].getType() == MiniScript::TYPE_VECTOR3) {
2037 Vector3 vec3Value;
2038 if (MiniScript::getVector3Value(argumentValues, 0, vec3Value, false) == true) {
2039 result = vec3Value;
2040 } else {
2041 Console::println("ScriptMethodSub::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument " + to_string(0) + ": vector3 expected");
2042 miniScript->startErrorScript();
2043 }
2044 } else {
2045 float floatValue;
2046 if (MiniScript::getFloatValue(argumentValues, 0, floatValue, false) == true) {
2047 result = Vector3(floatValue, floatValue, floatValue);
2048 } else {
2049 Console::println("ScriptMethodSub::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument " + to_string(0) + ": float expected");
2050 miniScript->startErrorScript();
2051 }
2052 }
2053 for (auto i = 1; i < argumentValues.size(); i++) {
2054 if (argumentValues[i].getType() == MiniScript::TYPE_VECTOR3) {
2055 Vector3 vec3Value;
2056 if (MiniScript::getVector3Value(argumentValues, i, vec3Value, false) == true) {
2057 result-= vec3Value;
2058 } else {
2059 Console::println("ScriptMethodSub::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument " + to_string(i) + ": vector3 expected");
2060 miniScript->startErrorScript();
2061 break;
2062 }
2063 } else {
2064 float floatValue;
2065 if (MiniScript::getFloatValue(argumentValues, i, floatValue, false) == true) {
2066 result-= Vector3(floatValue, floatValue, floatValue);
2067 } else {
2068 Console::println("ScriptMethodSub::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument " + to_string(i) + ": float expected");
2069 miniScript->startErrorScript();
2070 break;
2071 }
2072 }
2073 }
2074 returnValue.setValue(result);
2075 } else
2076 if (MiniScript::hasType(argumentValues, MiniScript::TYPE_FLOAT) == true) {
2077 bool valid = true;
2078 float result = 0.0f;
2079 {
2080 float floatValue;
2081 if (MiniScript::getFloatValue(argumentValues, 0, floatValue, false) == true) {
2082 result = floatValue;
2083 } else {
2084 Console::println("ScriptMethodSub::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument " + to_string(0) + ": float expected");
2085 miniScript->startErrorScript();
2086 valid = false;
2087 }
2088 }
2089 if (valid == true) {
2090 for (auto i = 1; i < argumentValues.size(); i++) {
2091 float floatValue;
2092 if (MiniScript::getFloatValue(argumentValues, i, floatValue, false) == true) {
2093 result-= floatValue;
2094 } else {
2095 Console::println("ScriptMethodSub::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument " + to_string(i) + ": float expected");
2096 miniScript->startErrorScript();
2097 break;
2098 }
2099 }
2100 returnValue.setValue(result);
2101 }
2102 } else {
2103 bool valid = true;
2104 int64_t result = 0LL;
2105 {
2106 int64_t intValue;
2107 if (MiniScript::getIntegerValue(argumentValues, 0, intValue, false) == true) {
2108 result = intValue;
2109 } else {
2110 Console::println("ScriptMethodSub::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument " + to_string(0) + ": integer expected");
2111 miniScript->startErrorScript();
2112 valid = false;
2113 }
2114 }
2115 if (valid == true) {
2116 for (auto i = 1; i < argumentValues.size(); i++) {
2117 int64_t intValue;
2118 if (MiniScript::getIntegerValue(argumentValues, i, intValue, false) == true) {
2119 result-= intValue;
2120 } else {
2121 Console::println("ScriptMethodSub::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument " + to_string(i) + ": integer expected");
2122 miniScript->startErrorScript();
2123 break;
2124 }
2125 }
2126 returnValue.setValue(result);
2127 }
2128 }
2129 }
2130 bool isVariadic() override {
2131 return true;
2132 }
2133 bool isMixedReturnValue() override {
2134 return true;
2135 }
2136 ScriptOperator getOperator() override {
2137 return OPERATOR_SUBTRACTION;
2138 }
2139 };
2140 registerMethod(new ScriptMethodSub(this));
2141 }
2142 {
2143 //
2144 class ScriptMethodMul: public ScriptMethod {
2145 private:
2146 MiniScript* miniScript { nullptr };
2147 public:
2148 ScriptMethodMul(MiniScript* miniScript): ScriptMethod({}, ScriptVariableType::TYPE_VOID), miniScript(miniScript) {}
2149 const string getMethodName() override {
2150 return "mul";
2151 }
2152 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
2153 if (MiniScript::hasType(argumentValues, MiniScript::TYPE_VECTOR3) == true) {
2154 auto valid = true;
2155 Vector3 result;
2156 if (argumentValues[0].getType() == MiniScript::TYPE_VECTOR3) {
2157 Vector3 vec3Value;
2158 if (MiniScript::getVector3Value(argumentValues, 0, vec3Value, false) == true) {
2159 result = vec3Value;
2160 } else {
2161 Console::println("ScriptMethodMul::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument " + to_string(0) + ": vector3 expected");
2162 miniScript->startErrorScript();
2163 valid = false;
2164 }
2165 } else {
2166 float floatValue;
2167 if (MiniScript::getFloatValue(argumentValues, 0, floatValue, false) == true) {
2168 result = Vector3(floatValue, floatValue, floatValue);
2169 } else {
2170 Console::println("ScriptMethodMul::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument " + to_string(0) + ": float expected");
2171 miniScript->startErrorScript();
2172 valid = false;
2173 }
2174 }
2175 if (valid == true) {
2176 for (auto i = 1; i < argumentValues.size(); i++) {
2177 if (argumentValues[i].getType() == MiniScript::TYPE_VECTOR3) {
2178 Vector3 vec3Value;
2179 if (MiniScript::getVector3Value(argumentValues, i, vec3Value, false) == true) {
2180 result*= vec3Value;
2181 } else {
2182 Console::println("ScriptMethodMul::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument " + to_string(i) + ": vector3 expected");
2183 miniScript->startErrorScript();
2184 break;
2185 }
2186 } else {
2187 float floatValue;
2188 if (MiniScript::getFloatValue(argumentValues, i, floatValue, false) == true) {
2189 result*= Vector3(floatValue, floatValue, floatValue);
2190 } else {
2191 Console::println("ScriptMethodMul::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument " + to_string(i) + ": float expected");
2192 miniScript->startErrorScript();
2193 break;
2194 }
2195 }
2196 }
2197 returnValue.setValue(result);
2198 }
2199 } else
2200 if (MiniScript::hasType(argumentValues, MiniScript::TYPE_FLOAT) == true) {
2201 auto valid = true;
2202 float result = 0.0f;
2203 {
2204 float floatValue;
2205 if (MiniScript::getFloatValue(argumentValues, 0, floatValue, false) == true) {
2206 result = floatValue;
2207 } else {
2208 Console::println("ScriptMethodMul::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument " + to_string(0) + ": float expected");
2209 miniScript->startErrorScript();
2210 valid = false;
2211 }
2212 }
2213 if (valid == true) {
2214 for (auto i = 1; i < argumentValues.size(); i++) {
2215 float floatValue;
2216 if (MiniScript::getFloatValue(argumentValues, i, floatValue, false) == true) {
2217 result*= floatValue;
2218 } else {
2219 Console::println("ScriptMethodMul::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument " + to_string(i) + ": float expected");
2220 miniScript->startErrorScript();
2221 break;
2222 }
2223 }
2224 returnValue.setValue(result);
2225 }
2226 } else {
2227 auto valid = true;
2228 int64_t result = 0LL;
2229 {
2230 int64_t intValue;
2231 if (MiniScript::getIntegerValue(argumentValues, 0, intValue, false) == true) {
2232 result = intValue;
2233 } else {
2234 Console::println("ScriptMethodMul::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument " + to_string(0) + ": integer expected");
2235 miniScript->startErrorScript();
2236 valid = false;
2237 }
2238 }
2239 if (valid == true) {
2240 for (auto i = 1; i < argumentValues.size(); i++) {
2241 int64_t intValue;
2242 if (MiniScript::getIntegerValue(argumentValues, i, intValue, false) == true) {
2243 result*= intValue;
2244 } else {
2245 Console::println("ScriptMethodMul::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument " + to_string(i) + ": integer expected");
2246 miniScript->startErrorScript();
2247 break;
2248 }
2249 }
2250 returnValue.setValue(result);
2251 }
2252 }
2253 }
2254 bool isVariadic() override {
2255 return true;
2256 }
2257 bool isMixedReturnValue() override {
2258 return true;
2259 }
2260 ScriptOperator getOperator() override {
2262 }
2263 };
2264 registerMethod(new ScriptMethodMul(this));
2265 }
2266 {
2267 //
2268 class ScriptMethodDiv: public ScriptMethod {
2269 private:
2270 MiniScript* miniScript { nullptr };
2271 public:
2272 ScriptMethodDiv(MiniScript* miniScript): ScriptMethod({}, ScriptVariableType::TYPE_VOID), miniScript(miniScript) {}
2273 const string getMethodName() override {
2274 return "div";
2275 }
2276 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
2277 if (MiniScript::hasType(argumentValues, MiniScript::TYPE_VECTOR3) == true) {
2278 auto valid = true;
2279 Vector3 result;
2280 if (argumentValues[0].getType() == MiniScript::TYPE_VECTOR3) {
2281 Vector3 vec3Value;
2282 if (MiniScript::getVector3Value(argumentValues, 0, vec3Value, false) == true) {
2283 result = vec3Value;
2284 } else {
2285 Console::println("ScriptMethodDiv::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument " + to_string(0) + ": vector3 expected");
2286 miniScript->startErrorScript();
2287 valid = false;
2288 }
2289 } else {
2290 float floatValue;
2291 if (MiniScript::getFloatValue(argumentValues, 0, floatValue, false) == true) {
2292 result = Vector3(floatValue, floatValue, floatValue);
2293 } else {
2294 Console::println("ScriptMethodDiv::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument " + to_string(0) + ": float expected");
2295 miniScript->startErrorScript();
2296 valid = false;
2297 }
2298 }
2299 if (valid == true) {
2300 for (auto i = 1; i < argumentValues.size(); i++) {
2301 if (argumentValues[i].getType() == MiniScript::TYPE_VECTOR3) {
2302 Vector3 vec3Value;
2303 if (MiniScript::getVector3Value(argumentValues, i, vec3Value, false) == true) {
2304 result/= vec3Value;
2305 } else {
2306 Console::println("ScriptMethodDiv::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument " + to_string(i) + ": vector3 expected");
2307 miniScript->startErrorScript();
2308 break;
2309 }
2310 } else {
2311 float floatValue;
2312 if (MiniScript::getFloatValue(argumentValues, i, floatValue, false) == true) {
2313 result/= Vector3(floatValue, floatValue, floatValue);
2314 } else {
2315 Console::println("ScriptMethodDiv::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument " + to_string(i) + ": float expected");
2316 miniScript->startErrorScript();
2317 break;
2318 }
2319 }
2320 }
2321 returnValue.setValue(result);
2322 }
2323 } else
2324 if (MiniScript::hasType(argumentValues, MiniScript::TYPE_FLOAT) == true) {
2325 auto valid = true;
2326 float result = 0.0f;
2327 {
2328 float floatValue;
2329 if (MiniScript::getFloatValue(argumentValues, 0, floatValue, false) == true) {
2330 result = floatValue;
2331 } else {
2332 Console::println("ScriptMethodDiv::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument " + to_string(0) + ": float expected");
2333 miniScript->startErrorScript();
2334 valid = false;
2335 }
2336 }
2337 if (valid == true) {
2338 for (auto i = 1; i < argumentValues.size(); i++) {
2339 float floatValue;
2340 if (MiniScript::getFloatValue(argumentValues, i, floatValue, false) == true) {
2341 result/= floatValue;
2342 } else {
2343 Console::println("ScriptMethodDiv::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument " + to_string(i) + ": float expected");
2344 miniScript->startErrorScript();
2345 break;
2346 }
2347 }
2348 returnValue.setValue(result);
2349 }
2350 } else {
2351 auto valid = true;
2352 int64_t result = 0LL;
2353 {
2354 int64_t intValue;
2355 if (MiniScript::getIntegerValue(argumentValues, 0, intValue, false) == true) {
2356 result = intValue;
2357 } else {
2358 Console::println("ScriptMethodDiv::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument " + to_string(0) + ": integer expected");
2359 miniScript->startErrorScript();
2360 valid = false;
2361 }
2362 }
2363 if (valid == true) {
2364 for (auto i = 1; i < argumentValues.size(); i++) {
2365 int64_t intValue;
2366 if (MiniScript::getIntegerValue(argumentValues, i, intValue, false) == true) {
2367 result/= intValue;
2368 } else {
2369 Console::println("ScriptMethodDiv::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument " + to_string(i) + ": integer expected");
2370 miniScript->startErrorScript();
2371 break;
2372 }
2373 }
2374 returnValue.setValue(result);
2375 }
2376 }
2377 }
2378 bool isVariadic() override {
2379 return true;
2380 }
2381 bool isMixedReturnValue() override {
2382 return true;
2383 }
2384 ScriptOperator getOperator() override {
2385 return OPERATOR_DIVISION;
2386 }
2387 };
2388 registerMethod(new ScriptMethodDiv(this));
2389 }
2390 // vector3 methods
2391 {
2392 //
2393 class ScriptMethodVec3: public ScriptMethod {
2394 private:
2395 MiniScript* miniScript { nullptr };
2396 public:
2397 ScriptMethodVec3(MiniScript* miniScript):
2399 {
2400 {.type = ScriptVariableType::TYPE_FLOAT, .name = "x", .optional = false },
2401 {.type = ScriptVariableType::TYPE_FLOAT, .name = "y", .optional = false },
2402 {.type = ScriptVariableType::TYPE_FLOAT, .name = "z", .optional = false }
2403 },
2404 ScriptVariableType::TYPE_VECTOR3
2405 ),
2406 miniScript(miniScript) {}
2407 const string getMethodName() override {
2408 return "vec3";
2409 }
2410 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
2411 Vector3 result;
2412 float xValue;
2413 float yValue;
2414 float zValue;
2415 if (MiniScript::getFloatValue(argumentValues, 0, xValue, false) == true &&
2416 MiniScript::getFloatValue(argumentValues, 1, yValue, false) == true &&
2417 MiniScript::getFloatValue(argumentValues, 2, zValue, false) == true) {
2418 returnValue.setValue(Vector3(xValue, yValue, zValue));
2419 } else {
2420 Console::println("ScriptMethodVec3::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: float expected, @ argument 1: float expected, @ argument 2: float expected");
2421 miniScript->startErrorScript();
2422 }
2423 }
2424 };
2425 registerMethod(new ScriptMethodVec3(this));
2426 }
2427 {
2428 //
2429 class ScriptMethodVec3ComputeLength: public ScriptMethod {
2430 private:
2431 MiniScript* miniScript { nullptr };
2432 public:
2433 ScriptMethodVec3ComputeLength(MiniScript* miniScript):
2435 {
2436 {.type = ScriptVariableType::TYPE_VECTOR3, .name = "vec3", .optional = false }
2437 },
2438 ScriptVariableType::TYPE_FLOAT),
2439 miniScript(miniScript) {}
2440 const string getMethodName() override {
2441 return "vec3.computeLength";
2442 }
2443 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
2444 Vector3 vec3;
2445 if (MiniScript::getVector3Value(argumentValues, 0, vec3, false) == true) {
2446 returnValue.setValue(vec3.computeLength());
2447 } else {
2448 Console::println("ScriptMethodVec3ComputeLength::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: vector3 expected");
2449 miniScript->startErrorScript();
2450 }
2451 }
2452 };
2453 registerMethod(new ScriptMethodVec3ComputeLength(this));
2454 }
2455 {
2456 //
2457 class ScriptMethodVec3ComputeLengthSquared: public ScriptMethod {
2458 private:
2459 MiniScript* miniScript { nullptr };
2460 public:
2461 ScriptMethodVec3ComputeLengthSquared(MiniScript* miniScript):
2463 {
2464 {.type = ScriptVariableType::TYPE_VECTOR3, .name = "vec3", .optional = false }
2465 },
2466 ScriptVariableType::TYPE_FLOAT),
2467 miniScript(miniScript) {}
2468 const string getMethodName() override {
2469 return "vec3.computeLengthSquared";
2470 }
2471 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
2472 Vector3 vec3;
2473 if (MiniScript::getVector3Value(argumentValues, 0, vec3, false) == true) {
2474 returnValue.setValue(vec3.computeLengthSquared());
2475 } else {
2476 Console::println("ScriptMethodVec3ComputeLengthSquared::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: vector3 expected");
2477 miniScript->startErrorScript();
2478 }
2479 }
2480 };
2481 registerMethod(new ScriptMethodVec3ComputeLengthSquared(this));
2482 }
2483 {
2484 //
2485 class ScriptMethodVec3ComputeDotProduct: public ScriptMethod {
2486 private:
2487 MiniScript* miniScript { nullptr };
2488 public:
2489 ScriptMethodVec3ComputeDotProduct(MiniScript* miniScript):
2491 {
2492 {.type = ScriptVariableType::TYPE_VECTOR3, .name = "a", .optional = false },
2493 {.type = ScriptVariableType::TYPE_VECTOR3, .name = "b", .optional = false }
2494 },
2495 ScriptVariableType::TYPE_FLOAT),
2496 miniScript(miniScript) {}
2497 const string getMethodName() override {
2498 return "vec3.computeDotProduct";
2499 }
2500 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
2501 Vector3 a;
2502 Vector3 b;
2503 if (MiniScript::getVector3Value(argumentValues, 0, a, false) == true &&
2504 MiniScript::getVector3Value(argumentValues, 1, b, false) == true) {
2505 returnValue.setValue(Vector3::computeDotProduct(a, b));
2506 } else {
2507 Console::println("ScriptMethodVec3ComputeDotProduct::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: vector3 expected, @ argument 1: vector3 expected");
2508 miniScript->startErrorScript();
2509 }
2510 }
2511 };
2512 registerMethod(new ScriptMethodVec3ComputeDotProduct(this));
2513 }
2514 {
2515 //
2516 class ScriptMethodVec3ComputeCrossProduct: public ScriptMethod {
2517 private:
2518 MiniScript* miniScript { nullptr };
2519 public:
2520 ScriptMethodVec3ComputeCrossProduct(MiniScript* miniScript):
2522 {
2523 {.type = ScriptVariableType::TYPE_VECTOR3, .name = "a", .optional = false },
2524 {.type = ScriptVariableType::TYPE_VECTOR3, .name = "b", .optional = false }
2525 },
2526 ScriptVariableType::TYPE_VECTOR3),
2527 miniScript(miniScript) {}
2528 const string getMethodName() override {
2529 return "vec3.computeCrossProduct";
2530 }
2531 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
2532 Vector3 a;
2533 Vector3 b;
2534 if (MiniScript::getVector3Value(argumentValues, 0, a, false) == true &&
2535 MiniScript::getVector3Value(argumentValues, 1, b, false) == true) {
2536 returnValue.setValue(Vector3::computeCrossProduct(a, b));
2537 } else {
2538 Console::println("ScriptMethodVec3ComputeCrossProduct::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: vector3 expected, @ argument 1: vector3 expected");
2539 miniScript->startErrorScript();
2540 }
2541 }
2542 };
2543 registerMethod(new ScriptMethodVec3ComputeCrossProduct(this));
2544 }
2545 {
2546 //
2547 class ScriptMethodVec3Normalize: public ScriptMethod {
2548 private:
2549 MiniScript* miniScript { nullptr };
2550 public:
2551 ScriptMethodVec3Normalize(MiniScript* miniScript):
2553 {
2554 {.type = ScriptVariableType::TYPE_VECTOR3, .name = "vec3", .optional = false },
2555 },
2556 ScriptVariableType::TYPE_VECTOR3),
2557 miniScript(miniScript) {}
2558 const string getMethodName() override {
2559 return "vec3.normalize";
2560 }
2561 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
2562 Vector3 vec3;
2563 if (MiniScript::getVector3Value(argumentValues, 0, vec3, false) == true) {
2564 returnValue.setValue(vec3.normalize());
2565 } else {
2566 Console::println("ScriptMethodVec3Normalize::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: vector3 expected");
2567 miniScript->startErrorScript();
2568 }
2569 }
2570 };
2571 registerMethod(new ScriptMethodVec3Normalize(this));
2572 }
2573 {
2574 //
2575 class ScriptMethodVec3ComputeAngle: public ScriptMethod {
2576 private:
2577 MiniScript* miniScript { nullptr };
2578 public:
2579 ScriptMethodVec3ComputeAngle(MiniScript* miniScript):
2581 {
2582 {.type = ScriptVariableType::TYPE_VECTOR3, .name = "a", .optional = false },
2583 {.type = ScriptVariableType::TYPE_VECTOR3, .name = "b", .optional = false },
2584 {.type = ScriptVariableType::TYPE_VECTOR3, .name = "n", .optional = false },
2585 },
2586 ScriptVariableType::TYPE_FLOAT),
2587 miniScript(miniScript) {}
2588 const string getMethodName() override {
2589 return "vec3.computeAngle";
2590 }
2591 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
2592 Vector3 a;
2593 Vector3 b;
2594 Vector3 n;
2595 if (MiniScript::getVector3Value(argumentValues, 0, a, false) == true &&
2596 MiniScript::getVector3Value(argumentValues, 1, b, false) == true &&
2597 MiniScript::getVector3Value(argumentValues, 2, n, false) == true) {
2598 returnValue.setValue(Vector3::computeAngle(a, b, n));
2599 } else {
2600 Console::println("ScriptMethodVec3ComputeAngle::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: vector3 expected, @ argument 1: vector3 expected @ argument 2: vector3 expected");
2601 miniScript->startErrorScript();
2602 }
2603 }
2604 };
2605 registerMethod(new ScriptMethodVec3ComputeAngle(this));
2606 }
2607 {
2608 //
2609 class ScriptMethodVec3GetX: public ScriptMethod {
2610 private:
2611 MiniScript* miniScript { nullptr };
2612 public:
2613 ScriptMethodVec3GetX(MiniScript* miniScript):
2615 {
2616 {.type = ScriptVariableType::TYPE_VECTOR3, .name = "vec3", .optional = false },
2617 },
2618 ScriptVariableType::TYPE_FLOAT),
2619 miniScript(miniScript) {}
2620 const string getMethodName() override {
2621 return "vec3.getX";
2622 }
2623 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
2624 Vector3 vec3;
2625 if (MiniScript::getVector3Value(argumentValues, 0, vec3, false) == true) {
2626 returnValue.setValue(vec3.getX());
2627 } else {
2628 Console::println("ScriptMethodVec3GetX::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: vector3 expected");
2629 miniScript->startErrorScript();
2630 }
2631 }
2632 };
2633 registerMethod(new ScriptMethodVec3GetX(this));
2634 }
2635 {
2636 //
2637 class ScriptMethodVec3GetY: public ScriptMethod {
2638 private:
2639 MiniScript* miniScript { nullptr };
2640 public:
2641 ScriptMethodVec3GetY(MiniScript* miniScript):
2643 {
2644 {.type = ScriptVariableType::TYPE_VECTOR3, .name = "vec3", .optional = false },
2645 },
2646 ScriptVariableType::TYPE_FLOAT),
2647 miniScript(miniScript) {}
2648 const string getMethodName() override {
2649 return "vec3.getY";
2650 }
2651 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
2652 Vector3 vec3;
2653 if (MiniScript::getVector3Value(argumentValues, 0, vec3, false) == true) {
2654 returnValue.setValue(vec3.getY());
2655 } else {
2656 Console::println("ScriptMethodVec3GetY::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: vector3 expected");
2657 miniScript->startErrorScript();
2658 }
2659 }
2660 };
2661 registerMethod(new ScriptMethodVec3GetY(this));
2662 }
2663 {
2664 //
2665 class ScriptMethodVec3GetZ: public ScriptMethod {
2666 private:
2667 MiniScript* miniScript { nullptr };
2668 public:
2669 ScriptMethodVec3GetZ(MiniScript* miniScript):
2671 {
2672 {.type = ScriptVariableType::TYPE_VECTOR3, .name = "vec3", .optional = false },
2673 },
2674 ScriptVariableType::TYPE_FLOAT),
2675 miniScript(miniScript) {}
2676 const string getMethodName() override {
2677 return "vec3.getZ";
2678 }
2679 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
2680 Vector3 vec3;
2681 if (MiniScript::getVector3Value(argumentValues, 0, vec3, false) == true) {
2682 returnValue.setValue(vec3.getZ());
2683 } else {
2684 Console::println("ScriptMethodVec3GetZ::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: vector3 expected");
2685 miniScript->startErrorScript();
2686 }
2687 }
2688 };
2689 registerMethod(new ScriptMethodVec3GetZ(this));
2690 }
2691 // transformations
2692 {
2693 //
2694 class ScriptMethodTransformations: public ScriptMethod {
2695 private:
2696 MiniScript* miniScript { nullptr };
2697 public:
2698 ScriptMethodTransformations(MiniScript* miniScript):
2700 {
2701 },
2702 ScriptVariableType::TYPE_TRANSFORMATIONS),
2703 miniScript(miniScript) {}
2704 const string getMethodName() override {
2705 return "transformations";
2706 }
2707 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
2708 Transformations transformations;
2709 Vector3 vec3Value;
2710 if (argumentValues.size() >= 1) {
2711 if (MiniScript::getVector3Value(argumentValues, 0, vec3Value, true) == true) {
2712 transformations.setTranslation(vec3Value);
2713 } else {
2714 Console::println("ScriptMethodTransformations::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: vector3 expected");
2715 miniScript->startErrorScript();
2716 }
2717 }
2718 if (argumentValues.size() >= 2) {
2719 if (MiniScript::getVector3Value(argumentValues, 1, vec3Value, true) == true) {
2720 transformations.setScale(vec3Value);
2721 } else {
2722 Console::println("ScriptMethodTransformations::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 1: vector3 expected");
2723 miniScript->startErrorScript();
2724 }
2725 }
2726 for (auto i = 2; i < argumentValues.size(); i++) {
2727 if (MiniScript::getVector3Value(argumentValues, i, vec3Value, true) == true) {
2728 transformations.addRotation(vec3Value, 0.0f);
2729 } else {
2730 Console::println("ScriptMethodTransformations::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument " + to_string(i) + ": vector3 expected");
2731 miniScript->startErrorScript();
2732 }
2733 }
2734 transformations.update();
2735 returnValue.setValue(transformations);
2736 }
2737 bool isVariadic() override {
2738 return true;
2739 }
2740 };
2741 registerMethod(new ScriptMethodTransformations(this));
2742 }
2743 {
2744 //
2745 class ScriptMethodTransformationsGetTranslation: public ScriptMethod {
2746 private:
2747 MiniScript* miniScript { nullptr };
2748 public:
2749 ScriptMethodTransformationsGetTranslation(MiniScript* miniScript):
2751 {
2752 {.type = ScriptVariableType::TYPE_TRANSFORMATIONS, .name = "transformations", .optional = false },
2753 },
2754 ScriptVariableType::TYPE_VECTOR3),
2755 miniScript(miniScript) {}
2756 const string getMethodName() override {
2757 return "transformations.getTranslation";
2758 }
2759 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
2760 Transformations transformations;
2761 if (MiniScript::getTransformationsValue(argumentValues, 0, transformations, false) == true) {
2762 returnValue.setValue(transformations.getTranslation());
2763 } else {
2764 Console::println("ScriptMethodTransformationsGetTranslation::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: transformations expected");
2765 miniScript->startErrorScript();
2766 }
2767 }
2768 };
2769 registerMethod(new ScriptMethodTransformationsGetTranslation(this));
2770 }
2771 {
2772 //
2773 class ScriptMethodTransformationsSetTranslation: public ScriptMethod {
2774 private:
2775 MiniScript* miniScript { nullptr };
2776 public:
2777 ScriptMethodTransformationsSetTranslation(MiniScript* miniScript):
2779 {
2780 {.type = ScriptVariableType::TYPE_TRANSFORMATIONS, .name = "transformations", .optional = false },
2781 {.type = ScriptVariableType::TYPE_VECTOR3, .name = "translation", .optional = false },
2782 },
2783 ScriptVariableType::TYPE_TRANSFORMATIONS),
2784 miniScript(miniScript) {}
2785 const string getMethodName() override {
2786 return "transformations.setTranslation";
2787 }
2788 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
2789 Transformations transformations;
2790 Vector3 translation;
2791 if (MiniScript::getTransformationsValue(argumentValues, 0, transformations, false) == true &&
2792 MiniScript::getVector3Value(argumentValues, 1, translation, false) == true) {
2793 transformations.setTranslation(translation);
2794 transformations.update();
2795 returnValue.setValue(transformations);
2796 } else {
2797 Console::println("ScriptMethodTransformationsSetTranslation::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: transformations expected, @ argument 1: vector3 expected");
2798 miniScript->startErrorScript();
2799 }
2800 }
2801 };
2802 registerMethod(new ScriptMethodTransformationsSetTranslation(this));
2803 }
2804 {
2805 //
2806 class ScriptMethodTransformationsGetScale: public ScriptMethod {
2807 private:
2808 MiniScript* miniScript { nullptr };
2809 public:
2810 ScriptMethodTransformationsGetScale(MiniScript* miniScript):
2812 {
2813 {.type = ScriptVariableType::TYPE_TRANSFORMATIONS, .name = "transformations", .optional = false },
2814 },
2815 ScriptVariableType::TYPE_VECTOR3),
2816 miniScript(miniScript) {}
2817 const string getMethodName() override {
2818 return "transformations.getScale";
2819 }
2820 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
2821 Transformations transformations;
2822 if (MiniScript::getTransformationsValue(argumentValues, 0, transformations, false) == true) {
2823 returnValue.setValue(transformations.getScale());
2824 } else {
2825 Console::println("ScriptMethodTransformationsGetScale::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: transformations expected");
2826 miniScript->startErrorScript();
2827 }
2828 }
2829 };
2830 registerMethod(new ScriptMethodTransformationsGetScale(this));
2831 }
2832 {
2833 //
2834 class ScriptMethodTransformationsSetScale: public ScriptMethod {
2835 private:
2836 MiniScript* miniScript { nullptr };
2837 public:
2838 ScriptMethodTransformationsSetScale(MiniScript* miniScript):
2840 {
2841 {.type = ScriptVariableType::TYPE_TRANSFORMATIONS, .name = "transformations", .optional = false },
2842 {.type = ScriptVariableType::TYPE_VECTOR3, .name = "scale", .optional = false },
2843 },
2844 ScriptVariableType::TYPE_TRANSFORMATIONS),
2845 miniScript(miniScript) {}
2846 const string getMethodName() override {
2847 return "transformations.setScale";
2848 }
2849 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
2850 Transformations transformations;
2851 Vector3 scale;
2852 if (MiniScript::getTransformationsValue(argumentValues, 0, transformations, false) == true &&
2853 MiniScript::getVector3Value(argumentValues, 1, scale, false) == true) {
2854 transformations.setScale(scale);
2855 transformations.update();
2856 returnValue.setValue(transformations);
2857 } else {
2858 Console::println("ScriptMethodTransformationsSetScale::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: transformations expected, @ argument 1: vector3 expected");
2859 miniScript->startErrorScript();
2860 }
2861 }
2862 };
2863 registerMethod(new ScriptMethodTransformationsSetScale(this));
2864 }
2865 {
2866 //
2867 class ScriptMethodTransformationsGetRotationAxis: public ScriptMethod {
2868 private:
2869 MiniScript* miniScript { nullptr };
2870 public:
2871 ScriptMethodTransformationsGetRotationAxis(MiniScript* miniScript):
2873 {
2874 {.type = ScriptVariableType::TYPE_TRANSFORMATIONS, .name = "transformations", .optional = false },
2875 {.type = ScriptVariableType::TYPE_INTEGER, .name = "idx", .optional = false },
2876 },
2877 ScriptVariableType::TYPE_VECTOR3),
2878 miniScript(miniScript) {}
2879 const string getMethodName() override {
2880 return "transformations.getRotationAxis";
2881 }
2882 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
2883 int64_t idx;
2884 Transformations transformations;
2885 if (MiniScript::getTransformationsValue(argumentValues, 0, transformations, false) == true &&
2886 MiniScript::getIntegerValue(argumentValues, 1, idx, false) == true) {
2887 if (idx < transformations.getRotationCount()) {
2888 returnValue.setValue(transformations.getRotationAxis(idx));
2889 } else {
2890 Console::println("ScriptMethodTransformationsGetRotationAxis::executeMethod(): " + getMethodName() + "(): rotation index invalid: " + to_string(idx) + " / " + to_string(transformations.getRotationCount()));
2891 miniScript->startErrorScript();
2892 }
2893 } else {
2894 Console::println("ScriptMethodTransformationsGetRotationAxis::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: transformations expected, @ argument 1: integer expected");
2895 miniScript->startErrorScript();
2896 }
2897 }
2898 };
2899 registerMethod(new ScriptMethodTransformationsGetRotationAxis(this));
2900 }
2901 {
2902 //
2903 class ScriptMethodTransformationsGetRotationAngle: public ScriptMethod {
2904 private:
2905 MiniScript* miniScript { nullptr };
2906 public:
2907 ScriptMethodTransformationsGetRotationAngle(MiniScript* miniScript):
2909 {
2910 {.type = ScriptVariableType::TYPE_TRANSFORMATIONS, .name = "transformations", .optional = false },
2911 {.type = ScriptVariableType::TYPE_INTEGER, .name = "idx", .optional = false },
2912 },
2913 ScriptVariableType::TYPE_FLOAT),
2914 miniScript(miniScript) {}
2915 const string getMethodName() override {
2916 return "transformations.getRotationAngle";
2917 }
2918 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
2919 int64_t idx;
2920 Transformations transformations;
2921 if (MiniScript::getTransformationsValue(argumentValues, 0, transformations, false) == true &&
2922 MiniScript::getIntegerValue(argumentValues, 1, idx, false) == true) {
2923 if (idx < transformations.getRotationCount()) {
2924 returnValue.setValue(transformations.getRotationAngle(idx));
2925 } else {
2926 Console::println("ScriptMethodTransformationsGetRotationAngle::executeMethod(): " + getMethodName() + "(): rotation index invalid: " + to_string(idx) + " / " + to_string(transformations.getRotationCount()));
2927 miniScript->startErrorScript();
2928 }
2929 } else {
2930 Console::println("ScriptMethodTransformationsGetRotationAngle::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: transformations expected, @ argument 1: integer expected");
2931 miniScript->startErrorScript();
2932 }
2933 }
2934 };
2935 registerMethod(new ScriptMethodTransformationsGetRotationAngle(this));
2936 }
2937 {
2938 //
2939 class ScriptMethodTransformationsSetRotationAngle: public ScriptMethod {
2940 private:
2941 MiniScript* miniScript { nullptr };
2942 public:
2943 ScriptMethodTransformationsSetRotationAngle(MiniScript* miniScript):
2945 {
2946 {.type = ScriptVariableType::TYPE_TRANSFORMATIONS, .name = "transformations", .optional = false },
2947 {.type = ScriptVariableType::TYPE_INTEGER, .name = "idx", .optional = false },
2948 {.type = ScriptVariableType::TYPE_FLOAT, .name = "angle", .optional = false },
2949 },
2950 ScriptVariableType::TYPE_TRANSFORMATIONS),
2951 miniScript(miniScript) {}
2952 const string getMethodName() override {
2953 return "transformations.setRotationAngle";
2954 }
2955 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
2956 int64_t idx;
2957 Transformations transformations;
2958 float angle;
2959 if (MiniScript::getTransformationsValue(argumentValues, 0, transformations, false) == true &&
2960 MiniScript::getIntegerValue(argumentValues, 1, idx, false) == true &&
2961 MiniScript::getFloatValue(argumentValues, 2, angle, false) == true) {
2962 if (idx < transformations.getRotationCount()) {
2963 transformations.setRotationAngle(idx, angle);
2964 transformations.update();
2965 returnValue.setValue(transformations);
2966 } else {
2967 Console::println("ScriptMethodTransformationsSetRotationAngle::executeMethod(): " + getMethodName() + "(): rotation index invalid: " + to_string(idx) + " / " + to_string(transformations.getRotationCount()));
2968 miniScript->startErrorScript();
2969 }
2970 } else {
2971 Console::println("ScriptMethodTransformationsSetRotationAngle::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: transformations expected, @ argument 1: integer expected, @ argument 2: float expected");
2972 miniScript->startErrorScript();
2973 }
2974 }
2975 };
2976 registerMethod(new ScriptMethodTransformationsSetRotationAngle(this));
2977 }
2978 {
2979 //
2980 class ScriptMethodTransformationsMultiply: public ScriptMethod {
2981 private:
2982 MiniScript* miniScript { nullptr };
2983 public:
2984 ScriptMethodTransformationsMultiply(MiniScript* miniScript):
2986 {
2987 {.type = ScriptVariableType::TYPE_TRANSFORMATIONS, .name = "transformations", .optional = false },
2988 {.type = ScriptVariableType::TYPE_VECTOR3, .name = "vec3", .optional = false },
2989 },
2990 ScriptVariableType::TYPE_VECTOR3),
2991 miniScript(miniScript) {}
2992 const string getMethodName() override {
2993 return "transformations.multiply";
2994 }
2995 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
2996 Transformations transformations;
2997 Vector3 vec3;
2998 if (MiniScript::getTransformationsValue(argumentValues, 0, transformations, false) == true &&
2999 MiniScript::getVector3Value(argumentValues, 1, vec3, false) == true) {
3000 returnValue.setValue(transformations.getTransformationsMatrix() * vec3);
3001 } else {
3002 Console::println("ScriptMethodTransformationsSetScale::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: transformations expected, @ argument 1: vector3 expected");
3003 miniScript->startErrorScript();
3004 }
3005 }
3006 };
3007 registerMethod(new ScriptMethodTransformationsMultiply(this));
3008 }
3009 {
3010 //
3011 class ScriptMethodTransformationsRotate: public ScriptMethod {
3012 private:
3013 MiniScript* miniScript { nullptr };
3014 public:
3015 ScriptMethodTransformationsRotate(MiniScript* miniScript):
3017 {
3018 {.type = ScriptVariableType::TYPE_TRANSFORMATIONS, .name = "transformations", .optional = false },
3019 {.type = ScriptVariableType::TYPE_VECTOR3, .name = "vec3", .optional = false },
3020 },
3021 ScriptVariableType::TYPE_VECTOR3),
3022 miniScript(miniScript) {}
3023 const string getMethodName() override {
3024 return "transformations.rotate";
3025 }
3026 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
3027 Transformations transformations;
3028 Vector3 vec3;
3029 if (MiniScript::getTransformationsValue(argumentValues, 0, transformations, false) == true &&
3030 MiniScript::getVector3Value(argumentValues, 1, vec3, false) == true) {
3031 returnValue.setValue(transformations.getRotationsQuaternion() * vec3);
3032 } else {
3033 Console::println("ScriptMethodTransformationsSetScale::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: transformations expected, @ argument 1: vector3 expected");
3034 miniScript->startErrorScript();
3035 }
3036 }
3037 };
3038 registerMethod(new ScriptMethodTransformationsRotate(this));
3039 }
3040 // bool methods
3041 {
3042 //
3043 class ScriptMethodBool: public ScriptMethod {
3044 private:
3045 MiniScript* miniScript { nullptr };
3046 public:
3047 ScriptMethodBool(MiniScript* miniScript):
3049 {
3050 {.type = ScriptVariableType::TYPE_BOOLEAN, .name = "bool", .optional = false }
3051 },
3052 ScriptVariableType::TYPE_BOOLEAN
3053 ),
3054 miniScript(miniScript) {}
3055 const string getMethodName() override {
3056 return "bool";
3057 }
3058 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
3059 bool boolValue;
3060 if (MiniScript::getBooleanValue(argumentValues, 0, boolValue, false) == true) {
3061 returnValue.setValue(boolValue);
3062 } else {
3063 Console::println("ScriptMethodBool::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: boolean expected");
3064 miniScript->startErrorScript();
3065 return;
3066 }
3067 }
3068 };
3069 registerMethod(new ScriptMethodBool(this));
3070 }
3071 {
3072 //
3073 class ScriptMethodNot: public ScriptMethod {
3074 private:
3075 MiniScript* miniScript { nullptr };
3076 public:
3077 ScriptMethodNot(MiniScript* miniScript):
3079 {
3080 {.type = ScriptVariableType::TYPE_BOOLEAN, .name = "bool", .optional = false }
3081 },
3082 ScriptVariableType::TYPE_BOOLEAN), miniScript(miniScript) {}
3083 const string getMethodName() override {
3084 return "not";
3085 }
3086 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
3087 bool booleanValue = false;
3088 if (MiniScript::getBooleanValue(argumentValues, 0, booleanValue, false) == true) {
3089 returnValue.setValue(!booleanValue);
3090 } else {
3091 Console::println("ScriptMethodNot::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: boolean expected");
3092 miniScript->startErrorScript();
3093 return;
3094 }
3095 }
3096 ScriptOperator getOperator() override {
3097 return OPERATOR_NOT;
3098 }
3099 };
3100 registerMethod(new ScriptMethodNot(this));
3101 }
3102 {
3103 //
3104 class ScriptMethodAnd: public ScriptMethod {
3105 private:
3106 MiniScript* miniScript { nullptr };
3107 public:
3108 ScriptMethodAnd(MiniScript* miniScript): ScriptMethod({}, ScriptVariableType::TYPE_BOOLEAN), miniScript(miniScript) {}
3109 const string getMethodName() override {
3110 return "and";
3111 }
3112 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
3113 returnValue.setValue(true);
3114 for (auto i = 0; i < argumentValues.size(); i++) {
3115 bool booleanValue;
3116 if (MiniScript::getBooleanValue(argumentValues, i, booleanValue, false) == false) {
3117 Console::println("ScriptMethodAnd::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument " + to_string(i) + ": boolean expected");
3118 miniScript->startErrorScript();
3119 } else
3120 if (booleanValue == false) {
3121 returnValue.setValue(false);
3122 break;
3123 }
3124 }
3125 }
3126 bool isVariadic() override {
3127 return true;
3128 }
3129 ScriptOperator getOperator() override {
3130 return OPERATOR_AND;
3131 }
3132 };
3133 registerMethod(new ScriptMethodAnd(this));
3134 }
3135 {
3136 //
3137 class ScriptMethodOr: public ScriptMethod {
3138 private:
3139 MiniScript* miniScript { nullptr };
3140 public:
3141 ScriptMethodOr(MiniScript* miniScript): ScriptMethod({}, ScriptVariableType::TYPE_BOOLEAN), miniScript(miniScript) {}
3142 const string getMethodName() override {
3143 return "or";
3144 }
3145 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
3146 returnValue.setValue(false);
3147 for (auto i = 0; i < argumentValues.size(); i++) {
3148 bool booleanValue;
3149 if (MiniScript::getBooleanValue(argumentValues, i, booleanValue, false) == false) {
3150 Console::println("ScriptMethodOr::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument " + to_string(i) + ": boolean expected");
3151 miniScript->startErrorScript();
3152 } else
3153 if (booleanValue == true) {
3154 returnValue.setValue(true);
3155 break;
3156 }
3157 }
3158 }
3159 bool isVariadic() override {
3160 return true;
3161 }
3162 ScriptOperator getOperator() override {
3163 return OPERATOR_OR;
3164 }
3165 };
3166 registerMethod(new ScriptMethodOr(this));
3167 }
3168 // string functions
3169 {
3170 //
3171 class ScriptMethodString: public ScriptMethod {
3172 private:
3173 MiniScript* miniScript { nullptr };
3174 public:
3175 ScriptMethodString(MiniScript* miniScript):
3177 {
3178 {.type = ScriptVariableType::TYPE_STRING, .name = "string", .optional = false }
3179 },
3180 ScriptVariableType::TYPE_STRING
3181 ),
3182 miniScript(miniScript) {}
3183 const string getMethodName() override {
3184 return "string";
3185 }
3186 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
3187 string stringValue;
3188 if (MiniScript::getStringValue(argumentValues, 0, stringValue, false) == true) {
3189 returnValue.setValue(stringValue);
3190 } else {
3191 Console::println("ScriptMethodString::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: string expected");
3192 miniScript->startErrorScript();
3193 }
3194 }
3195 };
3196 registerMethod(new ScriptMethodString(this));
3197 }
3198 {
3199 //
3200 class ScriptMethodSpace: public ScriptMethod {
3201 private:
3202 MiniScript* miniScript { nullptr };
3203 public:
3204 ScriptMethodSpace(MiniScript* miniScript):
3206 {
3207 {.type = ScriptVariableType::TYPE_INTEGER, .name = "spaces", .optional = true }
3208 },
3209 ScriptVariableType::TYPE_STRING
3210 ),
3211 miniScript(miniScript) {}
3212 const string getMethodName() override {
3213 return "space";
3214 }
3215 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
3216 int64_t spaces = 1;
3217 if (MiniScript::getIntegerValue(argumentValues, 0, spaces, true) == false) {
3218 Console::println("ScriptMethodSpace::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: integer expected");
3219 miniScript->startErrorScript();
3220 } else {
3221 string spacesString;
3222 for (auto i = 0; i < spaces; i++) spacesString+= " ";
3223 returnValue.setValue(spacesString);
3224 }
3225 }
3226 };
3227 registerMethod(new ScriptMethodSpace(this));
3228 }
3229 {
3230 //
3231 class ScriptMethodConcatenate: public ScriptMethod {
3232 private:
3233 MiniScript* miniScript { nullptr };
3234 public:
3235 ScriptMethodConcatenate(MiniScript* miniScript): ScriptMethod({}, ScriptVariableType::TYPE_STRING), miniScript(miniScript) {}
3236 const string getMethodName() override {
3237 return "concatenate";
3238 }
3239 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
3240 string result;
3241 for (auto& argumentValue: argumentValues) {
3242 result+= argumentValue.getValueString();
3243 }
3244 returnValue.setValue(result);
3245 }
3246 bool isVariadic() override {
3247 return true;
3248 }
3249 };
3250 registerMethod(new ScriptMethodConcatenate(this));
3251 }
3252 {
3253 //
3254 class ScriptMethodToUpperCase: public ScriptMethod {
3255 private:
3256 MiniScript* miniScript { nullptr };
3257 public:
3258 ScriptMethodToUpperCase(MiniScript* miniScript):
3260 {
3261 {.type = ScriptVariableType::TYPE_STRING, .name = "string", .optional = false }
3262 },
3263 ScriptVariableType::TYPE_STRING
3264 ),
3265 miniScript(miniScript) {}
3266 const string getMethodName() override {
3267 return "toUpperCase";
3268 }
3269 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
3270 string stringValue;
3271 if (MiniScript::getStringValue(argumentValues, 0, stringValue, false) == true) {
3272 returnValue.setValue(StringTools::toUpperCase(stringValue));
3273 } else {
3274 Console::println("ScriptMethodToUpperCase::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: string expected");
3275 miniScript->startErrorScript();
3276 }
3277 }
3278 };
3279 registerMethod(new ScriptMethodToUpperCase(this));
3280 }
3281 {
3282 //
3283 class ScriptMethodToLowerCase: public ScriptMethod {
3284 private:
3285 MiniScript* miniScript { nullptr };
3286 public:
3287 ScriptMethodToLowerCase(MiniScript* miniScript):
3289 {
3290 {.type = ScriptVariableType::TYPE_STRING, .name = "string", .optional = false }
3291 },
3292 ScriptVariableType::TYPE_STRING
3293 ),
3294 miniScript(miniScript) {}
3295 const string getMethodName() override {
3296 return "toLowerCase";
3297 }
3298 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
3299 string stringValue;
3300 if (MiniScript::getStringValue(argumentValues, 0, stringValue, false) == true) {
3301 returnValue.setValue(StringTools::toLowerCase(stringValue));
3302 } else {
3303 Console::println("ScriptMethodToLowerCase::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: string expected");
3304 miniScript->startErrorScript();
3305 }
3306 }
3307 };
3308 registerMethod(new ScriptMethodToLowerCase(this));
3309 }
3310 {
3311 //
3312 class ScriptMethodGetVariable: public ScriptMethod {
3313 private:
3314 MiniScript* miniScript { nullptr };
3315 public:
3316 ScriptMethodGetVariable(MiniScript* miniScript):
3318 {
3319 {.type = ScriptVariableType::TYPE_STRING, .name = "variable", .optional = false }
3320 },
3321 ScriptVariableType::TYPE_VOID
3322 ),
3323 miniScript(miniScript) {}
3324 const string getMethodName() override {
3325 return "getVariable";
3326 }
3327 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
3328 string variable;
3329 if (MiniScript::getStringValue(argumentValues, 0, variable, false) == true) {
3330 returnValue = miniScript->getVariable(variable);
3331 } else {
3332 Console::println("ScriptMethodGetVariable::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: string expected");
3333 miniScript->startErrorScript();
3334 }
3335 }
3336 bool isMixedReturnValue() override {
3337 return true;
3338 }
3339
3340 };
3341 registerMethod(new ScriptMethodGetVariable(this));
3342 }
3343 // set
3344 {
3345 //
3346 class ScriptMethodSetVariable: public ScriptMethod {
3347 private:
3348 MiniScript* miniScript { nullptr };
3349 public:
3350 ScriptMethodSetVariable(MiniScript* miniScript): ScriptMethod({}, ScriptVariableType::TYPE_VOID), miniScript(miniScript) {}
3351 const string getMethodName() override {
3352 return "setVariable";
3353 }
3354 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
3355 string variable;
3356 if (argumentValues.size() != 2 ||
3357 MiniScript::getStringValue(argumentValues, 0, variable, false) == false) {
3358 Console::println("ScriptMethodSetVariable::executeMethod(): " + getMethodName() + "(): parameter type mismatch @ argument 0: string expected, @ argument 1: mixed expected");
3359 miniScript->startErrorScript();
3360 } else {
3361 miniScript->setVariable(variable, argumentValues[1]);
3362 returnValue = argumentValues[1];
3363 }
3364 }
3365 bool isVariadic() override {
3366 return true;
3367 }
3368 bool isMixedReturnValue() override {
3369 return true;
3370 }
3371 ScriptOperator getOperator() override {
3372 return OPERATOR_SET;
3373 }
3374 };
3375 registerMethod(new ScriptMethodSetVariable(this));
3376 }
3377 // time
3378 {
3379 //
3380 class ScriptMethodGetCurrentMillis: public ScriptMethod {
3381 private:
3382 MiniScript* miniScript { nullptr };
3383 public:
3384 ScriptMethodGetCurrentMillis(MiniScript* miniScript):
3385 ScriptMethod({}, ScriptVariableType::TYPE_INTEGER),
3386 miniScript(miniScript) {}
3387 const string getMethodName() override {
3388 return "time.getCurrentMillis";
3389 }
3390 void executeMethod(const vector<ScriptVariable>& argumentValues, ScriptVariable& returnValue, const ScriptStatement& statement) override {
3391 returnValue.setValue(Time::getCurrentMillis());
3392 }
3393 };
3394 registerMethod(new ScriptMethodGetCurrentMillis(this));
3395 }
3396 // determine operators
3397 for (auto& methodIt: scriptMethods) {
3398 auto method = methodIt.second;
3399 auto methodOperator = method->getOperator();
3400 if (methodOperator != OPERATOR_NONE) {
3401 auto methodOperatorString = getOperatorAsString(methodOperator);
3402 auto scriptOperatorIt = scriptOperators.find(static_cast<uint8_t>(methodOperator));
3403 if (scriptOperatorIt != scriptOperators.end()) {
3404 Console::println("MiniScript::registerMethods(): operator '" + methodOperatorString + "' already registered for method '" + method->getMethodName() + "'");
3405 continue;
3406 }
3407 scriptOperators[static_cast<uint8_t>(methodOperator)] = method;
3408 }
3409 }
3410}
3411
3413}
3414
3415bool MiniScript::transpileScriptStatement(string& generatedCode, const string_view& method, const vector<string_view>& arguments, const ScriptStatement& statement, int scriptIdx, int& statementIdx, const unordered_map<string, vector<string>>& methodCodeMap, bool& scriptStateChanged, bool& scriptStopped, vector<string>& enabledNamedConditions, int depth, int argumentIdx, int parentArgumentIdx, const string& returnValue, const string& injectCode, int additionalIndent) {
3416 //
3417 statementIdx++;
3418 auto currentStatementIdx = statementIdx;
3419
3420 // find method code in method code map
3421 auto methodCodeMapIt = methodCodeMap.find(string(method));
3422 if (methodCodeMapIt == methodCodeMap.end()) {
3423 Console::println("MiniScript::transpileScriptStatement(): method '" + string(method) + "' not found!");
3424 return false;
3425 }
3426 auto& methodCode = methodCodeMapIt->second;
3427
3428 // indenting
3429 string minIndentString = "\t";
3430 string depthIndentString;
3431 for (auto i = 0; i < depth + additionalIndent; i++) depthIndentString+= "\t";
3432
3433 // comment about current statement
3434 generatedCode+= minIndentString + depthIndentString;
3435 generatedCode+= "// " + (depth > 0?"depth = " + to_string(depth):"") + (depth > 0 && argumentIdx != -1?" / ":"") + (argumentIdx != -1?"argument index = " + to_string(argumentIdx):"") + (depth > 0 || argumentIdx != -1?": ":"");
3436 generatedCode+= string(method) + "(";
3437 auto subArgumentIdx = 0;
3438 for (auto& argument: arguments) {
3439 if (subArgumentIdx > 0) generatedCode+= ", ";
3440 generatedCode+= string(argument);
3441 subArgumentIdx++;
3442 }
3443 generatedCode+= ")";
3444 generatedCode+= "\n";
3445
3446 // construct argument values
3447 vector<string> argumentValuesCode;
3448 if (depth > 0) {
3449 argumentValuesCode.push_back("ScriptVariable& returnValue = argumentValuesD" + to_string(depth - 1) + (parentArgumentIdx != -1?"AIDX" + to_string(parentArgumentIdx):"") + "[" + to_string(argumentIdx) + "];");
3450 } else {
3451 argumentValuesCode.push_back("ScriptVariable returnValue;");
3452 }
3453 argumentValuesCode.push_back("array<ScriptVariable, " + to_string(arguments.size()) + "> argumentValues;");
3454 argumentValuesCode.push_back("array<ScriptVariable, " + to_string(arguments.size()) + ">& argumentValuesD" + to_string(depth) + (argumentIdx != -1?"AIDX" + to_string(argumentIdx):"") + " = argumentValues;");
3455
3456 // argument values header
3457 generatedCode+= minIndentString + depthIndentString + "{" + "\n";
3458 // statement
3459 if (depth == 0) {
3460 if (scriptIdx == -1) {
3461 generatedCode+= minIndentString + depthIndentString + "\t" + "const ScriptStatement statement = {" + "\n";
3462 generatedCode+= minIndentString + depthIndentString + "\t\t" + ".line = " + to_string(statement.line) + "," + "\n";
3463 generatedCode+= minIndentString + depthIndentString + "\t\t" + ".statementIdx = " + to_string(statement.statementIdx) + "," + "\n";
3464 generatedCode+= minIndentString + depthIndentString + "\t\t" + ".statement = \"<unavailable>\"," + "\n";
3465 generatedCode+= minIndentString + depthIndentString + "\t\t" + ".gotoStatementIdx = " + to_string(statement.gotoStatementIdx) + "\n";
3466 generatedCode+= minIndentString + depthIndentString + "\t};" + "\n";
3467 } else {
3468 generatedCode+= minIndentString + depthIndentString + "\t" + "const ScriptStatement& statement = scripts[" + to_string(scriptIdx) + "].statements[" + to_string(statement.statementIdx) + "];" + "\n";
3469 }
3470 // TODO: this next one
3471 generatedCode+= minIndentString + depthIndentString + "\t" + "miniScript->scriptState.statementIdx = statement.statementIdx;" + "\n";
3472 }
3473 generatedCode+= minIndentString + depthIndentString + "\t" + "// required method code arguments" + "\n";
3474 // argument/return values
3475 for (auto& codeLine: argumentValuesCode) {
3476 generatedCode+= minIndentString + depthIndentString + "\t" + codeLine + "\n";
3477 }
3478 argumentValuesCode.clear();
3479
3480 // check arguments that require a method call
3481 {
3482 auto subArgumentIdx = 0;
3483 for (auto& argument: arguments) {
3484 if (argument.empty() == false &&
3485 StringTools::viewStartsWith(argument, "\"") == false &&
3486 StringTools::viewEndsWith(argument, "\"") == false &&
3487 argument.find('(') != string::npos &&
3488 argument.find(')') != string::npos) {
3489 argumentValuesCode.push_back("// argumentValues[" + to_string(subArgumentIdx) + "] --> returnValue of " + string(argument));
3490 } else
3491 // variable
3492 if (StringTools::viewStartsWith(argument, "$") == true) {
3493 // method call, call method and put its return value into argument value
3494 string generatedStatement = "getVariable(\"" + string(argument) + "\")";
3495 argumentValuesCode.push_back("// argumentValues[" + to_string(subArgumentIdx) + "] --> returnValue of " + string(generatedStatement));
3496 } else {
3497 // literal
3498 ScriptVariable argumentValue;
3499 if (StringTools::viewStartsWith(argument, "\"") == true &&
3500 StringTools::viewEndsWith(argument, "\"") == true) {
3501 argumentValuesCode.push_back("argumentValues[" + to_string(subArgumentIdx) + "].setValue(string(\"" + string(StringTools::viewSubstring(argument, 1, argument.size() - 1)) + "\"));");
3502 } else {
3503 MiniScript::ScriptVariable argumentValue;
3504 argumentValue.setImplicitTypedValueFromStringView(argument);
3505 switch (argumentValue.getType()) {
3506 case TYPE_VOID:
3507 break;
3508 case TYPE_BOOLEAN:
3509 {
3510 bool value;
3511 argumentValue.getBooleanValue(value);
3512 argumentValuesCode.push_back(string() + "argumentValues[" + to_string(subArgumentIdx) + "].setValue(" + (value == true?"true":"false") + ");");
3513 }
3514 break;
3515 case TYPE_INTEGER:
3516 {
3517 int64_t value;
3518 argumentValue.getIntegerValue(value);
3519 argumentValuesCode.push_back("argumentValues[" + to_string(subArgumentIdx) + "].setValue(static_cast<int64_t>(" + to_string(value) + "));");
3520 }
3521 break;
3522 case TYPE_FLOAT:
3523 {
3524 float value;
3525 argumentValue.getFloatValue(value);
3526 argumentValuesCode.push_back("argumentValues[" + to_string(subArgumentIdx) + "].setValue(" + to_string(value) + "f);");
3527 }
3528 break;
3529 case TYPE_STRING:
3530 {
3531 string value;
3532 argumentValue.getStringValue(value);
3533 argumentValuesCode.push_back("argumentValues[" + to_string(subArgumentIdx) + "].setValue(string(\"" + value + "\"));");
3534 }
3535 break;
3536 default:
3537 {
3538 Console::println("MiniScript::transpileScriptStatement(): '" + scriptFileName + "': @" + to_string(statement.line) + ": '" + statement.statement + "': '" + string(argument) + "': unknown argument type: " + argumentValue.getTypeAsString());
3539 break;
3540 }
3541 }
3542 }
3543 }
3544 subArgumentIdx++;
3545 }
3546 }
3547
3548 // argument values header
3549 for (auto& codeLine: argumentValuesCode) {
3550 generatedCode+= minIndentString + depthIndentString + "\t" + codeLine + "\n";
3551 }
3552 argumentValuesCode.clear();
3553
3554 // enabled named conditions
3555 if (method == "script.enableNamedCondition" && arguments.size()) {
3556 if (arguments.size() != 1) {
3557 Console::println("MiniScript::transpileScriptStatement(): '" + scriptFileName + "': @" + to_string(statement.line) + ": '" + statement.statement + "': script.enableNamedCondition(): expected string argument @ 0");
3558 } else {
3559 string name = string(arguments[0]);
3560 enabledNamedConditions.erase(
3561 remove(
3562 enabledNamedConditions.begin(),
3563 enabledNamedConditions.end(),
3564 name
3565 ),
3566 enabledNamedConditions.end()
3567 );
3568 enabledNamedConditions.push_back(name);
3569 }
3570 } else
3571 if (method == "script.disableNamedCondition") {
3572 if (arguments.size() != 1) {
3573 Console::println("MiniScript::transpileScriptStatement(): '" + scriptFileName + "': @" + to_string(statement.line) + ": '" + statement.statement + "': script.disableNamedCondition(): expected string argument @ 0");
3574 } else {
3575 string name = string(arguments[0]);
3576 enabledNamedConditions.erase(
3577 remove(
3578 enabledNamedConditions.begin(),
3579 enabledNamedConditions.end(),
3580 name
3581 ),
3582 enabledNamedConditions.end()
3583 );
3584 }
3585 }
3586
3587 // check arguments that require a method call
3588 {
3589 auto subArgumentIdx = 0;
3590 for (auto& argument: arguments) {
3591 if (argument.empty() == false &&
3592 StringTools::viewStartsWith(argument, "\"") == false &&
3593 StringTools::viewEndsWith(argument, "\"") == false &&
3594 argument.find('(') != string::npos &&
3595 argument.find(')') != string::npos) {
3596 // method call, call method and put its return value into argument value
3597 string_view subMethod;
3598 vector<string_view> subArguments;
3599 if (parseScriptStatement(argument, subMethod, subArguments) == true) {
3600 if (transpileScriptStatement(generatedCode, subMethod, subArguments, statement, scriptIdx, statementIdx, methodCodeMap, scriptStateChanged, scriptStopped, enabledNamedConditions, depth + 1, subArgumentIdx, argumentIdx, returnValue) == false) {
3601 Console::println("MiniScript::transpileScriptStatement(): transpileScriptStatement(): '" + scriptFileName + "': @" + to_string(statement.line) + ": '" + statement.statement + "': '" + string(argument) + "': parse error");
3602 }
3603 } else {
3604 Console::println("MiniScript::transpileScriptStatement(): parseScriptStatement(): '" + scriptFileName + "': @" + to_string(statement.line) + ": '" + statement.statement + "': '" + string(argument) + "': parse error");
3606 }
3607 } else
3608 // variable
3609 if (StringTools::viewStartsWith(argument, "$") == true) {
3610 // method call, call method and put its return value into argument value
3611 string generatedStatement = "getVariable(\"" + string(argument) + "\")";
3612 string_view subMethod;
3613 vector<string_view> subArguments;
3614 if (parseScriptStatement(generatedStatement, subMethod, subArguments) == true) {
3615 if (transpileScriptStatement(generatedCode, subMethod, subArguments, statement, scriptIdx, statementIdx, methodCodeMap, scriptStateChanged, scriptStopped, enabledNamedConditions, depth + 1, subArgumentIdx, argumentIdx, returnValue) == false) {
3616 Console::println("MiniScript::transpileScriptStatement(): transpileScriptStatement(): '" + scriptFileName + "': @" + to_string(statement.line) + ": '" + statement.statement + "': '" + string(argument) + "' --> '" + generatedStatement + "': parse error");
3617 }
3618 } else {
3619 Console::println("MiniScript::transpileScriptStatement(): parseScriptStatement(): '" + scriptFileName + "': @" + to_string(statement.line) + ": '" + statement.statement + "': '" + string(argument) + "' --> '" + generatedStatement + "': parse error");
3621 }
3622 } else {
3623 // no op
3624 }
3625 subArgumentIdx++;
3626 }
3627 }
3628
3629 // generate code
3630 generatedCode+= minIndentString + depthIndentString + "\t" + "// method code: " + string(method) + "\n";
3631 for (auto codeLine: methodCode) {
3632 codeLine = StringTools::replace(codeLine, "getMethodName()", "string(\"" + string(method) + "\")");
3633 // replace returns with gotos
3634 if (StringTools::regexMatch(codeLine, "[\\ \\t]*return[\\ \\t]*;.*") == true) {
3635 Console::println("MiniScript::transpileScriptStatement(): method '" + string(method) + "': return statement not supported!");
3636 return false;
3637 } else
3638 if (StringTools::regexMatch(codeLine, "[\\ \\t]*miniScript[\\ \\t]*->gotoStatementGoto[\\ \\t]*\\([\\ \\t]*statement[\\ \\t]*\\)[\\ \\t]*;[\\ \\t]*") == true) {
3639 if (statement.gotoStatementIdx != -1) {
3640 // find indent
3641 int indent = 0;
3642 for (auto i = 0; i < codeLine.size(); i++) {
3643 auto c = codeLine[i];
3644 if (c == '\t') {
3645 indent++;
3646 } else {
3647 break;
3648 }
3649 }
3650 string indentString;
3651 for (auto i = 0; i < indent; i++) indentString+= "\t";
3652 generatedCode+= minIndentString + indentString + depthIndentString + "\t" + "goto miniscript_statement_" + to_string(statement.gotoStatementIdx) + ";\n";
3653 }
3654 } else
3655 if (StringTools::regexMatch(codeLine, "[\\ \\t]*miniScript[\\ \\t]*->startErrorScript[\\ \\t]*\\([\\ \\t]*\\)[\\ \\t]*;[\\ \\t]*") == true) {
3656 generatedCode+= minIndentString + depthIndentString + "\t" + codeLine + " return" + (returnValue.empty() == false?" " + returnValue:"") + ";\n";
3657 } else
3658 if (StringTools::regexMatch(codeLine, "[\\ \\t]*miniScript[\\ \\t]*->emit[\\ \\t]*\\([\\ \\t]*[a-zA-Z0-9]*[\\ \\t]*\\)[\\ \\t]*;[\\ \\t]*") == true) {
3659 generatedCode+= minIndentString + depthIndentString + "\t" + codeLine + " return" + (returnValue.empty() == false?" " + returnValue:"") + ";\n";
3660 } else {
3661 if (StringTools::regexMatch(codeLine, ".*[\\ \\t]*miniScript[\\ \\t]*->[\\ \\t]*setScriptState[\\ \\t]*\\([\\ \\t]*.+[\\ \\t]*\\);.*") == true) {
3662 scriptStateChanged = true;
3663 }
3664 if (StringTools::regexMatch(codeLine, ".*[\\ \\t]*miniScript[\\ \\t]*->[\\ \\t]*stopScriptExecutation[\\ \\t]*\\([\\ \\t]*\\);.*") == true) {
3665 scriptStopped = true;
3666 }
3667 generatedCode+= minIndentString + depthIndentString + "\t" + codeLine + "\n";
3668 }
3669 }
3670
3671 // inject code if we have any to inject
3672 if (injectCode.empty() == false) {
3673 generatedCode+= minIndentString + depthIndentString + "\t" + injectCode + "\n";
3674 }
3675
3676 //
3677 generatedCode+= minIndentString + depthIndentString + "}" + "\n";
3678
3679 //
3680 return true;
3681}
3682
3683bool MiniScript::transpile(string& generatedCode, int scriptIdx, const unordered_map<string, vector<string>>& methodCodeMap) {
3684 if (scriptIdx < 0 || scriptIdx >= scripts.size()) {
3685 Console::println("MiniScript::transpile(): invalid script index");
3686 return false;
3687 }
3688
3689 //
3690 auto& script = scripts[scriptIdx];
3691
3692 //
3693 Console::println("MiniScript::transpile(): transpiling code for condition = '" + script.condition + "', with name '" + script.name + "'");
3694 auto statementIdx = 0;
3695 string methodIndent = "\t";
3696 string generatedCodeHeader;
3697
3698 // TODO: move me into a method
3699 generatedCodeHeader+= methodIndent + "// -1 means complete method call" + "\n";
3700 generatedCodeHeader+= methodIndent + "if (miniScriptGotoStatementIdx == -1) {" + "\n";
3701 generatedCodeHeader+= methodIndent + "\t" + "resetScriptExecutationState(" + to_string(scriptIdx) + ", STATE_NEXT_STATEMENT);" + "\n";
3702 generatedCodeHeader+= methodIndent + "}" + "\n";
3703 // TODO: end
3704 generatedCodeHeader+= methodIndent + "auto miniScript = this;" + "\n";
3705 generatedCodeHeader+= methodIndent + "miniScript->scriptState.scriptIdx = " + to_string(scriptIdx) + ";" + "\n";
3706
3707 // method name
3708 string methodName =
3709 (script.conditionType == MiniScript::Script::CONDITIONTYPE_ON?"on_":"on_enabled_") +
3710 (script.name.empty() == false?script.name:(
3711 StringTools::regexMatch(script.condition, "[a-zA-Z0-9]+") == true?
3712 script.condition:
3713 to_string(scriptIdx)
3714 )
3715 );
3716
3717 //
3718 unordered_set<int> gotoStatementIdxSet;
3719 for (auto scriptStatement: script.statements) gotoStatementIdxSet.insert(scriptStatement.gotoStatementIdx);
3720
3721 //
3722 vector<string> enabledNamedConditions;
3723 auto scriptStateChanged = false;
3724 for (auto scriptStatement: script.statements) {
3725 //
3726 string_view method;
3727 vector<string_view> arguments;
3728 if (parseScriptStatement(scriptStatement.statement, method, arguments) == false) {
3729 Console::println("MiniScript::transpileScriptStatement(): '" + scriptFileName + "': " + scriptStatement.statement + "@" + to_string(scriptStatement.line) + ": failed to parse statement");
3730 return false;
3731 }
3732 //
3733 if (scriptStateChanged == true) {
3734 generatedCodeHeader+= methodIndent + "if (miniScriptGotoStatementIdx == " + to_string(scriptStatement.statementIdx) + ") goto miniscript_statement_" + to_string(scriptStatement.statementIdx) + "; else" + "\n";
3735 }
3736
3737 // enabled named conditions
3738 if (enabledNamedConditions.empty() == false) {
3739 generatedCode+= "\n";
3740 generatedCode+= methodIndent + "// enabled named conditions" + "\n";
3741 generatedCode+= methodIndent + "{" + "\n";
3742 generatedCode+= methodIndent + "\t" + "auto scriptIdxToStart = determineNamedScriptIdxToStart();" + "\n";
3743 generatedCode+= methodIndent + "\t" + "if (scriptIdxToStart != -1 && scriptIdxToStart != scriptState.scriptIdx) {" + "\n";
3744 generatedCode+= methodIndent + "\t" + "resetScriptExecutationState(scriptIdxToStart, STATE_NEXT_STATEMENT);" + "\n";
3745 generatedCode+= methodIndent + "\t" + "scriptState.timeEnabledConditionsCheckLast = Time::getCurrentMillis();" + "\n";
3746 generatedCode+= methodIndent + "\t" + "return;" + "\n";
3747 generatedCode+= methodIndent + "\t" + "}" + "\n";
3748 generatedCode+= methodIndent + "}" + "\n";
3749 }
3750
3751 // statement_xyz goto label
3752 generatedCode+= "\n";
3753 generatedCode+= methodIndent + "// Statement: " + to_string(scriptStatement.statementIdx) + "\n";
3754 if (scriptStateChanged == true || gotoStatementIdxSet.find(scriptStatement.statementIdx) != gotoStatementIdxSet.end()) {
3755 generatedCode+= methodIndent + "miniscript_statement_" + to_string(scriptStatement.statementIdx) + ":" + "\n";
3756 }
3757 scriptStateChanged = false;
3758 auto scriptStopped = false;
3759 transpileScriptStatement(generatedCode, method, arguments, scriptStatement, scriptIdx, statementIdx, methodCodeMap, scriptStateChanged, scriptStopped, enabledNamedConditions);
3760 if (scriptStopped == true) {
3761 generatedCode+= methodIndent + "if (scriptState.running == false) {" + "\n";
3762 generatedCode+= methodIndent + "\t" + "return;" + "\n";
3763 generatedCode+= methodIndent + "}" + "\n";
3764 }
3765 if (scriptStateChanged == true) {
3766 generatedCode+= methodIndent + "if (scriptState.state.state != STATE_NEXT_STATEMENT) {" + "\n";
3767 generatedCode+= methodIndent + "\t" + "miniScript->scriptState.statementIdx++;" + "\n";
3768 generatedCode+= methodIndent + "\t" + "return;" + "\n";
3769 generatedCode+= methodIndent + "}" + "\n";
3770 }
3771 }
3772 generatedCode+= methodIndent + "scriptState.scriptIdx = -1;" + "\n";
3773 generatedCode+= methodIndent + "scriptState.statementIdx = -1;" + "\n";
3774 generatedCode+= methodIndent + "setScriptState(STATE_WAIT_FOR_CONDITION);" + "\n";
3775
3776 //
3777 generatedCodeHeader+= methodIndent + "if (miniScriptGotoStatementIdx != -1 && miniScriptGotoStatementIdx != 0) Console::println(\"MiniScript::" + methodName + "(): Can not go to statement \" + to_string(miniScriptGotoStatementIdx));" + "\n";
3778 //
3779 generatedCode = generatedCodeHeader + generatedCode;
3780 return true;
3781}
3782
3783bool MiniScript::transpileScriptCondition(string& generatedCode, int scriptIdx, const unordered_map<string, vector<string>>& methodCodeMap, const string& returnValue, const string& injectCode, int depth) {
3784 if (scriptIdx < 0 || scriptIdx >= scripts.size()) {
3785 Console::println("MiniScript::transpile(): invalid script index");
3786 return false;
3787 }
3788
3789 //
3790 Console::println("MiniScript::transpile(): transpiling code condition for condition = '" + scripts[scriptIdx].condition + "', with name '" + scripts[scriptIdx].name + "'");
3791 auto statementIdx = 0;
3792 string methodIndent = "\t";
3793
3794 const MiniScript::ScriptStatement scriptStatement = {
3795 .line = scripts[scriptIdx].line,
3796 .statementIdx = 0,
3797 .statement = scripts[scriptIdx].condition,
3798 .gotoStatementIdx = -1
3799 };
3800
3801 //
3802 string_view method;
3803 vector<string_view> arguments;
3804 if (parseScriptStatement(scriptStatement.statement, method, arguments) == false) {
3805 Console::println("MiniScript::transpileScriptStatement(): '" + scriptFileName + "': " + scriptStatement.statement + "@" + to_string(scriptStatement.line) + ": failed to parse statement");
3806 return false;
3807 }
3808
3809 //
3810 auto scriptStateChanged = false;
3811 auto scriptStopped = false;
3812 vector<string >enabledNamedConditions;
3813 transpileScriptStatement(generatedCode, method, arguments, scriptStatement, -1, statementIdx, methodCodeMap, scriptStateChanged, scriptStopped, enabledNamedConditions, 0, -1, -1, returnValue, injectCode, depth + 1);
3814
3815 //
3816 generatedCode+= methodIndent + "\n";
3817
3818 //
3819 return true;
3820}
Transformations which contain scale, rotations and translation.
const Matrix4x4 & getTransformationsMatrix() const
void setRotationAngle(const int idx, const float angle)
void setTranslation(const Vector3 &translation)
Set translation.
const Quaternion & getRotationsQuaternion() const
const Vector3 & getScale() const
void setScale(const Vector3 &scale)
Set scale.
const Vector3 & getRotationAxis(const int idx) const
const int getRotationCount() const
virtual void update()
Computes transformation matrix.
const Vector3 & getTranslation() const
void addRotation(const Vector3 &axis, const float angle)
Add rotation.
const float getRotationAngle(const int idx) const
Dynamic rigid/static rigid/collision body class.
Definition: Body.h:43
Dynamic physics world class.
Definition: World.h:38
Prototype definition.
Definition: Prototype.h:49
Scene entity definition.
Definition: SceneEntity.h:24
Standard math functions.
Definition: Math.h:21
3D vector 3 class
Definition: Vector3.h:22
float getY() const
Definition: Vector3.h:119
float getX() const
Definition: Vector3.h:103
float computeLength() const
Definition: Vector3.h:202
float getZ() const
Definition: Vector3.h:136
Vector3 & normalize()
Normalize the vector.
Definition: Vector3.h:288
float computeLengthSquared() const
Definition: Vector3.h:209
File system singleton class.
Definition: FileSystem.h:14
Character class.
Definition: Character.h:15
static bool isSpace(char character)
Returns if character is a white space.
Definition: Character.h:48
Console class.
Definition: Console.h:26
static void println()
Print new line to console.
Definition: Console.cpp:78
static void print(const string &str)
Print given string.
Definition: Console.cpp:69
virtual const string getMethodName()=0
virtual void execute()=0
Execute script state machine state.
static const string getTypeAsString(ScriptVariableType type)
Definition: MiniScript.h:469
bool getIntegerValue(int64_t &value, bool optional=false) const
Get integer value from given variable.
Definition: MiniScript.h:209
bool getStringValue(string &value, bool optional=false) const
Get string value from given variable.
Definition: MiniScript.h:282
ScriptVariableType getType() const
Definition: MiniScript.h:163
bool getFloatValue(float &value, bool optional=false) const
Get float value from given variable.
Definition: MiniScript.h:250
bool getBooleanValue(bool &value, bool optional=false) const
Get boolean value from given variable.
Definition: MiniScript.h:173
void setImplicitTypedValueFromStringView(const string_view &value)
Set implicit typed value given by value string.
Definition: MiniScript.h:444
void setValue(bool value)
Set boolean value from given value into variable.
Definition: MiniScript.h:364
vector< Script > scripts
Definition: MiniScript.h:708
bool transpile(string &generatedCode, int scriptIdx, const unordered_map< string, vector< string > > &methodCodeMap)
Transpile a script statement.
void resetScriptExecutationState(int scriptIdx, StateMachineState stateMachineState)
Reset script execution state.
Definition: MiniScript.h:773
static bool getBooleanValue(const vector< ScriptVariable > &arguments, int idx, bool &value, bool optional=false)
Get boolean value from given variable.
Definition: MiniScript.h:1054
static bool getVector3Value(const vector< ScriptVariable > &arguments, int idx, Vector3 &value, bool optional=false)
Get vector3 value from given variable.
Definition: MiniScript.h:1170
void loadScript(const string &pathName, const string &fileName)
Load script.
Definition: MiniScript.cpp:481
void registerStateMachineState(ScriptStateMachineState *state)
Register script state machine state.
Definition: MiniScript.cpp:75
static string getOperatorAsString(ScriptOperator scriptOperator)
Get operator as string.
Definition: MiniScript.h:1000
unordered_map< int, ScriptStateMachineState * > scriptStateMachineStates
Definition: MiniScript.h:829
virtual void startScript()
Start script.
Definition: MiniScript.cpp:743
const string findLeftArgument(const string statement, int position, int &length)
Find left argument in statement beginning from position.
Definition: MiniScript.cpp:974
static bool getFloatValue(const vector< ScriptVariable > &arguments, int idx, float &value, bool optional=false)
Get float value from given variable.
Definition: MiniScript.h:1112
unordered_map< string, ScriptMethod * > scriptMethods
Definition: MiniScript.h:828
ScriptVariable executeScriptStatement(const string_view &method, const vector< string_view > &arguments, const ScriptStatement &statement)
Execute a script statement.
Definition: MiniScript.cpp:254
virtual void execute()
Execute.
Definition: MiniScript.cpp:462
virtual void registerStateMachineStates()
Register state machine states.
virtual void initializeNative()
Initialize native mini script.
Definition: MiniScript.cpp:84
void setVariable(const string &name, const ScriptVariable &variable)
Set script variable.
Definition: MiniScript.h:1255
virtual void registerVariables()
Register variables.
static constexpr bool VERBOSE
Definition: MiniScript.h:822
bool transpileScriptStatement(string &generatedCode, const string_view &method, const vector< string_view > &arguments, const ScriptStatement &statement, int scriptIdx, int &statementIdx, const unordered_map< string, vector< string > > &methodCodeMap, bool &scriptStateChanged, bool &scriptStopped, vector< string > &enabledNamedConditions, int depth=0, int argumentIdx=-1, int parentArgumentIdx=-1, const string &returnValue=string(), const string &injectCode=string(), int additionalIndent=0)
Transpile script statement.
virtual void registerMethods()
Register methods.
virtual void emit(const string &condition)
Emit.
Definition: MiniScript.cpp:389
static bool getIntegerValue(const vector< ScriptVariable > &arguments, int idx, int64_t &value, bool optional=false)
Get integer value from given variable.
Definition: MiniScript.h:1083
static const bool isOperatorChar(char c)
Returns if char is operator char.
Definition: MiniScript.h:880
static bool hasType(const vector< ScriptVariable > &arguments, ScriptVariableType type)
Check if arguments contain argument with given type.
Definition: MiniScript.h:1029
virtual ~MiniScript()
Destructor.
Definition: MiniScript.cpp:69
bool getNextStatementOperator(const string &statement, ScriptStatementOperator &nextOperator)
Determine next not substituted operator in statement.
Definition: MiniScript.cpp:872
bool transpileScriptCondition(string &generatedCode, int scriptIdx, const unordered_map< string, vector< string > > &methodCodeMap, const string &returnValue, const string &injectCode, int depth=0)
Transpile a script condition.
void stopScriptExecutation()
Stop script execution.
Definition: MiniScript.h:789
void setNative(bool native)
Set native.
Definition: MiniScript.h:728
const string getInformation()
Get miniscript instance information.
const string getArgumentsAsString(const vector< string_view > &arguments)
Returns arguments as string placed in a vector of string_views.
Definition: MiniScript.h:846
const string doStatementPreProcessing(const string &statement)
Do statement pre processing, 1) replace operators with corresponding methods.
static bool getTransformationsValue(const vector< ScriptVariable > &arguments, int idx, Transformations &value, bool optional=false)
Get transformations value from given variable.
Definition: MiniScript.h:1199
const ScriptVariable getVariable(const string &name)
Returns variable with given name.
Definition: MiniScript.h:1237
void startErrorScript()
Start error script.
Definition: MiniScript.h:976
const string trimArgument(const string &argument)
Trim argument and remove unnessessary parenthesis.
Definition: MiniScript.cpp:929
void executeStateMachine()
Execute state machine.
Definition: MiniScript.cpp:416
void registerMethod(ScriptMethod *method)
Register script method.
Definition: MiniScript.cpp:87
vector< Script > nativeScripts
Definition: MiniScript.h:709
const string findRightArgument(const string statement, int position, int &length)
Find right argument in statement beginning from position.
Definition: MiniScript.cpp:937
bool parseScriptStatement(const string_view &statement, string_view &method, vector< string_view > &arguments)
Parse a script statement.
Definition: MiniScript.cpp:120
virtual int determineNamedScriptIdxToStart()
Determine named script index to start.
Definition: MiniScript.cpp:816
void gotoStatementGoto(const ScriptStatement &statement)
Goto statement from given statements goto statement.
Definition: MiniScript.h:744
static bool getStringValue(const vector< ScriptVariable > &arguments, int idx, string &value, bool optional=false)
Get string value from given variable.
Definition: MiniScript.h:1141
virtual int determineScriptIdxToStart()
Determine script index to start.
Definition: MiniScript.cpp:756
void setScriptState(int state)
Set script state machine state.
Definition: MiniScript.h:802
unordered_map< uint8_t, ScriptMethod * > scriptOperators
Definition: MiniScript.h:830
void executeScriptLine()
Execute a single script line.
Definition: MiniScript.cpp:96
SHA256 hash class.
Definition: SHA256.h:16
static const string encode(const string &decodedString)
Encodes an string to SHA256 string.
Definition: SHA256.h:23
String tokenizer class.
String tools class.
Definition: StringTools.h:20
static const bool viewStartsWith(const string_view &src, const string &prefix)
Checks if string starts with prefix.
Definition: StringTools.h:39
static const string_view viewSubstring(const string_view &src, int32_t beginIndex)
Returns substring of given string from begin index.
Definition: StringTools.h:143
static const bool endsWith(const string &src, const string &suffix)
Checks if string ends with suffix.
Definition: StringTools.h:49
static const string trim(const string &src)
Trim string.
Definition: StringTools.cpp:51
static const bool viewEndsWith(const string_view &src, const string &suffix)
Checks if string ends with suffix.
Definition: StringTools.h:61
static const string toUpperCase(const string &src)
Transform string to upper case.
Definition: StringTools.cpp:94
static const string_view viewTrim(const string_view &src)
Trim string.
Definition: StringTools.cpp:76
static const bool startsWith(const string &src, const string &prefix)
Checks if string starts with prefix.
Definition: StringTools.h:29
static const string substring(const string &src, int32_t beginIndex)
Returns substring of given string from begin index.
Definition: StringTools.h:133
static bool regexMatch(const string &src, const string &pattern)
Do regex pattern matching.
static const string toLowerCase(const string &src)
Transform string to lower case.
Definition: StringTools.cpp:88
static const string replace(const string &src, const char what, const char by, int beginIndex=0)
Replace char with another char.
Definition: StringTools.cpp:27
Time utility class.
Definition: Time.h:21
static int64_t getCurrentMillis()
Retrieve current time in milliseconds.
Definition: Time.h:28
unordered_map< string, ScriptVariable * > variables
Definition: MiniScript.h:696
unordered_map< int, int64_t > forTimeStarted
Definition: MiniScript.h:697