/// <inheritdoc /> public IEnumerable <QueryToken> ParseQueryTokens(string queryText) { if (queryText is null) { throw new ArgumentNullException(nameof(queryText)); } var state = State.None; var tokenStart = (int?)null; var tolerance = 0; QueryToken?createTokenForYielding(int endIndex) { if (tokenStart != null) { var token = QueryToken.ForText(queryText.Substring(tokenStart.Value, endIndex - tokenStart.Value)); tokenStart = null; return(token); } return(null); } for (var i = 0; i < queryText.Length; i++) { var current = queryText[i]; if (char.IsWhiteSpace(current)) { var token = createTokenForYielding(i); if (token != null) { yield return(token.Value); } } else { switch (state) { case State.None: switch (current) { case '&': yield return(QueryToken.ForOperator(QueryTokenType.AndOperator)); break; case '|': yield return(QueryToken.ForOperator(QueryTokenType.OrOperator)); break; case '>': yield return(QueryToken.ForOperator(QueryTokenType.PrecedingOperator)); break; case '=': if (tokenStart == null) { throw new QueryParserException(ExceptionMessages.UnexpectedOperator, "="); } yield return(QueryToken.ForFieldFilter(queryText.Substring(tokenStart.Value, i - tokenStart.Value))); tokenStart = null; break; case ')': var token = createTokenForYielding(i); if (token != null) { yield return(token.Value); } yield return(QueryToken.ForOperator(QueryTokenType.CloseBracket)); break; case '(': yield return(QueryToken.ForOperator(QueryTokenType.OpenBracket)); break; case '~': tolerance = 0; state = State.ProcessingNearOperator; break; case '"': state = State.ProcessingString; yield return(QueryToken.ForOperator(QueryTokenType.BeginAdjacentTextOperator)); break; default: tokenStart = tokenStart ?? i; break; } break; case State.ProcessingString: switch (current) { case '"': state = State.None; var token = createTokenForYielding(i); if (token != null) { yield return(token.Value); } yield return(QueryToken.ForOperator(QueryTokenType.EndAdjacentTextOperator)); break; default: tokenStart = tokenStart ?? i; break; } break; case State.ProcessingNearOperator: switch (current) { case '>': yield return(QueryToken.ForOperatorWithTolerance(QueryTokenType.PrecedingNearOperator, tolerance)); state = State.None; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': tolerance = (tolerance * 10) + (current - '0'); break; default: yield return(QueryToken.ForOperatorWithTolerance(QueryTokenType.NearOperator, tolerance)); state = State.None; // Skip back a character to re-process it now state is None i -= 1; break; } break; } } } if (tokenStart != null) { yield return(QueryToken.ForText(queryText.Substring(tokenStart.Value, queryText.Length - tokenStart.Value))); } }