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());
        }
Esempio n. 2
0
        private Expression BuildBinaryExpression(ParseTreeNode root, CompilerState state)
        {
            root.RequireChildren(3);

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

            var op = GetBinaryOperator(root.ChildNodes[1]);

            if (op == "in" || op == "not in")
            {
                return(BuildInclusionExpression(root, leftExpr, op, state));
            }

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

            if (!ExpressionTreeExtensions.TryAdjustVoid(ref leftExpr, ref rightExpr))
            {
                throw new CompilationException("This operation is not defined when both arguments are void", root);
            }

            Expression expr;

            leftExpr  = leftExpr.RemoveNullability();
            rightExpr = rightExpr.RemoveNullability();

            if (leftExpr.IsDateTime() && rightExpr.IsDateTime() ||
                leftExpr.IsTimeSpan() && rightExpr.IsTimeSpan())
            {
                #region DateTime and DateTime, or TimeSpan and TimeSpan
                switch (op)
                {
                case "+":
                    if (leftExpr.IsDateTime() && rightExpr.IsDateTime())
                    {
                        throw new CompilationException("Datetime values cannot be added to one another", root);
                    }
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.Add);
                    break;

                case "-":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.Subtract);
                    break;

                case "=":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.Equal);
                    break;

                case "!=":
                case "<>":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.NotEqual);
                    break;

                case ">":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.GreaterThan);
                    break;

                case "<":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.LessThan);
                    break;

                case "<=":
                case "!>":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.LessThanOrEqual);
                    break;

                case ">=":
                case "!<":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.GreaterThanOrEqual);
                    break;

                default:
                    throw new CompilationException("Binary operator not supported for datetime values: " + op, root.ChildNodes[1]);
                }
                #endregion
            }
            else if (leftExpr.IsDateTime() && rightExpr.IsTimeSpan())
            {
                #region DateTime and TimeSpan or TimeSpan and DateTime
                switch (op)
                {
                case "+":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.Add);
                    break;

                case "-":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.Subtract);
                    break;

                default:
                    throw new CompilationException("Binary operator not supported for datetime and timespan: " + op, root.ChildNodes[1]);
                }
                #endregion
            }
            else if (leftExpr.IsTimeSpan() && rightExpr.IsDateTime())
            {
                #region TimeSpan and DateTime
                switch (op)
                {
                case "+":
                    expr = ConstantHelper.TryEvalConst(root, rightExpr, leftExpr, ExpressionType.Add);
                    break;

                default:
                    throw new CompilationException("Binary operator not supported for timespan and datetime: " + op, root.ChildNodes[1]);
                }
                #endregion
            }
            else if (leftExpr.IsString() && rightExpr.IsString())
            {
                #region String and String
                switch (op)
                {
                case "+":
                    var concat = ReflectionHelper.StringConcat;
                    expr = ConstantHelper.TryEvalConst(root, concat, leftExpr, rightExpr);
                    break;

                case "=":
                case "!=":
                case "<>":
                    expr = PrepareStringEquality(root, leftExpr, rightExpr);
                    if (op[0] != '=')
                    {
                        expr = ConstantHelper.TryEvalConst(root, expr, ExpressionType.Not, expr.Type);
                    }
                    break;

                case ">":
                    expr = ConstantHelper.TryEvalConst(root, PrepareStringComparison(root, leftExpr, rightExpr), Expression.Constant(0), ExpressionType.GreaterThan);
                    break;

                case "<":
                    expr = ConstantHelper.TryEvalConst(root, PrepareStringComparison(root, leftExpr, rightExpr), Expression.Constant(0), ExpressionType.LessThan);
                    break;

                case "<=":
                case "!>":
                    expr = ConstantHelper.TryEvalConst(root, PrepareStringComparison(root, leftExpr, rightExpr), Expression.Constant(0), ExpressionType.LessThanOrEqual);
                    break;

                case ">=":
                case "!<":
                    expr = ConstantHelper.TryEvalConst(root, PrepareStringComparison(root, leftExpr, rightExpr), Expression.Constant(0), ExpressionType.GreaterThanOrEqual);
                    break;

                case "like":
                    throw new CompilationException("Instead of LIKE, use predefined functions StartsWith, EndsWith and Contains", root.ChildNodes[1]);

                default:
                    throw new CompilationException("Binary operator not supported for strings: " + op, root.ChildNodes[1]);
                }
                #endregion
            }
            else if (leftExpr.IsNumeric() && rightExpr.IsNumeric())
            {
                #region Numeric and Numeric
                ExpressionTreeExtensions.AdjustArgumentsForBinaryOperation(ref leftExpr, ref rightExpr, root);

                switch (op)
                {
                case "+":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.Add);
                    break;

                case "-":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.Subtract);
                    break;

                case "*":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.Multiply);
                    break;

                case "/":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.Divide);
                    break;

                case "%":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.Modulo);
                    break;

                case "&":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.And);
                    break;

                case "|":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.Or);
                    break;

                case "^":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.ExclusiveOr);
                    break;

                case "<":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.LessThan);
                    break;

                case "<=":
                case "!>":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.LessThanOrEqual);
                    break;

                case ">":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.GreaterThan);
                    break;

                case ">=":
                case "!<":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.GreaterThanOrEqual);
                    break;

                case "=":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.Equal);
                    break;

                case "!=":
                case "<>":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.NotEqual);
                    break;

                default:
                    throw new CompilationException("Binary operator not supported yet for numerics: " + op, root.ChildNodes[1]);
                }
                #endregion
            }
            else if (leftExpr.IsBoolean() && rightExpr.IsBoolean())
            {
                #region Boolean and Boolean
                switch (op)
                {
                case "and":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.AndAlso);
                    break;

                case "or":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.OrElse);
                    break;

                case "xor":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.ExclusiveOr);
                    break;

                case "=":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.Equal);
                    break;

                case "!=":
                case "<>":
                    expr = ConstantHelper.TryEvalConst(root, leftExpr, rightExpr, ExpressionType.NotEqual);
                    break;

                default:
                    throw new CompilationException("Binary operator not supported yet for booleans: " + op, root.ChildNodes[1]);
                }
                #endregion
            }
            else
            {
                throw new CompilationException(string.Format("Binary operator {0} is not yet supported for types {1} and {2}",
                                                             op, leftExpr.Type.FullName, rightExpr.Type.FullName), root.ChildNodes[1]);
            }

            return(expr);
        }