Inheritance: SymbolNode
        private bool TryResolveOdataExpression(MethodCallNode node, out Expression expression)
        {
            expression = null;

            switch (node.Name)
            {
            case "isof":
            case "cast":
                if (node.Children.Count > 2 || node.Children.Count < 1)
                {
                    throw CreateParseException(node, "Only one or two arguments to cast operator is allowed.");
                }

                TypeNameNode castTypeArg;
                Expression   operand;

                if (node.Children.Count == 1)
                {
                    castTypeArg = node.Children[0] as TypeNameNode;
                    operand     = this.thisParam;
                }
                else
                {
                    operand     = ParseExpression(node.Children[0]);
                    castTypeArg = node.Children[1] as TypeNameNode;
                }

                if (castTypeArg == null)
                {
                    throw CreateParseException(node, "Argument to cast is required to be a type literal.");
                }

                var type = ResolveType(castTypeArg);
                if (node.Name == "cast")
                {
                    expression = Expression.Convert(operand, type);
                }
                else if (node.Name == "isof")
                {
                    expression = Expression.TypeIs(operand, type);
                }
                return(true);

            case "iif":
                ParseConditionalOperator(node, ref expression);
                return(true);
            }

            var memberCandidates = QueryFunctionMapping.GetMemberCandidates(node.Name, node.Children.Count);

            foreach (var memberMapping in memberCandidates)
            {
                if (TryResolveMemberMapping(memberMapping, node, out expression))
                {
                    return(true);
                }
            }

            return(false);
        }
        private void ParseConditionalOperator(MethodCallNode node, ref Expression expression)
        {
            if (node.Children.Count != 3)
            {
                throw CreateParseException(node,
                                           "Conditional requires three arguments: iif(test, iftrue, iffalse).");
            }

            var testExpr = ParseExpression(node.Children[0]);
            var ifTrue   = ParseExpression(node.Children[1]);
            var ifFalse  = ParseExpression(node.Children[2]);

            if (ifTrue.Type != ifFalse.Type)
            {
                PeformImplicitConversionForNodePair(ref ifTrue, ref ifFalse);
            }

            expression = Expression.Condition(testExpr, ifTrue, ifFalse);
        }
        private Expression ParseMethodCallNode(MethodCallNode node, Expression memberExpression)
        {
            if (memberExpression == null)
            {
                throw new ArgumentNullException(nameof(memberExpression));
            }

            if (memberExpression == this.thisParam)
            {
                if (node.HasArguments)
                {
                    Expression expression;
                    if (TryResolveOdataExpression(node, out expression))
                    {
                        return(expression);
                    }
                }
            }

            throw CreateParseException(node, "Could not recognize method " + node.Name);
        }
        private bool TryResolveMemberMapping(QueryFunctionMapping.MemberMapping memberMapping,
                                             MethodCallNode node,
                                             out Expression expression)
        {
            expression = null;
            var reorderedArgs = memberMapping.ReorderArguments(node.Children);
            var method        = memberMapping.Member as MethodInfo;
            var property      = memberMapping.Member as PropertyInfo;

            if (method != null)
            {
                Expression instance = null;

                if (!method.IsStatic)
                {
                    instance = ParseExpression(reorderedArgs[0], this.thisParam, null);
                    if (!TryResolveGenericInstanceMethod(instance, ref method))
                    {
                        return(false);
                    }
                }

                // Convert each node and check whether argument matches..
                var argArrayOffset   = method.IsStatic ? 0 : 1;
                var methodParameters = method.GetParameters();
                if (methodParameters.Length != reorderedArgs.Count - argArrayOffset)
                {
                    var message =
                        $"Number parameters count ({methodParameters.Length}) for method {method.DeclaringType?.FullName}.{method.Name} does not match provided argument count ({reorderedArgs.Count - argArrayOffset})";
                    throw CreateParseException(node, message);
                }

                var argExprArray = new Expression[methodParameters.Length];

                if (!method.IsGenericMethodDefinition)
                {
                    for (var i = 0; i < methodParameters.Length; i++)
                    {
                        var param   = methodParameters[i];
                        var argNode = reorderedArgs[i + argArrayOffset];
                        var argExpr = ParseExpression(argNode, this.thisParam, param.ParameterType);

                        if (!param.ParameterType.IsAssignableFrom(argExpr.Type))
                        {
                            return(false);
                        }

                        argExprArray[i] = argExpr;
                    }
                }
                else
                {
                    var methodDefinition = method;
                    var methodTypeArgs   = method.GetGenericArguments();

                    for (var i = 0; i < methodParameters.Length; i++)
                    {
                        var param   = methodParameters[i];
                        var argNode = reorderedArgs[i + argArrayOffset];
                        var argExpr = ParseExpression(argNode, this.thisParam, param.ParameterType);

                        bool typeArgsWasResolved;
                        if (
                            !TypeExtensions.TryFillGenericTypeParameters(param.ParameterType, argExpr.Type, methodTypeArgs,
                                                                         out typeArgsWasResolved))
                        {
                            return(false);
                        }

                        if (typeArgsWasResolved)
                        {
                            // Upgrade to real method when all type args are resolved!!
                            method           = methodDefinition.MakeGenericMethod(methodTypeArgs);
                            methodParameters = method.GetParameters();
                        }

                        argExprArray[i] = argExpr;
                    }
                }

                expression = Expression.Call(instance, method, argExprArray);
                expression = memberMapping.PostResolveHook(expression);
                return(true);
            }
            if (property != null)
            {
                var instance = ParseExpression(reorderedArgs[0], this.thisParam, null);
                if (!TryResolveGenericInstanceMethod(instance, ref property))
                {
                    return(false);
                }

                expression = Expression.MakeMemberAccess(instance, property);
                expression = memberMapping.PostResolveHook(expression);
                return(true);
            }
            return(false);
        }
        private Expression ParseBinaryOperator(BinaryOperatorNode binaryOperatorNode)
        {
            var rightNode = binaryOperatorNode.Right;
            var leftNode  = binaryOperatorNode.Left;

            if (binaryOperatorNode.NodeType == NodeType.Dot)
            {
                if (rightNode.NodeType == NodeType.MethodCall)
                {
                    var origCallNode = (MethodCallNode)rightNode;
                    // Rewrite extension method call to static method call of tree:
                    // We do this by taking inserting the first node before arg nodes of extension method call.
                    var staticMethodArgs = leftNode.WrapAsEnumerable().Concat(rightNode.Children);
                    var staticMethodCall = new MethodCallNode(origCallNode.Name, staticMethodArgs);

                    return(ParseExpression(staticMethodCall));
                }
                var left = ParseExpression(leftNode);
                return(ParseExpression(rightNode, left, null));
            }

            if (binaryOperatorNode.NodeType == NodeType.As)
            {
                if (rightNode.NodeType != NodeType.TypeNameLiteral)
                {
                    throw CreateParseException(binaryOperatorNode, "Right side of as operator is required to be a type literal.");
                }

                return(Expression.TypeAs(ParseExpression(leftNode), ResolveType((TypeNameNode)rightNode)));
            }
            if (binaryOperatorNode.NodeType == NodeType.In)
            {
                return(ParseInOperator(binaryOperatorNode));
            }

            var leftChild  = ParseExpression(leftNode);
            var rightChild = ParseExpression(rightNode);

            PeformImplicitConversionForNodePair(ref leftChild, ref rightChild);

            switch (binaryOperatorNode.NodeType)
            {
            case NodeType.AndAlso:
                return(Expression.AndAlso(leftChild, rightChild));

            case NodeType.OrElse:
                return(Expression.OrElse(leftChild, rightChild));

            case NodeType.Add:
                return(Expression.Add(leftChild, rightChild));

            case NodeType.Subtract:
                return(Expression.Subtract(leftChild, rightChild));

            case NodeType.Multiply:
                return(Expression.Multiply(leftChild, rightChild));

            case NodeType.Modulo:
                return(Expression.Modulo(leftChild, rightChild));

            case NodeType.Divide:
                return(Expression.Divide(leftChild, rightChild));

            case NodeType.Equal:
                return(ParseBinaryOperator(leftChild, rightChild, ExpressionType.Equal));

            case NodeType.LessThan:
                return(Expression.LessThan(leftChild, rightChild));

            case NodeType.GreaterThan:
                return(Expression.GreaterThan(leftChild, rightChild));

            case NodeType.GreaterThanOrEqual:
                return(Expression.GreaterThanOrEqual(leftChild, rightChild));

            case NodeType.LessThanOrEqual:
                return(Expression.LessThanOrEqual(leftChild, rightChild));

            case NodeType.NotEqual:
                return(ParseBinaryOperator(leftChild, rightChild, ExpressionType.NotEqual));

            case NodeType.CaseInsensitiveEqual:
                return(ParseCaseInsensitiveEqualOperator(leftChild, rightChild));

            default:
                throw new NotImplementedException($"Don't know how to handle node type {binaryOperatorNode.NodeType}.");
            }
        }