public static void Enter() { do { AnsiConsole.Write("> "); var userInput = Console.ReadLine() .Trim(); //if the input is a command if (userInput.StartsWith("/")) { command.Commands.Execute(userInput, "calc"); continue; } //if the input is an assignment if (userInput.Contains("=")) { Variables.Assign(userInput); continue; } var expression = new Expression(userInput); if (expression.IsValid) { var value = expression.Solve(); if (value is null) { continue; } AnsiConsole.MarkupLine($"[blue]{value}[/]"); } } while (true); }
/// <summary> /// Parse input /// </summary> /// <param name="input">Console input</param> /// <returns>If program is active</returns> public static bool Parse(string input) { input = TranslateCulture(input); List <string> lexedValues = Lexer.Split(input); if (lexedValues.Count <= 0) { return(true); } // handle any exceptions thrown and display them as parse errors try { // check the keyword switch (lexedValues[0].ToLower()) { case "let": Variables.Define(lexedValues[1], GetValue(lexedValues, 3)); break; case "set": Variables.Assign(lexedValues[1], GetValue(lexedValues, 3)); break; case "calc": Console.WriteLine(GetValue(lexedValues, 1)); break; case "print": Console.WriteLine(Controllers.Variables.GetValue(lexedValues[1])); break; case "rem": Variables.Destroy(lexedValues[1]); break; case "exit": return(false); } } catch (Exception ex) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine($"Parse error: {ex.Message}"); Console.ResetColor(); } return(true); }
public Evaluator(Parser interpreter) { p = interpreter; interpreter.evaluator = this; var includeStatement = new Operation("include") { canBeGlobal = true, association = Operation.Association.None, unaryFunction = (none) => { List <string> toInclude = new List <string>(); p.Eat("l curly"); while (p.CurrentToken != "r curly") { toInclude.Add(p.GetIdentifier().ToLower()); if (p.CurrentToken != "r curly") { p.Eat("comma"); } } p.Eat(); /*toInclude.Reverse(); * * Lexer.inputText = Lexer.inputText.Substring(Lexer.pointer); * Lexer.pointer = 0; * foreach (var file in toInclude) * { * Lexer.inputText = System.IO.File.ReadAllText(file + ".hpy") + "\n" + Lexer.inputText; * } * * p.lastLexerPoint = 0; * p.PreviousToken = null; * p.CurrentToken = Lexer.GetNextToken();*/ return(Value.VOID); } }; var functionDeclaration = new Operation("function") { canBeGlobal = true, association = Operation.Association.None, unaryFunction = (none) => { if (p.depth > 0) { throw p.Exception("Can only declare a function in the global scope"); } if (p.CurrentToken == "inline") { p.Eat(); } string name = p.GetIdentifier(); List <string> argNames = new List <string>(); List <Value.BatchType> argTypes = new List <Value.BatchType>(); p.Eat("l bracket"); while (p.CurrentToken != "r bracket") { argNames.Add(p.Eat <string>("identifier")); p.Eat("colon"); argTypes.Add(Value.ParseType(p.GetIdentifier())); if (p.CurrentToken != "r bracket") { p.Eat("comma"); } } p.Eat(); Value.BatchType returnType = Value.BatchType.Void; if (p.CurrentToken == "colon") { p.Eat("colon"); returnType = Value.ParseType(p.GetIdentifier()); } var func = UserFunctions.Get(name, argTypes); int id = UserFunctions.GetID(func); Variables.suffix = "_F" + id; int index = p.output.Length; string originalOutput = p.output; if (func.inline) { for (int i = 0; i < argNames.Count; i++) { Variables.Create(argNames[i] + "_I" + id, argTypes[i]); } } else { p.Comment("BEGIN FUNCTION DECLARATION: " + UserFunctions.GetSignature(name, argTypes) + ": " + returnType.ToString().ToLower()); p.output += ":func_" + id + "\n"; for (int i = 0; i < argNames.Count; i++) { Variables.Create(argNames[i], new Value(argTypes[i], "%~" + (i + 1)), 1); } } currentContext = func; p.EvaluateBlock(); currentContext = null; if (!func.inline) { if (!p.output.Trim().EndsWith("goto :EOF")) { //Variables.variables.Exists(x => false); Variables.DeleteOutOfScopeVariables(); p.output += "goto :EOF\n"; } } //p.Comment("END FUNCTION DECLARATION"); if (UserFunctions.IsRecursive(func, func)) { throw p.Exception("Recursion is forbidden"); } Variables.suffix = ""; string newOutput = p.output; p.output = originalOutput; string code = newOutput.Substring(index); func.body = code; return(Value.VOID); } }; var variableDeclaration = new Operation("var") { canBeGlobal = true, association = Operation.Association.None, unaryFunction = (none) => { int depth = p.depth; string name; Value val; if (depth == 0) { if (p.CurrentToken == "global") { p.Eat(); } name = p.Eat <string>("identifier"); var v = Variables.Get(name); p.Eat("colon"); var type = Value.ParseType(p.GetIdentifier()); if (p.CurrentToken == "equals") { p.Eat(); val = Evaluate(); if (type != val.type) { throw p.Exception("Attempt to assign value of type " + val.type + " to a variable of type " + type); } Variables.Assign(name, val); } return(Value.VOID); } if (p.CurrentToken == "global") { p.Eat(); depth = 0; } name = p.Eat <string>("identifier"); if (Variables.variables.Exists(x => x.name == name)) { //throw p.Exception("Variable already exists in this scope: " + name); } if (p.CurrentToken == "colon") { p.Eat(); var type = Value.ParseType(p.GetIdentifier()); if (Value.TypeEquals(type, Value.BatchType.Void)) { throw p.Exception("Cannot declare a variable of type Void"); } if (p.CurrentToken != "equals") { Variables.Create(name, type, depth); return(Value.VOID); } p.Eat("equals"); val = Evaluate(); if (type != val.type) { throw p.Exception("Attempt to assign value of type " + val.type + " to a variable of type " + type); } Variables.Create(name, val, depth); } else { if (depth == 0) { throw p.Exception("Variables in the global scope must have their types specified"); } p.Eat("equals"); val = Evaluate(); if (Value.TypeEquals(val.type, Value.BatchType.Void)) { throw p.Exception("Cannot declare a variable of type Void"); } if (depth > 0) { Variables.Create(name, val, depth); } } return(Value.VOID); } }; var numberLiteral = new Operation("number") { eatOperator = false, association = Operation.Association.None, unaryFunction = (none) => new Value(Value.BatchType.Int, p.Eat <float>().ToString()) }; var stringLiteral = new Operation("string") { eatOperator = false, association = Operation.Association.None, unaryFunction = (none) => new Value(Value.BatchType.String, p.Eat <string>()) }; var boolLiteral = new Operation("bool") { eatOperator = false, association = Operation.Association.None, unaryFunction = (none) => new Value(Value.BatchType.Bool, p.Eat <bool>() ? "1==1" : "1==0") }; var bracket = new Operation("l bracket") { association = Operation.Association.None, unaryFunction = (none) => { var outp = Evaluate(); p.Eat("r bracket"); if (outp.type == Value.BatchType.Int) { return(new Value(Value.BatchType.Int, "(" + outp.value + ")")); } var temp = Variables.CreateTemporary(outp.value); return(new Value(outp.type, temp)); } }; var neg = new Operation("minus") { association = Operation.Association.Right, unaryFunction = (right) => new Value(Value.BatchType.Int, "-" + right) }; var mul = new Operation("multiply") { binaryFunction = (left, right) => binaryOp(left, right, Value.BatchType.Int, "*") }; var div = new Operation("divide") { binaryFunction = (left, right) => binaryOp(left, right, Value.BatchType.Int, "/") }; var mod = new Operation("modulus") { binaryFunction = (left, right) => binaryOp(left, right, Value.BatchType.Int, "%%") }; var add = new Operation("plus") { binaryFunction = (left, right) => binaryOp(left, right, Value.BatchType.Int, "+") }; var concat = new Operation("concat") { binaryFunction = (left, right) => new Value(Value.BatchType.String, left.value + right.value) }; var sub = new Operation("minus") { binaryFunction = (left, right) => binaryOp(left, right, Value.BatchType.Int, "-") }; var identifier = new Operation("identifier") { eatOperator = false, association = Operation.Association.None, unaryFunction = (none) => { string name = p.Eat <string>("identifier").ToString(); if (StandardLibrary.Exists(name)) { List <Value> args = new List <Value>(); p.Eat("l bracket"); while (p.CurrentToken != "r bracket") { args.Add(Evaluate()); if (p.CurrentToken != "r bracket") { p.Eat("comma"); } } p.Eat(); return(StandardLibrary.CallFunction(name, args.ToArray())); } else { if (p.CurrentToken == "l bracket") { p.Comment("FUNCTION CALL TO " + name); List <Value> args = new List <Value>(); p.Eat("l bracket"); while (p.CurrentToken != "r bracket") { args.Add(Evaluate()); if (p.CurrentToken != "r bracket") { p.Eat("comma"); } } p.Eat(); var func = UserFunctions.Get(name, args.Select(x => x.type).ToList()); int id = UserFunctions.GetID(func); currentContext?.calls.Add(func); if (func.inline) { for (int i = 0; i < func.argumentNames.Count; i++) { Variables.Create(func.argumentNames[i] + "_I" + id, args[i]); } p.output += func.body; for (int i = 0; i < func.argumentNames.Count; i++) { Variables.Delete(func.argumentNames[i] + "_I" + id); } } else { p.output += "call :func_" + id + " " + string.Join(" ", args.Select(x => (x.type == Value.BatchType.String && x.value.Contains(" ")) ? "\"" + x.value + "\"" : x.value)) + "\n"; } if (func.returnType == Value.BatchType.Void) { p.Comment("END FUNCTION CALL"); return(Value.VOID); } else { //string temp = Variables.CreateTemporary("%return_value%"); p.Comment("END FUNCTION CALL"); return(new Value(func.returnType, "%return_value%")); } } if (p.CurrentToken == "increment") { p.Eat(); Variables.VerifyType(name, Value.BatchType.Int); var va = Variables.Get(name); p.output += "set /a var_" + name + (va.depth > 0 ? Variables.suffix : "") + "+=1\n"; return(Value.VOID); } else if (p.CurrentToken == "decrement") { p.Eat(); Variables.VerifyType(name, Value.BatchType.Int); var va = Variables.Get(name); p.output += "set /a var_" + name + (va.depth > 0 ? Variables.suffix : "") + "-=1\n"; return(Value.VOID); } else if (p.CurrentToken == "add") { p.Eat(); var v = Evaluate(); var va = Variables.Get(name); p.output += "set /a var_" + name + (va.depth > 0 ? Variables.suffix : "") + "+=" + v.value + "\n"; return(Value.VOID); } else if (p.CurrentToken == "subtract") { p.Eat(); var v = Evaluate(); var va = Variables.Get(name); p.output += "set /a var_" + name + (va.depth > 0 ? Variables.suffix : "") + "-=" + v.value + "\n"; return(Value.VOID); } else if (p.CurrentToken == "string add") { p.Eat(); var v = Evaluate(); var va = Variables.Get(name); p.output += "set var_" + name + (va.depth > 0 ? Variables.suffix : "") + "=!var_" + name + Variables.suffix + "!" + v.value + "\n"; return(Value.VOID); } else if (p.CurrentToken == "inc mul") { p.Eat(); var v = Evaluate(); var va = Variables.Get(name); p.output += "set /a var_" + name + (va.depth > 0 ? Variables.suffix : "") + "*=" + v.value + "\n"; return(Value.VOID); } else if (p.CurrentToken == "inc div") { p.Eat(); var v = Evaluate(); var va = Variables.Get(name); p.output += "set /a var_" + name + (va.depth > 0 ? Variables.suffix : "") + "/=" + v.value + "\n"; return(Value.VOID); } else if (p.CurrentToken == "equals") { p.Eat(); var v = Evaluate(); if (!Variables.Exists(name) && !name.Contains("%")) { throw p.Exception("No such variable exists: " + name); } Variables.Assign(name, v); return(Value.VOID); } else { if (name.Contains("%")) { //var temp1 = Variables.CreateTemporary("var_" + name); //var temp2 = Variables.CreateTemporary(""); //var temp2name = Variables.NameFromTempReference(temp2); //p.EmitLn("call set " + temp2name + "=%%" + temp1 + "%%"); return(new Value(Value.BatchType.Indeterminate, "!var_" + name + "!")); } else { //return Variables.GetReference(name); if (currentContext != null && currentContext.inline) { return(Variables.GetReference(name + "_I" + UserFunctions.GetID(currentContext))); } else { return(Variables.GetReference(name)); } } //return Variables.GetReference(name); } } } }; var openBracket = new Operation("l curly") { eatOperator = false, association = Operation.Association.None, unaryFunction = (none) => { p.Eat(); return(new Value(Value.BatchType.Void, "")); } }; var closeBracket = new Operation("r curly") { eatOperator = false, association = Operation.Association.None, unaryFunction = (none) => { p.Eat(); return(new Value(Value.BatchType.Void, "")); } }; var ifStatement = new Operation("if") { association = Operation.Association.None, unaryFunction = (none) => { int selfid = ++ifid; var condition = EvaluateCondition(); string output = ""; string temp = Variables.CreateTemporary(condition.value); output += "if " + temp + " (\n"; output += "goto if_" + selfid + "\n"; output += ")"; string ifContents = p.PreEvaluateBlock(); List <string> elseIfContents = new List <string>(); int elseifs = 0; while (p.CurrentToken == "elseif") { elseifs++; p.Eat(); condition = EvaluateCondition(); temp = Variables.CreateTemporary(condition.value); output += " else (\nif " + temp + " (\n"; output += "goto if_" + selfid + "_elseif_" + elseifs + "\n"; output += ")"; elseIfContents.Add(p.PreEvaluateBlock()); } string elseContents = null; if (p.CurrentToken == "else") { p.Eat(); output += " else (\n"; output += "goto if_" + selfid + "_else\n"; output += ")"; elseContents = p.PreEvaluateBlock(); } for (int i = 0; i < elseifs; i++) { output += "\n)"; } output += "\ngoto if_" + selfid + "_end"; output += "\n:if_" + selfid + "\n"; output += ifContents; output += "goto if_" + selfid + "_end\n"; for (int i = 1; i <= elseifs; i++) { output += ":if_" + selfid + "_elseif_" + i + "\n"; output += elseIfContents[i - 1]; output += "goto if_" + selfid + "_end\n"; } if (elseContents != null) { output += ":if_" + selfid + "_else\n"; output += elseContents; } output += ":if_" + selfid + "_end\n"; p.output += output; return(new Value(Value.BatchType.Void, "")); } }; var whileLoop = new Operation("while") { association = Operation.Association.None, unaryFunction = (none) => { int loop = ++loopid; p.output += ":loop_" + loop + "\n"; var condition = EvaluateCondition(); var temp = Variables.CreateTemporary(condition.value); p.output += "if not " + temp + " goto loop_" + loop + "_end\n"; p.loopIDs.Push(loop); p.EvaluateBlock(); p.loopIDs.Pop(); p.output += "goto loop_" + loop + "\n"; p.output += ":loop_" + loop + "_end"; return(new Value(Value.BatchType.Void, "")); } }; var untilLoop = new Operation("until") { association = Operation.Association.None, unaryFunction = (none) => { int loop = ++loopid; p.output += ":loop_" + loop + "\n"; var condition = EvaluateCondition(); var temp = Variables.CreateTemporary(condition.value); p.output += "if " + temp + " goto loop_" + loop + "_end\n"; p.loopIDs.Push(loop); p.EvaluateBlock(); p.loopIDs.Pop(); p.output += "goto loop_" + loop + "\n"; p.output += ":loop_" + loop + "_end"; return(new Value(Value.BatchType.Void, "")); } }; void VerifyType(Value v, Value.BatchType type) { if (v.type != type) { throw p.Exception("Expected value of type " + type + ", not " + v.type); } } var forLoop = new Operation("from") { association = Operation.Association.None, unaryFunction = (none) => { var start = Evaluate(); VerifyType(start, Value.BatchType.Int); p.Eat("to"); var end = Evaluate(); VerifyType(end, Value.BatchType.Int); bool upwards; try { upwards = int.Parse(start.value) < int.Parse(end.value); } catch { upwards = true; } Value step; if (p.CurrentToken == "by") { p.Eat(); step = Evaluate(); VerifyType(step, Value.BatchType.Int); } else { step = new Value(Value.BatchType.Int, upwards ? "1" : "-1"); } bool usingVariable = p.CurrentToken == "with"; string name = null; string reference = null; if (usingVariable) { p.Eat(); string varname = p.Eat <string>(); reference = Variables.Create(varname, start, p.depth).value; // %var_x% name = "var_" + varname; // var_x } else { reference = Variables.CreateTemporaryInt(start.value); // %temp_x% name = Variables.NameFromTempReference(reference); // temp_x } int loop = ++loopid; p.output += ":loop" + loop + "\n"; p.output += "if not " + reference + " " + (upwards ? "LSS" : "GTR") + " " + end.value + " goto loop" + loop + "_end\n"; p.EvaluateBlock(); p.loopIDs.Pop(); p.output += "set /a " + name + (usingVariable ? Variables.suffix : "") + "+=" + step.value + "\n"; p.output += "goto loop" + loop + "\n"; p.output += ":loop" + loop + "_end\n"; if (usingVariable) { Variables.Delete(Variables.NameFromReference(reference)); } return(Value.VOID); } }; var continueStatement = new Operation("continue") { association = Operation.Association.None, unaryFunction = (none) => { return(new Value(Value.BatchType.Void, "goto loop" + p.loopIDs.Peek())); } }; var breakStatement = new Operation("break") { association = Operation.Association.None, unaryFunction = (none) => { return(new Value(Value.BatchType.Void, "goto loop" + p.loopIDs.Peek() + "_end")); } }; var not = new Operation("not") { association = Operation.Association.Right, unaryFunction = (right) => new Value(Value.BatchType.Bool, "not " + right) }; Value BoolOp(Value left, Value right, string op) { var tempL = Variables.FlexibleTemporary(left); var tempR = Variables.FlexibleTemporary(right); var tempResult = Variables.CreateTemporary("1==0"); var name = Variables.NameFromTempReference(tempResult); p.EmitLn("if " + tempL + " " + op + " " + tempR + " set " + name + "=1==1"); return(new Value(Value.BatchType.Bool, tempResult)); } Value IntComparisonOp(Value left, Value right, string op) { VerifyType(left, Value.BatchType.Int); VerifyType(right, Value.BatchType.Int); return(BoolOp(left, right, op)); } var eq = new Operation("double equal") { binaryFunction = (left, right) => BoolOp(left, right, "EQU") }; var neq = new Operation("not equal") { binaryFunction = (left, right) => BoolOp(left, right, "NEQ") }; var gr = new Operation("greater than") { binaryFunction = (left, right) => IntComparisonOp(left, right, "GTR") }; var ls = new Operation("less than") { binaryFunction = (left, right) => IntComparisonOp(left, right, "LSS") }; var geq = new Operation("greater or equal") { binaryFunction = (left, right) => IntComparisonOp(left, right, "GEQ") }; var leq = new Operation("less or equal") { binaryFunction = (left, right) => IntComparisonOp(left, right, "LEQ") }; var semicolon = new Operation("semicolon") { association = Operation.Association.None, unaryFunction = (none) => { return(Value.VOID); } }; var and = new Operation("and") { binaryFunction = (left, right) => { string reference = Variables.CreateTemporary("1==0"); string name = Variables.NameFromTempReference(reference); //p.output += NewTempBoolVar("1==0"); //int id = varid; p.output += "if " + left.value + " ( if " + right.value + " ( \n"; p.output += "set " + name + "=1==1\n"; p.output += ") \n)\n"; return(new Value(Value.BatchType.Bool, reference)); } }; var or = new Operation("or") { binaryFunction = (left, right) => { string reference = Variables.CreateTemporary("1==0"); string name = Variables.NameFromTempReference(reference); //p.output += NewTempBoolVar("1==1"); //int id = varid; p.output += "if " + left.value + " set " + name + "=1==1\n"; p.output += "if " + right.value + " set " + name + "=1==1\n"; return(new Value(Value.BatchType.Bool, reference)); } }; /* * var arrayLiteral = new Operation("l square") * { * association = Operation.Association.None, * unaryFunction = (none) => * { * string array = ""; * while(p.CurrentToken != "r square") * { * array += Evaluate() + " "; * * if (p.CurrentToken != "r square") * { * p.Eat("comma"); * } * } * p.Eat(); * * return new Value(Value.BatchType.Array, array.TrimEnd()); * } * };*/ var returnStatement = new Operation("return") { association = Operation.Association.None, unaryFunction = (none) => { if (currentContext == null) { throw p.Exception("Can only return a value from within a function"); } Value v; if (p.CurrentToken == "semicolon") { v = Value.VOID; } else { v = Evaluate(); p.output += "set " + (v.type == Value.BatchType.Int ? "/a " : "") + "\"return_value=" + v.value + "\"\n"; } if (currentContext.returnType != v.type) { throw p.Exception("Must return value of type " + currentContext.returnType + ", not " + v.type); } //returnValueType = v.type; int depth = p.depth; p.depth = 0; Variables.DeleteOutOfScopeVariables(); p.depth = depth; /*foreach (var name in variablesDeclaredInsideFunction) * { * name.de * }*/ return(new Value(Value.BatchType.Void, currentContext.inline ? "" : "goto :EOF")); } }; var referenceOf = new Operation("ampersand") { association = Operation.Association.None, unaryFunction = (none) => { var name = p.GetIdentifier(); if (name.Contains("%")) { return(new Value(Value.BatchType.String, name)); } var val = Variables.Get(name); return(new Value(Value.BatchType.String, name + (val.depth > 0 ? Variables.suffix : ""))); } }; var deleteStatement = new Operation("delete") { association = Operation.Association.None, unaryFunction = (none) => { var var = p.GetIdentifier(); Variables.Delete(var, true); return(Value.VOID); } }; var asStatement = new Operation("as") { association = Operation.Association.Left, unaryFunction = (left) => { VerifyType(left, Value.BatchType.Indeterminate); return(new Value(Value.ParseType(p.GetIdentifier()), left.value)); } }; void register(Operation op) { precedences.Last().Add(op); } void precedence() { precedences.Add(new List <Operation>()); } precedence(); register(semicolon); precedence(); register(referenceOf); //register(toStringLiteral); //register(thisLiteral); //register(baseLiteral); register(bracket); //register(undefinedLiteral); //register(arrayLiteral); register(numberLiteral); register(stringLiteral); register(boolLiteral); //register(tableLiteral); //register(charLiteral); ////register(instantiation); precedence(); register(identifier); //register(methodCall); //register(traverse); //register(index); precedence(); register(asStatement); precedence(); register(variableDeclaration); precedence(); register(neg); register(not); //precedence(); //register(pow); precedence(); register(mul); register(div); register(mod); precedence(); register(concat); register(add); register(sub); precedence(); register(eq); register(neq); register(gr); register(ls); register(geq); register(leq); precedence(); register(and); precedence(); register(or); //precedence(); //register(conditional); precedence(); register(closeBracket); register(openBracket); register(ifStatement); register(whileLoop); register(untilLoop); //register(includeStatement); //register(forEachLoop); register(continueStatement); register(breakStatement); register(functionDeclaration); //register(funcLiteral); register(returnStatement); register(deleteStatement); register(forLoop); //register(structureLiteral); //register(include); //register(tryCatch); //register(throwStatement); }
/** <summary>Runs unit tests on the mode calc. To see how the tests work, see <see cref="CalcTest"/>.</summary> * * <remarks>The function runs two types of tests, normal tests and variables tests (organized into their * respectively named regions. Normal tests are normal computations that do not include any variables. Normal * tests are run in a loop later down the function. On the other hand, variable tests are run when they are declared. * These tests test that variables and constants are working (like throwing an error if the variable has not been * defined yet). Variables cannot be freely controlled in loops, so the execution of variable tests occur outside * them.</remarks> */ // ReSharper disable once CognitiveComplexity public static void Enter() { #region NORMAL_TESTS List <CalcTest> tests = new List <CalcTest>(); tests.Add(new CalcTest("validation1", "3 *** 5", false, null, false, new [] { "validation" })); tests.Add(new CalcTest("validation2", "10-", false, null, false, new [] { "validation" })); tests.Add(new CalcTest("validation3", "4 * (2 + 3", false, null, false, new [] { "validation" })); tests.Add(new CalcTest("validation4", "4+3)", false, null, false, new [] { "validation" })); tests.Add(new CalcTest("validation5", "123+", false, null, false, new [] { "validation" })); tests.Add(new CalcTest("validation6", "1 23 456 7890", false, null, false, new [] { "validation" })); tests.Add(new CalcTest("validation7", "-100", true, -100, false, new [] { "validation" })); tests.Add(new CalcTest("validation8", "0.00000000000000000000", true, 0, false, new [] { "validation", "output" })); tests.Add(new CalcTest("validation9", "()", false, null, false, new [] { "validation", "parenthesis", "empty expression" })); tests.Add(new CalcTest("validation10", "() + 5", true, -5, false, new [] { "validation", "parenthesis", "empty expression" })); tests.Add(new CalcTest("validation11", "(-4 + ()) + 5", true, 26, true, new [] { "validation", "parenthesis", "empty expression" })); tests.Add(new CalcTest("test1", "3.0 + 3.0", true, 6, false, new [] { "addition" })); tests.Add(new CalcTest("test2", "-3.5 +-+ 2.5", true, -6, false, new [] { "addition", "decimals" })); tests.Add(new CalcTest("test3", "-1987.50 + 1987", true, -0.5, false, new [] { "addition", "decimals" })); tests.Add(new CalcTest("test4", "10 + 9.9999", true, 19.9999, false, new [] { "addition", "decimals" })); tests.Add(new CalcTest("test5", "34.999 + 1.0", true, 35.999, false, new [] { "addition", "decimals" })); tests.Add(new CalcTest("test6", "300000000 + 900000000", true, 1200000000, true, new [] { "addition", "large numbers" })); tests.Add(new CalcTest("test7", " 7.12345678 - 2.21098765", true, 4.91246913, false, new [] { "subtraction", "decimals" })); tests.Add(new CalcTest("test8", " -500 -+-- 12.3456789", true, -512.3456789, false, new [] { "subtraction", "decimals" })); tests.Add(new CalcTest("test9", "1.23456789 * -2.10987654", true, -2.6047858281483007, true, new [] { "multiplication", "decimals" })); tests.Add(new CalcTest("test10", "123456789 * 210987654", true, 26047858281483010, false, new [] { "multiplication", "large numbers" })); tests.Add(new CalcTest("test11", "- 500 * 123.456789", true, -61728.3945, true, new [] { "multiplication", "decimals" })); tests.Add(new CalcTest("test12", "3 + 8 * ((4 + 3) * 2 + 1) - 6 / (2 + 1)", true, 121, false, new [] { "addition", "subtraction", "multiplication", "division" })); tests.Add(new CalcTest("test13", "-+3+8((4+3)*2+1)-6/(2+1)", true, 115, false, new [] { "addition", "subtraction", "multiplication", "division" })); tests.Add(new CalcTest("test14", "+-2(-3+--4)/2", true, -1, false, new [] { "addition", "multiplication", "division" })); tests.Add(new CalcTest("test15", "8 * 3 + 12 * (4 - 2)", true, 48, true, new [] { "addition", "multiplication", "division" })); #endregion /* Variable tests can be executed with different circumstances. Like for example, a variable has not been defined yet, * a first test executes and returns an error (since the expression is invalid), then the variable is defined, * then the test passes. */ #region VARIABLE_TESTS List <CalcTest> variableTests = new List <CalcTest>(); Variables.Assign("someVariable = 10"); var test16 = new CalcTest("test16", "someVariable", true, 10, false, new [] { "variables" }); test16.Run(); var test17 = new CalcTest("test17", "pi * e * tau", true, Math.PI * Math.E * Math.Tau, false, new [] { "constants" }); test17.Run(); Variables.Assign("a = 4"); Variables.Assign("b = 5"); var test18 = new CalcTest("test18", "a*2+b*3+c*(2+3)", false, null, false, new [] { "addition", "multiplication", "division", "validation", "variables" }); test18.Run(); Variables.Assign("c = 6"); var test19 = new CalcTest("test19", "a*2+b*3+c*(2+3)", true, 53, false, new [] { "addition", "multiplication", "division", "variables" }); test19.Run(); Variables.Clear(); Variables.Assign("a=1"); Variables.Assign("b = a + 1"); Variables.Assign("c = b + 1"); var test20 = new CalcTest("test20", "c", true, 3, false, new [] { "addition", "variables" }); test20.Run(); Variables.Assign("a = 2"); Variables.Assign("b = a + 2"); var test21 = new CalcTest("test21", "c", true, 5, false, new [] { "addition", "dynamic variables" }); test21.Run(); Variables.Clear(); variableTests.Add(test16); variableTests.Add(test17); variableTests.Add(test18); variableTests.Add(test19); variableTests.Add(test20); variableTests.Add(test21); #endregion var message = ""; main : do { Console.Clear(); AnsiConsole.MarkupLine(message); message = ""; foreach (var test in tests) { test.Run(); AnsiConsole.MarkupLine($">{test.TestName} :{(test.IsPassed() ? "[green]" : "[red]")}" + $"{(test.IsPassed() ? "Passed" : "Failed")}[/]:"); AnsiConsole.Write(test.AddBreak ? "\n" : ""); } foreach (var test in variableTests) { AnsiConsole.MarkupLine($">{test.TestName} :{(test.IsPassed() ? "[green]" : "[red]")}" + $"{(test.IsPassed() ? "Passed" : "Failed")}[/]:"); AnsiConsole.Write(test.AddBreak ? "\n" : ""); } var userInput = Console.ReadLine(); foreach (var test in tests .Where(test => userInput.Equals(test.TestName))) { test.PrintDetails(); goto main; } foreach (var test in variableTests .Where(test => userInput.Equals(test.TestName))) { test.PrintDetails(); goto main; } message = "[red]Invalid Input!\nEnter the name of a test to see the test details![/]"; } while (true); }