private QueryNode ParseFunctionCallNode()
            {
                BinaryOperatorNode binaryNode = null;
                FunctionCallNode   node       = null;

                var stack = new Stack <FunctionCallNode>();

                while (_tokens.Count > 0)
                {
                    Token token = _tokens.Dequeue();

                    switch (token.TokenType)
                    {
                    case TokenType.Base64Binary:
                    case TokenType.Date:
                    case TokenType.DateTimeOffset:
                    case TokenType.Decimal:
                    case TokenType.Double:
                    case TokenType.Duration:
                    case TokenType.EdmType:
                    case TokenType.Enum:
                    case TokenType.False:
                    case TokenType.Guid:
                    case TokenType.Integer:
                    case TokenType.Null:
                    case TokenType.Single:
                    case TokenType.String:
                    case TokenType.TimeOfDay:
                    case TokenType.True:
                        ConstantNode constantNode = ConstantNodeParser.ParseConstantNode(token);

                        if (stack.Count > 0)
                        {
                            stack.Peek().AddParameter(constantNode);
                        }
                        else
                        {
                            if (binaryNode == null)
                            {
                                throw ODataException.BadRequest(ExceptionMessage.GenericUnableToParseFilter, "$filter");
                            }

                            binaryNode.Right = constantNode;
                        }

                        break;

                    case TokenType.BinaryOperator:
                        binaryNode = new BinaryOperatorNode(node, token.Value.ToBinaryOperatorKind(), null);
                        break;

                    case TokenType.CloseParentheses:
                        if (_groupingDepth == 0)
                        {
                            throw ODataException.BadRequest(ExceptionMessage.UnableToParseFilter($"the closing parenthesis not expected", token.Position), "$filter");
                        }

                        _groupingDepth--;

                        if (stack.Count > 0)
                        {
                            FunctionCallNode lastNode = stack.Pop();

                            if (stack.Count > 0)
                            {
                                stack.Peek().AddParameter(lastNode);
                            }
                            else
                            {
                                if (binaryNode != null)
                                {
                                    binaryNode.Right = lastNode;
                                }
                                else
                                {
                                    node = lastNode;
                                }
                            }
                        }

                        break;

                    case TokenType.Comma:
                        if (_tokens.Count > 0 && _tokens.Peek().TokenType == TokenType.CloseParentheses)
                        {
                            // If there is a comma in a function call, there should be another parameter followed by a closing comma
                            throw ODataException.BadRequest(ExceptionMessage.UnableToParseFilter($"the function {node?.Name} has a missing parameter or extra comma", token.Position), "$filter");
                        }

                        break;

                    case TokenType.FunctionName:
                        node = new FunctionCallNode(token.Value);
                        break;

                    case TokenType.OpenParentheses:
                        if (_tokens.Count > 0 && _tokens.Peek().TokenType == TokenType.CloseParentheses)
                        {
                            // All OData functions have at least 1 or 2 parameters
                            throw ODataException.BadRequest(ExceptionMessage.UnableToParseFilter($"the function {node?.Name} has no parameters specified", token.Position), "$filter");
                        }

                        _groupingDepth++;
                        stack.Push(node);
                        break;

                    case TokenType.PropertyName:
                        var propertyAccessNode = new PropertyAccessNode(PropertyPath.For(token.Value, _model));

                        if (stack.Count > 0)
                        {
                            stack.Peek().AddParameter(propertyAccessNode);
                        }
                        else
                        {
                            if (binaryNode == null)
                            {
                                throw ODataException.BadRequest(ExceptionMessage.GenericUnableToParseFilter, "$filter");
                            }

                            binaryNode.Right = propertyAccessNode;
                        }

                        break;

                    default:
                        throw ODataException.BadRequest(ExceptionMessage.UnableToParseFilter($"unexpected {token.Value}", token.Position), "$filter");
                    }
                }

                if (binaryNode != null)
                {
                    return(binaryNode);
                }

                return(node);
            }
            private QueryNode ParsePropertyAccessNode()
            {
                QueryNode result = null;

                QueryNode          leftNode     = null;
                BinaryOperatorKind operatorKind = BinaryOperatorKind.None;
                QueryNode          rightNode    = null;

                while (_tokens.Count > 0)
                {
                    Token token = _tokens.Dequeue();

                    switch (token.TokenType)
                    {
                    case TokenType.Base64Binary:
                    case TokenType.Date:
                    case TokenType.DateTimeOffset:
                    case TokenType.Decimal:
                    case TokenType.Double:
                    case TokenType.Duration:
                    case TokenType.Enum:
                    case TokenType.False:
                    case TokenType.Guid:
                    case TokenType.Integer:
                    case TokenType.Null:
                    case TokenType.Single:
                    case TokenType.String:
                    case TokenType.TimeOfDay:
                    case TokenType.True:
                        rightNode = ConstantNodeParser.ParseConstantNode(token);
                        break;

                    case TokenType.BinaryOperator:
                        if (operatorKind != BinaryOperatorKind.None)
                        {
                            result    = new BinaryOperatorNode(leftNode, operatorKind, rightNode);
                            leftNode  = null;
                            rightNode = null;
                        }

                        operatorKind = token.Value.ToBinaryOperatorKind();
                        break;

                    case TokenType.CloseParentheses:
                        _groupingDepth--;
                        break;

                    case TokenType.FunctionName:
                        rightNode = new FunctionCallNode(token.Value);
                        break;

                    case TokenType.OpenParentheses:
                        _groupingDepth++;
                        break;

                    case TokenType.PropertyName:
                        var propertyAccessNode = new PropertyAccessNode(PropertyPath.For(token.Value, _model));

                        if (leftNode is null)
                        {
                            leftNode = propertyAccessNode;
                        }
                        else if (rightNode is null)
                        {
                            rightNode = propertyAccessNode;
                        }

                        break;

                    default:
                        throw ODataException.BadRequest(ExceptionMessage.UnableToParseFilter($"unexpected {token.Value}", token.Position), "$filter");
                    }
                }

                result = result is null
                    ? new BinaryOperatorNode(leftNode, operatorKind, rightNode)
                    : new BinaryOperatorNode(result, operatorKind, leftNode ?? rightNode);

                return(result);
            }