diff --git a/Source/MediocreMapper/BeatLanguage.cpp b/Source/MediocreMapper/BeatLanguage.cpp new file mode 100644 index 0000000..ede2942 --- /dev/null +++ b/Source/MediocreMapper/BeatLanguage.cpp @@ -0,0 +1,288 @@ +#include "BeatMapLanInterpreter.h" + +BeatMapLanInterpreter::BeatMapLanInterpreter(extFuns functions) +{ + externalFunctions = functions; +} + + +BeatMapLanInterpreter::~BeatMapLanInterpreter() +{ +} + +void BeatMapLanInterpreter::interpret(string code) +{ + pos = 0; + bmlCode = code; + + while (pos < bmlCode.length) + { + if (bmlCode[pos] == key_fun) + { + pos++; + + string fun_type = pop_next(' '); + + jumpSpaces(); + + if (fun_type.compare(fun_type_function)) + { + string function_name = pop_next(' '); + functions.push_back({ function_name, pos }); + gotoNext(key_block_closing_bracket); + } + else if (fun_type.compare(fun_type_main)) + { + executeFun(pos, vector(), vector()); + return; + } + } + else if (code[pos] == key_comment) + { + pos++; + gotoNext(key_comment); + } + else + { + pos++; + } + } +} + +string BeatMapLanInterpreter::pop_next(char escape_char) +{ + string builder; + bool variable = false; + + if (bmlCode[pos] == key_variable) { + pos++; + variable = true; + } + else if (bmlCode[pos] == key_math_opening_bracket) { + pos++; + return evalMathExpression(); + } + else if (bmlCode[pos] == key_request_return) { + pos++; + string fun_name = pop_next(' '); + gotoNext(key_block_opening_bracket); + return callFunction(fun_name, popArgs()); + } + + while (bmlCode[pos] != escape_char) + { + if (bmlCode[pos] == key_section_marker) + { + while (bmlCode[pos] != key_section_marker) + { + builder.push_back(bmlCode[pos]); + pos++; + } + pos++; + } + else + { + builder.push_back(bmlCode[pos]); + pos++; + } + } + pos++; + + if (variable) + { + return getVarVal(builder); + } + else + { + return builder; + } +} + +void BeatMapLanInterpreter::gotoNext(char key) +{ + while (bmlCode[pos] != key) pos++; + pos++; +} + +void BeatMapLanInterpreter::jumpSpaces() +{ + while (bmlCode[pos] == ' ') pos++; +} + +vector BeatMapLanInterpreter::popArgs() +{ + vector argsList; + + while (bmlCode[pos] != key_fun_args_closing_bracket) + { + jumpSpaces(); + argsList.push_back(pop_next(key_args_seperator)); + } + pos++; + + return argsList; +} + +void BeatMapLanInterpreter::addOrAssignVar(string name, string value) +{ + for (BmlVariable &variable : variables) + { + if (name.compare(variable.name)) + { + variable.value = value; + return; + } + } + + variables.push_back({ name, value }); +} + +string BeatMapLanInterpreter::getVarVal(string name) +{ + for (BmlVariable &var : variables) + { + if (var.name == name) + { + return var.value; + } + } + + return error_variable_not_found; +} + +string BeatMapLanInterpreter::evalMathExpression() +{ + jumpSpaces(); + string varOne = pop_next(' '); + jumpSpaces(); + string operation = pop_next(' '); + jumpSpaces(); + string varTwo = pop_next(' '); + + gotoNext(key_math_closing_bracket); + + if (operation.compare("+")) + { + return to_string(atof(varOne.c_str()) + atof(varTwo.c_str())); + } + else if (operation.compare("-")) + { + return to_string(atof(varOne.c_str()) - atof(varTwo.c_str())); + } + else if (operation.compare("*")) + { + return to_string(atof(varOne.c_str()) * atof(varTwo.c_str())); + } + else if (operation.compare("/")) + { + return to_string(atof(varOne.c_str()) / atof(varTwo.c_str())); + } + else + { + return error_invalid_math_operator; + } +} + +string BeatMapLanInterpreter::executeFun(int m_pos, vector args_names, vector args) +{ + int old_pos = pos; + pos = m_pos; + + gotoNext(key_block_opening_bracket); + jumpSpaces(); + + while (bmlCode[pos] != key_block_closing_bracket) + { + if (bmlCode[pos] == key_comment) + { + pos++; + gotoNext(key_comment); + } + else if (bmlCode[pos] == key_pattern) + { + pos++; + + string timestamp = pop_next(key_pattern_seperator); + string t_inverted = pop_next(key_pattern_seperator); + int runs = atof(pop_next(' ').c_str()); + + double old_offset = currentOffset; + currentOffset += atof(timestamp.c_str()); + + bool old_inverted = inverted; + inverted = t_inverted.compare("true"); //Converting from string to bool + + for (int i = 0; i < runs; i++) + { + currentOffset += atof(executeFun(pos, vector{ "p_timestamp", "p_inverted", "p_total_runs", "p_current_run_index" }, vector{ timestamp, t_inverted, to_string(runs), to_string(i)}).c_str()); + } + + gotoNext(key_block_closing_bracket); + + currentOffset = old_offset; + inverted = old_inverted; + } + else if (bmlCode[pos] == key_return_p1 && bmlCode[pos + 1] == key_return_p2) + { + pos += 2; + jumpSpaces(); + string s_out = pop_next(' '); + pos = old_pos; + return s_out; + } + else if (bmlCode[pos] == key_variable) + { + pos++; + string var_name = pop_next(' '); + jumpSpaces(); + if (bmlCode[pos] == key_assignment) + { + pos++; + jumpSpaces(); + addOrAssignVar(var_name, pop_next(' ')); + } + } + else + { + string fun_call = pop_next(' '); + gotoNext(key_fun_args_opening_bracket); + callFunction(fun_call, popArgs()); + } + + jumpSpaces(); + } + + pos = old_pos; + + return no_return; +} + +string BeatMapLanInterpreter::callFunction(string name, vector args) +{ + if (name == fun_place_cube) + { + externalFunctions.p_cube(atof(args[0].c_str()), atoi(args[1].c_str()), atoi(args[2].c_str())); + return no_return; + } + else + { + //Now we have to check for custom functions + + for (BmlFunction &fun : functions) + { + if (fun.name.compare(name)) + { + int old_pos = pos; + pos = fun.pos; + + vector arg_names = popArgs(); + + pos = old_pos; + + return executeFun(fun.pos, arg_names, args); + } + } + } + + return error_function_not_found; +} diff --git a/Source/MediocreMapper/BeatLanguage.h b/Source/MediocreMapper/BeatLanguage.h new file mode 100644 index 0000000..58a71ea --- /dev/null +++ b/Source/MediocreMapper/BeatLanguage.h @@ -0,0 +1,85 @@ +#pragma once +#include +#include + +using namespace std; + +class BeatMapLanInterpreter +{ +public: + //typedefs + typedef void placeCube(double timestamp, int type, int value); + + struct extFuns { + placeCube * p_cube; + }; + + extFuns externalFunctions; + + //Public Constants + const string error_variable_not_found = "VARIABLE_NOT_FOUND"; + const string error_invalid_math_operator = "INVALID_OPERATOR"; + const string error_function_not_found = "FUNCTION_NOT_FOUND"; + const string no_return = "NO_RETURN"; + + struct BmlVariable { + string name; + string value; + }; + + struct BmlFunction { + string name; + int pos; + }; + + //Functions + BeatMapLanInterpreter(extFuns functions); + ~BeatMapLanInterpreter(); + + void interpret(string code); +private: + //Constants + const char key_fun = '#'; + const char key_comment = '§'; + const char key_pattern = '!'; + const char key_pattern_seperator = ':'; + const char key_block_opening_bracket = '{'; + const char key_block_closing_bracket = '}'; + const char key_math_opening_bracket = '['; + const char key_math_closing_bracket = ']'; + const char key_section_marker = '"'; + const char key_variable = '_'; + const char key_assignment = '='; + const char key_request_return = '?'; + const char key_return_p1 = '<'; + const char key_return_p2 = '-'; + const char key_fun_args_opening_bracket = '('; + const char key_fun_args_closing_bracket = ')'; + const char key_args_seperator = ','; + + const string fun_type_function = "fun"; + const string fun_type_main = "main"; + + const string fun_place_cube = "Cube"; + + //Global Vriables + double currentOffset = 0.0; + bool inverted = false; + + string bmlCode = ""; + int pos = 0; + + vector variables; + vector functions; + + //Fuctions + string pop_next(char escape_char); + void gotoNext(char key); + void jumpSpaces(); + vector popArgs(); + void addOrAssignVar(string name, string value); + string getVarVal(string name); + string evalMathExpression(); + string executeFun(int m_pos, vector args_names, vector args); + string callFunction(string name, vector args); +}; \ No newline at end of file