public override void Visit(TernaryExpression expression)
        {
            expression.LeftExpression.Accept(this);
            var test = _result;

            expression.MiddleExpression.Accept(this);
            var ifTrue = _result;

            expression.RightExpression.Accept(this);
            var ifFalse = _result;

            _result = L.Expression.Condition(test, ifTrue, ifFalse);
        }
        private L.Expression UnwrapNullable(L.Expression expression)
        {
            var ti = expression.Type.GetTypeInfo();

            if (ti.IsGenericType && ti.GetGenericTypeDefinition() == typeof(Nullable <>))
            {
                return(L.Expression.Condition(
                           L.Expression.Property(expression, "HasValue"),
                           L.Expression.Property(expression, "Value"),
                           L.Expression.Default(expression.Type.GetTypeInfo().GenericTypeArguments[0])));
            }

            return(expression);
        }
        public override void Visit(Identifier function)
        {
            if (_context == null)
            {
                _result = L.Expression.Constant(_parameters[function.Name]);
            }
            else
            {
                var args = new ParameterArgs();
                OnEvaluateParameter(function.Name, args);
                if (args.HasResult)
                {
                    _result = L.Expression.Constant(args.Result);
                    return;
                }

                _result = L.Expression.PropertyOrField(_context, function.Name);
            }
        }
        AlignFloatingPointTypes(
            L.Expression originalTrue,
            L.Expression originalFalse)
        {
            bool TryConvert(L.Expression from, Type to, out L.Expression converted)
            {
                try
                {
                    converted = L.Expression.Convert(from, to);
                    return(true);
                }
                catch (InvalidOperationException)
                {
                    converted = null;
                    return(false);
                }
            }

            var(originalTrueType, originalFalseType) =
                (originalTrue.Type, originalFalse.Type);
            if (originalTrueType == originalFalseType)
            {
                return(originalTrue, originalFalse);
            }

            if (TryConvert(
                    originalTrue,
                    originalFalseType,
                    out var convertedTrue))
            {
                return(convertedTrue, originalFalse);
            }

            if (TryConvert(
                    originalFalse,
                    originalTrueType,
                    out var convertedFalse))
            {
                return(originalTrue, convertedFalse);
            }

            return(originalTrue, originalFalse);
        }
        public override void Visit(UnaryExpression expression)
        {
            expression.Expression.Accept(this);
            switch (expression.Type)
            {
            case UnaryExpressionType.Not:
                _result = L.Expression.Not(_result);
                break;

            case UnaryExpressionType.Negate:
                _result = L.Expression.Negate(_result);
                break;

            case UnaryExpressionType.BitwiseNot:
                _result = L.Expression.Not(_result);
                break;

            default:
                throw new ArgumentOutOfRangeException();
            }
        }
        private L.Expression WithCommonNumericType(L.Expression left, L.Expression right,
                                                   Func <L.Expression, L.Expression, L.Expression> action, BinaryExpressionType expressiontype = BinaryExpressionType.Unknown)
        {
            left  = UnwrapNullable(left);
            right = UnwrapNullable(right);

            if (_options.HasFlag(EvaluateOptions.BooleanCalculation))
            {
                if (left.Type == typeof(bool))
                {
                    left = L.Expression.Condition(left, L.Expression.Constant(1.0), L.Expression.Constant(0.0));
                }

                if (right.Type == typeof(bool))
                {
                    right = L.Expression.Condition(right, L.Expression.Constant(1.0), L.Expression.Constant(0.0));
                }
            }

            var precedence = new[]
            {
                typeof(decimal),
                typeof(double),
                typeof(float),
                typeof(ulong),
                typeof(long),
                typeof(uint),
                typeof(int),
                typeof(ushort),
                typeof(short),
                typeof(byte),
                typeof(sbyte)
            };

            int l = Array.IndexOf(precedence, left.Type);
            int r = Array.IndexOf(precedence, right.Type);

            if (l >= 0 && r >= 0)
            {
                var type = precedence[Math.Min(l, r)];
                if (left.Type != type)
                {
                    left = L.Expression.Convert(left, type);
                }

                if (right.Type != type)
                {
                    right = L.Expression.Convert(right, type);
                }
            }
            L.Expression comparer = null;
            if (IgnoreCaseString)
            {
                if (Ordinal)
                {
                    comparer = L.Expression.Property(null, typeof(StringComparer).GetProperty("OrdinalIgnoreCase"));
                }
                else
                {
                    comparer = L.Expression.Property(null, typeof(StringComparer).GetProperty("CurrentCultureIgnoreCase"));
                }
            }
            else
            {
                comparer = L.Expression.Property(null, typeof(StringComparer).GetProperty("Ordinal"));
            }

            if (comparer != null && (typeof(string).Equals(left.Type) || typeof(string).Equals(right.Type)))
            {
                switch (expressiontype)
                {
                case BinaryExpressionType.Equal: return(L.Expression.Call(comparer, typeof(StringComparer).GetRuntimeMethod("Equals", new[] { typeof(string), typeof(string) }), new L.Expression[] { left, right }));

                case BinaryExpressionType.NotEqual: return(L.Expression.Not(L.Expression.Call(comparer, typeof(StringComparer).GetRuntimeMethod("Equals", new[] { typeof(string), typeof(string) }), new L.Expression[] { left, right })));

                case BinaryExpressionType.GreaterOrEqual: return(L.Expression.GreaterThanOrEqual(L.Expression.Call(comparer, typeof(StringComparer).GetRuntimeMethod("Compare", new[] { typeof(string), typeof(string) }), new L.Expression[] { left, right }), L.Expression.Constant(0)));

                case BinaryExpressionType.LesserOrEqual: return(L.Expression.LessThanOrEqual(L.Expression.Call(comparer, typeof(StringComparer).GetRuntimeMethod("Compare", new[] { typeof(string), typeof(string) }), new L.Expression[] { left, right }), L.Expression.Constant(0)));

                case BinaryExpressionType.Greater: return(L.Expression.GreaterThan(L.Expression.Call(comparer, typeof(StringComparer).GetRuntimeMethod("Compare", new[] { typeof(string), typeof(string) }), new L.Expression[] { left, right }), L.Expression.Constant(0)));

                case BinaryExpressionType.Lesser: return(L.Expression.LessThan(L.Expression.Call(comparer, typeof(StringComparer).GetRuntimeMethod("Compare", new[] { typeof(string), typeof(string) }), new L.Expression[] { left, right }), L.Expression.Constant(0)));
                }
            }
            return(action(left, right));
        }
        public override void Visit(BinaryExpression expression)
        {
            expression.LeftExpression.Accept(this);
            var left = _result;

            expression.RightExpression.Accept(this);
            var right = _result;

            switch (expression.Type)
            {
            case BinaryExpressionType.And:
                _result = L.Expression.AndAlso(left, right);
                break;

            case BinaryExpressionType.Or:
                _result = L.Expression.OrElse(left, right);
                break;

            case BinaryExpressionType.NotEqual:
                _result = WithCommonNumericType(left, right, L.Expression.NotEqual, expression.Type);
                break;

            case BinaryExpressionType.LesserOrEqual:
                _result = WithCommonNumericType(left, right, L.Expression.LessThanOrEqual, expression.Type);
                break;

            case BinaryExpressionType.GreaterOrEqual:
                _result = WithCommonNumericType(left, right, L.Expression.GreaterThanOrEqual, expression.Type);
                break;

            case BinaryExpressionType.Lesser:
                _result = WithCommonNumericType(left, right, L.Expression.LessThan, expression.Type);
                break;

            case BinaryExpressionType.Greater:
                _result = WithCommonNumericType(left, right, L.Expression.GreaterThan, expression.Type);
                break;

            case BinaryExpressionType.Equal:
                _result = WithCommonNumericType(left, right, L.Expression.Equal, expression.Type);
                break;

            case BinaryExpressionType.Minus:
                if (Checked)
                {
                    _result = WithCommonNumericType(left, right, L.Expression.SubtractChecked);
                }
                else
                {
                    _result = WithCommonNumericType(left, right, L.Expression.Subtract);
                }
                break;

            case BinaryExpressionType.Plus:
                if (Checked)
                {
                    _result = WithCommonNumericType(left, right, L.Expression.AddChecked);
                }
                else
                {
                    _result = WithCommonNumericType(left, right, L.Expression.Add);
                }
                break;

            case BinaryExpressionType.Modulo:
                _result = WithCommonNumericType(left, right, L.Expression.Modulo);
                break;

            case BinaryExpressionType.Div:
                _result = WithCommonNumericType(left, right, L.Expression.Divide);
                break;

            case BinaryExpressionType.Times:
                if (Checked)
                {
                    _result = WithCommonNumericType(left, right, L.Expression.MultiplyChecked);
                }
                else
                {
                    _result = WithCommonNumericType(left, right, L.Expression.Multiply);
                }
                break;

            case BinaryExpressionType.BitwiseOr:
                _result = L.Expression.Or(left, right);
                break;

            case BinaryExpressionType.BitwiseAnd:
                _result = L.Expression.And(left, right);
                break;

            case BinaryExpressionType.BitwiseXOr:
                _result = L.Expression.ExclusiveOr(left, right);
                break;

            case BinaryExpressionType.LeftShift:
                _result = L.Expression.LeftShift(left, right);
                break;

            case BinaryExpressionType.RightShift:
                _result = L.Expression.RightShift(left, right);
                break;

            default:
                throw new ArgumentOutOfRangeException();
            }
        }
        private L.Expression[] PrepareMethodArgumentsIfValid(ParameterInfo[] parameters, L.Expression[] arguments)
        {
            if (!parameters.Any() && !arguments.Any())
            {
                return(arguments);
            }
            if (!parameters.Any())
            {
                return(null);
            }
            bool paramsMatchArguments = true;

            var  lastParameter    = parameters.Last();
            bool hasParamsKeyword = lastParameter.IsDefined(typeof(ParamArrayAttribute));

            if (hasParamsKeyword && parameters.Length > arguments.Length)
            {
                return(null);
            }
            L.Expression[] newArguments            = new L.Expression[parameters.Length];
            L.Expression[] paramsKeywordArgument   = null;
            Type           paramsElementType       = null;
            int            paramsParameterPosition = 0;

            if (!hasParamsKeyword)
            {
                paramsMatchArguments &= parameters.Length == arguments.Length;
                if (!paramsMatchArguments)
                {
                    return(null);
                }
            }
            else
            {
                paramsParameterPosition = lastParameter.Position;
                paramsElementType       = lastParameter.ParameterType.GetElementType();
                paramsKeywordArgument   = new L.Expression[arguments.Length - parameters.Length + 1];
            }

            for (int i = 0; i < arguments.Length; i++)
            {
                var isParamsElement = hasParamsKeyword && i >= paramsParameterPosition;
                var argumentType    = arguments[i].Type;
                var parameterType   = isParamsElement ? paramsElementType : parameters[i].ParameterType;
                paramsMatchArguments &= argumentType == parameterType;
                if (!paramsMatchArguments)
                {
                    return(null);
                }
                if (!isParamsElement)
                {
                    newArguments[i] = arguments[i];
                }
                else
                {
                    paramsKeywordArgument[i - paramsParameterPosition] = arguments[i];
                }
            }

            if (hasParamsKeyword)
            {
                newArguments[paramsParameterPosition] = L.Expression.NewArrayInit(paramsElementType, paramsKeywordArgument);
            }
            return(newArguments);
        }
 public LambdaExpressionVistor(L.ParameterExpression context, EvaluateOptions options)
 {
     _context = context;
     _options = options;
 }
        public override void Visit(Function function)
        {
            var functionArgs = new FunctionArgs
            {
                Parameters = new Expression[function.Expressions.Length]
            };

            // Don't call parameters right now, instead let the function do it as needed.
            // Some parameters shouldn't be called, for instance, in a if(), the "not" value might be a division by zero
            // Evaluating every value could produce unexpected behaviour
            for (int i = 0; i < function.Expressions.Length; i++)
            {
                functionArgs.Parameters[i] = new Expression(function.Expressions[i], _options);
                functionArgs.Parameters[i].EvaluateFunction  += EvaluateFunction;
                functionArgs.Parameters[i].EvaluateParameter += EvaluateParameter;

                // Assign the parameters of the Expression to the arguments so that custom Functions and Parameters can use them
                functionArgs.Parameters[i].Parameters = Parameters;
            }

            OnEvaluateFunction(IgnoreCaseString ? function.Identifier.Name.ToLower() : function.Identifier.Name, functionArgs);

            var args = new L.Expression[function.Expressions.Length];

            for (int i = 0; i < function.Expressions.Length; i++)
            {
                function.Expressions[i].Accept(this);
                args[i] = _result;
            }

            switch (function.Identifier.Name.ToLowerInvariant())
            {
            case "abs":
                if (function.Expressions.Length != 1)
                {
                    throw new ArgumentException("Abs() takes exactly 1 argument");
                }

                var useDouble = _options.HasFlag(
                    EvaluateOptions.UseDoubleForAbsFunction);

                MethodInfo   absMethod;
                L.Expression absArg0;
                if (useDouble)
                {
                    absMethod = typeof(Math).GetRuntimeMethod(
                        nameof(Math.Abs),
                        new[] { typeof(double) });
                    absArg0 = L.Expression.Convert(args[0], typeof(double));
                }
                else
                {
                    absMethod = typeof(Math).GetRuntimeMethod(
                        nameof(Math.Abs),
                        new[] { typeof(decimal) });
                    absArg0 = L.Expression.Convert(args[0], typeof(decimal));
                }

                _result = L.Expression.Call(absMethod, absArg0);
                break;

            case "acos":
                if (function.Expressions.Length != 1)
                {
                    throw new ArgumentException("Acos() takes exactly 1 argument");
                }

                var acosMethod = typeof(Math).GetRuntimeMethod(
                    nameof(Math.Acos),
                    new[] { typeof(double) });
                var acosArg0 = L.Expression.Convert(args[0], typeof(double));
                _result = L.Expression.Call(acosMethod, acosArg0);
                break;

            case "asin":
                if (function.Expressions.Length != 1)
                {
                    throw new ArgumentException("Asin() takes exactly 1 argument");
                }

                var asinMethod = typeof(Math).GetRuntimeMethod(
                    nameof(Math.Asin),
                    new[] { typeof(double) });
                var asinArg0 = L.Expression.Convert(args[0], typeof(double));
                _result = L.Expression.Call(asinMethod, asinArg0);
                break;

            case "atan":
                if (function.Expressions.Length != 1)
                {
                    throw new ArgumentException("Atan() takes exactly 1 argument");
                }

                var atanMethod = typeof(Math).GetRuntimeMethod(
                    nameof(Math.Atan),
                    new[] { typeof(double) });
                var atanArgs0 = L.Expression.Convert(args[0], typeof(double));
                _result = L.Expression.Call(atanMethod, atanArgs0);
                break;

            case "ceiling":
                if (function.Expressions.Length != 1)
                {
                    throw new ArgumentException("Ceiling() takes exactly 1 argument");
                }

                var ceilingMethod = typeof(Math).GetRuntimeMethod(
                    nameof(Math.Ceiling),
                    new[] { typeof(double) });
                var ceilingArgs0 = L.Expression.Convert(args[0], typeof(double));
                _result = L.Expression.Call(ceilingMethod, ceilingArgs0);
                break;

            case "cos":
                if (function.Expressions.Length != 1)
                {
                    throw new ArgumentException("Cos() takes exactly 1 argument");
                }

                var cosMethod = typeof(Math).GetRuntimeMethod(
                    nameof(Math.Cos),
                    new[] { typeof(double) });
                var cosArgs0 = L.Expression.Convert(args[0], typeof(double));
                _result = L.Expression.Call(cosMethod, cosArgs0);
                break;

            case "exp":
                if (function.Expressions.Length != 1)
                {
                    throw new ArgumentException("Exp() takes exactly 1 argument");
                }

                var expMethod = typeof(Math).GetRuntimeMethod(
                    nameof(Math.Exp),
                    new[] { typeof(double) });
                var expArgs0 = L.Expression.Convert(args[0], typeof(double));
                _result = L.Expression.Call(expMethod, expArgs0);
                break;

            case "floor":
                if (function.Expressions.Length != 1)
                {
                    throw new ArgumentException("Floor() takes exactly 1 argument");
                }

                var floorMethod = typeof(Math).GetRuntimeMethod(
                    nameof(Math.Floor),
                    new[] { typeof(double) });
                var floorArgs0 = L.Expression.Convert(args[0], typeof(double));
                _result = L.Expression.Call(floorMethod, floorArgs0);
                break;

            case "ieeeremainder":
                if (function.Expressions.Length != 2)
                {
                    throw new ArgumentException("IEEEReaminer() takes exactly 2 arguments");
                }

                var ieeeMethod = typeof(Math).GetRuntimeMethod(
                    nameof(Math.IEEERemainder),
                    new[] { typeof(double), typeof(double) });
                var ieeeMethodArgs0 = L.Expression.Convert(
                    args[0],
                    typeof(double));
                var ieeeMethodArgs1 = L.Expression.Convert(
                    args[1],
                    typeof(double));
                _result = L.Expression.Call(ieeeMethod, ieeeMethodArgs0, ieeeMethodArgs1);
                break;

            case "log":
                if (function.Expressions.Length != 2)
                {
                    throw new ArgumentException("Log() takes exactly 2 arguments");
                }

                var logMethod = typeof(Math).GetRuntimeMethod(
                    nameof(Math.Log),
                    new[] { typeof(double), typeof(double) });
                var logMethodArgs0 = L.Expression.Convert(
                    args[0],
                    typeof(double));
                var logMethodArgs1 = L.Expression.Convert(
                    args[1],
                    typeof(double));
                _result = L.Expression.Call(logMethod, logMethodArgs0, logMethodArgs1);
                break;

            case "log10":
                if (function.Expressions.Length != 1)
                {
                    throw new ArgumentException("Log10() takes exactly 1 argument");
                }

                var log10Method = typeof(Math).GetRuntimeMethod(
                    nameof(Math.Log10),
                    new[] { typeof(double) });
                var log10Args0 = L.Expression.Convert(args[0], typeof(double));
                _result = L.Expression.Call(log10Method, log10Args0);
                break;

            case "round":
                var roundParameterCount = function.Expressions.Length;
                if (roundParameterCount == 0 || roundParameterCount > 2)
                {
                    throw new ArgumentException("Round() takes exactly 1 or 2 arguments");
                }

                var rounding =
                    _options.HasFlag(EvaluateOptions.RoundAwayFromZero)
                            ? MidpointRounding.AwayFromZero
                            : MidpointRounding.ToEven;

                if (roundParameterCount == 1)
                {
                    var roundMethod = typeof(Math).GetRuntimeMethod(
                        nameof(Math.Round),
                        new[]
                    {
                        typeof(double),
                        typeof(MidpointRounding)
                    });

                    var roundMethodArg0 = L.Expression.Convert(
                        args[0],
                        typeof(double));

                    _result = L.Expression.Call(
                        roundMethod,
                        roundMethodArg0,
                        L.Expression.Constant(rounding));
                }
                else
                {
                    var roundMethod = typeof(Math).GetRuntimeMethod(
                        nameof(Math.Round),
                        new[]
                    {
                        typeof(double), typeof(int),
                        typeof(MidpointRounding)
                    });
                    var roundMethodArg0 = L.Expression.Convert(
                        args[0],
                        typeof(double));
                    var roundMethodArg1 = L.Expression.Convert(
                        args[1],
                        typeof(int));
                    _result = L.Expression.Call(
                        roundMethod,
                        roundMethodArg0,
                        roundMethodArg1,
                        L.Expression.Constant(rounding));
                }

                break;

            case "sign":
                if (function.Expressions.Length != 1)
                {
                    throw new ArgumentException("Sign() takes exactly 1 argument");
                }

                var signMethod = typeof(Math).GetRuntimeMethod(
                    nameof(Math.Sign),
                    new[] { typeof(double) });
                var signArgs0 = L.Expression.Convert(args[0], typeof(double));
                _result = L.Expression.Call(signMethod, signArgs0);
                break;

            case "sin":
                if (function.Expressions.Length != 1)
                {
                    throw new ArgumentException("Sin() takes exactly 1 argument");
                }

                var sinMethod = typeof(Math).GetRuntimeMethod(
                    nameof(Math.Sin),
                    new[] { typeof(double) });
                var sinArgs0 = L.Expression.Convert(args[0], typeof(double));
                _result = L.Expression.Call(sinMethod, sinArgs0);
                break;

            case "sqrt":
                if (function.Expressions.Length != 1)
                {
                    throw new ArgumentException("Sqrt() takes exactly 1 argument");
                }

                var sqrtMethod = typeof(Math).GetRuntimeMethod(
                    nameof(Math.Sqrt),
                    new[] { typeof(double) });
                var sqrtArgs0 = L.Expression.Convert(args[0], typeof(double));
                _result = L.Expression.Call(sqrtMethod, sqrtArgs0);
                break;

            case "tan":
                if (function.Expressions.Length != 1)
                {
                    throw new ArgumentException("Tan() takes exactly 1 argument");
                }

                var tanMethod = typeof(Math).GetRuntimeMethod(
                    nameof(Math.Tan),
                    new[] { typeof(double) });
                var tanArgs0 = L.Expression.Convert(args[0], typeof(double));
                _result = L.Expression.Call(tanMethod, tanArgs0);
                break;

            case "truncate":
                if (function.Expressions.Length != 1)
                {
                    throw new ArgumentException("Truncate() takes exactly 1 argument");
                }

                var truncateMethod = typeof(Math).GetRuntimeMethod(
                    nameof(Math.Truncate),
                    new[] { typeof(double) });
                var truncateArgs0 = L.Expression.Convert(args[0], typeof(double));
                _result = L.Expression.Call(truncateMethod, truncateArgs0);
                break;

            case "if":
                if (args[0].Type != typeof(bool))
                {
                    throw new ArgumentException("if statement must have a boolean condition to process");
                }
                var(args1, args2) = AlignFloatingPointTypes(args[1], args[2]);
                _result           = L.Expression.Condition(args[0], args1, args2);
                break;

            case "in":
                var items = L.Expression.NewArrayInit(args[0].Type,
                                                      new ArraySegment <L.Expression>(args, 1, args.Length - 1).ToArray());
                var smi = typeof(Array).GetRuntimeMethod("IndexOf", new[] { typeof(Array), typeof(object) });
                var r   = L.Expression.Call(smi, L.Expression.Convert(items, typeof(Array)), L.Expression.Convert(args[0], typeof(object)));
                _result = L.Expression.GreaterThanOrEqual(r, L.Expression.Constant(0));
                break;

            case "min":
                var min_arg0 = L.Expression.Convert(args[0], typeof(double));
                var min_arg1 = L.Expression.Convert(args[1], typeof(double));
                _result = L.Expression.Condition(L.Expression.LessThan(min_arg0, min_arg1), min_arg0, min_arg1);
                break;

            case "max":
                var max_arg0 = L.Expression.Convert(args[0], typeof(double));
                var max_arg1 = L.Expression.Convert(args[1], typeof(double));
                _result = L.Expression.Condition(L.Expression.GreaterThan(max_arg0, max_arg1), max_arg0, max_arg1);
                break;

            case "pow":
                var pow_arg0 = L.Expression.Convert(args[0], typeof(double));
                var pow_arg1 = L.Expression.Convert(args[1], typeof(double));
                _result = L.Expression.Power(pow_arg0, pow_arg1);
                break;

            default:
                if (functionArgs.HasResult)
                {
                    _result = L.Expression.Constant(functionArgs.Result);
                    break;
                }

                var mi = FindMethod(function.Identifier.Name, args);
                _result = L.Expression.Call(_context, mi.BaseMethodInfo, mi.PreparedArguments);
                break;
            }
        }
 public override void Visit(ValueExpression expression)
 {
     _result = L.Expression.Constant(expression.Value);
 }