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()); }
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); }