static int MatchGroup(string text, int groupStartIndex, int endIndex, ICollection <QueryError> errors, out StringView sv, out Match match, out bool matched) { sv = text.GetStringView(); match = null; if (groupStartIndex >= text.Length || text[groupStartIndex] != '(') { matched = false; return(-1); } matched = true; if (groupStartIndex < 0 || groupStartIndex >= text.Length) { errors.Add(new QueryError { index = 0, reason = $"A group should have been found but index was {groupStartIndex}" }); return(-1); } var parenthesisCounter = 1; var groupEndIndex = groupStartIndex + 1; for (; groupEndIndex < text.Length && parenthesisCounter > 0; ++groupEndIndex) { if (text[groupEndIndex] == '(') { ++parenthesisCounter; } else if (text[groupEndIndex] == ')') { --parenthesisCounter; } } // Because of the final ++groupEndIndex, decrement the index --groupEndIndex; var charConsumed = groupEndIndex - groupStartIndex + 1; if (parenthesisCounter != 0) { errors.Add(new QueryError { index = groupStartIndex, length = 1, reason = $"Unbalanced parentheses" }); return(-1); } sv = text.GetStringView(groupStartIndex + 1, groupStartIndex + charConsumed - 1); if (sv.IsNullOrWhiteSpace()) { errors.Add(new QueryError { index = groupStartIndex, length = charConsumed, reason = $"Empty group" }); return(-1); } return(charConsumed); }