Esempio n. 1
0
        /// <summary>
        /// Inverse expression of one parameter
        /// </summary>
        /// <param name="expression">Expression Y=F(X)</param>
        /// <param name="parameter">Type and name of Y parameter</param>
        /// <returns>Inverted expression X = F_back(Y)</returns>
        public Lambda InverseExpression(Expression expression, ParameterExpression parameter)
        {
            var    recInfo = new RecursiveInfo();
            String dummy   = null;

            InverseExpressionInternal(expression, recInfo, ref dummy);

            if (recInfo.FoundedParamName == null)
            {
                throw new InverseException(String.Format("Parameter was not found in expression '{0}'!", expression));
            }

            // difficult with constant subtrees: we write to string all constant subtrees,
            // but some of them can take Convert operator, which converted to string as Convert(arg).
            // when we try to parse this string, an error occured, because "Convert(arg)" is not
            // a valid expression
            // Solution: remove all Convert(arg) substrings from result string using regex
            // Big problem: we can't remove Convert because it play important role: 1/2 = 0, ((double)1)/2 = 0.5 !!
            // Solution № 2: as Convert element looks bad in ToString() we need to generate substring by constant subtree manually,
            // this is not very hard task. Good.
            // Other solution: switch to Expression based inverse, where we no need to generate string by Expression,
            // only expressions. But I don't wish to do this, because

            var paramName = parameter.Name;

            var invertedExp = String.Format(recInfo.InvertedExp, paramName);

            var res = interpreter.Parse(invertedExp, new Parameter(parameter.Name, parameter.Type));

            Debug.WriteLine(res.ExpressionText);
            return(res);
        }
