/// <summary> /// This constructor will add some basic operators, functions, and variables /// to the parser. Please note that you are able to change that using /// boolean flags /// </summary> /// <param name="loadPreDefinedFunctions">This will load "abs", "cos", "cosh", "arccos", "sin", "sinh", "arcsin", "tan", "tanh", "arctan", "sqrt", "rem", "round"</param> /// <param name="loadPreDefinedOperators">This will load "%", "*", ":", "/", "+", "-", ">", "<", "="</param> /// <param name="loadPreDefinedVariables">This will load "pi", "pi2", "pi05", "pi025", "pi0125", "pitograd", "piofgrad", "e", "phi", "major", "minor"</param> public MathParser(bool loadPreDefinedFunctions = true, bool loadPreDefinedOperators = true, bool loadPreDefinedVariables = true) { if (loadPreDefinedOperators) { // by default, we will load basic arithmetic operators. // please note, its possible to do it either inside the constructor, // or outside the class. the lowest value will be executed first! OperatorList.Add("^"); // to the power of OperatorList.Add("%"); // modulo OperatorList.Add(":"); // division 1 OperatorList.Add("/"); // division 2 OperatorList.Add("*"); // multiplication OperatorList.Add("-"); // subtraction OperatorList.Add("+"); // addition OperatorList.Add(">"); // greater than OperatorList.Add("≥"); // greater or equal than OperatorList.Add("<"); // less than OperatorList.Add("≤"); // less or equal than OperatorList.Add("="); // are equal OperatorList.Add("≠"); // not equal // when an operator is executed, the parser needs to know how. // this is how you can add your own operators. note, the order // in this list does not matter. OperatorAction.Add("^", Math.Pow); OperatorAction.Add("%", (numberA, numberB) => numberA % numberB); OperatorAction.Add(":", (numberA, numberB) => numberA / numberB); OperatorAction.Add("/", (numberA, numberB) => numberA / numberB); OperatorAction.Add("*", (numberA, numberB) => numberA * numberB); OperatorAction.Add("+", (numberA, numberB) => numberA + numberB); OperatorAction.Add("-", (numberA, numberB) => numberA - numberB); OperatorAction.Add(">", (numberA, numberB) => numberA > numberB ? 1 : 0); OperatorAction.Add("≥", (numberA, numberB) => numberA >= numberB ? 1 : 0); OperatorAction.Add("<", (numberA, numberB) => numberA < numberB ? 1 : 0); OperatorAction.Add("≤", (numberA, numberB) => numberA <= numberB ? 1 : 0); OperatorAction.Add("=", (numberA, numberB) => Math.Abs(numberA - numberB) < double.Epsilon ? 1 : 0); OperatorAction.Add("≠", (numberA, numberB) => Math.Abs(numberA - numberB) < double.Epsilon ? 0 : 1); } if (loadPreDefinedFunctions) { // these are the basic functions you might be able to use. // as with operators, localFunctions might be adjusted, i.e. // you can add or remove a function. // please open the "MathosTest" project, and find MathParser.cs // in "CustomFunction" you will see three ways of adding // a new function to this variable! // EACH FUNCTION MAY ONLY TAKE ONE PARAMETER, AND RETURN ONE // VALUE. THESE VALUES SHOULD BE IN "DOUBLE FORMAT"! LocalFunctions.Add("abs", x => Math.Abs(x[0])); LocalFunctions.Add("cos", x => Math.Cos(x[0])); LocalFunctions.Add("cosh", x => Math.Cosh(x[0])); LocalFunctions.Add("arccos", x => Math.Acos(x[0])); LocalFunctions.Add("sec", x => 1.0 / Math.Cos(x[0])); LocalFunctions.Add("cosec", x => 1.0 / Math.Sin(x[0])); LocalFunctions.Add("cotan", x => 1.0 / Math.Tan(x[0])); LocalFunctions.Add("sin", x => Math.Sin(x[0])); LocalFunctions.Add("sinh", x => Math.Sinh(x[0])); LocalFunctions.Add("arcsin", x => Math.Asin(x[0])); LocalFunctions.Add("tan", x => Math.Tan(x[0])); LocalFunctions.Add("tanh", x => Math.Tanh(x[0])); LocalFunctions.Add("arctan", x => Math.Atan(x[0])); LocalFunctions.Add("radtodeg", x => x[0] / Math.PI * 180.0); LocalFunctions.Add("degtorad", x => x[0] / 180.0 * Math.PI); LocalFunctions.Add("arctan2", x => Math.Atan2(x[0], x[1])); LocalFunctions.Add("distance", x => Math.Sqrt(Math.Pow((x[2] - x[0]), 2.0) + Math.Pow((x[3] - x[1]), 2.0))); LocalFunctions.Add("max", x => Math.Max(x[0], x[1])); LocalFunctions.Add("min", x => Math.Min(x[0], x[1])); LocalFunctions.Add("random", x => RandomNumber(x[0], x[1])); LocalFunctions.Add("sqrt", x => Math.Sqrt(x[0])); LocalFunctions.Add("rem", x => Math.IEEERemainder(x[0], x[1])); LocalFunctions.Add("root", x => Math.Pow(x[0], 1.0 / x[1])); LocalFunctions.Add("pow", x => Math.Pow(x[0], x[1])); LocalFunctions.Add("exp", x => Math.Exp(x[0])); //LocalFunctions.Add("log", x => (decimal)Math.Log((double)x[0])); //LocalFunctions.Add("log10", x => (decimal)Math.Log10((double)x[0])); LocalFunctions.Add("log", delegate(double[] input) { // input[0] is the number // input[1] is the base switch (input.Length) { case 1: return(Math.Log(input[0])); case 2: return(Math.Log(input[0], input[1])); default: return(0); // false } }); LocalFunctions.Add("round", delegate(double[] input) { // input[0] is the number // input[1] is the decimals switch (input.Length) { case 1: return(Math.Round(input[0])); case 2: return(Math.Round(input[0], (int)input[1])); default: return(0); // false } }); //LocalFunctions.Add("round", x => Math.Round(x[0])); LocalFunctions.Add("truncate", x => x[0] < 0 ? -Math.Floor(-x[0]) : Math.Floor(x[0])); LocalFunctions.Add("floor", x => Math.Floor(x[0])); LocalFunctions.Add("ceiling", x => Math.Ceiling(x[0])); LocalFunctions.Add("sign", x => Math.Sign(x[0])); LocalFunctions.Add("or", x => OrFunction(x)); LocalFunctions.Add("and", x => AndFunction(x)); LocalFunctions.Add("if", x => IfFunction(x[0], x[1], x[2])); LocalStringFunctions.Add("hex2dec", x => Hex2DecFunction(x)); } if (loadPreDefinedVariables) { // local variables such as pi can also be added into the parser. LocalVariables.Add("pi", 3.14159265358979323846264338327950288); // the simplest variable! LocalVariables.Add("pi2", 6.28318530717958647692528676655900576); LocalVariables.Add("pi05", 1.57079632679489661923132169163975144); LocalVariables.Add("pi025", 0.78539816339744830961566084581987572); LocalVariables.Add("pi0125", 0.39269908169872415480783042290993786); LocalVariables.Add("pitograd", 57.2957795130823208767981548141051704); LocalVariables.Add("piofgrad", 0.01745329251994329576923690768488612); LocalVariables.Add("pitorad", 57.2957795130823208767981548141051704); LocalVariables.Add("piofrad", 0.01745329251994329576923690768488612); LocalVariables.Add("e", 2.71828182845904523536028747135266249); LocalVariables.Add("phi", 1.61803398874989484820458683436563811); LocalVariables.Add("major", 0.61803398874989484820458683436563811); LocalVariables.Add("minor", 0.38196601125010515179541316563436189); } }
private double MathParserLogic(List <string> tokens) { // Variables replacement for (var i = 0; i < tokens.Count; i++) { if (LocalVariables.Keys.Contains(tokens[i])) { tokens[i] = LocalVariables[tokens[i]].ToString(CultureInfo); } } while (tokens.IndexOf("(") != -1) { // getting data between "(" and ")" var open = tokens.LastIndexOf("("); var close = tokens.IndexOf(")", open); // in case open is -1, i.e. no "(" // , open == 0 ? 0 : open - 1 if (open >= close) { throw new ArithmeticException(I18n.Translate("internal/MathParser/noclosure", "No closing bracket/parenthesis. Token: {0}", open.ToString(CultureInfo))); } var roughExpr = new List <string>(); for (var i = open + 1; i < close; i++) { roughExpr.Add(tokens[i]); } double tmpResult; var args = new List <double>(); var sargs = new List <string>(); var functionName = tokens[open == 0 ? 0 : open - 1]; if (LocalFunctions.Keys.Contains(functionName)) { if (roughExpr.Contains(",")) { // converting all arguments into a decimal array for (var i = 0; i < roughExpr.Count; i++) { var defaultExpr = new List <string>(); var firstCommaOrEndOfExpression = (roughExpr.IndexOf(",", i) != -1) ? roughExpr.IndexOf(",", i) : roughExpr.Count; while (i < firstCommaOrEndOfExpression) { defaultExpr.Add(roughExpr[i++]); } args.Add(defaultExpr.Count == 0 ? 0 : BasicArithmeticalExpression(defaultExpr)); } // finally, passing the arguments to the given function tmpResult = double.Parse(LocalFunctions[functionName](args.ToArray()).ToString(CultureInfo), CultureInfo); } else { // but if we only have one argument, then we pass it directly to the function tmpResult = double.Parse( LocalFunctions[functionName](new[] { BasicArithmeticalExpression(roughExpr) }) .ToString(CultureInfo), CultureInfo); } } else if (LocalStringFunctions.Keys.Contains(functionName)) { if (roughExpr.Contains(",")) { // converting all arguments into a decimal array for (var i = 0; i < roughExpr.Count; i++) { var defaultExpr = new List <string>(); var firstCommaOrEndOfExpression = (roughExpr.IndexOf(",", i) != -1) ? roughExpr.IndexOf(",", i) : roughExpr.Count; while (i < firstCommaOrEndOfExpression) { defaultExpr.Add(roughExpr[i++]); } sargs.Add(defaultExpr.Count == 0 ? "0" : defaultExpr[0]); } // finally, passing the arguments to the given function tmpResult = double.Parse(LocalStringFunctions[functionName](sargs.ToArray()).ToString(CultureInfo), CultureInfo); } else { // but if we only have one argument, then we pass it directly to the function tmpResult = double.Parse( LocalStringFunctions[functionName](new[] { roughExpr[0] }) .ToString(CultureInfo), CultureInfo); } } else { // if no function is need to execute following expression, pass it // to the "BasicArithmeticalExpression" method. tmpResult = BasicArithmeticalExpression(roughExpr); } // when all the calculations have been done // we replace the "opening bracket with the result" // and removing the rest. tokens[open] = tmpResult.ToString(CultureInfo); tokens.RemoveRange(open + 1, close - open); if (LocalFunctions.Keys.Contains(functionName)) { // if we also executed a function, removing // the function name as well. tokens.RemoveAt(open - 1); } else if (LocalStringFunctions.Keys.Contains(functionName)) { // if we also executed a function, removing // the function name as well. tokens.RemoveAt(open - 1); } } // at this point, we should have replaced all brackets // with the appropriate values, so we can simply // calculate the expression. it's not so complex // any more! return(BasicArithmeticalExpression(tokens)); }