private Filter <T> Reduce(QuickFilterToken token) { while (stack.Count > 0) { Filter <T> top = stack.Pop(); if (stack.Count == 0) { return(top); } Filter <T> op = stack.Peek(); if (token > op.Precidence) { return(top); } op = stack.Pop(); // now combine "top" and "op" if (op is FilterKeyword <T> ) { FilterKeyword <T> keyword = (FilterKeyword <T>)op; op = Combine(keyword, top); } else if (op is FilterAnd <T> ) { FilterAnd <T> and = (FilterAnd <T>)op; and.Right = Combine(and.Right, top); } else if (op is FilterOr <T> ) { FilterOr <T> or = (FilterOr <T>)op; or.Right = Combine(or.Right, top); } else if (op is FilterNot <T> ) { FilterNot <T> not = (FilterNot <T>)op; not.Right = Combine(not.Right, top); } else if (op is FilterParens <T> ) { FilterParens <T> parens = (FilterParens <T>)op; parens.Expression = Combine(parens.Expression, top); } stack.Push(op); } return(null); }
/// <summary> /// This is the tokenizer, it returns a stream of tokens found in the given string. /// </summary> /// <param name="quickFilter">The string to tokenize</param> /// <returns></returns> private IEnumerable <Tuple <QuickFilterToken, string> > GetFilterTokens(string quickFilter) { StringBuilder sb = new StringBuilder(); for (int i = 0, n = quickFilter.Length; i < n; i++) { QuickFilterToken token = QuickFilterToken.None; string literal = null; bool previousCouldBeKeyword = false; bool thisCouldBeKeyword = false; char ch = quickFilter[i]; if (ch == '&' || ch == '+') { token = QuickFilterToken.And; literal = ch.ToString(); } else if (ch == '"') { int j = quickFilter.IndexOf('"', i + 1); if (j > i + 1) { sb.Append(quickFilter.Substring(i + 1, j - i - 1)); i = j; token = QuickFilterToken.Literal; literal = sb.ToString(); sb.Length = 0; previousCouldBeKeyword = true; } else { sb.Append(ch); } } else if (ch == '|') // we do not recognize "," as "or" because "," exists as a thousands separator in $amounts, so that would be ambiguous. { token = QuickFilterToken.Or; literal = ch.ToString(); } else if (ch == '!') { token = QuickFilterToken.Not; literal = ch.ToString(); } else if (ch == '(') { token = QuickFilterToken.LeftParen; literal = ch.ToString(); previousCouldBeKeyword = true; } else if (ch == ')') { token = QuickFilterToken.RightParen; literal = ch.ToString(); } else if (Char.IsWhiteSpace(ch)) { token = QuickFilterToken.Literal; literal = sb.ToString(); sb.Length = 0; thisCouldBeKeyword = true; } else { sb.Append(ch); } if (token != QuickFilterToken.None) { string previous = sb.ToString(); sb.Length = 0; if (!string.IsNullOrEmpty(previous)) { if (previousCouldBeKeyword && string.Compare(previous, "and", StringComparison.OrdinalIgnoreCase) == 0) { yield return(new Tuple <QuickFilterToken, string>(QuickFilterToken.And, previous)); } else if (previousCouldBeKeyword && string.Compare(previous, "or", StringComparison.OrdinalIgnoreCase) == 0) { yield return(new Tuple <QuickFilterToken, string>(QuickFilterToken.Or, previous)); } else if (previousCouldBeKeyword && string.Compare(previous, "not", StringComparison.OrdinalIgnoreCase) == 0) { yield return(new Tuple <QuickFilterToken, string>(QuickFilterToken.Not, previous)); } yield return(new Tuple <QuickFilterToken, string>(QuickFilterToken.Literal, previous)); } if (!string.IsNullOrEmpty(literal)) { if (thisCouldBeKeyword && string.Compare(literal, "and", StringComparison.OrdinalIgnoreCase) == 0) { yield return(new Tuple <QuickFilterToken, string>(QuickFilterToken.And, literal)); } else if (thisCouldBeKeyword && string.Compare(literal, "or", StringComparison.OrdinalIgnoreCase) == 0) { yield return(new Tuple <QuickFilterToken, string>(QuickFilterToken.Or, literal)); } else if (thisCouldBeKeyword && string.Compare(literal, "not", StringComparison.OrdinalIgnoreCase) == 0) { yield return(new Tuple <QuickFilterToken, string>(QuickFilterToken.Not, literal)); } else { yield return(new Tuple <QuickFilterToken, string>(token, literal)); } } } } if (sb.Length > 0) { yield return(new Tuple <QuickFilterToken, string>(QuickFilterToken.Literal, sb.ToString())); } }