Esempio n. 2
0
        /// <summary>
        /// Generate inversed expression tree from original expression tree of one parameter
        /// using recursion
        /// </summary>
        /// <param name="expr">Original expression</param>
        /// <param name="recInfo">Out expression</param>
        /// <returns>NodeType - const or variable</returns>
        private NodeType InverseExpressionInternal(Expression expr, RecursiveInfo recInfo, ref string constantExpression)
        {
            switch (expr.NodeType)
            {
            case ExpressionType.Add:
            case ExpressionType.Subtract:
            case ExpressionType.Multiply:
            case ExpressionType.Divide:
            {
                var binExp = expr as BinaryExpression;

                string leftConstant = null, rightConstant = null;
                var    leftOperandType  = InverseExpressionInternal(binExp.Left, recInfo, ref leftConstant);
                var    rightOperandType = InverseExpressionInternal(binExp.Right, recInfo, ref rightConstant);

                var nodeType = (leftOperandType == NodeType.Variable || rightOperandType == NodeType.Variable)
                                        ? NodeType.Variable
                                        : NodeType.Constant;

                if (nodeType == NodeType.Variable)
                {
                    var constantPlace = leftOperandType == NodeType.Constant ? ConstantPlace.Left : ConstantPlace.Right;
                    var constant      = leftOperandType == NodeType.Constant ? leftConstant :rightConstant;
                    recInfo.InvertedExp = String.Format(recInfo.InvertedExp, inversedFuncs[expr.NodeType, constantPlace](constant));
                }
                else
                {
                    constantExpression = String.Format("({0}{1}{2})", leftConstant, NodeTypeToString(binExp.NodeType), rightConstant);
                }

                return(nodeType);
            }

            case ExpressionType.Parameter:
            {
                var parameter = expr as ParameterExpression;

                if (recInfo.FoundedParamName == null)
                {
                    recInfo.FoundedParamName = parameter.Name;
                    recInfo.InvertedExp      = RES;
                    return(NodeType.Variable);
                }

                if (recInfo.FoundedParamName == parameter.Name)
                {
                    throw new InverseException(String.Format("Variable {0} is defined more than one time!", recInfo.FoundedParamName));
                }
                else
                {
                    throw new InverseException(String.Format("More than one variables are defined in expression: {0} and {1}", recInfo.FoundedParamName, parameter.Name));
                }
            }

            case ExpressionType.Constant:
            {
                var constant = expr as ConstantExpression;
                constantExpression = String.Format(CultureInfo.InvariantCulture, "({0})", constant.Value);
                return(NodeType.Constant);
            }

            case ExpressionType.Convert:
            {
                var    convertExpr = expr as UnaryExpression;
                string constant    = null;
                var    operandType = InverseExpressionInternal(convertExpr.Operand, recInfo, ref constant);

                if (operandType == NodeType.Constant)
                {
                    constantExpression = "((" + convertExpr.Type.Name + ")" + constant + ")";
                }
                else
                {
                    recInfo.InvertedExp = String.Format(recInfo.InvertedExp, "((" + convertExpr.Operand.Type.Name + ")" + RES + ")");
                }
                return(operandType);
            }

            case ExpressionType.Negate:
            {
                var    negateExpr  = expr as UnaryExpression;
                string constant    = null;
                var    operandType = InverseExpressionInternal(negateExpr.Operand, recInfo, ref constant);

                if (operandType == NodeType.Constant)
                {
                    constantExpression = "(-" + constant + ")";
                }
                else
                {
                    recInfo.InvertedExp = String.Format(recInfo.InvertedExp, "(-" + RES + ")");
                }
                return(operandType);
            }

            case ExpressionType.Not:
            {
                var convertExpr = expr as UnaryExpression;

                string constant    = null;
                var    operandType = InverseExpressionInternal(convertExpr.Operand, recInfo, ref constant);

                if (operandType == NodeType.Constant)
                {
                    constantExpression = "(" + NodeTypeToString(ExpressionType.Not) + constant + ")";
                }
                else
                {
                    recInfo.InvertedExp = String.Format(recInfo.InvertedExp, "(" + NodeTypeToString(ExpressionType.Not) + RES + ")");
                }
                return(operandType);
            }

            case ExpressionType.Call:
            {
                var methodExpr = expr as MethodCallExpression;

                var methodName = methodExpr.Method.DeclaringType.Name + "." + methodExpr.Method.Name;
                if (!inversedMathFuncs.ContainsKey(methodName))
                {
                    throw new InverseException(String.Format("Unsupported method call expression: {0}", expr));
                }

                string     leftConstant = null, rightConstant = null;
                var        leftOperandType = InverseExpressionInternal(methodExpr.Arguments[0], recInfo, ref leftConstant);
                NodeType?  rightOperandType = null;
                Expression leftOperand, rightOperand = null;

                leftOperand = methodExpr.Arguments[0];

                if (methodExpr.Arguments.Count == 2)
                {
                    rightOperandType = InverseExpressionInternal(methodExpr.Arguments[1], recInfo, ref rightConstant);
                    rightOperand     = methodExpr.Arguments[1];
                }

                string inversedRes = null;
                if (leftOperandType == NodeType.Variable)
                {
                    inversedRes = inversedMathFuncs[methodName, ConstantPlace.Right](rightConstant);
                }
                else
                if (rightOperandType.HasValue && rightOperandType.Value == NodeType.Variable)
                {
                    inversedRes = inversedMathFuncs[methodName, ConstantPlace.Left](leftConstant);
                }
                else
                {
                    //constant
                    constantExpression = methodName + "(" + leftConstant;
                    if (rightOperandType != null)
                    {
                        constantExpression += ", " + rightConstant;
                    }
                    constantExpression += ")";
                }

                if (inversedRes != null)
                {
                    recInfo.InvertedExp = String.Format(recInfo.InvertedExp, inversedRes);
                }

                return(inversedRes == null ? NodeType.Constant : NodeType.Variable);
            }

            case ExpressionType.MemberAccess:
            {
                var memberExpr = expr as MemberExpression;

                if (memberExpr.Member.DeclaringType.Name == "Math")
                {
                    constantExpression = String.Format(CultureInfo.InvariantCulture, "({0})", memberExpr.Member.DeclaringType.Name + "." + memberExpr.Member.Name);
                    return(NodeType.Constant);
                }
                else
                {
                    throw new InverseException(String.Format("Unsupported method call expression: {0}", expr));
                }
            }

            default:
                throw new InverseException(String.Format("Unsupported expression: {0}", expr));
            }
        }