/// <summary>
        /// If return type of the given <paramref name="expression"/> does not match <paramref name="type"/>,
        /// attempts to adjust it. Returns same or new Expression object.
        /// If supplied type is <c>null</c>, expression becomes Action.
        /// </summary>
        /// <param name="expression">Parse tree root</param>
        /// <param name="type">Desired return type, or null to produce an action</param>
        /// <exception cref="ArgumentNullException"><paramref name="expression"/> is null</exception>
        /// <exception cref="CompilationException">Could produce a safe adjustment</exception>
        public Expression AdjustReturnType(Expression expression, Type type)
        {
            if (expression == null)
            {
                throw new ArgumentNullException("expression");
            }

            return(type != null?ExpressionTreeExtensions.AdjustReturnType(null, expression, type) : expression);
        }
Beispiel #2
0
        private object CompileImpl(Expression expression, CompilerState state)
        {
            if (state == null)
            {
                throw new ArgumentNullException("state");
            }

            state.RawReturnType = expression.Type;

            while (expression.CanReduce)
            {
                expression = expression.Reduce();
            }

            LambdaExpression lambda;
            var paramTypes = state.CompileToAction ? new Type[state.Arguments.Count] : new Type[state.Arguments.Count + 1];

            for (var i = 0; i < state.Arguments.Count; i++)
            {
                paramTypes[i] = state.Arguments[i].Type;
            }

            if (state.CompileToAction)
            {
                lambda = Expression.Lambda(Expression.GetActionType(paramTypes), expression, state.Arguments);
            }
            else
            {
                var returnType = state.ReturnType ?? state.RawReturnType;
                paramTypes[paramTypes.Length - 1] = returnType;

                expression = ExpressionTreeExtensions.AdjustReturnType(null, expression, returnType);
                lambda     = Expression.Lambda(expression, state.Arguments);

                if (!ReferenceEquals(returnType, lambda.ReturnType))
                {
                    throw new CompilationException(string.Format("Expected return type: {0}. Real return type: {1}", returnType.FullName, lambda.ReturnType.FullName));
                }
            }

            if (lambda.Parameters.Count != state.Arguments.Count)
            {
                throw new CompilationException(string.Format("Expected number of arguments: {0}. Real number: {1}", state.Arguments.Count, lambda.Parameters.Count));
            }

            for (var i = 0; i < lambda.Parameters.Count; i++)
            {
                if (!ReferenceEquals(lambda.Parameters[i].Type, state.Arguments[i].Type))
                {
                    throw new CompilationException(
                              string.Format("Expected type of argument {0}: {1}. Real type: {2}", i, state.Arguments[i].Type.FullName, lambda.Parameters[i].Type.FullName));
                }
            }

            return(lambda.Compile());
        }
        private static Expression PredefinedAtom_ToDateTime(ParseTreeNode root, CompilerState state)
        {
            root.RequireChildren(2);
            var argNodes = root.RequireChild(null, 1, 0);

            if (argNodes.ChildNodes.Count == 3 || argNodes.ChildNodes.Count == 6 || argNodes.ChildNodes.Count == 7)
            {
                var args      = new Expression[argNodes.ChildNodes.Count];
                var typeArray = new Type[args.Length];
                var typeInt32 = typeof(Int32);
                for (var i = 0; i < args.Length; i++)
                {
                    var node = argNodes.RequireChild(null, i);
                    var expr = ExpressionTreeExtensions.AdjustReturnType(node, state.ParentRuntime.Analyze(node, state), typeInt32);
                    args[i]      = expr;
                    typeArray[i] = typeInt32;
                }

                var ctr = typeof(DateTime).GetConstructor(typeArray);
                if (ctr == null)
                {
                    throw new Exception(string.Format("Could not locate datetime constructor with {0} int arguments", typeArray.Length));
                }
                return(ConstantHelper.TryEvalConst(root, ctr, args));
            }

            if (argNodes.ChildNodes.Count == 2)
            {
                var arg1Node    = root.RequireChild(null, 1, 0, 0);
                var valueString = state.ParentRuntime.Analyze(arg1Node, state).RemoveNullability();
                valueString.RequireString(arg1Node);

                var arg2Node     = root.RequireChild(null, 1, 0, 1);
                var formatString = state.ParentRuntime.Analyze(arg2Node, state).RemoveNullability();
                formatString.RequireString(arg2Node);

                return(ConstantHelper.TryEvalConst(root, ReflectionHelper.DateTimeParseExact,
                                                   valueString, formatString, Expression.Constant(null, typeof(IFormatProvider))));
            }

            if (argNodes.ChildNodes.Count == 1)
            {
                var arg1Node = root.RequireChild(null, 1, 0, 0);
                var value    = state.ParentRuntime.Analyze(arg1Node, state).RemoveNullability();
                value.RequireInteger(arg1Node);

                var dateTimeFromBinary = ReflectionHelper.GetOrAddMethod1(typeof(DateTime), "FromBinary", typeof(Int64));
                return(ConstantHelper.TryEvalConst(root, dateTimeFromBinary, value));
            }

            throw new CompilationException("ToDateTime has four overloads: with two, three, six and seven arguments", root);
        }
        private Expression PredefinedAtom_IfNull(ParseTreeNode root, CompilerState state)
        {
            root.RequireChildren(2);

            var arg1Node       = root.RequireChild(null, 1, 0, 0);
            var argument       = state.ParentRuntime.Analyze(arg1Node, state);
            var isnullableType = argument.IsNullableType();

            var arg2Node = root.RequireChild(null, 1, 0, 1);
            var ifnull   = state.ParentRuntime.Analyze(arg2Node, state);

            if (!ExpressionTreeExtensions.TryAdjustVoid(ref argument, ref ifnull))
            {
                throw new CompilationException("Could not adjust void blocks", root);
            }

            if (argument.IsVoid())
            {
                return(ifnull);
            }

            ifnull = ExpressionTreeExtensions.AdjustReturnType(arg2Node, ifnull, argument.Type.GetUnderlyingType());

            Expression result;

            if (argument.Type.IsValueType)
            {
                result = isnullableType
                    ? Expression.Condition(Expression.Field(argument, "HasValue"), Expression.Field(argument, "Value"), ifnull)
                    : argument;
            }
            else
            {
                // now only reference types remaining
                var constantExpression = argument as ConstantExpression;
                if (constantExpression != null)
                {
                    result = ReferenceEquals(constantExpression.Value, null) ? ifnull : argument;
                }
                else
                {
                    result = Expression.Condition(Expression.Equal(argument, Expression.Constant(null)), ifnull, argument);
                }
            }

            return(result.IsVoid() ? result : result.RemoveNullability());
        }
