MinitScript  0.9.31 PRE-BETA
Transpiler.cpp
Go to the documentation of this file.
2 
3 #include <algorithm>
4 #include <string>
5 #include <string_view>
6 #include <unordered_map>
7 #include <unordered_set>
8 #include <vector>
9 
11 #include <minitscript/math/Math.h>
20 
22 
23 using std::find;
24 using std::remove;
25 using std::sort;
26 using std::string;
27 using std::string_view;
28 using std::to_string;
29 using std::unique;
30 using std::unordered_map;
31 using std::unordered_set;
32 using std::vector;
33 
44 
45 void Transpiler::transpile(MinitScript* minitScript, const string& transpilationFileName, const vector<string>& minitScriptExtensionFileNames) {
46  auto scriptFileName = minitScript->getScriptPathName() + "/" + minitScript->getScriptFileName();
47  //
48  Console::printLine(scriptFileName + ": Processing script");
49  //
50  auto replace = [](const vector<string> input, const string& startTag, const string& endTag, const string& replacement, vector<string>& output) -> bool {
51  auto reject = false;
52  auto replaceSuccess = false;
53  for (auto i = 0; i < input.size(); i++) {
54  const auto& line = input[i];
55  auto trimmedLine = StringTools::trim(line);
56  if (StringTools::startsWith(trimmedLine, "//") == true) {
57  if (reject == false) output.push_back(line);
58  continue;
59  }
60  if (trimmedLine == startTag) {
61  reject = true;
62  output.push_back(line);
63  } else
64  if (trimmedLine == endTag) {
65  reject = false;
66  replaceSuccess = true;
67  output.push_back(replacement);
68  output.push_back(line);
69  } else {
70  if (reject == false) output.push_back(line);
71  }
72  }
73  //
74  return replaceSuccess;
75  };
76 
77  //
78  unordered_map<string, vector<string>> methodCodeMap;
79  auto allMethods = getAllClassesMethodNames(minitScript);
80 
81  //
82  vector<string> transpilationUnitIncludes;
83  vector<string> transpilationUnitUsings;
84 
85  //
86  vector<string> transpilationUnits;
87  for (const auto& transpilationUnit: minitScript->getTranspilationUnits()) transpilationUnits.push_back(transpilationUnit);
88  for (const auto& transpilationUnit: minitScriptExtensionFileNames) transpilationUnits.push_back(transpilationUnit);
89  for (const auto& transpilationUnit: transpilationUnits) {
90  vector<string> transpilationUnitCode;
91  FileSystem::getContentAsStringArray(FileSystem::getPathName(transpilationUnit), FileSystem::getFileName(transpilationUnit), transpilationUnitCode);
92  for (auto i = 0; i < transpilationUnitCode.size(); i++) {
93  const auto& line = transpilationUnitCode[i];
94  auto trimmedLine = StringTools::trim(line);
95  if (StringTools::startsWith(trimmedLine, "#include ") == true) {
96  transpilationUnitIncludes.push_back(trimmedLine);
97  } else
98  if (StringTools::startsWith(trimmedLine, "using ") == true) {
99  transpilationUnitUsings.push_back(trimmedLine);
100  }
101  if (StringTools::startsWith(trimmedLine, "registerMethod") == true ||
102  StringTools::startsWith(trimmedLine, "minitScript->registerMethod") == true) {
103  auto bracketCount = 0;
104  string className;
105  if (StringTools::firstIndexOf(StringTools::substring(trimmedLine, 14), "new") == string::npos) {
106  Console::printLine(transpilationUnit + ": registerMethod @ " + to_string(i) + ": '" + trimmedLine + "': unable to determine class name");
107  } else {
108  auto classNameStartIdx = trimmedLine.find("registerMethod") + 14 + 5;
109  for (auto j = classNameStartIdx; j < trimmedLine.size(); j++) {
110  auto c = trimmedLine[j];
111  if (c == '(') break;
112  if (c == ' ') continue;
113  className+= c;
114  }
115  gatherMethodCode(transpilationUnitCode, className, i, methodCodeMap);
116  }
117  }
118  }
119  }
120  //
121  sort(transpilationUnitIncludes.begin(), transpilationUnitIncludes.end());
122  transpilationUnitIncludes.erase(unique(transpilationUnitIncludes.begin(), transpilationUnitIncludes.end()), transpilationUnitIncludes.end());
123 
124  //
125  sort(transpilationUnitUsings.begin(), transpilationUnitUsings.end());
126  transpilationUnitUsings.erase(unique(transpilationUnitUsings.begin(), transpilationUnitUsings.end()), transpilationUnitUsings.end());
127 
128  //
129  Console::printLine(minitScript->getInformation());
130 
131  //
132  const auto& scripts = minitScript->getScripts();
133 
134  // determine variables
135  unordered_set<string> globalVariables;
136  vector<unordered_set<string>> localVariables(scripts.size());
137  determineVariables(minitScript, globalVariables, localVariables);
138 
139  //
140  string declarationIndent = "\t";
141  string definitionIndent = "\t";
142  string generatedExecuteCode;
143  {
144  auto scriptIdx = 0;
145  for (const auto& script: scripts) {
146  auto methodName = createMethodName(minitScript, scriptIdx);
147  generatedExecuteCode+= declarationIndent + "\t\t" + "if (getScriptState().scriptIdx == " + to_string(scriptIdx) + ") " + methodName + "(scriptState.statementIdx); else\n";
148  scriptIdx++;
149  }
150  }
151  if (generatedExecuteCode.empty() == false) generatedExecuteCode+= declarationIndent + "\t\t\t" + ";\n";
152 
153  // determine "initialize" and "nothing" script indices
154  auto initializeScriptIdx = -1;
155  auto nothingScriptIdx = -1;
156  {
157  auto scriptIdx = 0;
158  for (const auto& script: scripts) {
159  //
160  if (StringTools::regexMatch(script.condition, "[a-zA-Z0-9_]+") == true) {
161  if (script.condition == "nothing") nothingScriptIdx = scriptIdx;
162  if (script.condition == "initialize") initializeScriptIdx = scriptIdx;
163  }
164  //
165  scriptIdx++;
166  }
167  }
168 
169  // member access evaluation
170  vector<string> memberAccessEvaluationDeclarations;
171  vector<string> memberAccessEvaluationDefinitions;
172  generateEvaluateMemberAccessArrays(minitScript, memberAccessEvaluationDeclarations, memberAccessEvaluationDefinitions);
173 
174  //
175  string minitScriptClassName = FileSystem::getFileName(transpilationFileName);
176  string generatedDeclarations = "\n";
177  generatedDeclarations+= declarationIndent + "/**" + "\n";
178  generatedDeclarations+= declarationIndent + " * Public destructor" + "\n";
179  generatedDeclarations+= declarationIndent + " */" + "\n";
180  generatedDeclarations+= declarationIndent + "inline ~" + minitScriptClassName + "() {" + "\n";
181  generatedDeclarations+= declarationIndent + "\t" + "if (native == true) {" + "\n";
182  for (const auto& variable: globalVariables) {
183  generatedDeclarations+= declarationIndent + "\t\t" + createGlobalVariableName(variable) + ".unset();" + "\n";
184  }
185  {
186  auto scriptIdx = 0;
187  for (const auto& script: scripts) {
188  if (script.type == MinitScript::Script::TYPE_ON ||
189  script.type == MinitScript::Script::TYPE_ONENABLED ||
190  script.type == MinitScript::Script::TYPE_STACKLET) {
191  scriptIdx++;
192  continue;
193  }
194  auto methodName = createMethodName(minitScript, scriptIdx);
195  auto shortMethodName = createShortMethodName(minitScript, scriptIdx);
196  generatedDeclarations+= declarationIndent + "\t\t" + "if (" + shortMethodName + "_Stack.empty() == false) _Console::printLine(\"~" + minitScriptClassName + "(): Warning: " + methodName + ": stack not empty, size = \" + to_string(" + shortMethodName + "_Stack.size()));" + "\n";
197  scriptIdx++;
198  }
199  }
200  generatedDeclarations+= declarationIndent + "\t" + "}" + "\n";
201 
202  generatedDeclarations+= declarationIndent + "}" + "\n";
203  generatedDeclarations+= "\n";
204  generatedDeclarations+= declarationIndent + "// overridden methods" + "\n";
205  generatedDeclarations+= declarationIndent + "void registerMethods() override;" + "\n";
206  generatedDeclarations+= declarationIndent + "inline void registerVariables() override {" + "\n";
207  if (globalVariables.empty() == false) {
208  generatedDeclarations+= declarationIndent + "\t" + "if (native == true) {" + "\n";
209  for (const auto& variable: globalVariables) {
210  generatedDeclarations+= declarationIndent + "\t\t" + createGlobalVariableName(variable) + ".unset();" + "\n";
211  }
212  generatedDeclarations+= declarationIndent + "\t" + "}" + "\n";
213  generatedDeclarations+= declarationIndent + "\t" + "//" + "\n";
214  }
215  generatedDeclarations+= declarationIndent + "\t" + minitScript->getBaseClass() + "::registerVariables();" + "\n";
216  generatedDeclarations+= declarationIndent + "\t" + "if (native == true) {" + "\n";
217  generatedDeclarations+= declarationIndent + "\t\t" + "// global script variables" + "\n";
218  for (const auto& variable: globalVariables) {
219  generatedDeclarations+= declarationIndent + "\t\t" + "if (hasVariable(\"" + variable + "\") == false) setVariable(\"" + variable + "\", Variable())" + ";" + "\n";
220  generatedDeclarations+= declarationIndent + "\t\t" + createGlobalVariableName(variable) + " = getVariable(\"" + variable + "\", nullptr, true);" + "\n";
221  }
222  generatedDeclarations+= declarationIndent + "\t" + "}" + "\n";
223  generatedDeclarations+= declarationIndent + "}" + "\n";
224  generatedDeclarations+= declarationIndent + "void emit(const string& condition) override;" + "\n";
225  generatedDeclarations+= declarationIndent + "inline void startScript() override {" + "\n";
226  generatedDeclarations+= declarationIndent + "\t" + "if (native == false) {" + "\n";
227  generatedDeclarations+= declarationIndent + "\t" + "\t" + minitScript->getBaseClass() + "::startScript();" + "\n";
228  generatedDeclarations+= declarationIndent + "\t" + "\t" + "return;" + "\n";
229  generatedDeclarations+= declarationIndent + "\t" + "}" + "\n";
230  generatedDeclarations+= declarationIndent + "\t" + "getScriptState().running = true;" + "\n";
231  generatedDeclarations+= declarationIndent + "\t" + "registerVariables();" + "\n";
232  generatedDeclarations+= declarationIndent + "\t" + "resetScriptExecutationState(" + to_string(initializeScriptIdx) + ", STATEMACHINESTATE_NEXT_STATEMENT);" + "\n";
233  generatedDeclarations+= declarationIndent + "}" + "\n";
234  generatedDeclarations+= declarationIndent + "inline void execute() override {" + "\n";
235  generatedDeclarations+= declarationIndent + "\t" + "if (native == false) {" + "\n";
236  generatedDeclarations+= declarationIndent + "\t" + "\t" + minitScript->getBaseClass() + "::execute();" + "\n";
237  generatedDeclarations+= declarationIndent + "\t" + "\t" + "return;" + "\n";
238  generatedDeclarations+= declarationIndent + "\t" + "}" + "\n";
239  generatedDeclarations+= declarationIndent + "\t" + "auto& scriptState = getScriptState();" + "\n";
240  generatedDeclarations+= declarationIndent + "\t" + "if (scriptState.running == false) return;" + "\n";
241  generatedDeclarations+= declarationIndent + "\t" + "// execute while having statements to be processed" + "\n";
242  generatedDeclarations+= declarationIndent + "\t" + "if (scriptState.state == STATEMACHINESTATE_NEXT_STATEMENT) {" + "\n";
243  generatedDeclarations+= declarationIndent + "\t" + "\t" + "// take goto statement index into account" + "\n";
244  generatedDeclarations+= declarationIndent + "\t" + "\t" + "if (scriptState.gotoStatementIdx != STATEMENTIDX_NONE) {" + "\n";
245  generatedDeclarations+= declarationIndent + "\t" + "\t" + "\t" + "scriptState.statementIdx = scriptState.gotoStatementIdx;" + "\n";
246  generatedDeclarations+= declarationIndent + "\t" + "\t" + "\t" + "scriptState.gotoStatementIdx = STATEMENTIDX_NONE;" + "\n";
247  generatedDeclarations+= declarationIndent + "\t" + "\t" + "}" + "\n";
248  generatedDeclarations+= declarationIndent + "\t" + "\t" + "//" + "\n";
249  generatedDeclarations+= generatedExecuteCode;
250  generatedDeclarations+= declarationIndent + "\t" + "}" + "\n";
251  generatedDeclarations+= declarationIndent + "\t" + "if (getScriptState().running == false) return;" + "\n";
252  generatedDeclarations+= declarationIndent + "\t" + "if (isFunctionRunning() == false && deferredEmit.empty() == false) {" + "\n";
253  generatedDeclarations+= declarationIndent + "\t\t" + "auto condition = deferredEmit;" + "\n";
254  generatedDeclarations+= declarationIndent + "\t\t" + "deferredEmit.clear();" + "\n";
255  generatedDeclarations+= declarationIndent + "\t\t" + "emit(condition);" + "\n";
256  generatedDeclarations+= declarationIndent + "\t" + "}" + "\n";
257  generatedDeclarations+= declarationIndent + "\t" + "if (getScriptState().running == false) return;" + "\n";
258  generatedDeclarations+= declarationIndent + "\t" + "executeStateMachine();" + "\n";
259  generatedDeclarations+= declarationIndent + "\t" + "// try garbage collection" + "\n";
260  generatedDeclarations+= declarationIndent + "\t" + "tryGarbageCollection();" + "\n";
261  generatedDeclarations+= declarationIndent + "}" + "\n";
262  generatedDeclarations+= "\n";
263  generatedDeclarations+= string() + "protected:" + "\n";
264 
265  //
266  for (const auto& memberAccessEvaluationDeclaration: memberAccessEvaluationDeclarations) {
267  generatedDeclarations+= declarationIndent + memberAccessEvaluationDeclaration + "\n";
268  }
269  generatedDeclarations+= "\n";
270 
271  //
272  generatedDeclarations+= declarationIndent + "// overridden methods" + "\n";
273  generatedDeclarations+= declarationIndent + "void initializeNative() override;" + "\n";
274  generatedDeclarations+= declarationIndent + "int determineScriptIdxToStart() override;" + "\n";
275  generatedDeclarations+= declarationIndent + "int determineNamedScriptIdxToStart() override;" + "\n";
276  generatedDeclarations+= "\n";
277 
278  string registerMethodsDefinitions;
279  registerMethodsDefinitions+= "void " + minitScriptClassName + "::registerMethods() {" + "\n";
280  registerMethodsDefinitions+= definitionIndent + minitScript->getBaseClass() + "::registerMethods();" + "\n";
281  registerMethodsDefinitions+= definitionIndent + "if (native == false) return;" + "\n";
282  //
283  for (const auto& memberAccessEvaluationDefintion: memberAccessEvaluationDefinitions) {
284  registerMethodsDefinitions+= definitionIndent + memberAccessEvaluationDefintion + "\n";
285  }
286  registerMethodsDefinitions+= string() + "}" + "\n";
287 
288  //
289  string emitDefinition;
290  emitDefinition+= "void " + minitScriptClassName + "::emit(const string& condition) {" + "\n";
291  emitDefinition+= definitionIndent + "if (native == false) {" + "\n";
292  emitDefinition+= definitionIndent + "\t" + minitScript->getBaseClass() + "::emit(condition);" + "\n";
293  emitDefinition+= definitionIndent + "\t" + "return;" + "\n";
294  emitDefinition+= definitionIndent + "}" + "\n";
295  emitDefinition+= definitionIndent + "if (isFunctionRunning() == true) {" + "\n";
296  emitDefinition+= definitionIndent + "\t" + "deferredEmit = condition;" + "\n";
297  emitDefinition+= definitionIndent + "\t" + "return;" + "\n";
298  emitDefinition+= definitionIndent + "}" + "\n";
299  emitDefinition+= definitionIndent + "emitted = true;" + "\n";
300 
301  //
302  string generatedDefinitions = "\n";
303  string initializeNativeDefinition;
304  initializeNativeDefinition+= "void " + minitScriptClassName + "::initializeNative() {" + "\n";
305  initializeNativeDefinition+= definitionIndent + "setNative(true);" + "\n";
306  initializeNativeDefinition+= definitionIndent + "setNativeHash(\"" + minitScript->getNativeHash() + "\");" + "\n";
307  initializeNativeDefinition+= definitionIndent + "setNativeScripts(" + "\n";
308  initializeNativeDefinition+= definitionIndent + "\t" + "{" + "\n";
309  {
310  auto scriptIdx = 0;
311  for (const auto& script: scripts) {
312  initializeNativeDefinition+= definitionIndent + "\t" + "\t" + "Script(" + "\n";
313  initializeNativeDefinition+= definitionIndent + "\t" + "\t" + "\t" + getScriptTypeEnumIdentifier(script.type) + "," + "\n";
314  initializeNativeDefinition+= definitionIndent + "\t" + "\t" + "\t" + to_string(script.line) + "," + "\n";
315  initializeNativeDefinition+= definitionIndent + "\t" + "\t" + "\t" + "\"" + escapeString(script.condition) + "\"," + "\n";
316  initializeNativeDefinition+= definitionIndent + "\t" + "\t" + "\t" + "\"" + escapeString(script.executableCondition) + "\"," + "\n";
317  initializeNativeDefinition+= definitionIndent + "\t" + "\t" + "\t" + "Statement(" + "\n";
318  initializeNativeDefinition+= definitionIndent + "\t" + "\t" + "\t" + "\t" + to_string(script.conditionStatement.line) + "," + "\n";
319  initializeNativeDefinition+= definitionIndent + "\t" + "\t" + "\t" + "\t" + to_string(script.conditionStatement.statementIdx) + "," + "\n";
320  initializeNativeDefinition+= definitionIndent + "\t" + "\t" + "\t" + "\t" + "\"" + escapeString(script.conditionStatement.statement) + "\"," + "\n";
321  initializeNativeDefinition+= definitionIndent + "\t" + "\t" + "\t" + "\t" + "\"" + escapeString(script.conditionStatement.executableStatement) + "\"," + "\n";
322  initializeNativeDefinition+= definitionIndent + "\t" + "\t" + "\t" + "\t" + to_string(script.conditionStatement.gotoStatementIdx) + "\n";
323  initializeNativeDefinition+= definitionIndent + "\t" + "\t" + "\t" + ")," + "\n";
324  initializeNativeDefinition+= definitionIndent + "\t" + "\t" + "\t" + "{}," + "\n";
325  initializeNativeDefinition+= definitionIndent + "\t" + "\t" + "\t" + "\"" + script.name + "\"," + "\n";
326  initializeNativeDefinition+= definitionIndent + "\t" + "\t" + "\t" + (script.emitCondition == true?"true":"false") + "," + "\n";
327  initializeNativeDefinition+= definitionIndent + "\t" + "\t" + "\t" + "{" + "\n";
328  auto statementIdx = MinitScript::STATEMENTIDX_FIRST;
329  for (const auto& statement: script.statements) {
330  initializeNativeDefinition+= definitionIndent + "\t" + "\t" + "\t" + "\t" + "Statement(" + "\n";
331  initializeNativeDefinition+= definitionIndent + "\t" + "\t" + "\t" + "\t" + "\t" + to_string(statement.line) + "," + "\n";
332  initializeNativeDefinition+= definitionIndent + "\t" + "\t" + "\t" + "\t" + "\t" + to_string(statement.statementIdx) + "," + "\n";
333  initializeNativeDefinition+= definitionIndent + "\t" + "\t" + "\t" + "\t" + "\t" + "\"" + escapeString(statement.statement) + "\"," + "\n";
334  initializeNativeDefinition+= definitionIndent + "\t" + "\t" + "\t" + "\t" + "\t" + "\"" + escapeString(statement.executableStatement) + "\"," + "\n";
335  initializeNativeDefinition+= definitionIndent + "\t" + "\t" + "\t" + "\t" + "\t" + to_string(statement.gotoStatementIdx) + "\n";
336  initializeNativeDefinition+= definitionIndent + "\t" + "\t" + "\t" + "\t" + ")" + (statementIdx < script.statements.size() - 1?",":"") + "\n";
337  statementIdx++;
338  }
339  initializeNativeDefinition+= definitionIndent + "\t" + "\t" + "\t" + "}," + "\n";
340  initializeNativeDefinition+= definitionIndent + "\t" + "\t" + "\t" + "{},\n";
341  initializeNativeDefinition+= definitionIndent + "\t" + "\t" + "\t" + (script.callable == true?"true":"false") + ",\n";
342  initializeNativeDefinition+= definitionIndent + "\t" + "\t" + "\t" + "{\n";
343  auto argumentIdx = 0;
344  for (const auto& argument: script.arguments) {
345  initializeNativeDefinition+= definitionIndent + "\t" + "\t" + "\t" + "\t" + "Script::Argument(" + "\n";
346  initializeNativeDefinition+= definitionIndent + "\t" + "\t" + "\t" + "\t" + "\t" + "\"" + argument.name + "\"," + "\n";
347  initializeNativeDefinition+= definitionIndent + "\t" + "\t" + "\t" + "\t" + "\t" + (argument.reference == true?"true":"false") + ",\n";
348  initializeNativeDefinition+= definitionIndent + "\t" + "\t" + "\t" + "\t" + "\t" + (argument.privateScope == true?"true":"false") + "\n";
349  initializeNativeDefinition+= definitionIndent + "\t" + "\t" + "\t" + "\t" ")" + (argumentIdx != script.arguments.size() - 1?",":"") + "\n";
350  argumentIdx++;
351  }
352  initializeNativeDefinition+= definitionIndent + "\t" + "\t" + "\t" + "}\n";
353  initializeNativeDefinition+= definitionIndent + "\t" + "\t" + ")" + (scriptIdx < scripts.size() - 1?",":"") + "\n";
354  scriptIdx++;
355  }
356  }
357  initializeNativeDefinition+= definitionIndent + "\t" + "}" + "\n";
358  initializeNativeDefinition+= definitionIndent + ");" + "\n";
359  initializeNativeDefinition+= definitionIndent + "setNativeFunctions(" + "\n";
360  initializeNativeDefinition+= definitionIndent + "\t" + "{" + "\n";
361  auto functionItIdx = 0;
362  for (const auto& [functionName, functionScriptIdx]: minitScript->functions) {
363  initializeNativeDefinition+= definitionIndent + "\t" + "\t" + "{" + "\n";
364  initializeNativeDefinition+= definitionIndent + "\t" + "\t" + "\t" + "\"" + functionName + "\"," + "\n";
365  initializeNativeDefinition+= definitionIndent + "\t" + "\t" + "\t" + to_string(functionScriptIdx) + "\n";
366  initializeNativeDefinition+= definitionIndent + "\t" + "\t" + "}" + (functionItIdx != minitScript->functions.size() - 1?",":"") + "\n";
367  functionItIdx++;
368  }
369  initializeNativeDefinition+= definitionIndent + "\t" + "}" + "\n";
370  initializeNativeDefinition+= definitionIndent + ");" + "\n";
371  initializeNativeDefinition+= string() + "}" + "\n";
372 
373  //
374  string generatedDetermineScriptIdxToStartDefinition = "\n";
375  generatedDetermineScriptIdxToStartDefinition+= "int " + minitScriptClassName + "::determineScriptIdxToStart() {" + "\n";
376  generatedDetermineScriptIdxToStartDefinition+= definitionIndent + "#define MINITSCRIPT_METHOD_POPSTACK()" + "\n";
377  generatedDetermineScriptIdxToStartDefinition+= definitionIndent + "if (native == false) return MinitScript::determineScriptIdxToStart();" + "\n";
378  generatedDetermineScriptIdxToStartDefinition+= definitionIndent + "// MinitScript setup" + "\n";
379  generatedDetermineScriptIdxToStartDefinition+= definitionIndent + "auto minitScript = this;" + "\n";
380  generatedDetermineScriptIdxToStartDefinition+= definitionIndent + "//" + "\n";
381  string generatedDetermineNamedScriptIdxToStartDefinition = "\n";
382  generatedDetermineNamedScriptIdxToStartDefinition+= "int " + minitScriptClassName + "::determineNamedScriptIdxToStart() {" + "\n";
383  generatedDetermineNamedScriptIdxToStartDefinition+= definitionIndent + "#define MINITSCRIPT_METHOD_POPSTACK()" + "\n";
384  generatedDetermineNamedScriptIdxToStartDefinition+= definitionIndent + "if (native == false) return MinitScript::determineNamedScriptIdxToStart();" + "\n";
385  generatedDetermineNamedScriptIdxToStartDefinition+= definitionIndent + "auto minitScript = this;" + "\n";
386  generatedDetermineNamedScriptIdxToStartDefinition+= definitionIndent + "for (const auto& enabledNamedCondition: enabledNamedConditions) {" + "\n";
387  {
388  auto scriptIdx = 0;
389  for (const auto& script: scripts) {
390  // method name
391  auto methodName = createMethodName(minitScript, scriptIdx);
392  auto shortMethodName = createShortMethodName(minitScript, scriptIdx);
393 
394  // emit name
395  string emitName =
396  (script.name.empty() == false?script.name:(
397  StringTools::regexMatch(script.condition, "[a-zA-Z0-9_]+") == true?
398  script.condition:
399  to_string(scriptIdx)
400  )
401  );
402 
403  // emit code
404  if (script.type == MinitScript::Script::TYPE_ON && StringTools::regexMatch(script.condition, "[a-zA-Z0-9_]+") == true) {
405  string emitDefinitionIndent = "\t";
406  emitDefinition+= emitDefinitionIndent + "if (condition == \"" + emitName + "\") {" + "\n";
407  emitDefinition+= emitDefinitionIndent + "\t" + "resetScriptExecutationState(" + to_string(scriptIdx) + ", STATEMACHINESTATE_NEXT_STATEMENT);" + "\n";
408  emitDefinition+= emitDefinitionIndent + "} else" + "\n";
409  }
410 
411  // declaration
412  generatedDeclarations+= declarationIndent + "/**" + "\n";
413  generatedDeclarations+= declarationIndent + " * MinitScript transpilation of: " + getScriptTypeReadableName(script.type) + ": " + script.condition + (script.name.empty() == false?" (" + script.name + ")":"") + "\n";
414  generatedDeclarations+= declarationIndent + " * @param minitScriptGotoStatementIdx MinitScript goto statement index" + "\n";
415  generatedDeclarations+= declarationIndent + " */" + "\n";
416  generatedDeclarations+= declarationIndent + "void " + methodName + "(int minitScriptGotoStatementIdx);" + "\n";
417  generatedDeclarations+= "\n";
418 
419  // transpile definition
420  generatedDefinitions+= "void " + minitScriptClassName + "::" + methodName + "(int minitScriptGotoStatementIdx) {" + "\n";
421  string generatedSubCode;
422 
423  // local variables, which applies to stacklets and functions
424  if (localVariables[scriptIdx].empty() == false) {
425  generatedDefinitions+= definitionIndent + "// local script variables" + "\n";
426  generatedDefinitions+= definitionIndent + "#define MINITSCRIPT_METHOD_POPSTACK() " + shortMethodName + "_Stack.pop();" + "\n";
427  generatedDefinitions+= definitionIndent + "// STATEMENTIDX_FIRST means complete method call" + "\n";
428  generatedDefinitions+= definitionIndent + "if (minitScriptGotoStatementIdx == STATEMENTIDX_FIRST) {" + "\n";
429  generatedDefinitions+= definitionIndent + "\t" + "auto& _lv = " + shortMethodName + "_Stack.emplace();" + "\n";
430  for (const auto& variable: localVariables[scriptIdx]) {
431  generatedDefinitions+= definitionIndent + "\t" + "if (hasVariable(\"" + variable + "\") == false) setVariable(\"" + variable + "\", Variable())" + "; _lv." + createLocalVariableName(variable) + " = getVariable(\"" + variable + "\", nullptr, true);" + "\n";
432  }
433  generatedDefinitions+= definitionIndent + "}" + "\n";
434  generatedDefinitions+= definitionIndent + "//" + "\n";
435  generatedDefinitions+= definitionIndent + "auto& _lv = " + shortMethodName + "_Stack.top();" + "\n";
436  } else {
437  // we can still have a stacklet without local variables
438  if (script.type == MinitScript::Script::TYPE_STACKLET) {
439  auto scopeScriptIdx = minitScript->getStackletScopeScriptIdx(scriptIdx);
440  if (scopeScriptIdx != MinitScript::SCRIPTIDX_NONE) {
441  auto scopeShortMethodName = createShortMethodName(minitScript, minitScript->getStackletScopeScriptIdx(scriptIdx));
442  generatedDefinitions+= definitionIndent + "// local script variables" + "\n";
443  generatedDefinitions+= definitionIndent + "auto& _lv = " + scopeShortMethodName + "_Stack.top();" + "\n";
444  }
445  } else
446  // ok, reset emitted for conditions and enabled conditions
447  if (script.type == MinitScript::Script::TYPE_ON ||
448  script.type == MinitScript::Script::TYPE_ONENABLED) {
449  generatedDefinitions+= definitionIndent + "// reset emitted" + "\n";
450  generatedDefinitions+= definitionIndent + "emitted = false;" + "\n";
451  }
452  // no local variables or condition/enabled condition
453  generatedDefinitions+= definitionIndent + "// local script variables" + "\n";
454  generatedDefinitions+= definitionIndent + "#define MINITSCRIPT_METHOD_POPSTACK()" + "\n";
455  }
456 
457  // transpile array access operator and map/set initializer
458  string arrayAccessMethodsDefinitions;
459  string arrayMapSetInitializerDefinitions;
460 
461  //
462  for (auto statementIdx = 0; statementIdx < script.statements.size(); statementIdx++) {
463  string statementArrayMapSetInitializerDefinitions;
464  //
465  generateArrayMapSetInitializer(
466  minitScript,
467  statementArrayMapSetInitializerDefinitions,
468  MinitScript::SCRIPTIDX_NONE,
469  script.type == MinitScript::Script::TYPE_FUNCTION || script.type == MinitScript::Script::TYPE_STACKLET?
470  scriptIdx:
471  MinitScript::SCRIPTIDX_NONE,
472  minitScriptClassName,
473  methodName,
474  script.syntaxTree[statementIdx],
475  script.statements[statementIdx],
476  methodCodeMap,
477  allMethods,
478  false,
479  {},
480  1
481  );
482  if (statementArrayMapSetInitializerDefinitions.empty() == false) statementArrayMapSetInitializerDefinitions+= "\n";
483  arrayMapSetInitializerDefinitions+= statementArrayMapSetInitializerDefinitions;
484  //
485  generateArrayAccessMethods(
486  minitScript,
487  arrayAccessMethodsDefinitions,
488  minitScriptClassName,
489  MinitScript::SCRIPTIDX_NONE,
490  script.type == MinitScript::Script::TYPE_FUNCTION || script.type == MinitScript::Script::TYPE_STACKLET?
491  scriptIdx:
492  MinitScript::SCRIPTIDX_NONE,
493  methodName,
494  script.syntaxTree[statementIdx],
495  script.statements[statementIdx],
496  methodCodeMap,
497  allMethods,
498  false,
499  {},
500  1
501  );
502  }
503 
504  //
505  generatedDefinitions+= arrayMapSetInitializerDefinitions;
506  generatedDefinitions+= arrayAccessMethodsDefinitions;
507 
508  //
509  transpile(minitScript, minitScriptClassName, generatedSubCode, scriptIdx, methodCodeMap, allMethods);
510  generatedDefinitions+= generatedSubCode;
511 
512  // local variables
513  if (localVariables[scriptIdx].empty() == false) {
514  generatedDefinitions+= definitionIndent + "//" + "\n";
515  generatedDefinitions+= definitionIndent + "MINITSCRIPT_METHOD_POPSTACK();" + "\n";
516  }
517  generatedDefinitions+= definitionIndent + "#undef MINITSCRIPT_METHOD_POPSTACK" + "\n";
518 
519  //
520  generatedDefinitions+= string() + "}" + "\n\n";
521 
522  //
523  if (script.emitCondition == false) {
524  if (script.type == MinitScript::Script::TYPE_ONENABLED) {
525  generatedDetermineNamedScriptIdxToStartDefinition+= definitionIndent;
526  generatedDetermineNamedScriptIdxToStartDefinition+= definitionIndent + "\t" + "// next statements belong to tested enabled named condition with name \"" + script.name + "\"" + "\n";
527  generatedDetermineNamedScriptIdxToStartDefinition+= definitionIndent + "\t" + "if (enabledNamedCondition == \"" + script.name + "\") {" + "\n";
528  } else {
529  generatedDetermineScriptIdxToStartDefinition+= definitionIndent + "{" + "\n";
530  }
531  //
532  string arrayMapSetInitializerDefinitions;
533  string arrayAccessMethodsDefinitions;
534  //
535  generateArrayMapSetInitializer(
536  minitScript,
537  arrayMapSetInitializerDefinitions,
538  MinitScript::SCRIPTIDX_NONE,
539  MinitScript::SCRIPTIDX_NONE,
540  minitScriptClassName,
541  methodName,
542  script.conditionSyntaxTree,
543  script.conditionStatement,
544  methodCodeMap,
545  allMethods,
546  true,
547  {},
548  script.type == MinitScript::Script::TYPE_ONENABLED?3:2
549  );
550  if (arrayMapSetInitializerDefinitions.empty() == false) arrayMapSetInitializerDefinitions+= "\n";
551  //
552  generateArrayAccessMethods(
553  minitScript,
554  arrayAccessMethodsDefinitions,
555  minitScriptClassName,
556  MinitScript::SCRIPTIDX_NONE,
557  MinitScript::SCRIPTIDX_NONE,
558  methodName,
559  script.conditionSyntaxTree,
560  script.conditionStatement,
561  methodCodeMap,
562  allMethods,
563  true,
564  {},
565  script.type == MinitScript::Script::TYPE_ONENABLED?3:2
566  );
567  //
568  if (script.type == MinitScript::Script::TYPE_ON) {
569  generatedDetermineScriptIdxToStartDefinition+= arrayMapSetInitializerDefinitions;
570  generatedDetermineScriptIdxToStartDefinition+= arrayAccessMethodsDefinitions;
571  } else {
572  generatedDetermineNamedScriptIdxToStartDefinition+= arrayMapSetInitializerDefinitions;
573  generatedDetermineNamedScriptIdxToStartDefinition+= arrayAccessMethodsDefinitions;
574  }
575  //
576  transpileCondition(
577  minitScript,
578  script.type == MinitScript::Script::TYPE_ON?generatedDetermineScriptIdxToStartDefinition:generatedDetermineNamedScriptIdxToStartDefinition,
579  scriptIdx,
580  methodCodeMap,
581  allMethods,
582  "-1",
583  "bool returnValueBool; returnValue.getBooleanValue(returnValueBool); if (returnValueBool == true) return " + to_string(scriptIdx) + ";",
584  script.type == MinitScript::Script::TYPE_ONENABLED?1:0
585  );
586  //
587  if (script.type == MinitScript::Script::TYPE_ONENABLED) {
588  generatedDetermineNamedScriptIdxToStartDefinition+= definitionIndent + "\t" + "}" + "\n";
589  } else {
590  generatedDetermineScriptIdxToStartDefinition+= definitionIndent + "}" + "\n";
591  }
592  }
593 
594  //
595  scriptIdx++;
596  }
597  }
598 
599  //
600  generatedDetermineScriptIdxToStartDefinition+= "\n";
601  generatedDetermineScriptIdxToStartDefinition+= definitionIndent + "//" + "\n";
602  generatedDetermineScriptIdxToStartDefinition+= definitionIndent + "return " + to_string(nothingScriptIdx) + ";" + "\n";
603  generatedDetermineScriptIdxToStartDefinition+= definitionIndent + "#undef MINITSCRIPT_METHOD_POPSTACK" + "\n";
604  generatedDetermineScriptIdxToStartDefinition+= string() + "}" + "\n";
605  //
606  generatedDetermineNamedScriptIdxToStartDefinition+= definitionIndent + "}" + "\n";
607  generatedDetermineNamedScriptIdxToStartDefinition+= "\n";
608  generatedDetermineNamedScriptIdxToStartDefinition+= definitionIndent + "//" + "\n";
609  generatedDetermineNamedScriptIdxToStartDefinition+= definitionIndent + "return SCRIPTIDX_NONE;" + "\n";
610  generatedDetermineNamedScriptIdxToStartDefinition+= definitionIndent + "#undef MINITSCRIPT_METHOD_POPSTACK" + "\n";
611  generatedDetermineNamedScriptIdxToStartDefinition+= string() + "}" + "\n";
612 
613  //
614  {
615  string emitDefinitionIndent = "\t";
616  emitDefinition+= emitDefinitionIndent + "{" + "\n";
617  emitDefinition+= emitDefinitionIndent + "\t" + "_Console::printLine(\"" + minitScriptClassName + "::emit(): no condition with name: '\" + condition + \"'\");" + "\n";
618  emitDefinition+= emitDefinitionIndent + "}" + "\n";
619  emitDefinition+= string() + "}" + "\n";
620  }
621 
622  //
623  if (globalVariables.empty() == false) {
624  generatedDeclarations+= declarationIndent + "// global script variables" + "\n";
625  for (const auto& variable: globalVariables) {
626  generatedDeclarations+= declarationIndent + "Variable " + createGlobalVariableName(variable) + ";" + "\n";
627  }
628  generatedDeclarations+= "\n";
629  }
630 
631  //
632  {
633  auto scriptIdx = 0;
634  for (const auto& script: scripts) {
635  //
636  if (script.type == MinitScript::Script::TYPE_ON ||
637  script.type == MinitScript::Script::TYPE_ONENABLED ||
638  script.type == MinitScript::Script::TYPE_STACKLET) {
639  scriptIdx++;
640  continue;
641  }
642  // method name
643  auto shortMethodName = createShortMethodName(minitScript, scriptIdx);
644  //
645  generatedDeclarations+= declarationIndent + "// local script variables of: " + getScriptTypeReadableName(script.type) + ": " + script.condition + (script.name.empty() == false?" (" + script.name + ")":"") + "\n";
646  generatedDeclarations+= declarationIndent + "struct LV_" + shortMethodName + " {" + "\n";
647  for (const auto& variable: localVariables[scriptIdx]) {
648  generatedDeclarations+= declarationIndent + "\t" + "Variable " + createLocalVariableName(variable) + ";" + "\n";
649  }
650  generatedDeclarations+= declarationIndent + "\t" + "// destructor, we unset the variable references here" + "\n";
651  generatedDeclarations+= declarationIndent + "\t" + "~LV_" + shortMethodName + "() {" + "\n";
652  for (const auto& variable: localVariables[scriptIdx]) {
653  generatedDeclarations+= declarationIndent + "\t" + "\t" + createLocalVariableName(variable) + ".unset();" + "\n";
654  }
655  generatedDeclarations+= declarationIndent + "\t" + "}" + "\n";
656  generatedDeclarations+= declarationIndent + "};" + "\n";
657  generatedDeclarations+= declarationIndent + "stack<LV_" + shortMethodName + "> " + shortMethodName + "_Stack;" + "\n\n";
658  //
659  scriptIdx++;
660  }
661  }
662 
663  // sum up definitions
664  generatedDefinitions =
665  string("\n#define __MINITSCRIPT_TRANSPILATION__\n\n") +
666  initializeNativeDefinition +
667  registerMethodsDefinitions +
668  generatedDetermineScriptIdxToStartDefinition +
669  generatedDetermineNamedScriptIdxToStartDefinition + "\n" +
670  emitDefinition +
671  generatedDefinitions;
672 
673  // inject C++ definition code
674  {
675  //
676  auto injectedGeneratedCode = true;
677  //
678  vector<string> minitScriptCPP;
679  vector<string> generatedMinitScriptCPP;
680  auto transpilationCPPFileName = FileSystem::getPathName(transpilationFileName) + "/" + FileSystem::getFileName(transpilationFileName) + ".cpp";
681  if (FileSystem::exists(transpilationCPPFileName) == false) {
682  auto minitScriptCPPString = FileSystem::getContentAsString(MINITSCRIPT_DATA + "/resources/minitscript/templates/transpilation", "Transpilation.cpp");
683  minitScriptCPPString = StringTools::replace(minitScriptCPPString, "{$script}", scriptFileName);
684  minitScriptCPPString = StringTools::replace(minitScriptCPPString, "{$class-name}", minitScriptClassName);
685  minitScriptCPPString = StringTools::replace(minitScriptCPPString, "{$base-class}", minitScript->getBaseClass());
686  minitScriptCPPString = StringTools::replace(minitScriptCPPString, "{$base-class-header}", minitScript->getBaseClassHeader());
687  minitScriptCPP = StringTools::tokenize(minitScriptCPPString, "\n", true);
688  } else {
689  FileSystem::getContentAsStringArray(FileSystem::getPathName(transpilationCPPFileName), FileSystem::getFileName(transpilationCPPFileName), minitScriptCPP);
690  }
691  //
692  if (injectedGeneratedCode == true) {
693  //
694  string includes;
695  for (const auto& include: transpilationUnitIncludes) includes+= include + "\n";
696  //
697  injectedGeneratedCode = replace(
698  minitScriptCPP,
699  "/*__MINITSCRIPT_TRANSPILEDMINITSCRIPTCODE_INCLUDES_START__*/",
700  "/*__MINITSCRIPT_TRANSPILEDMINITSCRIPTCODE_INCLUDES_END__*/",
701  includes,
702  generatedMinitScriptCPP
703  );
704  if (injectedGeneratedCode == false) {
705  Console::printLine(scriptFileName + ": Could not inject generated C++ code, are you missing the /*__MINITSCRIPT_TRANSPILEDMINITSCRIPTCODE_INCLUDES_START__*/ and /*__MINITSCRIPT_TRANSPILEDMINITSCRIPTCODE_INCLUDES_END__*/ tags in file " + transpilationFileName + "?");
706  } else {
707  minitScriptCPP = generatedMinitScriptCPP;
708  generatedMinitScriptCPP.clear();
709  }
710  }
711  //
712  if (injectedGeneratedCode == true) {
713  //
714  string usings;
715  for (const auto& _using: transpilationUnitUsings) usings+= _using + "\n";
716  //
717  injectedGeneratedCode = replace(
718  minitScriptCPP,
719  "/*__MINITSCRIPT_TRANSPILEDMINITSCRIPTCODE_USINGS_START__*/",
720  "/*__MINITSCRIPT_TRANSPILEDMINITSCRIPTCODE_USINGS_END__*/",
721  usings,
722  generatedMinitScriptCPP
723  );
724  if (injectedGeneratedCode == false) {
725  Console::printLine(scriptFileName + ": Could not inject generated C++ code, are you missing the /*__MINITSCRIPT_TRANSPILEDMINITSCRIPTCODE_USINGS_START__*/ and /*__MINITSCRIPT_TRANSPILEDMINITSCRIPTCODE_USINGS_END__*/ tags in file " + transpilationFileName + "?");
726  } else {
727  minitScriptCPP = generatedMinitScriptCPP;
728  generatedMinitScriptCPP.clear();
729  }
730  }
731  //
732  if (injectedGeneratedCode == true) {
733  injectedGeneratedCode = replace(
734  minitScriptCPP,
735  "/*__MINITSCRIPT_TRANSPILEDMINITSCRIPTCODE_DEFINITIONS_START__*/",
736  "/*__MINITSCRIPT_TRANSPILEDMINITSCRIPTCODE_DEFINITIONS_END__*/",
737  generatedDefinitions,
738  generatedMinitScriptCPP
739  );
740  if (injectedGeneratedCode == false) {
741  Console::printLine(scriptFileName + ": Could not inject generated C++ code, are you missing the /*__MINITSCRIPT_TRANSPILEDMINITSCRIPTCODE_DEFINITIONS_START__*/ and /*__MINITSCRIPT_TRANSPILEDMINITSCRIPTCODE_DEFINITIONS_END__*/ tags in file " + transpilationFileName + "?");
742  } else {
743  minitScriptCPP.clear();
744  }
745  }
746  //
747  if (injectedGeneratedCode == true) {
748  FileSystem::setContentFromStringArray(
749  FileSystem::getPathName(transpilationCPPFileName),
750  FileSystem::getFileName(transpilationCPPFileName),
751  generatedMinitScriptCPP
752  );
753  //
754  Console::printLine(scriptFileName + ": Injected generated C++ code in file " + transpilationCPPFileName + ". Dont forget to rebuild your sources.");
755  }
756  }
757 
758  //
759  // inject C++ declaration code / header
760  {
761  vector<string> minitScriptClassHeader;
762  vector<string> generatedMinitScriptClassHeader;
763  auto transpilationHeaderFileName = FileSystem::getPathName(transpilationFileName) + "/" + FileSystem::getFileName(transpilationFileName) + ".h";
764  if (FileSystem::exists(transpilationHeaderFileName) == false) {
765  auto minitScriptHeaderString = FileSystem::getContentAsString(MINITSCRIPT_DATA + "/resources/minitscript/templates/transpilation", "Transpilation.h");
766  minitScriptHeaderString = StringTools::replace(minitScriptHeaderString, "{$script}", scriptFileName);
767  minitScriptHeaderString = StringTools::replace(minitScriptHeaderString, "{$class-name}", minitScriptClassName);
768  minitScriptHeaderString = StringTools::replace(minitScriptHeaderString, "{$base-class}", minitScript->getBaseClass());
769  minitScriptHeaderString = StringTools::replace(minitScriptHeaderString, "{$base-class-header}", minitScript->getBaseClassHeader());
770  minitScriptClassHeader = StringTools::tokenize(minitScriptHeaderString, "\n", true);
771  } else {
772  FileSystem::getContentAsStringArray(FileSystem::getPathName(transpilationHeaderFileName), FileSystem::getFileName(transpilationHeaderFileName), minitScriptClassHeader);
773  }
774  //
775  auto injectedGeneratedCode = replace(
776  minitScriptClassHeader,
777  "/*__MINITSCRIPT_TRANSPILEDMINITSCRIPTCODE_DECLARATIONS_START__*/",
778  "/*__MINITSCRIPT_TRANSPILEDMINITSCRIPTCODE_DECLARATIONS_END__*/",
779  generatedDeclarations,
780  generatedMinitScriptClassHeader
781  );
782  //
783  if (injectedGeneratedCode == false) {
784  Console::printLine(scriptFileName + ": Could not inject generated C++ code, are you missing the /*__MINITSCRIPT_TRANSPILEDMINITSCRIPTCODE_DECLARATIONS_START__*/ and /*__MINITSCRIPT_TRANSPILEDMINITSCRIPTCODE_DECLARATIONS_END__*/ tags in file " + transpilationFileName + "?");
785  } else {
786  //
787  FileSystem::setContentFromStringArray(
788  FileSystem::getPathName(transpilationHeaderFileName),
789  FileSystem::getFileName(transpilationHeaderFileName),
790  generatedMinitScriptClassHeader
791  );
792  //
793  Console::printLine(scriptFileName + ": Injected generated C++ code in header file " + transpilationHeaderFileName + ". Dont forget to rebuild your sources.");
794  }
795  }
796 }
797 
798 void Transpiler::untranspile(const string& scriptFileName, const string& transpilationFileName) {
799  Console::printLine(scriptFileName + ": Processing script");
800  //
801  auto replace = [](const vector<string> input, const string& startTag, const string& endTag, const string& replacement, vector<string>& output) -> bool {
802  auto reject = false;
803  auto replaceSuccess = false;
804  for (auto i = 0; i < input.size(); i++) {
805  const auto& line = input[i];
806  auto trimmedLine = StringTools::trim(line);
807  if (StringTools::startsWith(trimmedLine, "//") == true) {
808  if (reject == false) output.push_back(line);
809  continue;
810  }
811  if (trimmedLine == startTag) {
812  reject = true;
813  output.push_back(line);
814  } else
815  if (trimmedLine == endTag) {
816  reject = false;
817  replaceSuccess = true;
818  output.push_back(replacement);
819  output.push_back(line);
820  } else {
821  if (reject == false) output.push_back(line);
822  }
823  }
824  //
825  return replaceSuccess;
826  };
827  // de-inject C++ definition code
828  {
829  vector<string> minitScriptClass;
830  vector<string> minitScriptClassNew;
831  auto transpilationCPPFileName = FileSystem::getPathName(transpilationFileName) + "/" + FileSystem::getFileName(transpilationFileName) + ".cpp";
832  FileSystem::getContentAsStringArray(FileSystem::getPathName(transpilationCPPFileName), FileSystem::getFileName(transpilationCPPFileName), minitScriptClass);
833  //
834  auto injectedGeneratedCode = false;
835  {
836  injectedGeneratedCode = replace(
837  minitScriptClass,
838  "/*__MINITSCRIPT_TRANSPILEDMINITSCRIPTCODE_INCLUDES_START__*/",
839  "/*__MINITSCRIPT_TRANSPILEDMINITSCRIPTCODE_INCLUDES_END__*/",
840  string(),
841  minitScriptClassNew
842  );
843  minitScriptClass = minitScriptClassNew;
844  minitScriptClassNew.clear();
845  }
846  if (injectedGeneratedCode == true) {
847  injectedGeneratedCode = replace(
848  minitScriptClass,
849  "/*__MINITSCRIPT_TRANSPILEDMINITSCRIPTCODE_USINGS_START__*/",
850  "/*__MINITSCRIPT_TRANSPILEDMINITSCRIPTCODE_USINGS_END__*/",
851  string(),
852  minitScriptClassNew
853  );
854  minitScriptClass = minitScriptClassNew;
855  minitScriptClassNew.clear();
856  }
857  if (injectedGeneratedCode == true) {
858  injectedGeneratedCode = replace(
859  minitScriptClass,
860  "/*__MINITSCRIPT_TRANSPILEDMINITSCRIPTCODE_DEFINITIONS_START__*/",
861  "/*__MINITSCRIPT_TRANSPILEDMINITSCRIPTCODE_DEFINITIONS_END__*/",
862  string(),
863  minitScriptClassNew
864  );
865  }
866  //
867  if (injectedGeneratedCode == false) {
868  Console::printLine(scriptFileName + ": Could not remove generated C++ code, are you missing the /*__MINITSCRIPT_TRANSPILEDMINITSCRIPTCODE_DEFINITIONS_START__*/ and /*__MINITSCRIPT_TRANSPILEDMINITSCRIPTCODE_DEFINITIONS_END__*/ markers in file " + transpilationFileName + "?");
869  } else {
870  //
871  FileSystem::setContentFromStringArray(
872  FileSystem::getPathName(transpilationCPPFileName),
873  FileSystem::getFileName(transpilationCPPFileName),
874  minitScriptClassNew
875  );
876  //
877  Console::printLine(scriptFileName + ": Removed generated C++ code in file " + transpilationCPPFileName + ". Dont forget to rebuild your sources.");
878  }
879  }
880  //
881  // inject C++ declaration code / header
882  {
883  vector<string> minitScriptClassHeader;
884  vector<string> minitScriptClassHeaderNew;
885  auto transpilationHeaderFileName = FileSystem::getPathName(transpilationFileName) + "/" + FileSystem::getFileName(transpilationFileName) + ".h";
886  FileSystem::getContentAsStringArray(FileSystem::getPathName(transpilationHeaderFileName), FileSystem::getFileName(transpilationHeaderFileName), minitScriptClassHeader);
887  //
888  auto injectedGeneratedCode = false;
889  injectedGeneratedCode = replace(
890  minitScriptClassHeader,
891  "/*__MINITSCRIPT_TRANSPILEDMINITSCRIPTCODE_DECLARATIONS_START__*/",
892  "/*__MINITSCRIPT_TRANSPILEDMINITSCRIPTCODE_DECLARATIONS_END__*/",
893  string(),
894  minitScriptClassHeaderNew
895  );
896  //
897  if (injectedGeneratedCode == false) {
898  Console::printLine(scriptFileName + ": Could not remove generated C++ code, are you missing the /*__MINITSCRIPT_TRANSPILEDMINITSCRIPTCODE_DECLARATIONS_START__*/ and /*__MINITSCRIPT_TRANSPILEDMINITSCRIPTCODE_DECLARATIONS_END__*/ markers in file " + transpilationFileName + "?");
899  } else {
900  //
901  FileSystem::setContentFromStringArray(
902  FileSystem::getPathName(transpilationHeaderFileName),
903  FileSystem::getFileName(transpilationHeaderFileName),
904  minitScriptClassHeaderNew
905  );
906  //
907  Console::printLine(scriptFileName + ": Removed generated C++ code in header file " + transpilationHeaderFileName + ". Dont forget to rebuild your sources.");
908  }
909  }
910 }
911 
912 const unordered_set<string> Transpiler::getAllClassesMethodNames(MinitScript* minitScript) {
913  unordered_set<string> allMethods;
914  for (auto scriptMethod: minitScript->getMethods()) {
915  string className;
916  if (scriptMethod->getMethodName().rfind("::") != string::npos) className = StringTools::substring(scriptMethod->getMethodName(), 0, scriptMethod->getMethodName().rfind("::"));
917  if (className.empty() == true) continue;
918  string method =
919  StringTools::substring(
920  scriptMethod->getMethodName(),
921  className.size() + 2,
922  scriptMethod->getMethodName().size());
923  // first argument name of method must equal the name of the class
924  if (scriptMethod->getArgumentTypes().empty() == true ||
925  scriptMethod->getArgumentTypes()[0].name != StringTools::toLowerCase(className)) continue;
926  // first argument of method must be of type of the class
927  if (className != MinitScript::Variable::getTypeAsString(scriptMethod->getArgumentTypes()[0].type)) continue; //
928  allMethods.insert(method);
929  }
930  //
931  return allMethods;
932 }
933 
934 const vector<string> Transpiler::getAllClassesMethodNamesSorted(MinitScript* minitScript) {
935  auto allMethods = getAllClassesMethodNames(minitScript);
936  //
937  vector<string> result;
938  for (auto method: allMethods) result.push_back(method);
939  sort(result.begin(), result.end());
940  //
941  return result;
942 }
943 
944 const unordered_map<string, vector<string>> Transpiler::getClassesMethodNames(MinitScript* minitScript) {
945  unordered_map<string, vector<string>> methodByClasses;
946  for (auto scriptMethod: minitScript->getMethods()) {
947  string className;
948  if (scriptMethod->getMethodName().rfind("::") != string::npos) className = StringTools::substring(scriptMethod->getMethodName(), 0, scriptMethod->getMethodName().rfind("::"));
949  if (className.empty() == true) continue;
950  string method =
951  StringTools::substring(
952  scriptMethod->getMethodName(),
953  className.size() + 2,
954  scriptMethod->getMethodName().size());
955  // first argument name of method must equal the name of the class
956  if (scriptMethod->getArgumentTypes().empty() == true ||
957  scriptMethod->getArgumentTypes()[0].name != StringTools::toLowerCase(className)) continue;
958  // first argument of method must be of type of the class
959  if (className != MinitScript::Variable::getTypeAsString(scriptMethod->getArgumentTypes()[0].type)) continue;
960  //
961  methodByClasses[className].push_back(method);
962  }
963  //
964  return methodByClasses;
965 }
966 
967 void Transpiler::determineVariables(MinitScript* minitScript, unordered_set<string>& globalVariables, vector<unordered_set<string>>& localVariables) {
968  // TODO:
969  // Problem: Variables can be set before read or read before set
970  // we allow to read from GLOBAL variable also if local variable has not been found
971  // One fix would be to: Dont look variable up in parent context, but rather also need a $GLOBAL accessor
972  //
973  const auto& scripts = minitScript->getScripts();
974  //
975  {
976  auto scriptIdx = 0;
977  for (const auto& script: scripts) {
978  //
979  determineVariables(MinitScript::SCRIPTIDX_NONE, script.conditionSyntaxTree, globalVariables, localVariables);
980  //
981  for (auto statementIdx = 0; statementIdx < script.statements.size(); statementIdx++) {
982  determineVariables(
983  script.type == MinitScript::Script::TYPE_FUNCTION || script.type == MinitScript::Script::TYPE_STACKLET?
984  scriptIdx:
985  MinitScript::SCRIPTIDX_NONE,
986  script.syntaxTree[statementIdx],
987  globalVariables,
988  localVariables
989  );
990  }
991  //
992  scriptIdx++;
993  }
994  }
995  // move stacklet variables into their scopes
996  for (auto scriptIdx = 0; scriptIdx < localVariables.size(); scriptIdx++) {
997  const auto& script = scripts[scriptIdx];
998  if (script.type != MinitScript::Script::TYPE_STACKLET) continue;
999  //
1000  auto stackletScopeScriptIdx = minitScript->getStackletScopeScriptIdx(scriptIdx);
1001  for (const auto& variable: localVariables[scriptIdx]) {
1002  if (stackletScopeScriptIdx == MinitScript::SCRIPTIDX_NONE) {
1003  globalVariables.insert(variable);
1004  } else {
1005  localVariables[stackletScopeScriptIdx].insert(variable);
1006  }
1007  }
1008  localVariables[scriptIdx].clear();
1009  }
1010 }
1011 
1012 void Transpiler::determineVariables(int scriptIdx, const MinitScript::SyntaxTreeNode& syntaxTreeNode, unordered_set<string>& globalVariables, vector<unordered_set<string>>& localVariables) {
1013  auto addVariable = [&](const string& variableStatement) -> void {
1014  //
1015  if (scriptIdx == MinitScript::SCRIPTIDX_NONE ||
1016  StringTools::startsWith(variableStatement, "$$.") == true ||
1017  StringTools::startsWith(variableStatement, "$GLOBAL.") == true) {
1018  //
1019  if (StringTools::startsWith(variableStatement, "$$.") == true) {
1020  const auto variableName = createVariableName("$" + StringTools::substring(variableStatement, string_view("$$.").size()));
1021  globalVariables.insert(variableName);
1022  } else
1023  if (StringTools::startsWith(variableStatement, "$GLOBAL.") == true) {
1024  const auto variableName = createVariableName("$" + StringTools::substring(variableStatement, string_view("$GLOBAL.").size()));
1025  globalVariables.insert(variableName);
1026  } else {
1027  const auto variableName = createVariableName(variableStatement);
1028  globalVariables.insert(variableName);
1029  }
1030  } else {
1031  const auto variableName = createVariableName(variableStatement);
1032  localVariables[scriptIdx].insert(variableName);
1033  }
1034  };
1035  //
1036  switch (syntaxTreeNode.type) {
1037  case MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_LITERAL:
1038  {
1039  break;
1040  }
1041  case MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_FUNCTION:
1042  {
1043  for (const auto& argument: syntaxTreeNode.arguments) determineVariables(scriptIdx, argument, globalVariables, localVariables);
1044  //
1045  break;
1046  }
1047  case MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_METHOD:
1048  {
1049  if ((syntaxTreeNode.value.getValueAsString() == "getMethodArgumentVariable" ||
1050  syntaxTreeNode.value.getValueAsString() == "getVariable" ||
1051  syntaxTreeNode.value.getValueAsString() == "getVariableReference" ||
1052  syntaxTreeNode.value.getValueAsString() == "setVariable" ||
1053  syntaxTreeNode.value.getValueAsString() == "setVariableReference" ||
1054  syntaxTreeNode.value.getValueAsString() == "setConstant" ||
1055  syntaxTreeNode.value.getValueAsString() == "unsetVariableReference") &&
1056  syntaxTreeNode.arguments.empty() == false &&
1057  syntaxTreeNode.arguments[0].type == MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_LITERAL) {
1058  //
1059  addVariable(syntaxTreeNode.arguments[0].value.getValueAsString());
1060  } else
1061  if (syntaxTreeNode.value.getValueAsString() == "internal.script.evaluateMemberAccess") {
1062  if (syntaxTreeNode.arguments[0].value.getType() == MinitScript::TYPE_STRING) {
1063  addVariable(syntaxTreeNode.arguments[0].value.getValueAsString());
1064  }
1065  for (auto i = 3; i < syntaxTreeNode.arguments.size(); i+= 2) {
1066  if (syntaxTreeNode.arguments[i].value.getType() == MinitScript::TYPE_STRING) {
1067  addVariable(syntaxTreeNode.arguments[i].value.getValueAsString());
1068  }
1069  }
1070  }
1071  //
1072  for (const auto& argument: syntaxTreeNode.arguments) determineVariables(scriptIdx, argument, globalVariables, localVariables);
1073  //
1074  break;
1075  }
1076  default:
1077  break;
1078  }
1079 }
1080 
1081 void Transpiler::gatherMethodCode(
1082  const vector<string>& minitScriptExtensionsCode,
1083  const string& className,
1084  int registerLine,
1085  unordered_map<string, vector<string>>& methodCodeMap
1086 ) {
1087  // TODO: this is a bit ugly and can be improved a lot, lets see and get this to work first
1088  auto classDefinitionLine = -1;
1089  // get class definition start line
1090  for (auto i = registerLine; i >= 0; i--) {
1091  const auto& line = minitScriptExtensionsCode[i];
1092  auto trimmedLine = StringTools::trim(line);
1093  if (StringTools::regexMatch(trimmedLine, "class[\\ \\t]+" + className + "[\\ \\t]*:.*") == true) {
1094  classDefinitionLine = i;
1095  break;
1096  }
1097  }
1098  // nope
1099  if (classDefinitionLine == -1) {
1100  Console::printLine("Transpiler::gatherMethodCode(): did not found '" + className + "' definition");
1101  return;
1102  }
1103  //
1104  auto curlyBracketCount = 0;
1105  auto finished = false;
1106  auto haveExecuteMethodDeclaration = false;
1107  auto executeMethodCurlyBracketStart = -1;
1108  auto haveGetMethodNameDeclaration = false;
1109  auto getMethodNameCurlyBracketStart = -1;
1110  vector<string> executeMethodCode;
1111  string getMethodNameCode;
1112  for (auto i = classDefinitionLine; finished == false && i < minitScriptExtensionsCode.size(); i++) {
1113  const auto& line = minitScriptExtensionsCode[i];
1114  auto trimmedLine = StringTools::trim(line);
1115  // have getMethodName declaration, with following body
1116  if (StringTools::regexMatch(trimmedLine, "const[\\ \\t]+string[\\ \\t]+getMethodName()[\\ \\t]*\\(.*") == true) {
1117  haveGetMethodNameDeclaration = true;
1118  }
1119  // have executeMethod declaration, with following body
1120  if (StringTools::regexMatch(trimmedLine, "void[\\ \\t]+executeMethod[\\ \\t]*\\(.*") == true) {
1121  haveExecuteMethodDeclaration = true;
1122  }
1123  //
1124  for (auto j = 0; j < trimmedLine.size(); j++) {
1125  auto c = trimmedLine[j];
1126  if (c == '{') {
1127  curlyBracketCount++;
1128  // new code block,
1129  // if we have the declaration mark this curly bracket as executeMethod implementation start curly bracket
1130  if (haveExecuteMethodDeclaration == true) {
1131  executeMethodCurlyBracketStart = curlyBracketCount;
1132  }
1133  // if we have the declaration mark this curly bracket as getMethodName implementation start curly bracket
1134  if (haveGetMethodNameDeclaration == true) {
1135  getMethodNameCurlyBracketStart = curlyBracketCount;
1136  }
1137  } else
1138  if (c == '}') {
1139  // do we just leave our getMethodName implementation?
1140  if (getMethodNameCurlyBracketStart != -1 && curlyBracketCount == getMethodNameCurlyBracketStart) {
1141  // yup
1142  getMethodNameCurlyBracketStart = -1;
1143  }
1144  // do we just leave our executeMethod implementation?
1145  if (executeMethodCurlyBracketStart != -1 && curlyBracketCount == executeMethodCurlyBracketStart) {
1146  // yup
1147  executeMethodCurlyBracketStart = -1;
1148  }
1149  //
1150  curlyBracketCount--;
1151  // get out of here :D
1152  if (curlyBracketCount <= 0) {
1153  finished = true;
1154  break;
1155  }
1156  }
1157  }
1158  // is this getMethodName code?
1159  if (haveGetMethodNameDeclaration == false && getMethodNameCurlyBracketStart != -1 && curlyBracketCount >= getMethodNameCurlyBracketStart) {
1160  getMethodNameCode+= trimmedLine;
1161  }
1162  // is this executeMethod code?
1163  if (haveExecuteMethodDeclaration == false && executeMethodCurlyBracketStart != -1 && curlyBracketCount >= executeMethodCurlyBracketStart) {
1164  executeMethodCode.push_back(line);
1165  }
1166  // do we still process getMethodName declaration
1167  if (haveGetMethodNameDeclaration == true) {
1168  // ok unset
1169  haveGetMethodNameDeclaration = false;
1170  }
1171  // do we still process executeMethod declaration
1172  if (haveExecuteMethodDeclaration == true) {
1173  // ok unset
1174  haveExecuteMethodDeclaration = false;
1175  }
1176  }
1177 
1178  // determine method name
1179  string methodName;
1180  {
1181  auto haveMethodName = false;
1182  for (auto i = 0; i < getMethodNameCode.size(); i++) {
1183  auto c = getMethodNameCode[i];
1184  if (c == '"') {
1185  if (haveMethodName == false) haveMethodName = true; else
1186  break;
1187  } else
1188  if (haveMethodName == true) {
1189  methodName+= c;
1190  }
1191  }
1192  }
1193 
1194  // find min indent from method code and depth indent
1195  int minIndent = Integer::MAX_VALUE;
1196  for (const auto& codeLine: executeMethodCode) {
1197  auto indent = 0;
1198  for (auto i = 0; i < codeLine.size(); i++) {
1199  auto c = codeLine[i];
1200  if (c == '\t') {
1201  indent++;
1202  } else {
1203  break;
1204  }
1205  }
1206  minIndent = Math::min(minIndent, indent);
1207  }
1208 
1209  // remove indent
1210  for (auto& codeLine: executeMethodCode) {
1211  codeLine = StringTools::substring(codeLine, minIndent);
1212  }
1213 
1214  //
1215  auto methodCodeMapIt = methodCodeMap.find(methodName);
1216  if (methodCodeMapIt != methodCodeMap.end()) {
1217  Console::printLine("Transpiler::gatherMethodCode(): Not registering method with methodName: '" + methodName + "': method already registered");
1218  return;
1219  }
1220 
1221  //
1222  Console::printLine("Transpiler::gatherMethodCode(): registering method with methodName: '" + methodName + "'");
1223 
1224  //
1225  methodCodeMap[methodName] = executeMethodCode;
1226 }
1227 
1228 void Transpiler::generateVariableAccess(
1229  MinitScript* minitScript,
1230  string& generatedCode,
1231  int scriptConditionIdx,
1232  int scriptIdx,
1233  const string& variableName,
1234  const string& indent,
1235  bool getMethodArgumentVariable,
1236  bool getVariable,
1237  bool getVariableReference,
1238  bool setVariable,
1239  bool setVariableReference,
1240  bool setConstant,
1241  const string& returnValueStatement,
1242  const string& statementEnd,
1243  int getArgumentIdx,
1244  int setArgumentIdx
1245 ) {
1246  //
1247  auto haveFunction = false;
1248  auto haveScript = (scriptConditionIdx != MinitScript::SCRIPTIDX_NONE || scriptIdx != MinitScript::SCRIPTIDX_NONE);
1249  if (haveScript == true) {
1250  const auto& script = minitScript->getScripts()[scriptConditionIdx != MinitScript::SCRIPTIDX_NONE?scriptConditionIdx:scriptIdx];
1251  haveFunction =
1252  script.type == MinitScript::Script::TYPE_FUNCTION ||
1253  (script.type == MinitScript::Script::TYPE_STACKLET && minitScript->getStackletScopeScriptIdx(scriptIdx) != MinitScript::SCRIPTIDX_NONE);
1254  }
1255  //
1256  auto dollarDollarVariable = StringTools::startsWith(variableName, "$$.");
1257  auto dollarGlobalVariable = StringTools::startsWith(variableName, "$GLOBAL.");
1258  if (haveFunction == true ||
1259  dollarDollarVariable == true ||
1260  dollarGlobalVariable == true) {
1261  //
1262  if (dollarDollarVariable == true || dollarGlobalVariable == true) {
1263  auto globalVariableIdx = 0;
1264  string globalVariable;
1265  if (dollarDollarVariable == true) {
1266  globalVariable = "$" + StringTools::substring(variableName, string_view("$$.").size());
1267  globalVariableIdx = 3;
1268  }
1269  if (dollarGlobalVariable == true) {
1270  globalVariable = "$" + StringTools::substring(variableName, string_view("$GLOBAL.").size());
1271  globalVariableIdx = 8;
1272  }
1273  auto haveVariableStatement = variableHasStatement(globalVariable);
1274  if (getMethodArgumentVariable == true) {
1275  if (haveVariableStatement == true) {
1276  generatedCode+= indent + returnValueStatement + "getMethodArgumentVariable(&" + createGlobalVariableName(globalVariable) + ", \"$\" + StringTools::substring(arguments[" + to_string(getArgumentIdx) + "].getValueAsString(), " + to_string(globalVariableIdx) + "), &subStatement)" + statementEnd;
1277  } else {
1278  generatedCode+= indent + returnValueStatement + "Variable::createMethodArgumentVariable(&" + createGlobalVariableName(globalVariable) + ")" + statementEnd;
1279  }
1280  } else
1281  if (getVariable == true) {
1282  if (haveVariableStatement == true) {
1283  generatedCode+= indent + returnValueStatement + "getVariable(&" + createGlobalVariableName(globalVariable) + ", \"$\" + StringTools::substring(arguments[" + to_string(getArgumentIdx) + "].getValueAsString(), " + to_string(globalVariableIdx) + "), &subStatement, false)" + statementEnd;
1284  } else {
1285  generatedCode+= indent + returnValueStatement + "Variable::createNonReferenceVariable(&" + createGlobalVariableName(globalVariable) + ")" + statementEnd;
1286  }
1287  } else
1288  if (getVariableReference == true) {
1289  if (haveVariableStatement == true) {
1290  generatedCode+= indent + returnValueStatement + "getVariable(&" + createGlobalVariableName(globalVariable) + ", \"$\" + StringTools::substring(arguments[" + to_string(getArgumentIdx) + "].getValueAsString(), " + to_string(globalVariableIdx) + "), &subStatement, true)" + statementEnd;
1291  } else {
1292  generatedCode+= indent + returnValueStatement + "Variable::createReferenceVariable(&" + createGlobalVariableName(globalVariable) + ")" + statementEnd;
1293  }
1294  } else
1295  if (setVariable == true || setConstant == true) {
1296  if (haveVariableStatement == true) {
1297  if (setConstant == true) {
1298  generatedCode+= indent + "setConstant(&" + createGlobalVariableName(globalVariable) + ", \"$\" + StringTools::substring(arguments[" + to_string(getArgumentIdx) + "].getValueAsString(), " + to_string(globalVariableIdx) + "), arguments[" + to_string(setArgumentIdx) + "], &subStatement); returnValue = arguments[" + to_string(setArgumentIdx) + "]" + statementEnd;
1299  } else {
1300  generatedCode+= indent + "setVariable(&" + createGlobalVariableName(globalVariable) + ", \"$\" + StringTools::substring(arguments[" + to_string(getArgumentIdx) + "].getValueAsString(), " + to_string(globalVariableIdx) + "), arguments[" + to_string(setArgumentIdx) + "].isConstant() == true?MinitScript::Variable::createNonConstVariable(&arguments[" + to_string(setArgumentIdx) + "]):arguments[" + to_string(setArgumentIdx) + "], &subStatement); returnValue = arguments[" + to_string(setArgumentIdx) + "]" + statementEnd;
1301  }
1302  } else {
1303  generatedCode+= indent + "if (" + createGlobalVariableName(globalVariable) + ".isConstant() == true) _Console::printLine(getStatementInformation(statement) + \": Constant: " + globalVariable + ": assignment of constant is not allowed\"); else ";
1304  if (setConstant == true) {
1305  generatedCode+= "{ ";
1306  generatedCode+= createGlobalVariableName(globalVariable) + ".setValue(arguments[" + to_string(setArgumentIdx) + "]); ";
1307  generatedCode+= "setConstant(" + createGlobalVariableName(globalVariable) + "); ";
1308  generatedCode+= "} ";
1309  } else {
1310  generatedCode+= createGlobalVariableName(globalVariable) + ".setValue(arguments[" + to_string(setArgumentIdx) + "].isConstant() == true?MinitScript::Variable::createNonConstVariable(&arguments[" + to_string(setArgumentIdx) + "]):arguments[" + to_string(setArgumentIdx) + "]); ";
1311  }
1312  generatedCode+= returnValueStatement + "arguments[" + to_string(setArgumentIdx) + "]" + statementEnd;
1313  }
1314  } else
1315  if (setVariableReference == true) {
1316  if (haveVariableStatement == true) {
1317  generatedCode+= indent + "setVariable(&" + createGlobalVariableName(globalVariable) + ", \"$\" + StringTools::substring(arguments[" + to_string(getArgumentIdx) + "].getValueAsString(), " + to_string(globalVariableIdx) + "), arguments[" + to_string(setArgumentIdx) + "], &subStatement, true); returnValue = arguments[" + to_string(setArgumentIdx) + "]" + statementEnd;
1318  } else {
1319  generatedCode+= indent + "if (" + createGlobalVariableName(globalVariable) + ".isConstant() == true) _Console::printLine(getStatementInformation(statement) + \": Constant: " + globalVariable + ": assignment of constant is not allowed\"); else ";
1320  generatedCode+= createGlobalVariableName(globalVariable) + ".setReference(&arguments[" + to_string(setArgumentIdx) + "]); " + returnValueStatement + "arguments[" + to_string(setArgumentIdx) + "]" + statementEnd;
1321  }
1322  }
1323  } else {
1324  const auto& localVariable = variableName;
1325  auto haveVariableStatement = variableHasStatement(localVariable);
1326  if (getMethodArgumentVariable == true) {
1327  if (haveVariableStatement == true) {
1328  generatedCode+= indent + returnValueStatement + "getMethodArgumentVariable(&_lv." + createLocalVariableName(localVariable) + ", arguments[" + to_string(getArgumentIdx) + "].getValueAsString(), &subStatement)" + statementEnd;
1329  } else {
1330  generatedCode+= indent + returnValueStatement + "Variable::createMethodArgumentVariable(&_lv." + createLocalVariableName(localVariable) + ")" + statementEnd;
1331  }
1332  } else
1333  if (getVariable == true) {
1334  if (haveVariableStatement == true) {
1335  generatedCode+= indent + returnValueStatement + "getVariable(&_lv." + createLocalVariableName(localVariable) + ", arguments[" + to_string(getArgumentIdx) + "].getValueAsString(), &subStatement, false)" + statementEnd;
1336  } else {
1337  generatedCode+= indent + returnValueStatement + "Variable::createNonReferenceVariable(&_lv." + createLocalVariableName(localVariable) + ")" + statementEnd;
1338  }
1339  } else
1340  if (getVariableReference == true) {
1341  if (haveVariableStatement == true) {
1342  generatedCode+= indent + returnValueStatement + "getVariable(&_lv." + createLocalVariableName(localVariable) + ", arguments[" + to_string(getArgumentIdx) + "].getValueAsString(), &subStatement, true)" + statementEnd;
1343  } else {
1344  generatedCode+= indent + returnValueStatement + "Variable::createReferenceVariable(&_lv." + createLocalVariableName(localVariable) + ")" + statementEnd;
1345  }
1346  } else
1347  if (setVariable == true || setConstant == true) {
1348  if (haveVariableStatement == true) {
1349  if (setConstant == true) {
1350  generatedCode+= indent + "setConstant(&_lv." + createLocalVariableName(localVariable) + ", arguments[" + to_string(getArgumentIdx) + "].getValueAsString(), arguments[" + to_string(setArgumentIdx) + "], &subStatement); returnValue = arguments[" + to_string(setArgumentIdx) + "]" + statementEnd;
1351  } else {
1352  generatedCode+= indent + "setVariable(&_lv." + createLocalVariableName(localVariable) + ", arguments[" + to_string(getArgumentIdx) + "].getValueAsString(), arguments[" + to_string(setArgumentIdx) + "].isConstant() == true?MinitScript::Variable::createNonConstVariable(&arguments[" + to_string(setArgumentIdx) + "]):arguments[" + to_string(setArgumentIdx) + "], &subStatement); returnValue = arguments[" + to_string(setArgumentIdx) + "]" + statementEnd;
1353  }
1354  } else {
1355  generatedCode+= indent + "if (_lv." + createLocalVariableName(localVariable) + ".isConstant() == true) _Console::printLine(getStatementInformation(statement) + \": Constant: " + localVariable + ": assignment of constant is not allowed\"); else ";
1356  if (setConstant == true) {
1357  generatedCode+= "{ ";
1358  generatedCode+= "_lv." + createLocalVariableName(localVariable) + ".setValue(arguments[" + to_string(setArgumentIdx) + "]); ";
1359  generatedCode+= "setConstant(_lv." + createLocalVariableName(localVariable) + "); ";
1360  generatedCode+= "} ";
1361  } else {
1362  generatedCode+= "_lv." + createLocalVariableName(localVariable) + ".setValue(arguments[" + to_string(setArgumentIdx) + "].isConstant() == true?MinitScript::Variable::createNonConstVariable(&arguments[" + to_string(setArgumentIdx) + "]):arguments[" + to_string(setArgumentIdx) + "]); ";
1363  }
1364  generatedCode+= returnValueStatement + "arguments[" + to_string(setArgumentIdx) + "]" + statementEnd;
1365  }
1366  } else
1367  if (setVariableReference == true) {
1368  if (haveVariableStatement == true) {
1369  generatedCode+= indent + "setVariable(&_lv." + createLocalVariableName(localVariable) + ", arguments[" + to_string(getArgumentIdx) + "].getValueAsString(), arguments[" + to_string(setArgumentIdx) + "], &subStatement, true); returnValue = arguments[" + to_string(setArgumentIdx) + "]" + statementEnd;
1370  } else {
1371  generatedCode+= indent + "if (_lv." + createLocalVariableName(localVariable) + ".isConstant() == true) _Console::printLine(getStatementInformation(statement) + \": Constant: " + localVariable + ": assignment of constant is not allowed\"); else ";
1372  generatedCode+= "_lv." + createLocalVariableName(localVariable) + ".setReference(&arguments[" + to_string(setArgumentIdx) + "]); " + returnValueStatement + "arguments[" + to_string(setArgumentIdx) + "]" + statementEnd;
1373  }
1374  }
1375  }
1376  } else {
1377  //
1378  const auto& globalVariable = variableName;
1379  auto haveVariableStatement = variableHasStatement(globalVariable);
1380  if (getMethodArgumentVariable == true) {
1381  if (haveVariableStatement == true) {
1382  generatedCode+= indent + returnValueStatement + "getMethodArgumentVariable(&" + createGlobalVariableName(globalVariable) + ", arguments[" + to_string(getArgumentIdx) + "].getValueAsString(), &subStatement)" + statementEnd;
1383  } else {
1384  generatedCode+= indent + returnValueStatement + "Variable::createMethodArgumentVariable(&" + createGlobalVariableName(globalVariable) + ")" + statementEnd;
1385  }
1386  } else
1387  if (getVariable == true) {
1388  if (haveVariableStatement == true) {
1389  generatedCode+= indent + returnValueStatement + "getVariable(&" + createGlobalVariableName(globalVariable) + ", arguments[" + to_string(getArgumentIdx) + "].getValueAsString(), &subStatement, false)" + statementEnd;
1390  } else {
1391  generatedCode+= indent + returnValueStatement + "Variable::createNonReferenceVariable(&" + createGlobalVariableName(globalVariable) + ")" + statementEnd;
1392  }
1393  } else
1394  if (getVariableReference == true) {
1395  if (haveVariableStatement == true) {
1396  generatedCode+= indent + returnValueStatement + "getVariable(&" + createGlobalVariableName(globalVariable) + ", arguments[" + to_string(getArgumentIdx) + "].getValueAsString(), &subStatement, true)" + statementEnd;
1397  } else {
1398  generatedCode+= indent + returnValueStatement + "Variable::createReferenceVariable(&" + createGlobalVariableName(globalVariable) + ")" + statementEnd;
1399  }
1400  } else
1401  if (setVariable == true || setConstant == true) {
1402  if (haveVariableStatement == true) {
1403  if (setConstant == true) {
1404  generatedCode+= indent + "setConstant(&" + createGlobalVariableName(globalVariable) + ", arguments[" + to_string(getArgumentIdx) + "].getValueAsString(), arguments[" + to_string(setArgumentIdx) + "], &subStatement); " + returnValueStatement + "arguments[" + to_string(getArgumentIdx) + "]" + statementEnd;
1405  } else {
1406  generatedCode+= indent + "setVariable(&" + createGlobalVariableName(globalVariable) + ", arguments[" + to_string(getArgumentIdx) + "].getValueAsString(), arguments[" + to_string(setArgumentIdx) + "].isConstant() == true?MinitScript::Variable::createNonConstVariable(&arguments[" + to_string(setArgumentIdx) + "]):arguments[" + to_string(setArgumentIdx) + "], &subStatement); " + returnValueStatement + "arguments[" + to_string(getArgumentIdx) + "]" + statementEnd;
1407  }
1408  } else {
1409  generatedCode+= indent + "if (" + createGlobalVariableName(globalVariable) + ".isConstant() == true) _Console::printLine(getStatementInformation(statement) + \": Constant: " + globalVariable + ": assignment of constant is not allowed\"); else ";
1410  if (setConstant == true) {
1411  generatedCode+= "{ ";
1412  generatedCode+= createGlobalVariableName(globalVariable) + ".setValue(arguments[" + to_string(setArgumentIdx) + "]); ";
1413  generatedCode+= "setConstant(" + createGlobalVariableName(globalVariable) + "); ";
1414  generatedCode+= "} ";
1415  } else {
1416  generatedCode+= createGlobalVariableName(globalVariable) + ".setValue(arguments[" + to_string(setArgumentIdx) + "].isConstant() == true?MinitScript::Variable::createNonConstVariable(&arguments[" + to_string(setArgumentIdx) + "]):arguments[" + to_string(setArgumentIdx) + "]); ";
1417  }
1418  generatedCode+= returnValueStatement + "arguments[" + to_string(setArgumentIdx) + "]" + statementEnd;
1419  }
1420  } else
1421  if (setVariableReference == true) {
1422  if (haveVariableStatement == true) {
1423  generatedCode+= indent + "setVariable(&" + createGlobalVariableName(globalVariable) + ", arguments[" + to_string(getArgumentIdx) + "].getValueAsString(), arguments[" + to_string(setArgumentIdx) + "], &subStatement, true); " + returnValueStatement + "arguments[" + to_string(getArgumentIdx) + "]" + statementEnd;
1424  } else {
1425  generatedCode+= indent + "if (" + createGlobalVariableName(globalVariable) + ".isConstant() == true) _Console::printLine(getStatementInformation(statement) + \": Constant: " + globalVariable + ": assignment of constant is not allowed\"); else ";
1426  generatedCode+= createGlobalVariableName(globalVariable) + ".setReference(&arguments[" + to_string(setArgumentIdx) + "]); " + returnValueStatement + "arguments[" + to_string(setArgumentIdx) + "]" + statementEnd;
1427  }
1428  }
1429  }
1430 }
1431 
1432 void Transpiler::generateArrayAccessMethods(
1433  MinitScript* minitScript,
1434  string& generatedDefinitions,
1435  const string& minitScriptClassName,
1436  int scriptConditionIdx,
1437  int scriptIdx,
1438  const string& methodName,
1439  const MinitScript::SyntaxTreeNode& syntaxTree,
1440  const MinitScript::Statement& statement,
1441  const unordered_map<string, vector<string>>& methodCodeMap,
1442  const unordered_set<string>& allMethods,
1443  bool condition,
1444  const vector<int>& argumentIndices,
1445  int depth
1446  ) {
1447  //
1448  switch (syntaxTree.type) {
1449  case MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_LITERAL:
1450  {
1451  break;
1452  }
1453  case MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_METHOD:
1454  {
1455  if (syntaxTree.value.getValueAsString() == "getMethodArgumentVariable" ||
1456  syntaxTree.value.getValueAsString() == "getVariable" ||
1457  syntaxTree.value.getValueAsString() == "getVariableReference" ||
1458  syntaxTree.value.getValueAsString() == "setVariable" ||
1459  syntaxTree.value.getValueAsString() == "setVariableReference" ||
1460  syntaxTree.value.getValueAsString() == "setConstant") {
1461  //
1462  auto lamdaIndent = StringTools::indent(string(), "\t", depth);
1463  //
1464  for (auto argumentIdx = 0; argumentIdx < syntaxTree.arguments.size(); argumentIdx++) {
1465  auto argumentString = escapeString(syntaxTree.arguments[argumentIdx].value.getValueAsString());
1466  //
1467  auto nextArgumentIndices = argumentIndices;
1468  nextArgumentIndices.push_back(argumentIdx);
1469  // ignore array and map initializers
1470  if (StringTools::startsWith(argumentString, "[") == true ||
1471  StringTools::startsWith(argumentString, "{") == true) continue;
1472  //
1473  auto arrayAccessStatementIdx = 0;
1474  auto arrayAccessStatementLeftIdx = -1;
1475  auto arrayAccessStatementRightIdx = -1;
1476  auto quote = '\0';
1477  auto bracketCount = 0;
1478  for (auto i = 0; i < argumentString.size(); i++) {
1479  auto c = argumentString[i];
1480  // handle quotes
1481  if (quote != '\0') {
1482  // unset quote if closed
1483  // also we can ignore content of quote blocks
1484  if (c == quote) {
1485  quote = '\0';
1486  }
1487  } else
1488  if (c == '"' || c == '\'') {
1489  quote = c;
1490  } else
1491  if (c == '[') {
1492  if (bracketCount == 0) arrayAccessStatementLeftIdx = i;
1493  bracketCount++;
1494  } else
1495  if (c == ']') {
1496  bracketCount--;
1497  if (bracketCount == 0) {
1498  arrayAccessStatementRightIdx = i;
1499  //
1500  auto arrayAccessStatementString = StringTools::substring(argumentString, arrayAccessStatementLeftIdx + 1, arrayAccessStatementRightIdx);
1501  // array append operator []
1502  if (arrayAccessStatementString.empty() == true) {
1503  //
1504  arrayAccessStatementIdx++;
1505  //
1506  continue;
1507  }
1508  // check if literal
1509  MinitScript::Variable arrayAccessStatementAsScriptVariable;
1510  //
1511  arrayAccessStatementAsScriptVariable.setImplicitTypedValue(arrayAccessStatementString, minitScript, scriptIdx, statement);
1512  switch (arrayAccessStatementAsScriptVariable.getType()) {
1513  case MinitScript::TYPE_BOOLEAN:
1514  {
1515  bool booleanValue;
1516  if (arrayAccessStatementAsScriptVariable.getBooleanValue(booleanValue) == true) {
1517  generatedDefinitions+= lamdaIndent + "// MinitScript transpilation for a " + (condition == true?"condition":"statement") + " array access statement, statement index " + to_string(statement.statementIdx) + ", argument indices " + MinitScript::getArgumentIndicesAsString(nextArgumentIndices, ", ") + ", array access statement index " + to_string(arrayAccessStatementIdx) + "\n";
1518  generatedDefinitions+= lamdaIndent + "auto array_access_statement_" + to_string(statement.statementIdx) + "_" + MinitScript::getArgumentIndicesAsString(nextArgumentIndices, "_") + "_" + to_string(arrayAccessStatementIdx) + " = [&](const SubStatement& subStatement) -> Variable {" + "\n";
1519  generatedDefinitions+= lamdaIndent + " return Variable(" + (booleanValue == true?"true":"false") + ");" + "\n";
1520  generatedDefinitions+= lamdaIndent + "};" + "\n";
1521  }
1522  // literals
1523  arrayAccessStatementIdx++;
1524  //
1525  continue;
1526  }
1527  case MinitScript::TYPE_INTEGER:
1528  {
1529  int64_t integerValue;
1530  if (arrayAccessStatementAsScriptVariable.getIntegerValue(integerValue) == true) {
1531  generatedDefinitions+= lamdaIndent + "// MinitScript transpilation for a " + (condition == true?"condition":"statement") + " array access statement, statement index " + to_string(statement.statementIdx) + ", argument indices " + MinitScript::getArgumentIndicesAsString(nextArgumentIndices, ", ") + ", array access statement index " + to_string(arrayAccessStatementIdx) + "\n";
1532  generatedDefinitions+= lamdaIndent + "auto array_access_statement_" + to_string(statement.statementIdx) + "_" + MinitScript::getArgumentIndicesAsString(nextArgumentIndices, "_") + "_" + to_string(arrayAccessStatementIdx) + " = [&](const SubStatement& subStatement) -> Variable {" + "\n";
1533  generatedDefinitions+= lamdaIndent + " return Variable(static_cast<int64_t>(" + to_string(integerValue) + "ll));" + "\n";
1534  generatedDefinitions+= lamdaIndent + "};" + "\n";
1535  }
1536  // literals
1537  arrayAccessStatementIdx++;
1538  //
1539  continue;
1540  }
1541  case MinitScript::TYPE_FLOAT:
1542  {
1543  float floatValue;
1544  if (arrayAccessStatementAsScriptVariable.getFloatValue(floatValue) == true) {
1545  generatedDefinitions+= lamdaIndent + "// MinitScript transpilation for a " + (condition == true?"condition":"statement") + " array access statement, statement index " + to_string(statement.statementIdx) + ", argument indices " + MinitScript::getArgumentIndicesAsString(nextArgumentIndices, ", ") + ", array access statement index " + to_string(arrayAccessStatementIdx) + "\n";
1546  generatedDefinitions+= lamdaIndent + "auto array_access_statement_" + to_string(statement.statementIdx) + "_" + MinitScript::getArgumentIndicesAsString(nextArgumentIndices, "_") + "_" + to_string(arrayAccessStatementIdx) + " = [&](const SubStatement& subStatement) -> Variable {" + "\n";
1547  generatedDefinitions+= lamdaIndent + " return Variable(static_cast<int64_t>(" + to_string(static_cast<int64_t>(floatValue)) + "ll));" + "\n";
1548  generatedDefinitions+= lamdaIndent + "};" + "\n";
1549  }
1550  // literals
1551  arrayAccessStatementIdx++;
1552  //
1553  continue;
1554  }
1555  default:
1556  break;
1557  }
1558  // variable?
1559  if (StringTools::startsWith(arrayAccessStatementString, "$") == true) arrayAccessStatementString = "getVariable(\"" + arrayAccessStatementString + "\")";
1560  // parse array access statment at current index
1561  string_view arrayAccessMethodName;
1562  vector<MinitScript::ParserArgument> arrayAccessArguments;
1563  string accessObjectMemberStatement;
1564  // create a pseudo statement (information)
1565  MinitScript::Statement arrayAccessStatement(
1566  statement.line,
1567  statement.statementIdx,
1568  arrayAccessStatementString,
1569  arrayAccessStatementString,
1570  MinitScript::STATEMENTIDX_NONE
1571  );
1572  // parse script statement
1573  if (minitScript->parseStatement(string_view(arrayAccessStatementString), arrayAccessMethodName, arrayAccessArguments, arrayAccessStatement, accessObjectMemberStatement) == false) {
1574  break;
1575  }
1576  // create syntax tree for this array access
1577  MinitScript::SyntaxTreeNode arrayAccessSyntaxTree;
1578  if (minitScript->createStatementSyntaxTree(scriptIdx, arrayAccessMethodName, arrayAccessArguments, arrayAccessStatement, arrayAccessSyntaxTree) == false) {
1579  break;
1580  }
1581 
1582  //
1583  string transpiledCode;
1584  auto statementIdx = MinitScript::STATEMENTIDX_FIRST;
1585  auto scriptStateChanged = false;
1586  auto scriptStopped = false;
1587  vector<string >enabledNamedConditions;
1588  transpileStatement(
1589  minitScript,
1590  transpiledCode,
1591  arrayAccessSyntaxTree,
1592  arrayAccessStatement,
1593  scriptConditionIdx,
1594  scriptIdx,
1595  statementIdx,
1596  methodCodeMap,
1597  allMethods,
1598  scriptStateChanged,
1599  scriptStopped,
1600  enabledNamedConditions,
1601  0,
1602  {},
1603  "Variable()",
1604  "return returnValue;",
1605  1
1606  );
1607  generatedDefinitions+= lamdaIndent + "// MinitScript transpilation for array access statement, statement index " + to_string(statement.statementIdx) + ", argument indices " + MinitScript::getArgumentIndicesAsString(nextArgumentIndices, ", ") + ", array access statement index " + to_string(arrayAccessStatementIdx) + "\n";
1608  generatedDefinitions+= lamdaIndent + "auto array_access_statement_" + to_string(statement.statementIdx) + "_" + MinitScript::getArgumentIndicesAsString(nextArgumentIndices, "_") + "_" + to_string(arrayAccessStatementIdx) + " = [&](const SubStatement& subStatement) -> Variable {" + "\n";
1609  generatedDefinitions+= lamdaIndent + "\t" + "// MinitScript setup" + "\n";
1610  generatedDefinitions+= lamdaIndent + "\t" + "auto minitScript = this;" + "\n";
1611  generatedDefinitions+= transpiledCode;
1612  generatedDefinitions+= lamdaIndent + "};" + "\n";
1613  //
1614  arrayAccessStatementIdx++;
1615  }
1616  }
1617  }
1618  }
1619  }
1620  //
1621  auto argumentIdx = 0;
1622  for (const auto& argument: syntaxTree.arguments) {
1623  //
1624  auto nextArgumentIndices = argumentIndices;
1625  nextArgumentIndices.push_back(argumentIdx);
1626  //
1627  generateArrayAccessMethods(
1628  minitScript,
1629  generatedDefinitions,
1630  minitScriptClassName,
1631  scriptConditionIdx,
1632  scriptIdx,
1633  methodName,
1634  argument,
1635  statement,
1636  methodCodeMap,
1637  allMethods,
1638  condition,
1639  nextArgumentIndices,
1640  depth
1641  );
1642  //
1643  argumentIdx++;
1644  }
1645  }
1646  break;
1647  case MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_FUNCTION:
1648  {
1649  auto argumentIdx = 1;
1650  for (const auto& argument: syntaxTree.arguments) {
1651  //
1652  auto nextArgumentIndices = argumentIndices;
1653  nextArgumentIndices.push_back(argumentIdx);
1654  //
1655  generateArrayAccessMethods(
1656  minitScript,
1657  generatedDefinitions,
1658  minitScriptClassName,
1659  scriptConditionIdx,
1660  scriptIdx,
1661  methodName,
1662  argument,
1663  statement,
1664  methodCodeMap,
1665  allMethods,
1666  condition,
1667  nextArgumentIndices,
1668  depth
1669  );
1670  //
1671  argumentIdx++;
1672  }
1673  //
1674  break;
1675  }
1676  default:
1677  break;
1678  }
1679 }
1680 
1681 void Transpiler::generateEvaluateMemberAccessArrays(
1682  MinitScript* minitScript,
1683  vector<string>& generatedDeclarations,
1684  vector<string>& generatedDefinitions
1685 ) {
1686  //
1687  auto scriptMethods = minitScript->getMethods();
1688  auto allMethods = getAllClassesMethodNamesSorted(minitScript);
1689  auto methodsByClasses = getClassesMethodNames(minitScript);
1690  generatedDeclarations.push_back("// evaluate member access constants");
1691  generatedDeclarations.push_back("static constexpr int EVALUATEMEMBERACCESSARRAYIDX_NONE { -1 };");
1692  auto methodIdx = 0;
1693  for (const auto& method: allMethods) {
1694  generatedDeclarations.push_back("static constexpr int EVALUATEMEMBERACCESSARRAYIDX_" + StringTools::toUpperCase(method) + " { " + to_string(methodIdx) + " };");
1695  methodIdx++;
1696  }
1697  generatedDeclarations.push_back("");
1698  generatedDeclarations.push_back("// evaluate member access arrays");
1699  generatedDeclarations.push_back(
1700  "array<array<Method*, " +
1701  to_string(methodIdx) +
1702  ">, " +
1703  to_string((static_cast<int>(MinitScript::TYPE_PSEUDO_DATATYPES + MinitScript::getDataTypes().size()) - static_cast<int>(MinitScript::TYPE_STRING))) +
1704  "> evaluateMemberAccessArrays {};"
1705  );
1706  generatedDefinitions.push_back("evaluateMemberAccessArrays = {};");
1707  for (auto typeIdx = static_cast<int>(MinitScript::TYPE_STRING); typeIdx < static_cast<int>(MinitScript::TYPE_PSEUDO_DATATYPES + MinitScript::getDataTypes().size()); typeIdx++) {
1708  const auto& className = MinitScript::Variable::getTypeAsString(static_cast<MinitScript::VariableType>(typeIdx));
1709  const auto& methods = methodsByClasses[className];
1710  auto methodIdx = 0;
1711  for (const auto& method: allMethods) {
1712  //
1713  if (std::find(methods.begin(), methods.end(), method) == methods.end()) {
1714  methodIdx++;
1715  continue;
1716  }
1717  //
1718  generatedDefinitions.push_back("evaluateMemberAccessArrays[" + to_string(typeIdx - static_cast<int>(MinitScript::TYPE_STRING)) + "][" + "EVALUATEMEMBERACCESSARRAYIDX_" + StringTools::toUpperCase(method) + "] = getMethod(\"" + className + "::" + method + "\");");
1719  methodIdx++;
1720  }
1721  }
1722 }
1723 
1724 void Transpiler::generateArrayMapSetVariable(
1725  MinitScript* minitScript,
1726  int scriptConditionIdx,
1727  int scriptIdx,
1728  const MinitScript::Variable& variable,
1729  const unordered_map<string, vector<string>>& methodCodeMap,
1730  const unordered_set<string>& allMethods,
1731  const string& methodName,
1732  bool condition,
1733  const string& minitScriptClassName,
1734  string& generatedDefinitions,
1735  int depth,
1736  int initializerDepth,
1737  const string& postStatement
1738 ) {
1739  //
1740  string headerIndent = "\t";
1741  auto indent = StringTools::indent(string(), "\t", initializerDepth + depth);
1742  switch (variable.getType()) {
1743  case MinitScript::TYPE_NULL:
1744  {
1745  generatedDefinitions+= indent + "{" + "\n";
1746  generatedDefinitions+= indent + "\t" + "Variable variableD" + to_string(initializerDepth) + ";" + "\n";
1747  generatedDefinitions+= indent + "\t" + postStatement + "\n";
1748  generatedDefinitions+= indent + "}" + "\n";
1749  }
1750  break;
1751  case MinitScript::TYPE_BOOLEAN:
1752  {
1753  bool value;
1754  variable.getBooleanValue(value);
1755  generatedDefinitions+= indent + "{" + "\n";
1756  generatedDefinitions+= indent + "\t" + "Variable variableD" + to_string(initializerDepth) + "(" + (value == true?"true":"false") + ");" + "\n";
1757  generatedDefinitions+= indent + "\t" + postStatement + "\n";
1758  generatedDefinitions+= indent + "}" + "\n";
1759  }
1760  break;
1761  case MinitScript::TYPE_INTEGER:
1762  {
1763  int64_t value;
1764  variable.getIntegerValue(value);
1765  generatedDefinitions+= indent + "{" + "\n";
1766  generatedDefinitions+= indent + "\t" + "Variable variableD" + to_string(initializerDepth) + "(static_cast<int64_t>(" + to_string(value) + "ll));" + "\n";
1767  generatedDefinitions+= indent + "\t" + postStatement + "\n";
1768  generatedDefinitions+= indent + "}" + "\n";
1769  }
1770  break;
1771  case MinitScript::TYPE_FLOAT:
1772  {
1773  float value;
1774  variable.getFloatValue(value);
1775  generatedDefinitions+= indent + "{" + "\n";
1776  generatedDefinitions+= indent + "\t" + "Variable variableD" + to_string(initializerDepth) + "(" + to_string(value) + "f);" + "\n";
1777  generatedDefinitions+= indent + "\t" + postStatement + "\n";
1778  generatedDefinitions+= indent + "}" + "\n";
1779  }
1780  break;
1781  case MinitScript::TYPE_FUNCTION_CALL:
1782  {
1783  //
1784  const auto& statement = variable.getInitializer()->getStatement();
1785  string transpiledCode;
1786  auto statementIdx = MinitScript::STATEMENTIDX_FIRST;
1787  auto scriptStateChanged = false;
1788  auto scriptStopped = false;
1789  vector<string>enabledNamedConditions;
1790  transpileStatement(
1791  minitScript,
1792  transpiledCode,
1793  *variable.getInitializer()->getSyntaxTree(),
1794  statement,
1795  scriptConditionIdx,
1796  scriptIdx,
1797  statementIdx,
1798  methodCodeMap,
1799  allMethods,
1800  scriptStateChanged,
1801  scriptStopped,
1802  enabledNamedConditions,
1803  0,
1804  {},
1805  "Variable()",
1806  "const auto& variableD" + to_string(initializerDepth) + " = returnValue; " + postStatement + "\n", 1
1807  );
1808  generatedDefinitions+= transpiledCode;
1809  }
1810  break;
1811  case MinitScript::TYPE_FUNCTION_ASSIGNMENT:
1812  {
1813  string function;
1814  auto functionScriptIdx = MinitScript::SCRIPTIDX_NONE;
1815  variable.getFunctionValue(function, functionScriptIdx);
1816  function = escapeString(function);
1817  //
1818  generatedDefinitions+= indent + "{" + "\n";
1819  generatedDefinitions+= indent + "\t" + "Variable variableD" + to_string(initializerDepth) + ";" + "\n";
1820  generatedDefinitions+= indent + "\t" + "variableD" + to_string(initializerDepth) + ".setFunctionAssignment(\"" + function + "\", " + to_string(functionScriptIdx) + ");" + "\n";
1821  generatedDefinitions+= indent + "\t" + postStatement + "\n";
1822  generatedDefinitions+= indent + "}" + "\n";
1823  }
1824  break;
1825  case MinitScript::TYPE_STACKLET_ASSIGNMENT:
1826  {
1827  string stacket;
1828  auto stackletScriptIdx = MinitScript::SCRIPTIDX_NONE;
1829  variable.getStackletValue(stacket, stackletScriptIdx);
1830  stacket = escapeString(stacket);
1831  //
1832  generatedDefinitions+= indent + "{" + "\n";
1833  generatedDefinitions+= indent + "\t" + "Variable variableD" + to_string(initializerDepth) + ";" + "\n";
1834  generatedDefinitions+= indent + "\t" + "variableD" + to_string(initializerDepth) + ".setStackletAssignment(\"" + stacket + "\", " + to_string(stackletScriptIdx) + ");" + "\n";
1835  generatedDefinitions+= indent + "\t" + postStatement + "\n";
1836  generatedDefinitions+= indent + "}" + "\n";
1837  }
1838  break;
1839  case MinitScript::TYPE_STRING:
1840  {
1841  string value;
1842  variable.getStringValue(value);
1843  value = escapeString(value);
1844  //
1845  generatedDefinitions+= indent + "{" + "\n";
1846  generatedDefinitions+= indent + "\t" + "Variable variableD" + to_string(initializerDepth) + "(string(\"" + value + "\"));" + "\n";
1847  generatedDefinitions+= indent + "\t" + postStatement + "\n";
1848  generatedDefinitions+= indent + "}" + "\n";
1849  }
1850  break;
1851  case MinitScript::TYPE_ARRAY:
1852  {
1853  if (initializerDepth == 0) {
1854  generatedDefinitions+= string() + "{" + "\n";
1855  generatedDefinitions+= indent + "\t" + "// MinitScript setup" + "\n";
1856  generatedDefinitions+= indent + "\t" + "auto minitScript = this;" + "\n";
1857  generatedDefinitions+= indent + "\t" + "//" + "\n";
1858  } else {
1859  generatedDefinitions+= indent + "{" + "\n";
1860  }
1861  generatedDefinitions+= indent + "\t" + "Variable variableD" + to_string(initializerDepth) + ";" + "\n";
1862  generatedDefinitions+= indent + "\t" + "variableD" + to_string(initializerDepth) + ".setType(TYPE_ARRAY);" + "\n";
1863  const auto arrayValue = variable.getArrayPointer();
1864  for (const auto arrayEntry: *arrayValue) {
1865  generateArrayMapSetVariable(
1866  minitScript,
1867  scriptConditionIdx,
1868  scriptIdx,
1869  *arrayEntry,
1870  methodCodeMap,
1871  allMethods,
1872  methodName,
1873  condition,
1874  minitScriptClassName,
1875  generatedDefinitions,
1876  depth,
1877  initializerDepth + 1,
1878  "variableD" + to_string(initializerDepth) + ".pushArrayEntry(variableD" + to_string(initializerDepth + 1) + ");"
1879  );
1880  }
1881  generatedDefinitions+= indent + "\t" + postStatement + "\n";
1882  generatedDefinitions+= indent + "}";
1883  generatedDefinitions+= initializerDepth == 0?";":"\n";
1884  }
1885  break;
1886  case MinitScript::TYPE_MAP:
1887  {
1888  if (initializerDepth == 0) {
1889  generatedDefinitions+= string() + "{" + "\n";
1890  generatedDefinitions+= indent + "\t" + "// MinitScript setup" + "\n";
1891  generatedDefinitions+= indent + "\t" + "auto minitScript = this;" + "\n";
1892  generatedDefinitions+= indent + "\t" + "//" + "\n";
1893  } else {
1894  generatedDefinitions+= indent + "{" + "\n";
1895  }
1896  generatedDefinitions+= indent + "\t" + "Variable variableD" + to_string(initializerDepth) + ";" + "\n";
1897  generatedDefinitions+= indent + "\t" + "variableD" + to_string(initializerDepth) + ".setType(TYPE_MAP);" + "\n";
1898  const auto mapValue = variable.getMapPointer();
1899  for (const auto& [mapEntryName, mapEntryValue]: *mapValue) {
1900  auto mapEntryNameEscaped = escapeString(mapEntryName);
1901  generateArrayMapSetVariable(
1902  minitScript,
1903  scriptConditionIdx,
1904  scriptIdx,
1905  *mapEntryValue,
1906  methodCodeMap,
1907  allMethods,
1908  methodName,
1909  condition,
1910  minitScriptClassName,
1911  generatedDefinitions,
1912  depth,
1913  initializerDepth + 1,
1914  "variableD" + to_string(initializerDepth) + ".setMapEntry(\"" + mapEntryNameEscaped + "\", variableD" + to_string(initializerDepth + 1) + ", " + (mapEntryValue->isPrivate() == true?"true":"false") + ");"
1915  );
1916  }
1917  generatedDefinitions+= indent + "\t" + postStatement + "\n";
1918  generatedDefinitions+= indent + "}";
1919  generatedDefinitions+= initializerDepth == 0?";":"\n";
1920  }
1921  break;
1922  case MinitScript::TYPE_SET:
1923  {
1924  if (initializerDepth == 0) {
1925  generatedDefinitions+= string() + "{" + "\n";
1926  generatedDefinitions+= indent + "\t" + "// MinitScript setup" + "\n";
1927  generatedDefinitions+= indent + "\t" + "auto minitScript = this;" + "\n";
1928  generatedDefinitions+= indent + "\t" + "//" + "\n";
1929  } else {
1930  generatedDefinitions+= indent + "{" + "\n";
1931  }
1932  generatedDefinitions+= indent + "\t" + "Variable variableD" + to_string(initializerDepth) + ";" + "\n";
1933  generatedDefinitions+= indent + "\t" + "variableD" + to_string(initializerDepth) + ".setType(TYPE_SET);" + "\n";
1934  const auto setValue = variable.getSetPointer();
1935  for (const auto& key: *setValue) {
1936  generatedDefinitions+= indent + "\t" + "variableD" + to_string(initializerDepth) + ".insertSetKey(\"" + key + "\");" + "\n";
1937  }
1938  generatedDefinitions+= indent + "\t" + postStatement + "\n";
1939  generatedDefinitions+= indent + "}";
1940  generatedDefinitions+= initializerDepth == 0?";":"\n";
1941  }
1942  break;
1943  default: break;
1944  }
1945 }
1946 
1947 void Transpiler::generateArrayMapSetInitializer(
1948  MinitScript* minitScript,
1949  string& generatedDefinitions,
1950  int scriptConditionIdx,
1951  int scriptIdx,
1952  const string& minitScriptClassName,
1953  const string& methodName,
1954  const MinitScript::SyntaxTreeNode& syntaxTree,
1955  const MinitScript::Statement& statement,
1956  const unordered_map<string, vector<string>>& methodCodeMap,
1957  const unordered_set<string>& allMethods,
1958  bool condition,
1959  const vector<int>& argumentIndices,
1960  int depth
1961  ) {
1962  //
1963  auto indent = StringTools::indent(string(), "\t", depth);
1964  //
1965  switch (syntaxTree.type) {
1966  case MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_LITERAL:
1967  {
1968  switch(syntaxTree.value.getType()) {
1969  case MinitScript::TYPE_ARRAY:
1970  case MinitScript::TYPE_MAP:
1971  case MinitScript::TYPE_SET:
1972  {
1973  //
1974  string generatedInitializerDefinitions;
1975  //
1976  generateArrayMapSetVariable(
1977  minitScript,
1978  scriptConditionIdx,
1979  scriptIdx,
1980  syntaxTree.value,
1981  methodCodeMap,
1982  allMethods,
1983  methodName,
1984  condition,
1985  minitScriptClassName,
1986  generatedInitializerDefinitions,
1987  depth,
1988  0,
1989  "return variableD0;"
1990  );
1991  //
1992  generatedDefinitions+= indent + "// MinitScript transpilation for array/map/set initializer, statement index " + to_string(statement.statementIdx) + ", argument indices " + MinitScript::getArgumentIndicesAsString(argumentIndices, ", ") + "\n";
1993  generatedDefinitions+= indent + "auto initializer_" + to_string(statement.statementIdx) + "_" + MinitScript::getArgumentIndicesAsString(argumentIndices, "_") + " = [&](const SubStatement& subStatement) -> Variable ";
1994  generatedDefinitions+= generatedInitializerDefinitions;
1995  //
1996  break;
1997  }
1998  default: break;
1999  }
2000  //
2001  break;
2002  }
2003  case MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_METHOD:
2004  {
2005  auto argumentIdx = 0;
2006  for (const auto& argument: syntaxTree.arguments) {
2007  //
2008  auto nextArgumentIndices = argumentIndices;
2009  nextArgumentIndices.push_back(argumentIdx);
2010  //
2011  generateArrayMapSetInitializer(
2012  minitScript,
2013  generatedDefinitions,
2014  scriptConditionIdx,
2015  scriptIdx,
2016  minitScriptClassName,
2017  methodName,
2018  argument,
2019  statement,
2020  methodCodeMap,
2021  allMethods,
2022  condition,
2023  nextArgumentIndices,
2024  depth
2025  );
2026  //
2027  argumentIdx++;
2028  }
2029  break;
2030  }
2031  case MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_FUNCTION:
2032  {
2033  auto argumentIdx = 1; // TODO: check me!
2034  for (const auto& argument: syntaxTree.arguments) {
2035  //
2036  auto nextArgumentIndices = argumentIndices;
2037  nextArgumentIndices.push_back(argumentIdx);
2038  //
2039  generateArrayMapSetInitializer(
2040  minitScript,
2041  generatedDefinitions,
2042  scriptConditionIdx,
2043  scriptIdx,
2044  minitScriptClassName,
2045  methodName,
2046  argument,
2047  statement,
2048  methodCodeMap,
2049  allMethods,
2050  condition,
2051  nextArgumentIndices,
2052  depth
2053  );
2054  //
2055  argumentIdx++;
2056  }
2057  //
2058  break;
2059  }
2060  default:
2061  break;
2062  }
2063 }
2064 
2065 bool Transpiler::transpileStatement(
2066  MinitScript* minitScript,
2067  string& generatedCode,
2068  const MinitScript::SyntaxTreeNode& syntaxTree,
2069  const MinitScript::Statement& statement,
2070  int scriptConditionIdx,
2071  int scriptIdx,
2072  int& statementIdx,
2073  const unordered_map<string, vector<string>>& methodCodeMap,
2074  const unordered_set<string>& allMethods,
2075  bool& scriptStateChanged,
2076  bool& scriptStopped,
2077  vector<string>& enabledNamedConditions,
2078  int depth,
2079  const vector<int>& argumentIndices,
2080  const string& returnValue,
2081  const string& injectCode,
2082  int additionalIndent) {
2083  //
2084  statementIdx++;
2085  auto currentStatementIdx = statementIdx;
2086 
2087  // indenting
2088  string minIndentString = "\t";
2089  string depthIndentString;
2090  for (auto i = 0; i < depth + additionalIndent; i++) depthIndentString+= "\t";
2091 
2092  //
2093  struct ArrayAccessStatement {
2094  ArrayAccessStatement(
2095  int argumentIdx,
2096  int statementIdx,
2097  int leftIdx,
2098  int rightIdx,
2099  string statementMethod
2100  ):
2101  argumentIdx(argumentIdx),
2102  statementIdx(statementIdx),
2103  leftIdx(leftIdx),
2104  rightIdx(rightIdx),
2105  statementMethod(statementMethod)
2106  {}
2107  int argumentIdx;
2108  int statementIdx;
2109  int leftIdx;
2110  int rightIdx;
2111  string statementMethod;
2112  };
2113 
2114  //
2115  vector<ArrayAccessStatement> arrayAccessStatements;
2116 
2117  //
2118  switch (syntaxTree.type) {
2119  case MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_FUNCTION:
2120  case MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_STACKLET:
2121  {
2122  // check script user functions
2123  auto functionStackletScriptIdx = syntaxTree.getScriptIdx();
2124  if (functionStackletScriptIdx != MinitScript::SCRIPTIDX_NONE) {
2125  // have a wrapping script.call/script.callStacklet call
2126  MinitScript::SyntaxTreeNode callSyntaxTreeNode;
2127  callSyntaxTreeNode.type = MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_METHOD;
2128  callSyntaxTreeNode.value =
2129  syntaxTree.type == MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_FUNCTION?
2130  MinitScript::METHOD_SCRIPTCALLBYINDEX:
2131  MinitScript::METHOD_SCRIPTCALLSTACKLETBYINDEX;
2132  // construct argument for name of function/stacklet
2133  MinitScript::SyntaxTreeNode callArgumentSyntaxTreeNode;
2134  callArgumentSyntaxTreeNode.type = MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_LITERAL;
2135  callArgumentSyntaxTreeNode.value = MinitScript::Variable(functionStackletScriptIdx);
2136  // add argumnet for name of function/stacklet
2137  callSyntaxTreeNode.arguments.push_back(callArgumentSyntaxTreeNode);
2138  // stacklets have no arguments when calling them, functions do have them
2139  if (syntaxTree.type == MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_FUNCTION) {
2140  // add original parameter to call syntaxTree
2141  for (const auto& argument: syntaxTree.arguments) {
2142  callSyntaxTreeNode.arguments.push_back(argument);
2143  }
2144  }
2145  // assign script.call/script.callStacklet method
2146  auto method = minitScript->getMethod(callSyntaxTreeNode.value.getValueAsString());
2147  if (method == nullptr) {
2148  Console::printLine("Transpiler::transpileStatement(): method code not found: '" + callSyntaxTreeNode.value.getValueAsString() + "'");
2149  return false;
2150  }
2151  callSyntaxTreeNode.setMethod(method);
2152  return transpileStatement(
2153  minitScript,
2154  generatedCode,
2155  callSyntaxTreeNode,
2156  statement,
2157  scriptConditionIdx,
2158  scriptIdx,
2159  statementIdx,
2160  methodCodeMap,
2161  allMethods,
2162  scriptStateChanged,
2163  scriptStopped,
2164  enabledNamedConditions,
2165  depth,
2166  argumentIndices,
2167  returnValue,
2168  injectCode,
2169  additionalIndent
2170  );
2171  } else {
2172  Console::printLine("Transpiler::transpileStatement(): Function/stacklet not found: '" + syntaxTree.value.getValueAsString() + "'");
2173  return false;
2174  }
2175  //
2176  break;
2177  }
2178  case MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_METHOD:
2179  //
2180  if ((scriptConditionIdx != MinitScript::SCRIPTIDX_NONE ||
2181  scriptIdx != MinitScript::SCRIPTIDX_NONE) &&
2182  (syntaxTree.value.getValueAsString() == "getMethodArgumentVariable" ||
2183  syntaxTree.value.getValueAsString() == "getVariable" ||
2184  syntaxTree.value.getValueAsString() == "getVariableReference" ||
2185  syntaxTree.value.getValueAsString() == "setVariable" ||
2186  syntaxTree.value.getValueAsString() == "setVariableReference" ||
2187  syntaxTree.value.getValueAsString() == "setConstant")) {
2188  //
2189  for (auto argumentIdx = 0; argumentIdx < syntaxTree.arguments.size(); argumentIdx++) {
2190  //
2191  auto nextArgumentIndices = argumentIndices;
2192  nextArgumentIndices.push_back(argumentIdx);
2193  //
2194  auto argumentString = escapeString(syntaxTree.arguments[argumentIdx].value.getValueAsString());
2195  auto arrayAccessStatementIdx = 0;
2196  auto arrayAccessStatementLeftIdx = -1;
2197  auto arrayAccessStatementRightIdx = -1;
2198  auto quote = '\0';
2199  auto bracketCount = 0;
2200  auto lc = '\0';
2201  for (auto i = 0; i < argumentString.size(); i++) {
2202  auto c = argumentString[i];
2203  // handle quotes
2204  if ((c == '"' || c == '\'') && lc != '\\') {
2205  if (quote == '\0') {
2206  quote = c;
2207  } else
2208  if (quote == c) {
2209  quote = '\0';
2210  }
2211  } else
2212  if (c == '[') {
2213  if (bracketCount == 0) arrayAccessStatementLeftIdx = i;
2214  bracketCount++;
2215  } else
2216  if (c == ']') {
2217  bracketCount--;
2218  if (bracketCount == 0) {
2219  arrayAccessStatementRightIdx = i;
2220  //
2221  auto arrayAccessStatementString = StringTools::substring(argumentString, arrayAccessStatementLeftIdx + 1, arrayAccessStatementRightIdx);
2222  // array append operator []
2223  if (arrayAccessStatementString.empty() == true) {
2224  //
2225  arrayAccessStatementIdx++;
2226  //
2227  continue;
2228  }
2229  //
2230  const auto& script = minitScript->getScripts()[scriptConditionIdx != MinitScript::SCRIPTIDX_NONE?scriptConditionIdx:scriptIdx];
2231  //
2232  auto arrayAccessStatementMethod = string() + "array_access_statement_" + to_string(statement.statementIdx) + "_" + MinitScript::getArgumentIndicesAsString(nextArgumentIndices, "_") + "_" + to_string(arrayAccessStatementIdx);
2233  //
2234  generatedCode+= minIndentString + depthIndentString + "// we will use " + arrayAccessStatementMethod + "() to determine array access index"+ "\n";
2235  //
2236  arrayAccessStatements.emplace_back(
2237  argumentIdx,
2238  arrayAccessStatementIdx,
2239  arrayAccessStatementLeftIdx,
2240  arrayAccessStatementRightIdx,
2241  arrayAccessStatementMethod
2242  );
2243  //
2244  arrayAccessStatementIdx++;
2245  }
2246  }
2247  //
2248  lc = c;
2249  }
2250  }
2251  }
2252  //
2253  break;
2254  default:
2255  Console::printLine("Transpiler::transpileStatement(): " + minitScript->getStatementInformation(statement) + ": Function or method call expected, but got literal or 'none' syntaxTree");
2256  return false;
2257 
2258  }
2259 
2260  //
2261  auto method = syntaxTree.value.getValueAsString();
2262 
2263  // find method code in method code map
2264  auto methodCodeMapIt = methodCodeMap.find(method);
2265  if (methodCodeMapIt == methodCodeMap.end()) {
2266  Console::printLine("Transpiler::transpileStatement(): method code not found: '" + method + "'");
2267  return false;
2268  }
2269  const auto& methodCode = methodCodeMapIt->second;
2270 
2271  // script method
2272  auto scriptMethod = minitScript->getMethod(string(method));
2273  if (scriptMethod == nullptr) {
2274  Console::printLine("Transpiler::transpileStatement(): method not found: '" + method + "'");
2275  return false;
2276  }
2277 
2278  // comment about current statement
2279  generatedCode+= minIndentString + depthIndentString;
2280  generatedCode+= "// " + (depth > 0?"depth = " + to_string(depth):"") + (argumentIndices.empty() == false?" / argument index = " + to_string(argumentIndices.back()):"");
2281  generatedCode+= depth > 0 || argumentIndices.empty() == false?": ":"";
2282  generatedCode+= escapeString(syntaxTree.value.getValueAsString()) + "(" + minitScript->getArgumentsAsString(syntaxTree.arguments) + ")";
2283  generatedCode+= "\n";
2284 
2285  // argument values header
2286  generatedCode+= minIndentString + depthIndentString + "{" + "\n";
2287 
2288  // argument indices
2289  auto parentArgumentIdx = argumentIndices.size() >= 2?argumentIndices[argumentIndices.size() - 2]:MinitScript::ARGUMENTIDX_NONE;
2290  auto argumentIdx = argumentIndices.empty() == false?argumentIndices.back():MinitScript::ARGUMENTIDX_NONE;
2291 
2292  // statement
2293  if (depth == 0) {
2294  generatedCode+= minIndentString + depthIndentString + "\t" + "// statement setup" + "\n";
2295  if (scriptConditionIdx != MinitScript::SCRIPTIDX_NONE) {
2296  generatedCode+= minIndentString + depthIndentString + "\t" + "const auto& statement = scripts[" + to_string(scriptConditionIdx) + "].conditionStatement;" + "\n";
2297  generatedCode+= minIndentString + depthIndentString + "\t" + "const SubStatement subStatement(statement, " + to_string(syntaxTree.subLineIdx) + ");" + "\n";
2298  } else
2299  if (scriptIdx != MinitScript::SCRIPTIDX_NONE) {
2300  generatedCode+= minIndentString + depthIndentString + "\t" + "const auto& statement = scripts[" + to_string(scriptIdx) + "].statements[" + to_string(statement.statementIdx) + "];" + "\n";
2301  generatedCode+= minIndentString + depthIndentString + "\t" + "const SubStatement subStatement(statement, " + to_string(syntaxTree.subLineIdx) + ");" + "\n";
2302  }
2303  generatedCode+= minIndentString + depthIndentString + "\t" + "getScriptState().statementIdx = subStatement.statement->statementIdx;" + "\n";
2304  generatedCode+= minIndentString + depthIndentString + "\t" + "//" + "\n";
2305  } else {
2306  generatedCode+= minIndentString + depthIndentString + "\t" + "// sub statement setup" + "\n";
2307  if (scriptConditionIdx != MinitScript::SCRIPTIDX_NONE) {
2308  generatedCode+= minIndentString + depthIndentString + "\t" + "const SubStatement subStatement(statement, " + to_string(syntaxTree.subLineIdx) + ");" + "\n";
2309  } else
2310  if (scriptIdx != MinitScript::SCRIPTIDX_NONE) {
2311  generatedCode+= minIndentString + depthIndentString + "\t" + "const SubStatement subStatement(statement, " + to_string(syntaxTree.subLineIdx) + ");" + "\n";
2312  }
2313  }
2314 
2315  // construct argument values
2316  string indent = "\t";
2317  {
2318  vector<string> argumentsCode;
2319  if (depth > 0) {
2320  argumentsCode.push_back("auto& returnValue = argumentsD" + to_string(depth - 1) + (parentArgumentIdx != MinitScript::ARGUMENTIDX_NONE?"AIDX" + to_string(parentArgumentIdx):"") + "[" + to_string(argumentIdx) + "];");
2321  } else {
2322  argumentsCode.push_back("Variable returnValue;");
2323  }
2324  argumentsCode.push_back("array<Variable, " + to_string(syntaxTree.arguments.size()) + "> arguments {");
2325 
2326  // construct argument values
2327  if (syntaxTree.arguments.empty() == false) {
2328  generatedCode+= minIndentString + depthIndentString + "\t" + "// required method code arguments" + "\n";
2329  auto argumentIdx = 0;
2330  for (const auto& argument: syntaxTree.arguments) {
2331  //
2332  auto nextArgumentIndices = argumentIndices;
2333  nextArgumentIndices.push_back(argumentIdx);
2334  //
2335  auto lastArgument = argumentIdx == syntaxTree.arguments.size() - 1;
2336  switch (argument.type) {
2337  case MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_LITERAL:
2338  {
2339  switch (argument.value.getType()) {
2340  case MinitScript::TYPE_NULL:
2341  argumentsCode.push_back(indent + "Variable()" + (lastArgument == false?",":""));
2342  break;
2343  case MinitScript::TYPE_BOOLEAN:
2344  {
2345  bool value;
2346  argument.value.getBooleanValue(value);
2347  argumentsCode.push_back(indent + "Variable(" + (value == true?"true":"false") + ")" + (lastArgument == false?",":""));
2348  }
2349  break;
2350  case MinitScript::TYPE_INTEGER:
2351  {
2352  int64_t value;
2353  argument.value.getIntegerValue(value);
2354  argumentsCode.push_back(indent + + "Variable(static_cast<int64_t>(" + to_string(value) + "ll))" + (lastArgument == false?",":""));
2355  }
2356  break;
2357  case MinitScript::TYPE_FLOAT:
2358  {
2359  float value;
2360  argument.value.getFloatValue(value);
2361  argumentsCode.push_back(indent + + "Variable(" + to_string(value) + "f)" + (lastArgument == false?",":""));
2362  }
2363  break;
2364  case MinitScript::TYPE_STRING:
2365  case MinitScript::TYPE_FUNCTION_ASSIGNMENT:
2366  case MinitScript::TYPE_STACKLET_ASSIGNMENT:
2367  {
2368  string value;
2369  auto scriptIdx = MinitScript::SCRIPTIDX_NONE;
2370  // get it, while its hot
2371  if (argument.value.getType() == MinitScript::TYPE_FUNCTION_ASSIGNMENT) {
2372  argument.value.getFunctionValue(value, scriptIdx);
2373  } else
2374  if (argument.value.getType() == MinitScript::TYPE_STACKLET_ASSIGNMENT) {
2375  argument.value.getStackletValue(value, scriptIdx);
2376  } else {
2377  argument.value.getStringValue(value);
2378  }
2379  value = escapeString(value);
2380  // take array access statements into account
2381  auto arrayAccessStatementOffset = 0;
2382  for (auto& arrayAccessStatement: arrayAccessStatements) {
2383  if (arrayAccessStatement.argumentIdx != argumentIdx) continue;
2384  string arrayAccessStatementMethodCall = "\" + " + arrayAccessStatement.statementMethod + "(subStatement).getValueAsString() + \"";
2385  value =
2386  StringTools::substring(value, 0, arrayAccessStatement.leftIdx + 1 + arrayAccessStatementOffset) +
2387  arrayAccessStatementMethodCall +
2388  StringTools::substring(value, arrayAccessStatement.rightIdx + arrayAccessStatementOffset, value.size());
2389  arrayAccessStatementOffset-= (arrayAccessStatement.rightIdx - (arrayAccessStatement.leftIdx + 1)) - arrayAccessStatementMethodCall.size();
2390  }
2391  //
2392  argumentsCode.push_back(indent + "Variable(static_cast<VariableType>(" + to_string(argument.value.getType()) + "), string(\"" + value + "\"), " + to_string(scriptIdx) + ")" + (lastArgument == false?",":""));
2393  }
2394  //
2395  break;
2396  case MinitScript::TYPE_ARRAY:
2397  case MinitScript::TYPE_MAP:
2398  case MinitScript::TYPE_SET:
2399  {
2400  const auto& script = minitScript->getScripts()[scriptConditionIdx != MinitScript::SCRIPTIDX_NONE?scriptConditionIdx:scriptIdx];
2401  auto methodName = createMethodName(minitScript, scriptIdx); //
2402  auto initializerMethod = string() + "initializer_" + to_string(statement.statementIdx) + "_" + minitScript->getArgumentIndicesAsString(nextArgumentIndices, "_");
2403  argumentsCode.push_back(indent + initializerMethod + "(subStatement)" + (lastArgument == false?",":""));
2404  }
2405  break;
2406  default:
2407  {
2408  Console::printLine("Transpiler::transpileStatement(): " + minitScript->getStatementInformation(statement) + ": '" + argument.value.getAsString() + "': Unknown argument type: " + argument.value.getTypeAsString());
2409  break;
2410  }
2411  }
2412  break;
2413  }
2414  case MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_FUNCTION:
2415  case MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_STACKLET:
2416  {
2417  argumentsCode.push_back(indent + "Variable()" + (lastArgument == false?",":"") + " // arguments[" + to_string(argumentIdx) + "] --> returnValue of " + escapeString(argument.value.getValueAsString()) + "(" + minitScript->getArgumentsAsString(argument.arguments) + ")");
2418  break;
2419  }
2420  case MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_METHOD:
2421  {
2422  argumentsCode.push_back(indent + "Variable()" + (lastArgument == false?",":"") + " // arguments[" + to_string(argumentIdx) + "] --> returnValue of " + escapeString(argument.value.getValueAsString()) + "(" + minitScript->getArgumentsAsString(argument.arguments) + ")");
2423  break;
2424  }
2425  default:
2426  break;
2427  }
2428  //
2429  argumentIdx++;
2430  }
2431  }
2432  // end of arguments initialization
2433  argumentsCode.push_back("};");
2434 
2435  //
2436  argumentsCode.push_back("array<Variable, " + to_string(syntaxTree.arguments.size()) + ">& argumentsD" + to_string(depth) + (argumentIdx != MinitScript::ARGUMENTIDX_NONE?"AIDX" + to_string(argumentIdx):"") + " = arguments;");
2437 
2438  // argument values header
2439  for (const auto& codeLine: argumentsCode) {
2440  generatedCode+= minIndentString + depthIndentString + "\t" + codeLine + "\n";
2441  }
2442  }
2443 
2444  // enabled named conditions
2445  if (method == MinitScript::METHOD_ENABLENAMEDCONDITION && syntaxTree.arguments.empty() == false) {
2446  if (syntaxTree.arguments.size() != 1) {
2447  Console::printLine("Transpiler::transpileStatement(): " + minitScript->getStatementInformation(statement) + ": " + MinitScript::METHOD_ENABLENAMEDCONDITION + "(): Expected string argument @ 0");
2448  } else {
2449  string name = syntaxTree.arguments[0].value.getValueAsString();
2450  enabledNamedConditions.erase(
2451  remove(
2452  enabledNamedConditions.begin(),
2453  enabledNamedConditions.end(),
2454  name
2455  ),
2456  enabledNamedConditions.end()
2457  );
2458  enabledNamedConditions.push_back(name);
2459  }
2460  } else
2461  if (method == MinitScript::METHOD_DISABLENAMEDCONDITION && syntaxTree.arguments.empty() == false) {
2462  if (syntaxTree.arguments.size() != 1) {
2463  Console::printLine("Transpiler::transpileStatement(): " + minitScript->getStatementInformation(statement) + ": " + MinitScript::METHOD_DISABLENAMEDCONDITION + "(): Expected string argument @ 0");
2464  } else {
2465  string name = syntaxTree.arguments[0].value.getValueAsString();
2466  enabledNamedConditions.erase(
2467  remove(
2468  enabledNamedConditions.begin(),
2469  enabledNamedConditions.end(),
2470  name
2471  ),
2472  enabledNamedConditions.end()
2473  );
2474  }
2475  }
2476 
2477  // transpile method/function call argument
2478  {
2479  auto argumentIdx = 0;
2480  for (const auto& argument: syntaxTree.arguments) {
2481  switch (argument.type) {
2482  case MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_FUNCTION:
2483  case MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_STACKLET:
2484  case MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_METHOD:
2485  {
2486  //
2487  auto nextArgumentIndices = argumentIndices;
2488  nextArgumentIndices.push_back(argumentIdx);
2489 
2490  //
2491  if (transpileStatement(
2492  minitScript,
2493  generatedCode,
2494  argument,
2495  statement,
2496  scriptConditionIdx,
2497  scriptIdx,
2498  statementIdx,
2499  methodCodeMap,
2500  allMethods,
2501  scriptStateChanged,
2502  scriptStopped,
2503  enabledNamedConditions,
2504  depth + 1,
2505  nextArgumentIndices,
2506  returnValue,
2507  string(),
2508  additionalIndent
2509  ) == false) {
2510  Console::printLine("Transpiler::transpileStatement(): transpileStatement(): " + minitScript->getStatementInformation(statement) + ": '" + syntaxTree.value.getValueAsString() + "(" + minitScript->getArgumentsAsString(syntaxTree.arguments) + ")" + "': transpile error");
2511  }
2512  }
2513  //
2514  break;
2515  default:
2516  //
2517  break;
2518  }
2519  argumentIdx++;
2520  }
2521  }
2522 
2523  // special case: inject EVALUATEMEMBERACCESS_MEMBER for "internal.script.evaluateMemberAccess"
2524  if (scriptMethod != nullptr && scriptMethod->getMethodName() == "internal.script.evaluateMemberAccess") {
2525  //
2526  auto callArgumentIdx = 0;
2527  //
2528  if (syntaxTree.arguments[0].value.getType() != MinitScript::TYPE_NULL) {
2529  generateVariableAccess(
2530  minitScript,
2531  generatedCode,
2532  scriptConditionIdx,
2533  scriptIdx,
2534  syntaxTree.arguments[0].value.getValueAsString(),
2535  minIndentString + depthIndentString + "\t",
2536  false,
2537  false,
2538  true,
2539  false,
2540  false,
2541  false,
2542  "auto EVALUATEMEMBERACCESS_ARGUMENT" + to_string(callArgumentIdx) + " = "
2543  );
2544  } else {
2545  generatedCode+= minIndentString + depthIndentString + "\t" + "auto EVALUATEMEMBERACCESS_ARGUMENT" + to_string(callArgumentIdx) + " = Variable();" + "\n";
2546  }
2547  //
2548  callArgumentIdx++;
2549  //
2550  vector<string> argumentVariables;
2551  for (auto argumentIdx = 3; argumentIdx < syntaxTree.arguments.size(); argumentIdx+=2) {
2552  // do we have a this variable name?
2553  argumentVariables.push_back(string());
2554  //
2555  if (syntaxTree.arguments[argumentIdx].value.getType() != MinitScript::TYPE_NULL) {
2556  //
2557  generateVariableAccess(
2558  minitScript,
2559  argumentVariables[argumentVariables.size() - 1],
2560  scriptConditionIdx,
2561  scriptIdx,
2562  syntaxTree.arguments[argumentIdx].value.getValueAsString(),
2563  minIndentString + depthIndentString + "\t\t",
2564  false,
2565  false,
2566  true,
2567  false,
2568  false,
2569  false,
2570  string(),
2571  ",\n",
2572  argumentIdx,
2573  -1
2574  );
2575  } else {
2576  //
2577  argumentVariables[argumentVariables.size() - 1] = minIndentString + depthIndentString + "\t\t" + "Variable()," + "\n";
2578  }
2579  //
2580  callArgumentIdx++;
2581  }
2582  generatedCode+= minIndentString + depthIndentString + "\t" + "array<Variable, " + to_string(argumentVariables.size()) + "> EVALUATEMEMBERACCESS_ARGUMENTS" + " {" + "\n";
2583  for (const auto& argumentVariable: argumentVariables) generatedCode+= argumentVariable;
2584  generatedCode+= minIndentString + depthIndentString + "\t" + "};" + "\n";
2585  //
2586  if (allMethods.contains(syntaxTree.arguments[2].value.getValueAsString()) == true) {
2587 
2588  generatedCode+= minIndentString + depthIndentString + "\t" + "const auto EVALUATEMEMBERACCESS_MEMBER = EVALUATEMEMBERACCESSARRAYIDX_" + StringTools::toUpperCase(syntaxTree.arguments[2].value.getValueAsString()) + ";\n";
2589  } else {
2590  generatedCode+= minIndentString + depthIndentString + "\t" + "const auto EVALUATEMEMBERACCESS_MEMBER = EVALUATEMEMBERACCESSARRAYIDX_NONE;" + "\n";
2591  }
2592  }
2593 
2594  // do we have a variable look up or setting a variable?
2595  // transfer to use local method and global class variables
2596  if (syntaxTree.type == MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_METHOD &&
2597 
2598  (syntaxTree.value.getValueAsString() == "getMethodArgumentVariable" ||
2599  syntaxTree.value.getValueAsString() == "getVariable" ||
2600  syntaxTree.value.getValueAsString() == "getVariableReference" ||
2601  syntaxTree.value.getValueAsString() == "setVariable" ||
2602  syntaxTree.value.getValueAsString() == "setVariableReference" ||
2603  syntaxTree.value.getValueAsString() == "setConstant" ||
2604  syntaxTree.value.getValueAsString() == "unsetVariableReference") &&
2605  syntaxTree.arguments.empty() == false &&
2606  syntaxTree.arguments[0].type == MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_LITERAL) {
2607  // generate variable access
2608  generateVariableAccess(
2609  minitScript,
2610  generatedCode,
2611  scriptConditionIdx,
2612  scriptIdx,
2613  syntaxTree.arguments[0].value.getValueAsString(),
2614  minIndentString + depthIndentString + "\t",
2615  syntaxTree.value.getValueAsString() == "getMethodArgumentVariable",
2616  syntaxTree.value.getValueAsString() == "getVariable",
2617  syntaxTree.value.getValueAsString() == "getVariableReference",
2618  syntaxTree.value.getValueAsString() == "setVariable",
2619  syntaxTree.value.getValueAsString() == "setVariableReference",
2620  syntaxTree.value.getValueAsString() == "setConstant"
2621  );
2622  } else {
2623  const auto& script = minitScript->getScripts()[scriptConditionIdx != MinitScript::SCRIPTIDX_NONE?scriptConditionIdx:scriptIdx];
2624  // generate code
2625  generatedCode+= minIndentString + depthIndentString + "\t" + "// method code: " + string(method) + "\n";
2626  for (auto codeLine: methodCode) {
2627  codeLine = StringTools::replace(codeLine, "getMethodName()", "string(\"" + string(method) + "\")");
2628  // replace returns with gotos
2629  if (StringTools::regexMatch(codeLine, "[\\ \\t]*return[\\ \\t]*;.*") == true) {
2630  Console::printLine("Transpiler::transpileStatement(): method '" + string(method) + "': return statement not supported!");
2631  return false;
2632  } else
2633  if (StringTools::regexMatch(codeLine, "[\\ \\t]*minitScript[\\ \\t]*->gotoStatementGoto[\\ \\t]*\\([\\ \\t]*\\*subStatement\\.statement[\\ \\t]*\\)[\\ \\t]*;[\\ \\t]*") == true) {
2634  if (statement.gotoStatementIdx != MinitScript::STATEMENTIDX_NONE) {
2635  // find indent
2636  int indent = 0;
2637  for (auto i = 0; i < codeLine.size(); i++) {
2638  auto c = codeLine[i];
2639  if (c == '\t') {
2640  indent++;
2641  } else {
2642  break;
2643  }
2644  }
2645  string indentString;
2646  for (auto i = 0; i < indent; i++) indentString+= "\t";
2647  //
2648  generatedCode+= minIndentString + indentString + depthIndentString + "\t" + "goto minitscript_statement_" + to_string(statement.gotoStatementIdx) + ";\n";
2649  } else {
2650  // for now we exit the C++ method after setting the gotoStatement and reenter the C++ method
2651  generatedCode+= minIndentString + depthIndentString + "\t" + codeLine + " return;" + "\n";
2652  }
2653  } else
2654  if (StringTools::regexMatch(codeLine, "[\\ \\t]*minitScript[\\ \\t]*->gotoStatement[\\ \\t]*\\(.*\\)[\\ \\t]*;[\\ \\t]*") == true) {
2655  // TODO: those are the break and continue statements, we might improve this later
2656  // we support break and continue with several levels, so this is not easy to be solved by iterating the statements, lets see later
2657  // for now we exit the C++ method after setting the gotoStatement and reenter the C++ method
2658  generatedCode+= minIndentString + depthIndentString + "\t" + codeLine + " return;" + "\n";
2659  } else
2660  if (StringTools::regexMatch(codeLine, "[\\ \\t]*MINITSCRIPT_METHODUSAGE_COMPLAIN[\\ \\t]*\\([\\ \\t]*(.*)\\)[\\ \\t]*;[\\ \\t]*") == true ||
2661  StringTools::regexMatch(codeLine, "[\\ \\t]*MINITSCRIPT_METHODUSAGE_COMPLAINM[\\ \\t]*\\([\\ \\t]*(.*)[\\ \\t]*,[\\ \\t]*(.*)[\\ \\t]*\\)[\\ \\t]*;[\\ \\t]*") == true ||
2662  StringTools::regexMatch(codeLine, "[\\ \\t]*minitScript[\\ \\t]*->emit[\\ \\t]*\\([\\ \\t]*[a-zA-Z0-9]*[\\ \\t]*\\)[\\ \\t]*;[\\ \\t]*") == true) {
2663  // find indent
2664  int indent = 0;
2665  for (auto i = 0; i < codeLine.size(); i++) {
2666  auto c = codeLine[i];
2667  if (c == '\t') {
2668  indent++;
2669  } else {
2670  break;
2671  }
2672  }
2673  string indentString;
2674  for (auto i = 0; i < indent; i++) indentString+= "\t";
2675  codeLine = StringTools::trim(codeLine);
2676  //
2677  generatedCode+= minIndentString + depthIndentString + indentString + "\t{ " + codeLine + (script.type == MinitScript::Script::TYPE_ON || script.type == MinitScript::Script::TYPE_ONENABLED?" MINITSCRIPT_METHOD_POPSTACK(); return" + (returnValue.empty() == false?" " + returnValue:"") + ";":"") + " }\n";
2678  } else {
2679  if (StringTools::regexMatch(codeLine, ".*[\\ \\t]*minitScript[\\ \\t]*->[\\ \\t]*setScriptStateState[\\ \\t]*\\([\\ \\t]*.+[\\ \\t]*\\);.*") == true) {
2680  scriptStateChanged = true;
2681  }
2682  if (StringTools::regexMatch(codeLine, ".*[\\ \\t]*minitScript[\\ \\t]*->[\\ \\t]*stopScriptExecutation[\\ \\t]*\\([\\ \\t]*\\);.*") == true) {
2683  scriptStopped = true;
2684  } else
2685  if (StringTools::regexMatch(codeLine, ".*[\\ \\t]*minitScript[\\ \\t]*->[\\ \\t]*stopRunning[\\ \\t]*\\([\\ \\t]*\\);.*") == true) {
2686  scriptStopped = true;
2687  }
2688  generatedCode+= minIndentString + depthIndentString + "\t" + codeLine + "\n";
2689  }
2690  }
2691  }
2692 
2693  // inject code if we have any to inject
2694  if (injectCode.empty() == false) {
2695  generatedCode+= minIndentString + depthIndentString + "\t" + injectCode + "\n";
2696  }
2697 
2698  //
2699  generatedCode+= minIndentString + depthIndentString + "}" + "\n";
2700 
2701  //
2702  return true;
2703 }
2704 
2705 bool Transpiler::transpile(MinitScript* minitScript, const string& className, string& generatedCode, int scriptIdx, const unordered_map<string, vector<string>>& methodCodeMap, const unordered_set<string>& allMethods) {
2706  if (scriptIdx < 0 || scriptIdx >= minitScript->getScripts().size()) {
2707  Console::printLine("Transpiler::transpile(): invalid script index");
2708  return false;
2709  }
2710 
2711  //
2712  const auto& script = minitScript->getScripts()[scriptIdx];
2713 
2714  //
2715  Console::printLine("Transpiler::transpile(): transpiling code for " + getScriptTypeReadableName(script.type) + " = '" + script.condition + "', with name '" + script.name + "'");
2716 
2717  //
2718  string methodIndent = "\t";
2719  string generatedCodeHeader;
2720 
2721  //
2722  generatedCodeHeader+= methodIndent + "// script setup" + "\n";
2723  generatedCodeHeader+= methodIndent + "auto minitScript = this;" + "\n";
2724  generatedCodeHeader+= methodIndent + "getScriptState().scriptIdx = " + to_string(scriptIdx) + ";" + "\n";
2725 
2726  // method name
2727  auto methodName = createMethodName(minitScript, scriptIdx);
2728 
2729  //
2730  unordered_set<int> gotoStatementIdxSet;
2731  for (const auto& statement: script.statements) {
2732  if (statement.gotoStatementIdx != MinitScript::STATEMENTIDX_NONE) {
2733  gotoStatementIdxSet.insert(statement.statementIdx);
2734  gotoStatementIdxSet.insert(statement.gotoStatementIdx);
2735  }
2736  }
2737 
2738  //
2739  auto statementIdx = MinitScript::STATEMENTIDX_FIRST;
2740  vector<string> enabledNamedConditions;
2741  auto scriptStateChanged = false;
2742  for (auto scriptStatementIdx = MinitScript::STATEMENTIDX_FIRST; scriptStatementIdx < script.statements.size(); scriptStatementIdx++) {
2743  const auto& statement = script.statements[scriptStatementIdx];
2744  const auto& syntaxTree = script.syntaxTree[scriptStatementIdx];
2745  //
2746  if (scriptStateChanged == true || gotoStatementIdxSet.find(statement.statementIdx) != gotoStatementIdxSet.end()) {
2747  generatedCodeHeader+= methodIndent + "if (minitScriptGotoStatementIdx == " + to_string(statement.statementIdx) + ") goto minitscript_statement_" + to_string(statement.statementIdx) + "; else" + "\n";
2748  }
2749 
2750  // enabled named conditions
2751  if (enabledNamedConditions.empty() == false) {
2752  generatedCode+= "\n";
2753  generatedCode+= methodIndent + "// enabled named conditions" + "\n";
2754  generatedCode+= methodIndent + "{" + "\n";
2755  generatedCode+= methodIndent + "\t" + "auto scriptIdxToStart = determineNamedScriptIdxToStart();" + "\n";
2756  generatedCode+= methodIndent + "\t" + "if (scriptIdxToStart != SCRIPTIDX_NONE && scriptIdxToStart != getScriptState().scriptIdx) {" + "\n";
2757  generatedCode+= methodIndent + "\t\t" + "resetScriptExecutationState(scriptIdxToStart, STATEMACHINESTATE_NEXT_STATEMENT);" + "\n";
2758  generatedCode+= methodIndent + "\t\t" + "timeEnabledConditionsCheckLast = _Time::getCurrentMillis();" + "\n";
2759  generatedCode+= methodIndent + "\t\t" + "return;" + "\n";
2760  generatedCode+= methodIndent + "\t" + "}" + "\n";
2761  generatedCode+= methodIndent + "}" + "\n";
2762  }
2763 
2764  // statement_xyz goto label
2765  generatedCode+= methodIndent + "// statement: " + to_string(statement.statementIdx) + "\n";
2766  if (scriptStateChanged == true || gotoStatementIdxSet.find(statement.statementIdx) != gotoStatementIdxSet.end()) {
2767  generatedCode+= methodIndent + "minitscript_statement_" + to_string(statement.statementIdx) + ":" + "\n";
2768  }
2769  scriptStateChanged = false;
2770  auto scriptStopped = false;
2771  transpileStatement(
2772  minitScript,
2773  generatedCode,
2774  syntaxTree,
2775  statement,
2776  MinitScript::SCRIPTIDX_NONE,
2777  scriptIdx,
2778  statementIdx,
2779  methodCodeMap,
2780  allMethods,
2781  scriptStateChanged,
2782  scriptStopped,
2783  enabledNamedConditions
2784  );
2785  if (scriptStopped == true) {
2786  generatedCode+= methodIndent + "if (getScriptState().running == false) {" + "\n";
2787  generatedCode+= methodIndent + "\t" + "MINITSCRIPT_METHOD_POPSTACK();" + "\n";
2788  generatedCode+= methodIndent + "\t" + "return;" + "\n";
2789  generatedCode+= methodIndent + "}" + "\n";
2790  }
2791  if (scriptStateChanged == true) {
2792  generatedCode+= methodIndent + "if (getScriptState().state != STATEMACHINESTATE_NEXT_STATEMENT) {" + "\n";
2793  generatedCode+= methodIndent + "\t" + "getScriptState().statementIdx++;" + "\n";
2794  generatedCode+= methodIndent + "\t" + "return;" + "\n";
2795  generatedCode+= methodIndent + "}" + "\n";
2796  }
2797  }
2798  generatedCode+= methodIndent + "getScriptState().scriptIdx = SCRIPTIDX_NONE;" + "\n";
2799  generatedCode+= methodIndent + "getScriptState().statementIdx = STATEMENTIDX_NONE;" + "\n";
2800  generatedCode+= methodIndent + "setScriptStateState(STATEMACHINESTATE_WAIT_FOR_CONDITION);" + "\n";
2801 
2802  //
2803  generatedCodeHeader+= methodIndent + "if (minitScriptGotoStatementIdx != STATEMENTIDX_NONE && minitScriptGotoStatementIdx != STATEMENTIDX_FIRST) _Console::printLine(\"" + className + "::" + methodName + "(): Can not go to statement \" + to_string(minitScriptGotoStatementIdx));" + "\n";
2804  //
2805  generatedCode = generatedCodeHeader + generatedCode;
2806  return true;
2807 }
2808 
2809 bool Transpiler::transpileCondition(MinitScript* minitScript, string& generatedCode, int scriptIdx, const unordered_map<string, vector<string>>& methodCodeMap, const unordered_set<string>& allMethods, const string& returnValue, const string& injectCode, int depth) {
2810  if (scriptIdx < 0 || scriptIdx >= minitScript->getScripts().size()) {
2811  Console::printLine("Transpiler::transpileScriptCondition(): invalid script index");
2812  return false;
2813  }
2814 
2815  //
2816  const auto& script = minitScript->getScripts()[scriptIdx];
2817 
2818  //
2819  Console::printLine("Transpiler::transpileScriptCondition(): transpiling code condition for condition = '" + script.condition + "', with name '" + script.name + "'");
2820 
2821  //
2822  auto statementIdx = MinitScript::STATEMENTIDX_FIRST;
2823  auto scriptStateChanged = false;
2824  auto scriptStopped = false;
2825  vector<string >enabledNamedConditions;
2826  transpileStatement(
2827  minitScript,
2828  generatedCode,
2829  script.conditionSyntaxTree,
2830  script.conditionStatement,
2831  scriptIdx,
2832  MinitScript::SCRIPTIDX_NONE,
2833  statementIdx,
2834  methodCodeMap,
2835  allMethods,
2836  scriptStateChanged,
2837  scriptStopped,
2838  enabledNamedConditions,
2839  0,
2840  {},
2841  returnValue,
2842  injectCode,
2843  depth + 1
2844  );
2845 
2846  //
2847  generatedCode+= "\t\n";
2848 
2849  //
2850  return true;
2851 }
2852 
2853 const string Transpiler::createSourceCode(const MinitScript::SyntaxTreeNode& syntaxTreeNode) {
2854  //
2855  string result;
2856  switch (syntaxTreeNode.type) {
2857  case MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_LITERAL:
2858  {
2859  switch(syntaxTreeNode.value.getType()) {
2860  case MinitScript::TYPE_NULL:
2861  {
2862  result+= (result.empty() == false?", ":"") + string("<VOID>");
2863  break;
2864  }
2865  case MinitScript::TYPE_BOOLEAN:
2866  case MinitScript::TYPE_INTEGER:
2867  case MinitScript::TYPE_FLOAT:
2868  {
2869  result+= (result.empty() == false?", ":"") + syntaxTreeNode.value.getValueAsString();
2870  break;
2871  }
2872  case MinitScript::TYPE_STRING:
2873  {
2874  result+= (result.empty() == false?", ":"") + string("\"") + syntaxTreeNode.value.getValueAsString() + string("\"");
2875  break;
2876  }
2877  default:
2878  {
2879  result+= (result.empty() == false?", ":"") + string("<COMPLEX DATATYPE>");
2880  break;
2881  }
2882  }
2883  break;
2884  }
2885  case MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_METHOD:
2886  case MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_FUNCTION:
2887  case MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_STACKLET:
2888  {
2889  auto endElse = syntaxTreeNode.value.getValueAsString() == "end" || syntaxTreeNode.value.getValueAsString() == "else";
2890  result+= syntaxTreeNode.value.getValueAsString();
2891  if (endElse == false) result+= string("(");
2892  auto argumentIdx = 0;
2893  for (const auto& argument: syntaxTreeNode.arguments) {
2894  if (argumentIdx > 0) result+= ", ";
2895  result+= createSourceCode(argument);
2896  argumentIdx++;
2897  }
2898  if (endElse == false) result+= string(")");
2899  break;
2900  }
2901  default:
2902  break;
2903  }
2904  return result;
2905 }
2906 
2907 const string Transpiler::createSourceCode(MinitScript::Script::Type scriptType, const string& condition, const vector<MinitScript::Script::Argument>& functionArguments, const string& name, const MinitScript::SyntaxTreeNode& conditionSyntaxTree, const vector<MinitScript::SyntaxTreeNode>& syntaxTree) {
2908  //
2909  string result;
2910  //
2911  switch(scriptType) {
2912  case MinitScript::Script::TYPE_FUNCTION: {
2913  result+= "function: ";
2914  if (condition.empty() == false) {
2915  result+= condition;
2916  }
2917  auto argumentIdx = 0;
2918  result+= "(";
2919  for (const auto& argument: functionArguments) {
2920  if (argumentIdx > 0) result+= ", ";
2921  if (argument.reference == true) result+= "&";
2922  if (argument.privateScope == true) result+= "&";
2923  result+= argument.name;
2924  argumentIdx++;
2925  }
2926  result+= ")";
2927  break;
2928  }
2929  case MinitScript::Script::TYPE_ON:
2930  {
2931  result+= "on: ";
2932  if (condition.empty() == false) {
2933  result+= condition;
2934  }
2935  break;
2936  }
2937  case MinitScript::Script::TYPE_ONENABLED:
2938  {
2939  result+= "on-enabled: ";
2940  if (condition.empty() == false) {
2941  result+= condition;
2942  }
2943  break;
2944  }
2945  default: break;
2946  }
2947  if (conditionSyntaxTree.type != MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_NONE)
2948  result+= createSourceCode(conditionSyntaxTree);
2949  if (name.empty() == false) {
2950  result+= " := " + name + "\n";
2951  } else {
2952  result+= "\n";
2953  }
2954  //
2955  auto indent = 1;
2956  for (const auto& syntaxTreeNode: syntaxTree) {
2957  if (syntaxTreeNode.type == MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_METHOD) {
2958  if (syntaxTreeNode.value.getValueAsString() == "if") indent+= 0; else
2959  if (syntaxTreeNode.value.getValueAsString() == "elseif") indent-= 1; else
2960  if (syntaxTreeNode.value.getValueAsString() == "else") indent-= 1; else
2961  if (syntaxTreeNode.value.getValueAsString() == "end") indent-= 1; else
2962  if (syntaxTreeNode.value.getValueAsString() == "forTime") indent-= 0; else
2963  if (syntaxTreeNode.value.getValueAsString() == "forCondition") indent-= 0; else
2964  if (syntaxTreeNode.value.getValueAsString() == "switch") indent-= 0; else
2965  if (syntaxTreeNode.value.getValueAsString() == "case") indent-= 1; else
2966  if (syntaxTreeNode.value.getValueAsString() == "default") indent-= 1;
2967  }
2968  for (auto i = 0; i < indent; i++) result+= "\t";
2969  result+= createSourceCode(syntaxTreeNode) + "\n";
2970  if (syntaxTreeNode.type == MinitScript::SyntaxTreeNode::SCRIPTSYNTAXTREENODE_EXECUTE_METHOD) {
2971  if (syntaxTreeNode.value.getValueAsString() == "if") indent+= 1; else
2972  if (syntaxTreeNode.value.getValueAsString() == "elseif") indent+= 1; else
2973  if (syntaxTreeNode.value.getValueAsString() == "else") indent+= 1; else
2974  if (syntaxTreeNode.value.getValueAsString() == "end") indent+= 0; else
2975  if (syntaxTreeNode.value.getValueAsString() == "forTime") indent+= 1; else
2976  if (syntaxTreeNode.value.getValueAsString() == "forCondition") indent+= 1; else
2977  if (syntaxTreeNode.value.getValueAsString() == "switch") indent+= 0; else
2978  if (syntaxTreeNode.value.getValueAsString() == "case") indent+= 1; else
2979  if (syntaxTreeNode.value.getValueAsString() == "default") indent+= 1;
2980  }
2981  }
2982  return result;
2983 }
2984 
2985 const string Transpiler::createSourceCode(MinitScript* minitScript) {
2986  string result;
2987  // create source code
2988  for (const auto& script: minitScript->getScripts()) {
2989  result+= createSourceCode(script.type, script.emitCondition == true?script.condition:string(), script.arguments, script.name, script.conditionSyntaxTree, script.syntaxTree);
2990  result+= "\n";
2991  }
2992  //
2993  return result;
2994 }
Standard math functions.
Definition: Math.h:19
bool getIntegerValue(int64_t &value, bool optional=false) const
Get integer value from given variable.
Definition: MinitScript.h:1436
bool getStackletValue(string &stacklet, int &scriptIdx, bool optional=false) const
Get stacklet values from given variable.
Definition: MinitScript.h:1528
bool getStringValue(string &value, bool optional=false) const
Get string value from given variable.
Definition: MinitScript.h:1549
const unordered_map< string, Variable * > * getMapPointer() const
Definition: MinitScript.h:1924
const unordered_set< string > * getSetPointer() const
Definition: MinitScript.h:2059
const vector< Variable * > * getArrayPointer() const
Definition: MinitScript.h:1839
bool getFloatValue(float &value, bool optional=false) const
Get float value from given variable.
Definition: MinitScript.h:1474
bool getBooleanValue(bool &value, bool optional=false) const
Get boolean value from given variable.
Definition: MinitScript.h:1405
Initializer * getInitializer() const
Return initializer.
Definition: MinitScript.h:1376
const string getValueAsString(bool formatted=false, bool jsonCompatible=false, int depth=0) const
Print string representation of variable.
Definition: MinitScript.h:2404
void setImplicitTypedValue(const string &value, MinitScript *minitScript, int scriptIdx, const Statement &statement)
Set implicit typed value given by value string.
Definition: MinitScript.h:2180
bool getFunctionValue(string &function, int &scriptIdx, bool optional=false) const
Get function values from given variable.
Definition: MinitScript.h:1506
bool parseStatement(const string_view &executableStatement, string_view &methodName, vector< ParserArgument > &arguments, const Statement &statement, string &accessObjectMemberStatement)
Parse a statement.
virtual const string getBaseClassHeader()
virtual const string getBaseClass()
const string getStatementInformation(const Statement &statement, int subLineIdx=-1)
Return statement information.
Definition: MinitScript.h:4556
int getStackletScopeScriptIdx(int scriptIdx)
Return stacklet scope script index.
static const string getArgumentIndicesAsString(const vector< int > &argumentIndices, const string &delimiter)
Returns string representation of given argument indices.
Definition: MinitScript.h:4318
virtual const vector< string > getTranspilationUnits()
unordered_map< string, int > functions
Definition: MinitScript.h:3531
const string getInformation()
Get MinitScript instance information.
Method * getMethod(const string &methodName)
Get method by method name.
Definition: MinitScript.h:5289
const string getArgumentsAsString(const vector< SyntaxTreeNode > &arguments)
Returns arguments as string.
Definition: MinitScript.h:3006
const vector< Method * > getMethods()
const vector< Script > & getScripts()
Definition: MinitScript.h:4471
bool createStatementSyntaxTree(int scriptIdx, const string_view &methodName, const vector< ParserArgument > &arguments, const Statement &statement, SyntaxTreeNode &syntaxTree, int subLineIdx=0)
Create statement syntax tree.
MinitScript transpiler.
Definition: Transpiler.h:28
#define MINITSCRIPT_DATA
Definition: minitscript.h:22
std::exception Exception
Exception base class.
Definition: Exception.h:18