From 7c9ff819d564a540b4ed904f388319d41876c607 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thea=20Sch=C3=B6bl?= Date: Sun, 25 Nov 2018 18:09:02 +0100 Subject: [PATCH] Finished first C# Verison of the BeatMapLanguage Interpreter --- BeatMapLanguage/BeatMapLanguage.cs | 391 +++++++++++++++++++++++++ BeatMapLanguage/BeatMapLanguage.csproj | 2 +- BeatMapLanguage/Program.cs | 15 - 3 files changed, 392 insertions(+), 16 deletions(-) create mode 100644 BeatMapLanguage/BeatMapLanguage.cs delete mode 100644 BeatMapLanguage/Program.cs diff --git a/BeatMapLanguage/BeatMapLanguage.cs b/BeatMapLanguage/BeatMapLanguage.cs new file mode 100644 index 0000000..bca8a27 --- /dev/null +++ b/BeatMapLanguage/BeatMapLanguage.cs @@ -0,0 +1,391 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BeatMapLanguage +{ + class BmlFunction + { + public readonly string name; + public readonly int pos; + + public BmlFunction(string _name, int _pos) + { + name = _name; + pos = _pos; + } + } + + class BmlVariable + { + public readonly string name; + public string value; + + public BmlVariable(string _name, string _value) + { + name = _name; + value = _value; + } + } + + class Interpreter + { + //Error Codes + public static readonly string error_variable_not_found = "VARIABLE_NOT_FOUND"; + public static readonly string error_invalid_math_operator = "INVALID_OPERATOR"; + public static readonly string error_function_not_found = "FUNCTION_NOT_FOUND"; + + static readonly string no_return = "NO_RETURN"; + + //BML Specific Keys + static readonly char key_fun = '#'; + static readonly char key_comment = '§'; + static readonly char key_pattern = '!'; + static readonly char key_pattern_seperator = ':'; + static readonly char key_block_opening_bracket = '{'; + static readonly char key_block_closing_bracket = '}'; + static readonly char key_math_opening_bracket = '['; + static readonly char key_math_closing_bracket = ']'; + static readonly char key_section_marker = '"'; + static readonly char key_variable = '_'; + static readonly char key_assignment = '='; + static readonly char key_request_return = '?'; + static readonly char key_return_p1 = '<'; + static readonly char key_return_p2 = '-'; + static readonly char key_fun_args_opening_bracket = '('; + static readonly char key_fun_args_closing_bracket = ')'; + static readonly char key_args_seperator = ','; + + static readonly string fun_type_function = "fun"; + static readonly string fun_type_main = "main"; + + static readonly string fun_place_cube = "Cube"; + + //Global Variables + double currentOffset = 0.0; + bool inverted = false; + + string bmlCode = ""; + int pos = 0; + + List variables = new List(); + List functions = new List(); + + /* + * Dummy function for placing cubes + */ + void PlaceCube(double timestamp, int type, int value) + { + } + + /* + * Function returns the value of the next expression or the word to the escape char + */ + string PopNext(char escapeChar) + { + StringBuilder builder = new StringBuilder(); + + 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 = PopNext(' '); + GotoNext(key_block_opening_bracket); + return CallFunction(fun_name, PopArgs()); + } + + while (bmlCode[pos] != escapeChar) + { + if (bmlCode[pos] == key_section_marker) + { + while (bmlCode[pos] != key_section_marker) + { + builder.Append(bmlCode[pos]); + pos++; + } + pos++; + } + else + { + builder.Append(bmlCode[pos]); + pos++; + } + } + pos++; + + if (variable) + { + return GetVarVal(builder.ToString()); + } + else + { + return builder.ToString(); + } + } + + /* + * Goes to the next specified char. Because you specified the char, it already goes to the one after the specified char. + */ + void GotoNext(char key) + { + while (bmlCode[pos] != key) pos++; + pos++; + } + + /* + * Jumps to the next non-space char + */ + void JumpSpaces() + { + while (bmlCode[pos] == ' ') pos++; + } + + /* + * returns all arguemts of a function as an array + */ + string[] PopArgs() + { + List argsList = new List(); + + while (bmlCode[pos] != key_fun_args_closing_bracket) + { + JumpSpaces(); + argsList.Add(PopNext(key_args_seperator)); + } + pos++; + + return argsList.ToArray(); + } + + /* + * changes the value of a variable or creates it if non-existent + */ + void AddOrAssignVar(string name, string value) + { + foreach (BmlVariable variable in variables) + { + if (Equals(name, variable.name)) + { + variable.value = value; + return; + } + } + + variables.Add(new BmlVariable(name, value)); + } + + /* + * returns the value of the variable + */ + string GetVarVal(string name) + { + foreach (BmlVariable var in variables) + { + if (var.name == name) + { + return var.value; + } + } + + return error_variable_not_found; + } + + /* + * Evaluates Math Expressions + */ + string EvalMathExpression() + { + JumpSpaces(); + string varOne = PopNext(' '); + JumpSpaces(); + string operation = PopNext(' '); + JumpSpaces(); + string varTwo = PopNext(' '); + + GotoNext(key_math_closing_bracket); + + if (Equals(operation, "+")) + { + return (Double.Parse(varOne) + Double.Parse(varTwo)).ToString(); + } + else if (Equals(operation, "-")) + { + return (Double.Parse(varOne) - Double.Parse(varTwo)).ToString(); + } + else if (Equals(operation, "*")) + { + return (Double.Parse(varOne) * Double.Parse(varTwo)).ToString(); + } + else if (Equals(operation, "/")) + { + return (Double.Parse(varOne) / Double.Parse(varTwo)).ToString(); + } + else + { + return error_invalid_math_operator; + } + } + + string ExecuteFun(int m_pos, string[] args_names, string[] 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 = PopNext(key_pattern_seperator); + string t_inverted = PopNext(key_pattern_seperator); + int runs = Convert.ToInt32(Double.Parse(PopNext(' '))); + + double old_offset = currentOffset; + currentOffset += Double.Parse(timestamp); + + bool old_inverted = inverted; + inverted = Boolean.Parse(t_inverted); + + for (int i = 0; i < runs; i++) + { + currentOffset += Double.Parse(ExecuteFun(pos, new string[] { "p_timestamp", "p_inverted", "p_total_runs", "p_current_run_index" }, new string[] { timestamp, t_inverted, runs.ToString(), i.ToString() })); + } + + 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 = PopNext(' '); + pos = old_pos; + return s_out; + } + else if (bmlCode[pos] == key_variable) + { + pos++; + string var_name = PopNext(' '); + JumpSpaces(); + if (bmlCode[pos] == key_assignment) + { + pos++; + JumpSpaces(); + AddOrAssignVar(var_name, PopNext(' ')); + } + } + else + { + string fun_call = PopNext(' '); + GotoNext(key_fun_args_opening_bracket); + CallFunction(fun_call, PopArgs()); + } + + JumpSpaces(); + } + + pos = old_pos; + + return no_return; + } + + /* + * Calls the specified function + */ + string CallFunction(string name, string[] args) + { + if (name == fun_place_cube) + { + PlaceCube(Double.Parse(args[0]), int.Parse(args[1]), int.Parse(args[2])); + return no_return; + } + else + { + //Now we have to check for custom functions + + foreach (BmlFunction fun in functions) + { + if (Equals(fun.name, name)) + { + int old_pos = pos; + pos = fun.pos; + + string[] arg_names = PopArgs(); + + pos = old_pos; + + return ExecuteFun(fun.pos, arg_names, args); + } + } + } + + return error_function_not_found; + } + + /* + * This function is responsible for managing the everything from the beginning to the end. + */ + public void Interpret(string code) + { + pos = 0; + bmlCode = code; + + while (pos < bmlCode.Length) + { + if (bmlCode[pos] == key_fun) + { + pos++; + + string fun_type = PopNext(' '); + + JumpSpaces(); + + if (Equals(fun_type, fun_type_function)) + { + string function_name = PopNext(' '); + functions.Add(new BmlFunction(function_name, pos)); + GotoNext(key_block_closing_bracket); + } + else if (Equals(fun_type, fun_type_main)) + { + ExecuteFun(pos, new string[0], new string[0]); + return; + } + } + else if (code[pos] == key_comment) + { + pos++; + GotoNext(key_comment); + } + else + { + pos++; + } + } + } + } +} \ No newline at end of file diff --git a/BeatMapLanguage/BeatMapLanguage.csproj b/BeatMapLanguage/BeatMapLanguage.csproj index e8138eb..9a927ce 100644 --- a/BeatMapLanguage/BeatMapLanguage.csproj +++ b/BeatMapLanguage/BeatMapLanguage.csproj @@ -42,7 +42,7 @@ - + diff --git a/BeatMapLanguage/Program.cs b/BeatMapLanguage/Program.cs deleted file mode 100644 index d96ec9c..0000000 --- a/BeatMapLanguage/Program.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace BeatMapLanguage -{ - class Program - { - static void Main(string[] args) - { - } - } -}