Beispiel #5
0
        private Expression BuildBetweenExpression(ParseTreeNode root, CompilerState state)
        {
            root.RequireChildren(6);

            var variableNode = root.ChildNodes[0];
            var notOpt       = root.RequireChild("notOpt", 1);

            root.RequireChild("between", 2);
            root.RequireChild("and", 4);

            var argument = Analyze(variableNode, state).RemoveNullability();

            var leftNode = root.ChildNodes[3];
            var leftExpr = Analyze(leftNode, state);

            leftExpr.RequireNonVoid(leftNode);
            leftExpr = ExpressionTreeExtensions.AdjustReturnType(leftNode, leftExpr, argument.Type);

            var rightNode = root.ChildNodes[5];
            var rightExpr = Analyze(rightNode, state);

            rightExpr.RequireNonVoid(rightNode);
            rightExpr = ExpressionTreeExtensions.AdjustReturnType(rightNode, rightExpr, argument.Type);

            Expression lower, upper;

            if (argument.IsString())
            {
                lower = ConstantHelper.TryEvalConst(leftNode, PrepareStringComparison(leftNode, leftExpr, argument), Expression.Constant(0), ExpressionType.LessThanOrEqual);
                upper = ConstantHelper.TryEvalConst(rightNode, PrepareStringComparison(rightNode, rightExpr, argument), Expression.Constant(0), ExpressionType.GreaterThanOrEqual);
            }
            else
            {
                lower = ConstantHelper.TryEvalConst(leftNode, argument, leftExpr, ExpressionType.GreaterThanOrEqual);
                upper = ConstantHelper.TryEvalConst(rightNode, argument, rightExpr, ExpressionType.LessThanOrEqual);
            }

            var result = ConstantHelper.TryEvalConst(root, lower, upper, ExpressionType.AndAlso);

            if (notOpt.ChildNodes.Count > 0)
            {
                result = ConstantHelper.TryEvalConst(root, result, ExpressionType.Not);
            }

            return(result);
        }