// See http://en.wikipedia.org/wiki/Shunting-yard_algorithm private static ArrayList<QueryToken> PrepareQueryPlan(ArrayList<QueryToken> tokens, QueryOperatorPrecedence precedenceType) { ArrayList<QueryToken> output = new ArrayList<QueryToken>(); Stack<QueryToken> stack = new Stack<QueryToken>(); foreach (QueryToken token in tokens) // While there are tokens to be read: { switch (GetTokenType(token)) { case TokenType.Term: // If the token is a number... output.Add(token); // ...then add it to the output queue. while (stack.Count > 0 && GetTokenType(stack.Peek()) == TokenType.Function) { output.Add(stack.Pop()); } // *** this fixes function scope when used without brackets break; case TokenType.Function: // If the token is a function token... stack.Push(token); // ...then push it onto the stack. break; case TokenType.Operator: // If the token is an operator, o1, then: QueryToken o1 = token; QueryToken o2; // while there is an operator token, o2, at the top of the stack, and either o1 is left-associative and its precedence is equal to that of o2, // or o1 has precedence less than that of o2... while (stack.Count > 0 && GetTokenType(o2 = stack.Peek()) == TokenType.Operator && GetPrecedence(o1, precedenceType) <= GetPrecedence(o2, precedenceType)) { output.Add(stack.Pop()); // ...pop o2 off the stack, onto the output queue. } stack.Push(o1); // Push o1 onto the stack. break; case TokenType.LeftParenthesis: // If the token is a left parenthesis... stack.Push(token); // ...then push it onto the stack. break; case TokenType.RightParenthesis: // If the token is a right parenthesis: while (stack.Count > 0 && GetTokenType(stack.Peek()) != TokenType.LeftParenthesis) // Until the token at the top of the stack is a left parenthesis... { output.Add(stack.Pop()); // ...pop operators off the stack onto the output queue. } if (stack.Count == 0) { throw new Exception("Parenthesis mismatch."); } // If the stack runs out without finding a left parenthesis, then there are mismatched parentheses. stack.Pop(); // Pop the left parenthesis from the stack, but not onto the output queue. // If the token at the top of the stack is a function token, pop it onto the output queue. if (stack.Count > 0 && GetTokenType(stack.Peek()) == TokenType.Function) { output.Add(stack.Pop()); } break; } } // When there are no more tokens to read: while (stack.Count > 0) // While there are still operator tokens in the stack: { QueryToken o = stack.Pop(); // If the operator token on the top of the stack is a parenthesis, then there are mismatched parentheses. if (GetTokenType(o) == TokenType.LeftParenthesis || GetTokenType(o) == TokenType.RightParenthesis) { throw new Exception("Parenthesis mismatch."); } output.Add(o); // Pop the operator onto the output queue. } return output; }
public static ArrayList<QueryToken> PrepareQueryPlan(string query, string tokenRegex, QueryOperatorPrecedence precedenceType = QueryOperatorPrecedence.AndBeforeOr, bool caseSensitive = true) { ArrayList<QueryToken> tokens = TokenizeQuery(query, tokenRegex, caseSensitive); ArrayList<QueryToken> plan = PrepareQueryPlan(tokens, precedenceType); return plan; }
private static int GetPrecedence(string op, QueryOperatorPrecedence precedenceType) { int rev = precedenceType == QueryOperatorPrecedence.OrBeforeAnd ? 1 : 0; if (op == "AND") { return 1 - rev; } if (op == "OR") { return 0 + rev; } throw new ArgumentNotSupportedException("op"); }