private bool IsMatchCore(string test) { Stack <Checkpoint> checkpoints = new Stack <Checkpoint>(); int currentMatcher = 0; int i = 0; //See if we can just do a name match if (_isNameOnlyMatch) { i = test.LastIndexOf('/') + 1; } while (i < test.Length) { IMatcher matcher = _matchers[currentMatcher]; //If the matcher has a minimum zero width, isn't the last matcher and produces a checkpoint, // we don't need to actually test for a match at this stage //Otherwise, test whether the matcher can consume starting at the current position if (currentMatcher < _matchers.Count - 1 && matcher.ProducesCheckpoint && matcher.MinConsume == 0 || matcher.CanConsume(test, i, out i)) { //If the current matcher isn't that last one and it produces a checkpoint, stash // the checkpoint info if (currentMatcher < _matchers.Count - 1 && matcher.ProducesCheckpoint) { checkpoints.Push(new Checkpoint(matcher, currentMatcher + 1, i)); } ++currentMatcher; if (currentMatcher < _matchers.Count) { continue; } if (matcher.ProducesCheckpoint) { if (i < test.Length) { --currentMatcher; } continue; } } if (currentMatcher == _matchers.Count && i == test.Length) { return(true); } //If the match failed or we ran out of matchers, try to revert to an earlier checkpoint to re-evaluate //If we've got checkpoints left, back up one and see if it's usable while (checkpoints.Count > 0) { Checkpoint checkpoint = checkpoints.Pop(); //If the matcher is usable... // restore the checkpoint // advance the string position // reset the current matcher to the one that followed the checkpoint if (checkpoint.Matcher.CanConsume(test, checkpoint.StringPosition, out i)) { checkpoint.StringPosition = i; currentMatcher = checkpoint.NextMatcherIndex; checkpoints.Push(checkpoint); break; } } //If we ran out of checkpoints, the match has failed if (checkpoints.Count == 0) { return(false); } } return(i == test.Length && currentMatcher == _matchers.Count); }