#include <iostream> #include <cstdlib> #include <fstream> #include <string> #include <cstring> #include <map> #include <variant> #include <vector> using namespace std; enum class VarType { INTEGER, DECIMAL, STRING, UNKNOWN }; struct Variable { VarType type; variant<int, double, string> value; Variable() : type(VarType::UNKNOWN) {} Variable(VarType t, const variant<int, double, string>& v) : type(t), value(v) {} string toString() const { switch(type) { case VarType::INTEGER: return to_string(get<int>(value)); case VarType::DECIMAL: return to_string(get<double>(value)); case VarType::STRING: return get<string>(value); default: return "unknown"; } } }; class Interpreter { private: map<string, Variable> variables; bool isVerbose = false; const vector<string> builtInFunctions = { "exit", "log", "type", "run", "verbose", "str", "dec", "int" }; const vector<string> operators = { "+", "-", "*", "/" }; const vector<string> incrementors = { "++", "--" }; const vector<string> modifiers = { "+=", "-=", "*=", "/=" }; const vector<string> comparitors = { "==", ">", ">=", "=<", "!=", "!<", "!>", "!=<", "!>=" }; bool checkForDec(const string& str) { try { return stoi(str) * 1.0 != stod(str); } catch (...) { return false; } } bool checkForInt(const string& str) { try { return stoi(str) * 1.0 == stod(str); } catch (...) { return false; } } void error(const string& str) { cout << "\u001b[31m Error running code: " << str << "\u001b[39m" << endl; } void verbose(const string& str) { if (isVerbose) cout << "\u001b[36m Info: " << str << "\u001b[39m" << endl; } string interpolateVariables(const string& input) { string result; size_t pos = 0; bool inString = false; while (pos < input.length()) { if (input[pos] == '"') { inString = !inString; result += input[pos]; pos++; } else if (pos + 1 < input.length() && input[pos] == '$' && input[pos + 1] == '{') { size_t end = input.find('}', pos); if (end != string::npos) { string varName = input.substr(pos + 2, end - (pos + 2)); auto it = variables.find(varName); if (it != variables.end()) { result += it->second.toString(); } else { error("Variable " + varName + " not found"); result += "${" + varName + "}"; } pos = end + 1; } else { result += input[pos]; pos++; } } else { result += input[pos]; pos++; } } return result; } vector<pair<string, string>> preProcessTokens(const vector<pair<string, string>>& tokens) { vector<pair<string, string>> processed; for (const auto& [token, type] : tokens) { if (type == "str") { string processedStr = interpolateVariables(token); processed.push_back({processedStr, "str"}); } else if (token[0] == '$' && token.length() > 1) { string varName = token.substr(1); auto it = variables.find(varName); if (it != variables.end()) { processed.push_back({it->second.toString(), getTokenType(it->second.toString())}); } else { error("Variable " + varName + " not found"); processed.push_back({token, "unknown"}); } } else { processed.push_back({token, type}); } } return processed; } vector<pair<string, string>> tokenize(const string& input) { vector<pair<string, string>> tokens; string currentToken; bool stringOpen = false; for (char c : input) { if (c == ' ' && !stringOpen) { if (!currentToken.empty()) { tokens.push_back({currentToken, getTokenType(currentToken)}); currentToken.clear(); } } else { currentToken += c; } if (c == '"') stringOpen = !stringOpen; } if (!currentToken.empty()) { tokens.push_back({currentToken, getTokenType(currentToken)}); } return tokens; } string getTokenType(const string& token) { for (const auto& func : builtInFunctions) { if (token == func) return "function"; } for (const auto& inc : incrementors) { if (token == inc) return "incrementor"; } for (const auto& mod : modifiers) { if (token == mod) return "modifier"; } for (const auto& op : operators) { if (token == op) return "operator"; } for (const auto& comp : comparitors) { if (token == comp) return "comparitor"; } if (variables.find(token) != variables.end()) return "variable"; if (token == "=") return "equals"; if (token[0] == '"') return "str"; if (checkForInt(token)) return "int"; if (checkForDec(token)) return "dec"; return "unknown"; } variant<int, string, double> handleVariable(const string variable) { auto& var = variables[variable]; if (var.type == VarType::INTEGER) { return get<int>(var.value); } else if (var.type == VarType::STRING) { return get<string>(var.value); } else if (var.type == VarType::DECIMAL) { return get<double>(var.value); } error("Unknown variable " + variable); return "Error"; } int doIntMath(const vector<pair<string, string>>& tokens) { verbose("Var type is integer"); if (tokens.size() == 2 && tokens[1].second == "incrementor") { verbose("Incrementing..."); auto var = handleVariable(tokens[0].first); if (!std::holds_alternative<int>(var)) { error("Only integers can be incremented"); return 0; } if (tokens[1].first == "++") return get<int>(var) + 1; if (tokens[1].first == "--") return get<int>(var) - 1; return 0; } if (tokens.size() == 5 && tokens[1].first == "=" && tokens[3].second == "operator") { verbose("Detected an operator"); int left = (tokens[2].second == "variable") ? get<int>(handleVariable(tokens[2].first)) : stoi(tokens[2].first); int right = (tokens[4].second == "variable") ? get<int>(handleVariable(tokens[4].first)) : stoi(tokens[4].first); if (tokens[3].first == "+") return left + right; if (tokens[3].first == "-") return left - right; if (tokens[3].first == "*") return left * right; if (tokens[3].first == "/") { if (right == 0) { error("Division by zero"); return 0; } return left / right; } } error("Invalid operation"); return 0; } public: auto executeCommand(const string& input) { string processedInput = interpolateVariables(input); auto tokens = tokenize(input); if (tokens.empty()) return 0; tokens = preProcessTokens(tokens); if (tokens[0].first == "log") { verbose("Log command run"); if (tokens.size() < 2) { error("log requires an argument"); return 0; } const auto& [token, type] = tokens[1]; if (type == "str" || type == "int" || type == "dec") { cout << token << endl; } else { error("that type cannot be logged"); } return 0; } else if (tokens[0].first == "exit") { if (tokens.size() < 2) { exit(0); } else if (tokens.size() > 1) { const auto& [token, type] = tokens[1]; if (type == "int") { exit(stoi(token)); } else { error("exit argument must be integer"); } } return 0; } else if (tokens[0].first == "type") { if (tokens.size() < 2) { error("type requires an argument"); return 0; } const auto& [token, type] = tokens[1]; cout << type << endl; return 0; } else if (tokens[0].first == "run") { if (tokens.size() < 2) { error("run requires an argument"); return 0; } string inputStr = tokens[1].first; char input[inputStr.length() + 1]; strcpy(input, inputStr.c_str()); int returnCode = system(input); return 0; } else if (tokens[0].first == "str") { verbose("String command run"); if (tokens.size() < 2) { error("variable must have a name"); return 0; } if (tokens.size() < 4) { error("when defining a variable, set what the variable means"); return 0; } const auto& varName = tokens[1].first; if (tokens[2].first != "=") { error("when defining a variable, use '='"); return 0; } if (variables.find(varName) != variables.end()) { error("variable is already initialized"); return 0; } if (tokens[3].second != "str") { error("you've initialized a string, but set it's value to a different type"); return 0; } string value = tokens[3].first; value = value.substr(1, value.length() - 2); variables[varName] = Variable(VarType::STRING, value); verbose("String variable " + varName + "defined as " + value); return 0; } else if (tokens[0].first == "int") { verbose("Integer command run"); if (tokens.size() < 2) { error("variable must have a name"); return 0; } if (tokens.size() < 4) { error("when defining a variable, set what the variable means"); return 0; } const auto& varName = tokens[1].first; if (tokens[2].first != "=") { error("when defining a variable, use '='"); return 0; } if (variables.find(varName) != variables.end()) { error("variable is already initialized"); return 0; } if (tokens[3].second != "int") { error("you've initialized an integer, but set it's value to a different type"); return 0; } int value = stoi(tokens[3].first); variables[varName] = Variable(VarType::INTEGER, value); verbose("Integer variable " + varName + "defined as " + to_string(value)); return 0; } else if (tokens[0].first == "dec") { verbose("Decimal command run"); if (tokens.size() < 2) { error("variable must have a name"); return 0; } if (tokens.size() < 4) { error("when defining a variable, set what the variable means"); return 0; } const auto& varName = tokens[1].first; if (tokens[2].first != "=") { error("when defining a variable, use '='"); return 0; } if (variables.find(varName) != variables.end()) { error("variable is already initialized"); return 0; } if (tokens[3].second != "dec") { error("you've initialized a decimal, but set it's value to a different type"); return 0; } double value = stod(tokens[3].first); variables[varName] = Variable(VarType::DECIMAL, value); verbose("Decimal variable " + varName + "defined as " + to_string(value)); return 0; } else if (tokens[0].first == "verbose") { isVerbose = !isVerbose; return 0; } else if (tokens[0].first == "help") { cout << "Oktolang Help\nBuilt In Functions:\n help: This current help function\n log: Log something to the command line\n type: Find the type of the input\n str: Define a string\n int: Define a whole number\n dec: Define a number with a decimal place\n"; return 0; } else if (variables.find(tokens[0].first) != variables.end()) { verbose("Doing stuff with variables..."); if (tokens.size() < 2) { error("Expected an operator"); return 0; } auto& var = variables[tokens[0].first]; if (var.type == VarType::INTEGER) { verbose("Mathing..."); var.value = doIntMath(tokens); } else if (var.type == VarType::DECIMAL) { if (tokens[1].second == "incrementor") { if (tokens[1].first == "++") { double currentValue = get<double>(var.value); var.value = currentValue + 1; } else if (tokens[1].second == "--") { double currentValue = get<double>(var.value); var.value = currentValue - 1; } } else if (tokens[3].second == "operator" && tokens[1].second == "equals") { verbose("Detected an operator"); if (tokens[3].first == "+") { verbose("Adding..."); if (tokens[2].second == "variable") { verbose("Editing variable in 2nd token"); auto& varAdd = variables[tokens[2].first]; if (varAdd.type == VarType::INTEGER || varAdd.type == VarType::DECIMAL) { tokens[2].first = to_string(get<double>(varAdd.value)); tokens[2].second = "int"; } else { error("not all the variables you're adding are integers"); return 0; } } if (tokens[4].second == "variable") { verbose("Editing variable in 4th token"); auto& varAdd = variables[tokens[4].first]; if (varAdd.type == VarType::INTEGER || varAdd.type == VarType::DECIMAL) { tokens[4].first = to_string(get<double>(varAdd.value)); tokens[4].second = "int"; } else { error("not all the variables you're adding are integers"); return 0; } } if (tokens[2].second != "int" || tokens[4].second != "int") { verbose("Detected types are " + tokens[2].second + " and " + tokens[4].second); error("make sure you're adding integers and integers when setting an integer"); return 0; } verbose("Trying to add variables"); var.value = stod(tokens[2].first) + stoi(tokens[4].first); return 0; } if (tokens[3].first == "-") { verbose("Subtracting..."); if (tokens[2].second == "variable") { verbose("Editing variable in 2nd token"); auto& varAdd = variables[tokens[2].first]; if (varAdd.type == VarType::INTEGER || varAdd.type == VarType::DECIMAL) { tokens[2].first = to_string(get<double>(varAdd.value)); tokens[2].second = "int"; } else { error("not all the variables you're adding are integers"); return 0; } } if (tokens[4].second == "variable") { verbose("Editing variable in 4th token"); auto& varAdd = variables[tokens[4].first]; if (varAdd.type == VarType::INTEGER || varAdd.type == VarType::DECIMAL) { tokens[4].first = to_string(get<double>(varAdd.value)); tokens[4].second = "int"; } else { error("not all the variables you're adding are integers"); return 0; } } if (tokens[2].second != "int" || tokens[4].second != "int") { verbose("Detected types are " + tokens[2].second + " and " + tokens[4].second); error("make sure you're adding integers and integers when setting an integer"); return 0; } verbose("Trying to subtract variables"); var.value = stod(tokens[2].first) - stoi(tokens[4].first); return 0; } if (tokens[3].first == "*") { verbose("Multiplying..."); if (tokens[2].second == "variable") { verbose("Editing variable in 2nd token"); auto& varAdd = variables[tokens[2].first]; if (varAdd.type == VarType::INTEGER || varAdd.type == VarType::DECIMAL) { tokens[2].first = to_string(get<double>(varAdd.value)); tokens[2].second = "int"; } else { error("not all the variables you're adding are integers"); return 0; } } if (tokens[4].second == "variable") { verbose("Editing variable in 4th token"); auto& varAdd = variables[tokens[4].first]; if (varAdd.type == VarType::INTEGER || varAdd.type == VarType::DECIMAL) { tokens[4].first = to_string(get<double>(varAdd.value)); tokens[4].second = "int"; } else { error("not all the variables you're adding are integers"); return 0; } } if (tokens[2].second != "int" || tokens[4].second != "int") { verbose("Detected types are " + tokens[2].second + " and " + tokens[4].second); error("make sure you're adding integers and integers when setting an integer"); return 0; } verbose("Trying to multiply variables"); var.value = stod(tokens[2].first) * stoi(tokens[4].first); return 0; } if (tokens[3].first == "/") { verbose("Dividing..."); if (tokens[2].second == "variable") { verbose("Editing variable in 2nd token"); auto& varAdd = variables[tokens[2].first]; if (varAdd.type == VarType::INTEGER || varAdd.type == VarType::DECIMAL) { tokens[2].first = to_string(get<double>(varAdd.value)); tokens[2].second = "int"; } else { error("not all the variables you're adding are integers"); return 0; } } if (tokens[4].second == "variable") { verbose("Editing variable in 4th token"); auto& varAdd = variables[tokens[4].first]; if (varAdd.type == VarType::INTEGER || varAdd.type == VarType::DECIMAL) { tokens[4].first = to_string(get<double>(varAdd.value)); tokens[4].second = "int"; } else { error("not all the variables you're adding are integers"); return 0; } } if (tokens[2].second != "int" || tokens[4].second != "int") { verbose("Detected types are " + tokens[2].second + " and " + tokens[4].second); error("make sure you're adding integers and integers when setting an integer"); return 0; } if (tokens[2].first == "0" || tokens[4].first == "0") { error("Don't divide by zero or the end of the universe will be upon us you idiot"); verbose("(please don't try any funny business i'm begging you)"); return 0; } verbose("Trying to divide variables"); var.value = stod(tokens[2].first) / stoi(tokens[4].first); return 0; } } } return 0; } if (!tokens[0].first.empty()) { error("I don't know how that works"); return 0; } return 0; }; }; int main(int argc, char *argv[]) { cout << "Oktolang interpreter\n"; Interpreter interpreter; if (argc > 1) { ifstream inFile(argv[1]); string line; while (getline(inFile, line)) { if (line[0] == '#') continue; if (interpreter.executeCommand(line) != 0) return 1; } } else { string input; while (true) { cout << "> "; getline(cin, input); if (interpreter.executeCommand(input) != 0) break; } } return 0; }