/// <summary>
        /// Parse expression of variables and make SymbolicVariable
        /// </summary>
        /// <param name="expr"></param>
        /// <returns></returns>
        public static SymbolicVariable Parse(string expression)
        {
            var r = from zr in expression.Split(new string[] { ":=" }, StringSplitOptions.RemoveEmptyEntries)
                    where string.IsNullOrWhiteSpace(zr) == false
                    select zr.Trim();

            string[] rr = r.ToArray();

            string expr = string.Empty;

            if (rr.Length == 0)
            {
                return(null);
            }
            if (rr.Length > 1)
            {
                //function
                expr = rr[1];
            }
            else
            {
                expr = rr[0];
            }

            char[] separators = { '^', '*', '/', '+', '-', '(', '|' };
            char[] seps       = { '^', '*', '/', '+', '-', '|' };


            expr = expr.Replace(" ", "");

            //if (expression.StartsWith("-") ||expression.StartsWith("+")) expression = expression.Insert(1,"1*");

            // simple parsing
            // obeys the rules of priorities

            // Priorities
            //    ^  Power
            //    *  multiplication
            //    /  division
            //    +  Addition
            //    -  Subtraction


            bool NextGroupIsNegativeSign = false;

            // Tokenization is done by separating with operators
            SymbolicExpressionOperator Root = new SymbolicExpressionOperator();
            SymbolicExpressionOperator ep   = Root;

            StringBuilder TokenBuilder    = new StringBuilder();
            Stack <int>   PLevels         = new Stack <int>();
            bool          Inner           = false;
            bool          FunctionContext = false;

            for (int ix = 0; ix < expr.Length; ix++)
            {
                if (PLevels.Count == 0)
                {
                    // include the normal parsing when we are not in parenthesis group
                    if (separators.Contains(expr[ix]))
                    {
                        if ((expr[ix] == '-' || expr[ix] == '+') && ix == 0)
                        {
                            if (expr[ix] == '-')
                            {
                                // in case of -x  for example ..this will converted into operations
                                //  of -1 * x
                                ep.SymbolicExpression = SymbolicVariable.NegativeOne;
                                ep.Operation          = "*";

                                ep.Next = new SymbolicExpressionOperator();
                                ep      = ep.Next;      // advance the reference to the next node to make the linked list.

                                TokenBuilder = new StringBuilder();
                            }
                            else
                            {
                                // append the normal token
                                TokenBuilder.Append(expr[ix]);
                            }
                        }
                        else if (ix > 1 &&
                                 char.ToUpper(expr[ix - 1]) == 'E' && char.IsDigit(expr[ix - 2]) &&
                                 (expr[ix] == '-' || expr[ix] == '+'))
                        {
                            // case of 3e+2 4e-2  all cases with e in it.
                            TokenBuilder.Append(expr[ix]);
                        }
                        else if (expr[ix] == '(')
                        {
                            PLevels.Push(1);
                            var bb = ix > 0 ? separators.Contains(expr[ix - 1]) : true;
                            if (!bb)
                            {
                                //the previous charachter is normal word which indicates we reached a function
                                FunctionContext = true;
                                TokenBuilder.Append(expr[ix]);
                            }
                        }
                        else if (
                            seps.Contains(expr[ix - 1]) &&
                            (expr[ix] == '-' || expr[ix] == '+')
                            )
                        {
                            if (expr[ix + 1] == '(')
                            {
                                // so this sign is glued to the next parentheisis group

                                TokenBuilder.Append(expr[ix]);

                                // now TokenBuilder contains all signs before this

                                // and we need to preserve the information about next group
                                //   that it has a sign.

                                string signs = TokenBuilder.ToString();

                                int nve = signs.ToCharArray().Count(sign => sign == '-');

                                if ((nve % 2.0) != 0)
                                {
                                    // negative sign
                                    NextGroupIsNegativeSign = true;
                                }
                                TokenBuilder.Clear();
                            }
                            else
                            {
                                TokenBuilder.Append(expr[ix]);
                            }
                        }
                        else
                        {
                            // tokenize   when we reach any operator  or open '(' parenthesis
                            if (Inner)
                            {
                                ep.SymbolicExpression = Parse(TokenBuilder.ToString());

                                if (NextGroupIsNegativeSign)
                                {
                                    ep.SymbolicExpression = SymbolicVariable.Multiply(SymbolicVariable.NegativeOne, ep.SymbolicExpression);

                                    NextGroupIsNegativeSign = false;
                                }
                                Inner = false;
                            }
                            else
                            {
                                ep.SymbolicExpression = new SymbolicVariable(TokenBuilder.ToString());
                            }

                            TokenBuilder = new StringBuilder();

                            ep.Operation = expr[ix].ToString();
                            ep.Next      = new SymbolicExpressionOperator();
                            ep           = ep.Next; // advance the reference to the next node to make the linked list.
                        }
                    }
                    else
                    {
                        TokenBuilder.Append(expr[ix]);
                    }
                }
                else
                {
                    // we are in group
                    if (expr[ix] == '(')
                    {
                        PLevels.Push(1);
                    }
                    if (expr[ix] == ')')
                    {
                        PLevels.Pop();

                        if (PLevels.Count == 0)
                        {
                            Inner = true;
                            if (FunctionContext)
                            {
                                TokenBuilder.Append(expr[ix]);
                                FunctionContext = false;
                                Inner           = false; // because i am taking the function body as a whole in this parse pass.
                                // then inner parameters of the function will be parsed again
                            }
                        }
                        else
                        {
                            TokenBuilder.Append(expr[ix]);
                        }
                    }
                    else
                    {
                        TokenBuilder.Append(expr[ix]);
                    }
                }
            }

            // Last pass that escaped from the loop.
            if (Inner)
            {
                ep.SymbolicExpression = Parse(TokenBuilder.ToString());
                if (NextGroupIsNegativeSign)
                {
                    ep.SymbolicExpression = SymbolicVariable.Multiply(SymbolicVariable.NegativeOne, ep.SymbolicExpression);

                    NextGroupIsNegativeSign = false;
                }

                Inner = false;
            }
            else
            {
                ep.SymbolicExpression = new SymbolicVariable(TokenBuilder.ToString());
            }
            TokenBuilder = null;


            string[] Group = { "^"    /* Power for normal product '*' */
            };

            string[] SymGroup = { "|" /* Derivation operator */ };

            string[] Group1 = { "*" /* normal multiplication */,
                                "/" /* normal division */,
                                "%" /* modulus */ };


            string[] Group2 = { "+", "-" };

            /// Operator Groups Ordered by Priorities.
            string[][] OperatorGroups = { Group, SymGroup, Group1, Group2 };

            foreach (var opg in OperatorGroups)
            {
                SymbolicExpressionOperator eop = Root;

                //Pass for '[op]' and merge it  but from top to child :)  {forward)
                while (eop.Next != null)
                {
                    //if the operator in node found in the opg (current operator group) then execute the logic

                    if (opg.Count(c => c.Equals(eop.Operation, StringComparison.OrdinalIgnoreCase)) > 0)
                    {
                        short skip;
                        eop.SymbolicExpression = ArithExpression(eop, out skip);

                        //drop eop.Next
                        if (eop.Next.Next != null)
                        {
                            while (skip > 0)
                            {
                                eop.Operation = eop.Next.Operation;

                                eop.Next = eop.Next.Next;

                                skip--;
                            }
                        }
                        else
                        {
                            //no more nodes exit the loop

                            eop.Next      = null; //last item were processed.
                            eop.Operation = string.Empty;
                        }
                    }
                    else
                    {
                        eop = eop.Next;
                    }
                }
            }

            if (rr.Length > 1)
            {
                // there was a function defintion
                var fname = rr[0];

                Functions[fname] = Root.SymbolicExpression;
            }

            return(Root.SymbolicExpression);
        }
