#include #include #include #define DEVELOPER_MESSAGES false #define EXAMPLE_PROJECT false #if defined(__unix__) #define UNIX true #define WINDOWS false #elif defined(_MSC_VER) #define UNIX false #define WINDOWS true #endif #include #include #include #include #include #include #include #include #include #if UNIX #include #include #elif WINDOWS #include #endif #include "eval.h" #include "strops.h" #include "builtin.h" #include "main.h" #include "anyops.h" #include "ZS.h" using namespace std; using namespace boost; unordered_map globalVariableValues; unordered_map>> functionValues; boost::any GetVariableValue(const string& varName, const unordered_map& variableValues) { string classSubComponent; string baseName = trim(varName); if (count(varName, '.') > 0) { classSubComponent = trim(varName.substr(indexInStr(varName, '.') + 1, -1)); baseName = trim(split(varName, '.')[0]); } boost::any outputValue = nullType; auto iA = variableValues.find(baseName); auto iB = globalVariableValues.find(baseName); if (iA != variableValues.end()) outputValue = iA->second; else if (iB != globalVariableValues.end()) outputValue = iB->second; else outputValue = varName; if (count(varName, '.') > 0 && !outputValue.empty()) return GetClassSubComponent(outputValue, classSubComponent); else return outputValue; } bool IsVar(const string& varName, const unordered_map& variableValues) { if (variableValues.find(split(varName, '.')[0]) != variableValues.end() && split(varName, '.')[0] != "ZS") return true; else return false; } vector VarValues(const vector& varNames, unordered_map& variableValues) { vector realValues; for (int varIndex = 0; varIndex < varNames.size(); varIndex++) { string varName = trim(varNames.at(varIndex)); //realValues.push_back(EvalExpression(varName, variableValues)); auto iA = variableValues.find(varName); if (iA != variableValues.end()) { realValues.push_back(iA->second); } else { auto iB = globalVariableValues.find(varName); if (iB != globalVariableValues.end()) realValues.push_back(iB->second); else realValues.push_back(EvalExpression(varName, variableValues)); } } return realValues; } bool IsFunction(const string& funcName) { if (functionValues.find(funcName) != functionValues.end()) return true; else return false; } bool IsZSFunction(const string& funcName) { if (funcName[0] == 'Z' && funcName[1] == 'S' && funcName[2] == '.') return true; else return false; } boost::any EvalExpression(const string& ex, unordered_map& variableValues) { string expression = trim(ex); bool inQuotes = false; #if DEVELOPER_MESSAGES == true InterpreterLog(" old expression: |" + expression + "|"); #endif bool isFunc = IsFunction(split(expression, '(')[0]); bool isZS = split(expression, '.')[0] == "ZS"; // If no operations are applied, then return self if ((countOutsideParenthesis(expression, '+') == 0 && countOutsideParenthesis(expression, '-') == 0 && countOutsideParenthesis(expression, '*') == 0 && countOutsideParenthesis(expression, '/') == 0 && countOutsideParenthesis(expression, '^') == 0) || split(expression, '.')[0] == "ZS") { bool isFunc = IsFunction(split(expression, '(')[0]); if (isFunc && !inQuotes) { //cout << split(expression, '(')[0] << endl; string argContents = ""; int y = indexInStr(expression, '(') + 1; while (y < expression.size() && expression[y] != ')') { argContents += expression[y]; y++; } return ExecuteFunction(split(expression, '(')[0], VarValues(split(argContents, ','), variableValues)); } else if (split(expression, '.')[0] == "ZS" && !inQuotes) { string argContents = ""; int y = indexInStr(expression, '(') + 1; while (y < expression.size() && expression[y] != ')') { argContents += expression[y]; y++; } return ZSFunction(split(expression, '(')[0], VarValues(split(argContents, ','), variableValues)); } else return GetVariableValue(expression, variableValues); } string newExpression = ""; inQuotes = false; for (int i = 0; i < expression.size(); i++) { if (expression[i] == '\"') inQuotes = !inQuotes; if (isalpha(expression[i])) { string name = ""; while (i < expression.size() && (isalpha(expression[i]) || expression[i] == '.')) { name += expression[i]; i++; } //string varVal = GetVariableValue(name, variables, variableValues); bool isFunc = IsFunction(name); if (isFunc && !inQuotes) { string argContents = ""; i++; while (i < expression.size() && expression[i] != ')') { argContents += expression[i]; i++; } string returnVal = AnyAsString(ExecuteFunction(name, VarValues(split(argContents, ','), variableValues))); newExpression += returnVal; } else if (split(name, '.')[0] == "ZS" && !inQuotes) { string argContents = ""; int y = indexInStr(expression, '(') + 1; while (y < expression.size() && expression[y] != ')') { argContents += expression[y]; y++; } //cout << split(expression, '(')[0] << " " << argContents << endl; string returnVal = AnyAsString(ZSFunction(split(name, '(')[0], VarValues(split(argContents, ','), variableValues))); newExpression += returnVal; } else { if (inQuotes) newExpression += name; else newExpression += AnyAsString(GetVariableValue(name, variableValues)); } i--; } else { newExpression += expression[i]; } } #if DEVELOPER_MESSAGES == true InterpreterLog(" new expression: |" + newExpression + "|"); #endif bool addStrings = false; for (int i = 0; i < (int)newExpression.size(); i++) if (isalpha(newExpression[i]) || newExpression[i] == '\"') { addStrings = true; break; } if (addStrings) { inQuotes = false; string withoutParenthesis = ""; for (int i = 0; i < (int)newExpression.size(); i++) { if (newExpression[i] == '\"') { inQuotes = !inQuotes; continue; } if (inQuotes) withoutParenthesis += newExpression[i]; if (!inQuotes && newExpression[i] != '(' && newExpression[i] != ')' && newExpression[i] != '+' && newExpression[i] != ' ') withoutParenthesis += newExpression[i]; } //cout << "NewSTRING = " << Quoted(withoutParenthesis) << endl; return withoutParenthesis; } else return evaluate(newExpression); } bool BooleanLogic(const string& valA, const string& determinant, const string& valB, unordered_map& variableValues) { boost::any valARealValue = EvalExpression(valA, variableValues); boost::any valBRealValue = EvalExpression(valB, variableValues); #if DEVELOPER_MESSAGES == true InterpreterLog(AnyAsString(valARealValue) + " " + determinant + " " + AnyAsString(valBRealValue) + " : " + AnyAsString(valA) + " " + determinant + " " + AnyAsString(valB) + " : " + to_string(AnyAsString(valARealValue) == AnyAsString(valBRealValue))); #endif if (determinant == "==") return any_compare(valARealValue, valBRealValue); else if (determinant == "!=") return !any_compare(valARealValue, valBRealValue); else if (determinant == ">=") return AnyAsFloat(valARealValue) >= AnyAsFloat(valBRealValue); else if (determinant == "<=") return AnyAsFloat(valARealValue) <= AnyAsFloat(valBRealValue); else if (determinant == ">") return AnyAsFloat(valARealValue) > AnyAsFloat(valBRealValue); else if (determinant == "<") return AnyAsFloat(valARealValue) < AnyAsFloat(valBRealValue); else LogWarning("unrecognized determinant \'" + determinant + "\'"); return false; } int varOperation(const vector& str, unordered_map& variableValues) { if (IsVar(str.at(0), variableValues)) { // Checks if type is simple, like int or string if (any_type(variableValues[str.at(0)]) <= 3) { if (str.at(1) == "=") variableValues[str.at(0)] = EvalExpression(unWrapVec(vector(str.begin() + 2, str.end())), variableValues); else if (str.at(1) == "+=") variableValues[str.at(0)] = EvalExpression(str.at(0) + "+(" + unWrapVec(vector(str.begin() + 2, str.end())) + ")", variableValues); else if (str.at(1) == "-=") variableValues[str.at(0)] = AnyAsFloat(variableValues[str.at(0)]) - AnyAsFloat(EvalExpression(unWrapVec(vector(str.begin() + 2, str.end())), variableValues)); else if (str.at(1) == "*=") variableValues[str.at(0)] = AnyAsFloat(variableValues[str.at(0)]) * AnyAsFloat(EvalExpression(unWrapVec(vector(str.begin() + 2, str.end())), variableValues)); else if (str.at(1) == "/=") variableValues[str.at(0)] = AnyAsFloat(variableValues[str.at(0)]) / AnyAsFloat(EvalExpression(unWrapVec(vector(str.begin() + 2, str.end())), variableValues)); else LogWarning("unrecognized operator \'" + str.at(1) + "\'"); } // Else it is a Vec2. No other complex class can be operated on it's base form (ex. you can't do: Sprite += Sprite) else if (any_type(variableValues[str.at(0)]) == 5) { boost::any otherExpression = EvalExpression(unWrapVec(vector(str.begin() + 2, str.end())), variableValues); if (str.at(1) == "=") variableValues[str.at(0)] = otherExpression; else if (str.at(1) == "+=") variableValues[str.at(0)] = AnyAsVec2(variableValues[str.at(0)]) + AnyAsVec2(otherExpression); else if (str.at(1) == "-=") variableValues[str.at(0)] = AnyAsVec2(variableValues[str.at(0)]) - AnyAsVec2(otherExpression); else if (str.at(1) == "*=") variableValues[str.at(0)] = AnyAsVec2(variableValues[str.at(0)]) * AnyAsFloat(otherExpression); else if (str.at(1) == "/=") variableValues[str.at(0)] = AnyAsVec2(variableValues[str.at(0)]) / AnyAsFloat(otherExpression); else LogWarning("unrecognized operator \'" + str.at(1) + "\'"); } return 0; } else if (IsVar(str.at(0), globalVariableValues)) { // Checks if type is simple, like int or string if (any_type(globalVariableValues[str.at(0)]) <= 3) { if (str.at(1) == "=") globalVariableValues[str.at(0)] = EvalExpression(unWrapVec(vector(str.begin() + 2, str.end())), variableValues); else if (str.at(1) == "+=") globalVariableValues[str.at(0)] = EvalExpression(str.at(0) + "+(" + unWrapVec(vector(str.begin() + 2, str.end())) + ")", variableValues); else if (str.at(1) == "-=") globalVariableValues[str.at(0)] = AnyAsFloat(globalVariableValues[str.at(0)]) - AnyAsFloat(EvalExpression(unWrapVec(vector(str.begin() + 2, str.end())), variableValues)); else if (str.at(1) == "*=") globalVariableValues[str.at(0)] = AnyAsFloat(globalVariableValues[str.at(0)]) * AnyAsFloat(EvalExpression(unWrapVec(vector(str.begin() + 2, str.end())), variableValues)); else if (str.at(1) == "/=") globalVariableValues[str.at(0)] = AnyAsFloat(globalVariableValues[str.at(0)]) / AnyAsFloat(EvalExpression(unWrapVec(vector(str.begin() + 2, str.end())), variableValues)); else LogWarning("unrecognized operator \'" + str.at(1) + "\'"); } // Else it is a Vec2. No other complex class can be operated on it's base form (ex. you can't do: Sprite += Sprite) else if (any_type(globalVariableValues[str.at(0)]) == 5) { boost::any otherExpression = EvalExpression(unWrapVec(vector(str.begin() + 2, str.end())), variableValues); if (str.at(1) == "=") globalVariableValues[str.at(0)] = otherExpression; else if (str.at(1) == "+=") globalVariableValues[str.at(0)] = AnyAsVec2(globalVariableValues[str.at(0)]) + AnyAsVec2(otherExpression); else if (str.at(1) == "-=") globalVariableValues[str.at(0)] = AnyAsVec2(globalVariableValues[str.at(0)]) - AnyAsVec2(otherExpression); else if (str.at(1) == "*=") globalVariableValues[str.at(0)] = AnyAsVec2(globalVariableValues[str.at(0)]) * AnyAsFloat(otherExpression); else if (str.at(1) == "/=") globalVariableValues[str.at(0)] = AnyAsVec2(globalVariableValues[str.at(0)]) / AnyAsFloat(otherExpression); else LogWarning("unrecognized operator \'" + str.at(1) + "\'"); } return 0; } LogWarning("uninitialized variable or typo in \'" + str.at(0) + "\'"); return 1; } boost::any ProcessLine(const vector>& words, int lineNum, unordered_map& variableValues) { if (words.at(lineNum).at(0)[0] == '/' && words.at(lineNum).at(0)[1] == '/') return nullType; // If print statement (deprecated, now use ZS.System.Print() function) else if (words.at(lineNum).at(0) == "print") { cout << StringRaw(AnyAsString(EvalExpression(unWrapVec(vector(words.at(lineNum).begin() + 1, words.at(lineNum).end())), variableValues))) << endl; return nullType; } // Check if function return else if (words.at(lineNum).at(0) == "return") return EvalExpression(unWrapVec(vector(words.at(lineNum).begin() + 1, words.at(lineNum).end())), variableValues); // Check if it is ZS Builtin function else if (words.at(lineNum).at(0)[0] == 'Z' && words.at(lineNum).at(0)[1] == 'S' && words.at(lineNum).at(0)[2] == '.') return EvalExpression(unWrapVec(words.at(lineNum)), variableValues); // Check if it is function else if (IsFunction(trim(split(words.at(lineNum).at(0), '(')[0]))) { if (count(words.at(lineNum).at(0), '(') > 0 && count(words.at(lineNum).at(0), ')') > 0) ExecuteFunction(trim(split(words.at(lineNum).at(0), '(')[0]), vector()); else ExecuteFunction(trim(split(words.at(lineNum).at(0), '(')[0]), VarValues(split(RMParenthesis("(" + split(unWrapVec(rangeInVec(words.at(lineNum), 0, (int)words.at(lineNum).size() - 1)), '(')[1]), ','), variableValues)); return nullType; } // Check if global variable declaration else if (trim(words.at(lineNum).at(0)) == "global") { globalVariableValues[words.at(lineNum).at(2)] = EvalExpression(unWrapVec(slice(words.at(lineNum), 4, -1)), variableValues); return nullType; } // Iterate through all types to see if line inits or // re-inits a variable then store it with it's value else if (countInVector(types, trim(words.at(lineNum).at(0))) > 0) { variableValues[words.at(lineNum).at(1)] = EvalExpression(unWrapVec(slice(words.at(lineNum), 3, -1)), variableValues); return nullType; } // Check existing variables: If matches, then it means // the variables value is getting changed with an operator else if (count(words.at(lineNum).at(0), '.') == 0 && (IsVar(words.at(lineNum).at(0), variableValues) || IsVar(words.at(lineNum).at(0), globalVariableValues))) { // Evaluates what the operator (ex. '=', '+=') does to the value on the left by the value on the right varOperation(vector(words.at(lineNum).begin(), words.at(lineNum).end()), variableValues); return nullType; } // Check existing variables: To see if class sub component matches else if (count(words.at(lineNum).at(0), '.') > 0 && IsVar(split(words.at(lineNum).at(0), '.')[0], variableValues) || IsVar(split(words.at(lineNum).at(0), '.')[0], globalVariableValues)) { if (IsVar(split(words.at(lineNum).at(0), '.')[0], variableValues)) variableValues[split(words.at(lineNum).at(0), '.')[0]] = EditClassSubComponent(variableValues[split(words.at(lineNum).at(0), '.')[0]], words.at(lineNum).at(1), EvalExpression(unWrapVec(vector(words.at(lineNum).begin() + 2, words.at(lineNum).end())), variableValues), split(words.at(lineNum).at(0), '.')[1]); else if (IsVar(split(words.at(lineNum).at(0), '.')[0], globalVariableValues)) globalVariableValues[split(words.at(lineNum).at(0), '.')[0]] = EditClassSubComponent(globalVariableValues[split(words.at(lineNum).at(0), '.')[0]], words.at(lineNum).at(1), EvalExpression(unWrapVec(vector(words.at(lineNum).begin() + 2, words.at(lineNum).end())), variableValues), split(words.at(lineNum).at(0), '.')[1]); return nullType; } // Gathers while loop contents else if (words.at(lineNum).at(0) == "while") { vector> whileContents; vector whileParameters; for (int w = 1; w < (int)words.at(lineNum).size(); w++) whileParameters.push_back(words.at(lineNum)[w]); int numOfBrackets = 1; for (int p = lineNum + 2; p < (int)words.size(); p++) { numOfBrackets += countInVector(words.at(p), "{") - countInVector(words.at(p), "}"); if (numOfBrackets == 0) break; whileContents.push_back(words.at(p)); } whileContents = removeTabsWdArry(whileContents, 1); while (BooleanLogic(whileParameters.at(0), whileParameters.at(1), whileParameters.at(2), variableValues)) { //Iterate through all lines in while loop for (int lineNum = 0; lineNum < (int)whileContents.size(); lineNum++) { boost::any returnVal = ProcessLine(whileContents, lineNum, variableValues); if (!returnVal.empty()) return returnVal; } } return nullType; } // Gathers if statement contents else if (words.at(lineNum).at(0) == "if") { vector> ifContents; vector ifParameters; for (int w = 1; w < (int)words.at(lineNum).size(); w++) ifParameters.push_back(words.at(lineNum).at(w)); int numOfBrackets = 1; lineNum += 2; while (lineNum < (int)words.size()) { numOfBrackets += countInVector(words.at(lineNum), "{") - countInVector(words.at(lineNum), "}"); if (numOfBrackets == 0) break; ifContents.push_back(words.at(lineNum)); lineNum++; } ifContents = removeTabsWdArry(ifContents, 1); if (BooleanLogic(ifParameters.at(0), ifParameters.at(1), ifParameters.at(2), variableValues)) { //Iterate through all lines in if statement for (int l = 0; l < (int)ifContents.size(); l++) { boost::any returnVal = ProcessLine(ifContents, l, variableValues); if (!returnVal.empty()) return returnVal; } } //else if (words.size() > lineNum + 1) // if (words[lineNum + 1][0] == "else") // { // lineNum += 1; // vector elseContents; // int numOfBrackets = 1; // while (lineNum < (int)words.size()) // { // numOfBrackets += countInVector(words[lineNum], "{") - countInVector(words[lineNum], "}"); // if (numOfBrackets == 0) // break; // elseContents.push_back(""); // for (int w = 0; w < (int)words[lineNum].size(); w++) // { // elseContents[(int)elseContents.size() - 1] += words[lineNum][w] + " "; // } // lineNum++; // } // elseContents = removeTabs(elseContents, 2); // vector> innerWords; // for (int i = 0; i < (int)elseContents.size(); i++) // innerWords.push_back(split(elseContents[i], ' ')); // //Iterate through all lines in else statement // for (int lineNum = 0; lineNum < (int)elseContents.size(); lineNum++) // { // ProcessLine(innerWords, lineNum, variableValues); // } // return nullType; // } return nullType; } //// Gathers else statement contents //if (words[lineNum][0] == "else") //{ // //} return nullType; } boost::any ExecuteFunction(const string& functionName, const vector& inputVarVals) { // Get contents of function std::vector> words = functionValues[functionName]; unordered_map variableValues = {}; std::vector funcArgs = words.at(0); for (int i = 0; i < (int)inputVarVals.size(); i++) { if(i < funcArgs.size()) { variableValues[funcArgs[i]] = inputVarVals[i]; #if DEVELOPER_MESSAGES == true cout << functionName + " \x1B[33m" << funcArgs[i] << " == " << AnyAsString(inputVarVals[i]) << "\033[0m\t\t" << endl; #endif } } //Iterate through all lines in function for (int lineNum = 1; lineNum < (int)words.size(); lineNum++) { try { boost::any returnVal = ProcessLine(words, lineNum, variableValues); if (!returnVal.empty()) return returnVal; } catch (const std::exception&) { LogCriticalError("\'" + unWrapVec(words.at(lineNum)) + "\'\n In function: " + functionName + "\n Line: " + to_string(lineNum)); } } return nullType; } int parseZSharp(string script) { script = replace(script, " ", "\t"); #if DEVELOPER_MESSAGES InterpreterLog("Contents:\n" + script); #endif vector lines = split(script, '\n'); vector> words; for (int i = 0; i < (int)lines.size(); i++) words.push_back(split(lines.at(i), ' ')); #if DEVELOPER_MESSAGES InterpreterLog("Gather variables & functions..."); #endif // First go through entire script and iterate through all types to see if line is a variable // or function declaration, then store it with it's value for (int lineNum = 0; lineNum < (int)words.size(); lineNum++) { //Checks if it is function if (words.at(lineNum).at(0) == "func") { vector> functionContents; string functName = split(words.at(lineNum).at(1), '(')[0]; #if DEVELOPER_MESSAGES == true InterpreterLog("Load script function " + functName + "..."); #endif string args = ""; if (indexInStr(unWrapVec(words.at(lineNum)), ')') - indexInStr(unWrapVec(words.at(lineNum)), '(') > 1) for (int w = 1; w < (int)words.at(lineNum).size(); w++) // Get all words from the instantiation line: these are the args { args += replace(replace(words.at(lineNum).at(w), "(", " "), ")", ""); } args = trim(replace(args, functName + " ", "")); functionContents.push_back(split(args, ',')); int numOfBrackets = 1; for (int p = lineNum + 2; p < (int)words.size(); p++) { numOfBrackets += countInVector(words.at(p), "{") - countInVector(words.at(p), "}"); if (numOfBrackets == 0) break; functionContents.push_back(removeTabs(words.at(p), 1)); } functionValues[functName] = functionContents; } else { if (words.at(lineNum).at(0) == "string"){ globalVariableValues[words.at(lineNum).at(1)] = StringRaw(words.at(lineNum).at(3)); #if DEVELOPER_MESSAGES == true InterpreterLog("Load script variable " + words.at(lineNum).at(1) + "..."); #endif } else if (words.at(lineNum).at(0) == "int"){ globalVariableValues[words.at(lineNum).at(1)] = stoi(words.at(lineNum).at(3)); #if DEVELOPER_MESSAGES == true InterpreterLog("Load script variable " + words.at(lineNum).at(1) + "..."); #endif } else if (words.at(lineNum).at(0) == "float"){ globalVariableValues[words.at(lineNum).at(1)] = stof(words.at(lineNum).at(3)); #if DEVELOPER_MESSAGES == true InterpreterLog("Load script variable " + words.at(lineNum).at(1) + "..."); #endif } else if (words.at(lineNum).at(0) == "bool"){ globalVariableValues[words.at(lineNum).at(1)] = stob(words.at(lineNum).at(3)); #if DEVELOPER_MESSAGES == true InterpreterLog("Load script variable " + words.at(lineNum).at(1) + "..."); #endif } /*else LogWarning("unrecognized type \'" + words.at(lineNum).at(0) + "\' on line: " + to_string(lineNum));*/ } } #if DEVELOPER_MESSAGES InterpreterLog("Start Main()"); #endif // Executes main, which is the starting function ExecuteFunction("Main", vector {}); return 0; } int main(int argc, char* argv[]) { // Gathers builtin functions and variables GetBuiltins(ZSContents); functionValues = builtinFunctionValues; globalVariableValues = builtinVarVals; std::string scriptTextContents; // If scriptname is supplied and not in developer mode if (argc > 1) { std::string scriptPath = argv[1]; #if DEVELOPER_MESSAGES cout << scriptPath << endl; #endif std::string projectDirectory = scriptPath.substr(0, scriptPath.find_last_of("/\\")); #if UNIX // Get script contents auto ss = ostringstream{}; ifstream input_file(scriptPath); ss << input_file.rdbuf(); scriptTextContents = ss.str(); #if DEVELOPER_MESSAGES InterpreterLog("Gather script contents..."); #endif chdir(projectDirectory.c_str()); #if DEVELOPER_MESSAGES InterpreterLog("Change directory to " + projectDirectory + "..."); #endif #if DEVELOPER_MESSAGES string newPath = filesystem::current_path(); InterpreterLog("Current working directory is " + newPath); #endif #elif WINDOWS // Get script contents ifstream script(scriptPath); stringstream scriptString; scriptString << script.rdbuf(); scriptTextContents = scriptString.str(); #if DEVELOPER_MESSAGES InterpreterLog("Gather script contents..."); #endif std::wstring_convert> converter; std::wstring wide = converter.from_bytes(projectDirectory); LPCWSTR s = wide.c_str(); SetCurrentDirectory(s); #if DEVELOPER_MESSAGES InterpreterLog("Change directory to " + projectDirectory + "..."); #endif #endif } else { LogWarning("No script provided! Please drag and drop .ZS file over interpreter executable file, or provide it's path as a command-line argument."); cout << "Press Enter to Continue"; cin.ignore(); exit(1); } //system("pause"); #if DEVELOPER_MESSAGES InterpreterLog("Parsing..."); #endif parseZSharp(scriptTextContents); #if DEVELOPER_MESSAGES cout << "Press Enter to Continue"; cin.ignore(); exit(1); #endif return 0; }