private static ParameterPolicyParseResults ParseConstraints( string text, int currentIndex, int endIndex) { var constraints = new ArrayBuilder <RoutePatternParameterPolicyReference>(0); var state = ParseState.Start; var startIndex = currentIndex; do { var currentChar = currentIndex > endIndex ? null : (char?)text[currentIndex]; switch (state) { case ParseState.Start: switch (currentChar) { case null: state = ParseState.End; break; case ':': state = ParseState.ParsingName; startIndex = currentIndex + 1; break; case '(': state = ParseState.InsideParenthesis; break; case '=': state = ParseState.End; currentIndex--; break; } break; case ParseState.InsideParenthesis: switch (currentChar) { case null: state = ParseState.End; var constraintText = text.Substring(startIndex, currentIndex - startIndex); constraints.Add(RoutePatternFactory.ParameterPolicy(constraintText)); break; case ')': // Only consume a ')' token if // (a) it is the last token // (b) the next character is the start of the new constraint ':' // (c) the next character is the start of the default value. var nextChar = currentIndex + 1 > endIndex ? null : (char?)text[currentIndex + 1]; switch (nextChar) { case null: state = ParseState.End; constraintText = text.Substring(startIndex, currentIndex - startIndex + 1); constraints.Add(RoutePatternFactory.ParameterPolicy(constraintText)); break; case ':': state = ParseState.Start; constraintText = text.Substring(startIndex, currentIndex - startIndex + 1); constraints.Add(RoutePatternFactory.ParameterPolicy(constraintText)); startIndex = currentIndex + 1; break; case '=': state = ParseState.End; constraintText = text.Substring(startIndex, currentIndex - startIndex + 1); constraints.Add(RoutePatternFactory.ParameterPolicy(constraintText)); break; } break; case ':': case '=': // In the original implementation, the Regex would've backtracked if it encountered an // unbalanced opening bracket followed by (not necessarily immediatiely) a delimiter. // Simply verifying that the parantheses will eventually be closed should suffice to // determine if the terminator needs to be consumed as part of the current constraint // specification. var indexOfClosingParantheses = text.IndexOf(')', currentIndex + 1); if (indexOfClosingParantheses == -1) { constraintText = text.Substring(startIndex, currentIndex - startIndex); constraints.Add(RoutePatternFactory.ParameterPolicy(constraintText)); if (currentChar == ':') { state = ParseState.ParsingName; startIndex = currentIndex + 1; } else { state = ParseState.End; currentIndex--; } } else { currentIndex = indexOfClosingParantheses; } break; } break; case ParseState.ParsingName: switch (currentChar) { case null: state = ParseState.End; var constraintText = text.Substring(startIndex, currentIndex - startIndex); if (constraintText.Length > 0) { constraints.Add(RoutePatternFactory.ParameterPolicy(constraintText)); } break; case ':': constraintText = text.Substring(startIndex, currentIndex - startIndex); if (constraintText.Length > 0) { constraints.Add(RoutePatternFactory.ParameterPolicy(constraintText)); } startIndex = currentIndex + 1; break; case '(': state = ParseState.InsideParenthesis; break; case '=': state = ParseState.End; constraintText = text.Substring(startIndex, currentIndex - startIndex); if (constraintText.Length > 0) { constraints.Add(RoutePatternFactory.ParameterPolicy(constraintText)); } currentIndex--; break; } break; } currentIndex++; } while (state != ParseState.End); return(new ParameterPolicyParseResults(currentIndex, constraints.ToArray())); }