private TUpInfo Traverse([NotNull] TItem tail, [NotNull] Dictionary <TItem, TDependency[]> incidentDependencies, IBeforeDependencyGraphkenState <TItem, TDependency, ItemMatch, DependencyMatch> beforeDependencyState, TDownInfo rawDown, HashSet <TPathState> statesOnPath) { if (!beforeDependencyState.CanContinue) { throw new ArgumentException("Traverse must be called with continueable state", nameof(beforeDependencyState)); } _checkAbort(); TUpInfo upSum; bool visitSuccessors = ShouldVisitSuccessors(tail, _currentPath, out upSum); if (visitSuccessors) { // We are at this item for the first time - check whether we find a path to some defined end TDependency[] dependencies; if (!incidentDependencies.TryGetValue(tail, out dependencies)) { dependencies = NO_DEPENDENCIES; } foreach (var nextDep in dependencies) { bool dependencyIsCounted; IBeforeItemGraphkenState <TItem, TDependency, ItemMatch, DependencyMatch> beforeItemState = beforeDependencyState.Advance(nextDep, (m, d) => m.IsMatch(d), out dependencyIsCounted); if (beforeItemState.CanContinue) { TItem nextTail = nextDep.UsedItem; bool atEnd; bool itemIsCounted; IBeforeDependencyGraphkenState <TItem, TDependency, ItemMatch, DependencyMatch> beforeNextDependencyState = beforeItemState.Advance(nextTail, (m, i) => ItemMatch.IsMatch(m, i), out atEnd, out itemIsCounted); _currentPath.Push(nextDep); CountedEnum counted = dependencyIsCounted ? CountedEnum.DependencyCounted : itemIsCounted ? CountedEnum.UsedItemCounted : CountedEnum.NotCounted; var stateElement = CreateStateElement(beforeNextDependencyState, nextDep); bool newOnPath = statesOnPath.Add(stateElement); DownAndHere downAndHere = AfterPushDependency(_currentPath, atEnd, !newOnPath, counted, rawDown); TUpInfo childUp; if (_currentPath.Count < _maxRecursionDepth && beforeNextDependencyState.CanContinue && newOnPath) { childUp = Traverse(nextTail, incidentDependencies, beforeNextDependencyState, downAndHere.Down, statesOnPath); statesOnPath.Remove(stateElement); } else { childUp = default(TUpInfo); // ??? as above ____ } upSum = BeforePopDependency(_currentPath, atEnd, !newOnPath, counted, rawDown, downAndHere.Save, upSum, childUp); _currentPath.Pop(); } } } upSum = AfterVisitingSuccessors(visitSuccessors, tail, _currentPath, upSum); return(upSum); }
private TUpInfo Traverse([NotNull] TItem tail, [NotNull] Dictionary <TItem, TDependency[]> incidentDependencies, [NotNull, ItemCanBeNull] AbstractPathMatch <TDependency, TItem>[] expectedInnerPathMatches, int expectedPathMatchIndex, AbstractPathMatch <TDependency, TItem> endMatch, bool parentIsEnd, TDownInfo rawDown) { _checkAbort(); TUpInfo upSum; bool visitSuccessors = ShouldVisitSuccessors(tail, _currentPath, expectedPathMatchIndex, out upSum); if (parentIsEnd && endMatch != null && !endMatch.MultipleOccurrencesAllowed) { // If the end match matched, and the end match may occur only once, we do not visit the children } else { if (visitSuccessors) { // We are at this item for the first time - check whether we find a path to some defined end TDependency[] dependencies; if (!incidentDependencies.TryGetValue(tail, out dependencies)) { dependencies = NO_DEPENDENCIES; } foreach (var nextDep in dependencies) { TItem nextTail = nextDep.UsedItem; int newExpectedPathMatchIndex = expectedPathMatchIndex; bool mayContinue = true; AbstractPathMatch <TDependency, TItem> pathMatchOrNull = null; if (newExpectedPathMatchIndex < expectedInnerPathMatches.Length) { AbstractPathMatch <TDependency, TItem> m = expectedInnerPathMatches[newExpectedPathMatchIndex]; PathMatchResult pathResult = m.Match(nextDep, nextTail); switch (pathResult) { case PathMatchResult.Match: newExpectedPathMatchIndex++; pathMatchOrNull = m; break; case PathMatchResult.Stop: mayContinue = false; break; case PathMatchResult.Continue: break; default: throw new ArgumentOutOfRangeException(); } } bool isEnd; if (newExpectedPathMatchIndex >= expectedInnerPathMatches.Length) { // We are at or behind the path match end; if the current item or dependency matches, we have a real path end! // Check whether we are really at an end. // If no end match was provided (i.e., only a start pattern given), all items are accepted as end items. // Otherwise, we check whether the last item or dependency matches the end match. isEnd = endMatch == null || endMatch.IsMatch(nextDep, nextTail); } else { isEnd = false; } if (!isEnd && newExpectedPathMatchIndex == expectedPathMatchIndex) { // Check that no "used up" non-multiple-occurrence positive path // match matches - but only if we did not find a path match exactly here, // and we are not at the end (which is also "finding a match", namely // the endMatch). // This, I hope & believe, captures what one expects to hold implicitly: // "No loop backs" to previous positive single patterns mayContinue &= !expectedInnerPathMatches .Take(expectedPathMatchIndex) .Where(m => m != null && m.MayContinue && !m.MultipleOccurrencesAllowed) .Any(m => m.IsMatch(nextDep, nextTail)); } if (mayContinue) { _currentPath.Push(nextDep); DownAndHere downAndHere = AfterPushDependency(_currentPath, expectedPathMatchIndex, pathMatchOrNull, isEnd, rawDown); TUpInfo childUp = Traverse(nextTail, incidentDependencies, expectedInnerPathMatches, newExpectedPathMatchIndex, endMatch, isEnd, downAndHere.Down); upSum = BeforePopDependency(_currentPath, expectedPathMatchIndex, pathMatchOrNull, isEnd, downAndHere.Save, upSum, childUp); _currentPath.Pop(); } } } } upSum = AfterVisitingSuccessors(visitSuccessors, tail, _currentPath, expectedPathMatchIndex, upSum); return(upSum); }