Exemplo n.º 2
0
        /// <summary>
        /// Extracts words that begin with letter from expression.
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        public static string[] WordsFromExpression(string expression)
        {
            char[] separators = { '^', '*', '/', '+', '-', '(', '|', '<', '>', '=' };
            char[] seps       = { '^', '*', '/', '+', '-', '|' };
            expression = expression.Replace(" ", "");

            List <string> results = new List <string>();

            StringBuilder TokenBuilder    = new StringBuilder();
            Stack <int>   PLevels         = new Stack <int>();
            bool          Inner           = false;
            bool          FunctionContext = false;

            for (int ix = 0; ix < expression.Length; ix++)
            {
                if (PLevels.Count == 0)
                {
                    // include the normal parsing when we are not in parenthesis group
                    if (separators.Contains(expression[ix]))
                    {
                        if ((expression[ix] == '-' || expression[ix] == '+') && ix == 0)
                        {
                            //TokenBuilder.Append(expression[ix]);
                            // ignore the leading sign .. in this function we want the words only
                        }
                        else if (expression[ix] == '(')
                        {
                            PLevels.Push(1);
                            var bb = ix > 0 ? separators.Contains(expression[ix - 1]) : true;
                            if (!bb)
                            {
                                //the previous charachter is normal word which indicates we reached a function
                                FunctionContext = true;
                                TokenBuilder.Append(expression[ix]);
                            }
                        }
                        else if (seps.Contains(expression[ix - 1]) && (expression[ix] == '-' || expression[ix] == '+'))
                        {
                            TokenBuilder.Append(expression[ix]);
                        }
                        else
                        {
                            // tokenize   when we reach any operator  or open '(' parenthesis
                            if (Inner)
                            {
                                results.AddRange(WordsFromExpression(TokenBuilder.ToString()));
                                Inner = false;
                            }
                            else
                            {
                                results.Add(TokenBuilder.ToString());
                            }

                            TokenBuilder = new StringBuilder();
                        }
                    }
                    else
                    {
                        TokenBuilder.Append(expression[ix]);
                    }
                }
                else
                {
                    // we are in group
                    if (expression[ix] == '(')
                    {
                        PLevels.Push(1);
                    }
                    if (expression[ix] == ')')
                    {
                        PLevels.Pop();

                        if (PLevels.Count == 0)
                        {
                            Inner = true;
                            if (FunctionContext)
                            {
                                TokenBuilder.Append(expression[ix]);
                                FunctionContext = false;
                                Inner           = false; // because i am taking the function body as a whole in this parse pass.
                                // then inner parameters of the function will be parsed again
                            }
                        }
                        else
                        {
                            TokenBuilder.Append(expression[ix]);
                        }
                    }
                    else
                    {
                        TokenBuilder.Append(expression[ix]);
                    }
                }
            }

            // Last pass that escaped from the loop.
            if (Inner)
            {
                results.AddRange(WordsFromExpression(TokenBuilder.ToString()));
                Inner = false;
            }
            else
            {
                results.Add(TokenBuilder.ToString());
            }
            TokenBuilder = null;



            var ff = from z in results
                     where string.IsNullOrEmpty(z) == false && char.IsDigit(z, 0) == false
                     select z;

            return(ff.ToArray());
        }
        /// <summary>
        /// Returns an expression that can be compiled into a function to be called dynamically.
        /// </summary>
        /// <returns></returns>
        private Expression ParseDynamicExpression(ref Dictionary <string, ParameterExpression> discoveredParameters, string expression = null)
        {
            if (string.IsNullOrEmpty(expression))
            {
                // this is the final text to be parsed.
                expression = this.ToString();
            }

            bool NegativeExpression = false;

            if (expression.StartsWith("-"))
            {
                NegativeExpression = true;
                expression         = expression.TrimStart('-'); // remove any trailing minuses
            }


            char[] separators = { '^', '*', '/', '+', '-', '(', '<', '>', '=' };

            char[] operators = { '^', '*', '/', '+', '-' };


            expression = expression.Replace(" ", "");

            // simple parsing
            // obeys the rules of priorities

            // Priorities
            //    ^  Power
            //    *  multiplication
            //    /  division
            //    +  Addition
            //    -  Subtraction



            // Tokenization is done by separating with operators
            DynamicExpressionOperator Root = new DynamicExpressionOperator();
            DynamicExpressionOperator ep   = Root;

            StringBuilder TokenBuilder    = new StringBuilder();
            Stack <int>   PLevels         = new Stack <int>();
            bool          Inner           = false;
            bool          FunctionContext = false;

            #region method that will be reused
            Action <Dictionary <string, ParameterExpression> > redundantFunction = (parameters) =>
            {
                // Last pass that escaped from the loop.
                if (Inner)
                {
                    ep.DynamicExpression = ParseDynamicExpression(ref parameters, TokenBuilder.ToString());

                    Inner = false;
                }
                else
                {
                    double constant;
                    if (TokenBuilder.ToString().Equals("Infinity", StringComparison.OrdinalIgnoreCase) || TokenBuilder.ToString().Equals("inf", StringComparison.OrdinalIgnoreCase))
                    {
                        ep.DynamicExpression = Expression.Constant(double.PositiveInfinity, typeof(double));
                    }
                    else if (TokenBuilder.ToString().Equals("NaN", StringComparison.OrdinalIgnoreCase))
                    {
                        ep.DynamicExpression = Expression.Constant(double.NaN, typeof(double));
                    }
                    else if (TokenBuilder[0] == '%')
                    {
                        // constant
                        ep.DynamicExpression = Expression.Constant(MathConstants.Constant(TokenBuilder.ToString().TrimStart('%')), typeof(double));
                    }
                    else if (double.TryParse(TokenBuilder.ToString(), NumberStyles.Any, CultureInfo.InvariantCulture, out constant))
                    {
                        ep.DynamicExpression = Expression.Constant(constant, typeof(double));
                    }
                    else
                    {
                        var pname = TokenBuilder.ToString();

                        var FMatch = FunctionRegex.Match(pname);
                        // test if the parameter is a function
                        if (FMatch.Success)
                        {
                            // take the function name and search for it
                            // if you found it take the inner parameters and parse it independently
                            var fname = FMatch.Groups["function"].Value;

                            // search for the function in the math class
                            MethodInfo targetfunction;

                            if (CoMath.AvailableFunctions.Contains(fname, StringComparer.OrdinalIgnoreCase))
                            {
                                targetfunction = typeof(CoMath).GetMethod(
                                    fname
                                    , System.Reflection.BindingFlags.IgnoreCase | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static
                                    );
                            }
                            else if (fname.Equals("log", StringComparison.OrdinalIgnoreCase))
                            {
                                targetfunction = typeof(Math).GetMethod("Log", new Type[] { typeof(double) });
                            }
                            else if (fname.Equals("iif", StringComparison.OrdinalIgnoreCase))
                            {
                                targetfunction = typeof(CoLogic).GetMethod("IIF", new Type[] { typeof(bool), typeof(double), typeof(double) });
                            }
                            else
                            {
                                targetfunction = typeof(Math).GetMethod(
                                    fname
                                    , System.Reflection.BindingFlags.IgnoreCase | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static
                                    );
                            }

                            if (targetfunction != null)
                            {
                                // for now take the
                                var          fps      = FMatch.Groups["parameters"].Value;
                                string[]     pss      = TextTools.ComaSplit(fps);
                                Expression[] tfparams = new Expression[pss.Length];
                                for (int ixf = 0; ixf < pss.Length; ixf++)
                                {
                                    tfparams[ixf] = ParseDynamicExpression(ref parameters, pss[ixf]);
                                }
                                ep.DynamicExpression = Expression.Call(targetfunction, tfparams);
                            }
                            else
                            {
                                var fn = Functions.Keys.FirstOrDefault(cc => cc.StartsWith(fname));
                                if (!string.IsNullOrEmpty(fn))
                                {
                                    // this is good  because we have a body.
                                    ep.DynamicExpression = Functions[fn].ParseDynamicExpression(ref parameters);
                                }
                                else
                                {
                                    throw new SymbolicException(string.Format("The target function {0} couldn't be found", fname));
                                }
                            }
                        }
                        else
                        {
                            // this is an ordinary parameter based on our test.
                            ParameterExpression pe;
                            if (parameters.TryGetValue(pname, out pe))
                            {
                                ep.DynamicExpression = pe;
                            }
                            else
                            {
                                pe = Expression.Parameter(typeof(double), pname);

                                ep.DynamicExpression = pe;

                                parameters.Add(pname, pe);
                            }
                        }
                    }
                }
            };


            #endregion

            for (int ix = 0; ix < expression.Length; ix++)
            {
                if (PLevels.Count == 0)
                {
                    // include the normal parsing when we are not in parenthesis group
                    if (separators.Contains(expression[ix]))
                    {
                        if ((expression[ix] == '-' || expression[ix] == '+') && ix == 0)
                        {
                            // add first character if - or +
                            TokenBuilder.Append(expression[ix]);
                        }
                        else if (ix > 1 &&
                                 char.ToUpper(expression[ix - 1]) == 'E' && char.IsDigit(expression[ix - 2]) &&
                                 (expression[ix] == '-' || expression[ix] == '+'))
                        {
                            // test previous charachter if E    and charachter before it to be digit and current character is - or +
                            //    then we are in sceintific representation of number

                            // case of 3e+2 4e-2  all cases with e in it.

                            TokenBuilder.Append(expression[ix]);
                        }
                        else if (expression[ix] == '(')
                        {
                            // either we reached a function of expression group.
                            PLevels.Push(1);

                            // test for something other than separator in previous charachter
                            var OperatorBehind = ix > 0 ? separators.Contains(expression[ix - 1]) : true;

                            if (!OperatorBehind)
                            {
                                //the previous charachter is normal word which indicates we reached a function
                                FunctionContext = true;
                                TokenBuilder.Append(expression[ix]);
                            }
                        }
                        else if (operators.Contains(expression[ix - 1]) && (expression[ix] == '-' || expression[ix] == '+'))
                        {
                            // the case in which last charachter was another operator and followed by current (-) or (+)
                            TokenBuilder.Append(expression[ix]);
                        }
                        else
                        {
                            // tokenize   when we reach any operator  or open '(' parenthesis

                            redundantFunction(discoveredParameters);

                            TokenBuilder = new StringBuilder();

                            ep.Operation = expression[ix].ToString();

                            if (expression[ix] == '<' || expression[ix] == '>')
                            {
                                // check for equal sign after this
                                if (expression[ix + 1] == '=')
                                {
                                    ep.Operation += "=";
                                    ix++;
                                }
                            }

                            ep.Next = new DynamicExpressionOperator();
                            ep      = ep.Next;      // advance the reference to the next node to make the linked list.
                        }
                    }
                    else
                    {
                        TokenBuilder.Append(expression[ix]);
                    }
                }
                else
                {
                    // we are in group
                    if (expression[ix] == '(')
                    {
                        PLevels.Push(1);
                    }
                    if (expression[ix] == ')')
                    {
                        PLevels.Pop();

                        if (PLevels.Count == 0)
                        {
                            Inner = true;
                            if (FunctionContext)
                            {
                                TokenBuilder.Append(expression[ix]);
                                FunctionContext = false;
                                Inner           = false; // because i am taking the function body as a whole in this parse pass.
                                // then inner parameters of the function will be parsed again
                            }
                        }
                        else
                        {
                            TokenBuilder.Append(expression[ix]);
                        }
                    }
                    else
                    {
                        TokenBuilder.Append(expression[ix]);
                    }
                }
            }

            redundantFunction(discoveredParameters);

            TokenBuilder = null;


            string[] Group = { "^"    /* Power for normal product '*' */
            };


            string[] Group1 = { "*" /* normal multiplication */,
                                "/" /* normal division */,
                                "%" /* modulus */ };


            string[] Group2 = { "+", "-" };

            string[] Group3 = { "<", "<=", ">", ">=" };

            string[] Group4 = { "=", "<>" };

            /// Operator Groups Ordered by Priorities.
            string[][] OperatorGroups = { Group, Group1, Group2, Group3, Group4 };

            foreach (var opg in OperatorGroups)
            {
                DynamicExpressionOperator eop = Root;

                //Pass for '[op]' and merge it  but from top to child :)  {forward)
                while (eop.Next != null)
                {
                    //if the operator in node found in the opg (current operator group) then execute the logic

                    if (opg.Count(c => c.Equals(eop.Operation, StringComparison.OrdinalIgnoreCase)) > 0)
                    {
                        short skip;
                        eop.DynamicExpression = ArithExpression(eop, out skip);

                        //drop eop.Next
                        if (eop.Next.Next != null)
                        {
                            while (skip > 0)
                            {
                                eop.Operation = eop.Next.Operation;

                                eop.Next = eop.Next.Next;

                                skip--;
                            }
                        }
                        else
                        {
                            //no more nodes exit the loop

                            eop.Next      = null; //last item were processed.
                            eop.Operation = string.Empty;
                        }
                    }
                    else
                    {
                        eop = eop.Next;
                    }
                }
            }

            Expression FinalExpression;

            if (NegativeExpression)
            {
                FinalExpression = Expression.Multiply(Expression.Constant(-1.0, typeof(double)), Root.DynamicExpression);
            }
            else
            {
                FinalExpression = Root.DynamicExpression;
            }

            return(FinalExpression);
        }