Example #1
0
 /// <summary>
 /// Initializes a new instance of the <see cref="FilterNode"/> class that is a terminal node.
 /// </summary>
 /// <remarks>
 /// A terminal node is a relational expression, and contains no child nodes.
 /// </remarks>
 /// <param name="term">The expression term.</param>
 public FilterNode(RelationalExpression term)
 {
     if (term == null)
     {
         throw new ArgumentNullException("term");
     }
     this.term = term;
 }
Example #2
0
        /// <summary>
        /// Parses the specified text into a binary expression tree.
        /// </summary>
        /// <param name="expression">The expression text to parse.</param>
        /// <returns></returns>
        public static FilterNode Parse(string expression)
        {
            var tokens = new List <Token>(new Tokenizer(expression));

            // Combine terms and relational operators into expressions.
            while (true)
            {
                var evaluationIndex = -1;
                for (var i = 0; i < tokens.Count; i++)
                {
                    if (tokens[i].Type == TokenType.Relation)
                    {
                        evaluationIndex = i;
                        break;
                    }
                }

                if (evaluationIndex > -1)
                {
                    var expr = new RelationalExpression(tokens[evaluationIndex - 1].Term,
                                                        tokens[evaluationIndex + 1].Term,
                                                        tokens[evaluationIndex].Relation);
                    tokens.RemoveAt(evaluationIndex - 1);
                    tokens.RemoveAt(evaluationIndex - 1);
                    tokens.RemoveAt(evaluationIndex - 1);

                    tokens.Insert(evaluationIndex - 1, new Token(expr));

                    // Remove parentheses surrounding a resolved expression.
                    if (evaluationIndex - 2 > -1 && tokens[evaluationIndex - 2].Type == TokenType.OpenParen &&
                        evaluationIndex < tokens.Count && tokens[evaluationIndex].Type == TokenType.CloseParen)
                    {
                        // New resolved expression is now at evaluationIndex - 1.
                        tokens.RemoveAt(evaluationIndex - 2);
                        // New resolved expression is now at evaluationIndex - 2.
                        tokens.RemoveAt(evaluationIndex - 1);
                    }
                }
                else
                {
                    break;
                }
            }

            // If the token list contains only a single relational expression, we can return a simple filter node based on that.
            if (tokens.Count == 1 && tokens[0].Type == TokenType.ResolvedExpression)
            {
                return(new FilterNode(tokens[0].ResolvedExpression));
            }

            // The token list now contains only RelationalExpressions, conditional operators (AND and OR) and parentheses.
            // Combine the expressions and operators into nodes, prioritizing binding according to the natural order of precedence
            // for boolean operators and parentheses.
            var operatorPriorities = new Dictionary <LogicalOperator, int>();

            operatorPriorities.Add(LogicalOperator.Or, 1);
            operatorPriorities.Add(LogicalOperator.And, 2);
            var parenAddedPriority = operatorPriorities.Count;

            while (true)
            {
                var contextPriority       = 0;
                var evaluationPriorityMax = -1;
                var evaluationIndex       = -1;

                for (var i = 0; i < tokens.Count; i++)
                {
                    var type = tokens[i].Type;

                    if (type == TokenType.Condition)
                    {
                        var evaluationPriority = operatorPriorities[tokens[i].Condition] + contextPriority;
                        if (evaluationPriority > evaluationPriorityMax)
                        {
                            evaluationIndex       = i;
                            evaluationPriorityMax = evaluationPriority;
                        }
                    }
                    else if (type == TokenType.OpenParen)
                    {
                        contextPriority += parenAddedPriority;
                    }
                    else if (type == TokenType.CloseParen)
                    {
                        contextPriority -= parenAddedPriority;
                    }
                }

                if (evaluationIndex > -1)
                {
                    var prevToken = tokens[evaluationIndex - 1];
                    var nextToken = tokens[evaluationIndex + 1];

                    FilterNode nodeLeft  = null;
                    FilterNode nodeRight = null;

                    // Tokens surrounding the AND or OR must now be either a relational expression or a resolved FilterNode.
                    if (prevToken.Type == TokenType.ResolvedExpression)
                    {
                        nodeLeft = new FilterNode(prevToken.ResolvedExpression);
                    }
                    else
                    {
                        nodeLeft = prevToken.ResolvedNode;
                    }
                    if (nextToken.Type == TokenType.ResolvedExpression)
                    {
                        nodeRight = new FilterNode(nextToken.ResolvedExpression);
                    }
                    else
                    {
                        nodeRight = nextToken.ResolvedNode;
                    }

                    var node = new FilterNode(nodeLeft, nodeRight, tokens[evaluationIndex].Condition);

                    tokens.RemoveAt(evaluationIndex - 1);
                    tokens.RemoveAt(evaluationIndex - 1);
                    tokens.RemoveAt(evaluationIndex - 1);

                    tokens.Insert(evaluationIndex - 1, new Token(node));

                    // Remove parentheses surrounding a resolved node.
                    if (evaluationIndex - 2 > -1 && tokens[evaluationIndex - 2].Type == TokenType.OpenParen &&
                        evaluationIndex < tokens.Count && tokens[evaluationIndex].Type == TokenType.CloseParen)
                    {
                        // New resolved node is now at evaluationIndex - 1.
                        tokens.RemoveAt(evaluationIndex - 2);
                        // New resolved node is now at evaluationIndex - 2.
                        tokens.RemoveAt(evaluationIndex - 1);
                    }
                }
                else
                {
                    break;
                }
            }

            // The token list should now contain a single FilterNode.
            return(tokens[0].ResolvedNode);
        }
Example #3
0
 public Token(RelationalExpression expression)
 {
     type = TokenType.ResolvedExpression;
     expr = expression;
 }