/// <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; }
/// <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); }
public Token(RelationalExpression expression) { type = TokenType.ResolvedExpression; expr = expression; }