/// <summary> /// partial, semi-ordered tree matching algorithm. /// /// iterate over target string, no backtracking /// maintain data about matches in series match: /// indexes which are valid branched matches for the current index in target /// record of which leaves in the matcher have been matched /// </summary> /// <param name="indexInSymbolTarget"></param> /// <param name="seriesMatch"></param> /// <returns>a mapping from all symbols in seriesMatch back into the target string</returns> public bool MatchesForward( NativeMultipleHashSets.HashSetSlice includeSymbolsSet, int indexInSymbolTarget, SymbolSeriesSuffixMatcher seriesMatch, SymbolString <float> symbolString, int firstParameterCopyIndex, NativeArray <float> parameterCopyMemory, out byte paramsCopiedToMem, TmpNativeStack <BranchEventData> helperStack ) { if (!seriesMatch.HasGraphIndexes) { // this should be done in the parsing/compiling phase. should only have to happen once for the whole system, per matching rule. throw new System.Exception("graph indexes should be precomputed"); //seriesMatch.ComputeGraphIndexes(branchOpenSymbol, branchCloseSymbol); } // keep count of how many more matches are required at each level of the tree. // starts out as a copy of the child count array. each leaf will be at 0, and will go negative when matched. //var remainingMatchesAtIndexes = seriesMatch.childrenCounts.Clone() as int[]; return(MatchesForwardsAtIndexOrderingInvariant( includeSymbolsSet, indexInSymbolTarget, seriesMatch, symbolString, firstParameterCopyIndex, parameterCopyMemory, out paramsCopiedToMem, helperStack)); }
/// <summary> /// /// </summary> /// <param name="indexInSymbolTarget"></param> /// <param name="seriesMatch"></param> /// <returns>whether the match succeeded or not</returns> public bool MatchesBackwards( NativeMultipleHashSets.HashSetSlice includedSymbolSet, int indexInSymbolTarget, SymbolSeriesPrefixMatcher seriesMatch, SymbolString <float> symbolString, int firstParameterCopyIndex, NativeArray <float> parameterCopyMemory, out byte paramsCopiedToMem) { indexInSymbolTarget--; int matchingIndex = seriesMatch.graphNodeMemSpace.length - 1; paramsCopiedToMem = 0; for (; matchingIndex >= 0 && indexInSymbolTarget >= 0;) { var symbolToMatch = nativeRuleData.prefixMatcherSymbols[matchingIndex + seriesMatch.graphNodeMemSpace.index]; while (indexInSymbolTarget >= 0) { var currentSymbol = symbolString.symbols[indexInSymbolTarget]; if (!includedSymbolSet.Contains(currentSymbol) || currentSymbol == branchOpenSymbol) { indexInSymbolTarget--; } else if (currentSymbol == branchCloseSymbol) { indexInSymbolTarget = FindOpeningBranchIndexReadonly(indexInSymbolTarget) - 1; } else if (currentSymbol == symbolToMatch.targetSymbol && symbolToMatch.parameterLength == symbolString.parameters[indexInSymbolTarget].length) { // copy the parameters in reverse order, so they can be reversed in-place at end // on success var paramIndexing = symbolString.parameters[indexInSymbolTarget]; for (int i = paramIndexing.length - 1; i >= 0; i--) { parameterCopyMemory[paramsCopiedToMem + firstParameterCopyIndex] = symbolString.parameters[paramIndexing, i]; paramsCopiedToMem++; } indexInSymbolTarget--; matchingIndex--; break; } else { return(false); } } } if (matchingIndex == -1) { ReverseRange(parameterCopyMemory, firstParameterCopyIndex, paramsCopiedToMem); return(true); } return(false); }
/// <summary> /// check for a match, enforcing the same ordering in the target match as defined in the matching pattern. /// </summary> /// <param name="originIndexInTarget"></param> /// <param name="seriesMatch"></param> /// <param name="consumedTargetIndexes"></param> /// <returns></returns> private bool MatchesForwardsAtIndexOrderingInvariant( NativeMultipleHashSets.HashSetSlice includeSymbolSet, int originIndexInTarget, SymbolSeriesSuffixMatcher seriesMatch, SymbolString <float> symbolString, int firstParameterCopyIndex, NativeArray <float> parameterCopyMemory, out byte paramsCopiedToMem, TmpNativeStack <BranchEventData> helperStack ) { helperStack.Reset(); var targetParentIndexStack = helperStack;// new TmpNativeStack<BranchEventData>(5);// new Stack<BranchEventData>(); int currentParentIndexInTarget = originIndexInTarget; var targetIndexesToMatchIndexes = new NativeHashMap <int, int>(seriesMatch.graphNodeMemSpace.length, Allocator.Temp);// new Dictionary<int, int>(); paramsCopiedToMem = 0; var indexInMatchDFSState = seriesMatch.GetImmutableDepthFirstIterationState(nativeRuleData); targetIndexesToMatchIndexes.Add(originIndexInTarget, indexInMatchDFSState.currentIndex); if (!indexInMatchDFSState.Next(out indexInMatchDFSState)) { return(true); // if the match is empty, automatically matches. //return targetIndexesToMatchIndexes; } for (int indexInTarget = originIndexInTarget + 1; indexInTarget < symbolString.Length; indexInTarget++) { var targetSymbol = symbolString[indexInTarget]; if (!includeSymbolSet.Contains(targetSymbol)) { continue; } if (targetSymbol == branchOpenSymbol) { targetParentIndexStack.Push(new BranchEventData { currentParentIndex = currentParentIndexInTarget, openBranchSymbolIndex = indexInTarget, paramsCopiedAtThisPoint = paramsCopiedToMem }); } else if (targetSymbol == branchCloseSymbol) { // will encounter a close symbol in one of two cases: // 1. the branch in target has exactly matched the branch in the matcher, and we should just step down // 2. the branch in target has terminated early, meaning we must step down the branch chain and also // reverse the matcher DFS back to a common ancenstor if (targetParentIndexStack.Count <= 0) { // if we encounter the end of the branch which contains the origin index before full match, fail. return(false); } var lastBranch = targetParentIndexStack.Pop(); currentParentIndexInTarget = lastBranch.currentParentIndex; //paramsCopiedToMem = lastBranch.paramsCopiedAtThisPoint; var parentInMatch = targetIndexesToMatchIndexes[currentParentIndexInTarget]; var parentOfSearchState = indexInMatchDFSState.GetParentIndex(); if (parentInMatch != parentOfSearchState) { // if the parents dont match, that means that the algo will be stepping backwards to the last branch sybmol. paramsCopiedToMem = lastBranch.paramsCopiedAtThisPoint; } } else { // reverse the DFS in matcher, back to the last point which shares a parent with the current parent // this acts to ensure the entry to the match has a shared parent, if at all possible. // the reversal is necessary when a branching structure failed to match in the last step var parentInMatch = targetIndexesToMatchIndexes[currentParentIndexInTarget]; if (indexInMatchDFSState.FindPreviousWithParent(out var reversedMatchIndex, parentInMatch)) { indexInMatchDFSState = reversedMatchIndex; } var indexInMatch = indexInMatchDFSState.currentIndex; var currentTargetMatchesMatcher = TargetSymbolMatchesAndParentMatches( seriesMatch, targetIndexesToMatchIndexes, currentParentIndexInTarget, indexInTarget, indexInMatch, symbolString); if (currentTargetMatchesMatcher) { targetIndexesToMatchIndexes.Add(indexInTarget, indexInMatch); var paramsToCopy = symbolString.parameters[indexInTarget]; for (int paramIndex = 0; paramIndex < paramsToCopy.length; paramIndex++) { parameterCopyMemory[firstParameterCopyIndex + paramsCopiedToMem] = symbolString.parameters[paramsToCopy, paramIndex]; paramsCopiedToMem++; } currentParentIndexInTarget = indexInTarget; // series continuation includes implicit parenting if (!indexInMatchDFSState.Next(out indexInMatchDFSState)) { return(true); } } else { // symbol in target isn't a valid match, so no further symbols in the current target branching structure can match. // rewind back to the previous branching symbol, and skip this whole structure. // Or if we're not in a nested structure, fail. if (targetParentIndexStack.Count <= 0) { return(false); } var lastBranch = targetParentIndexStack.Pop(); currentParentIndexInTarget = lastBranch.currentParentIndex; //paramsCopiedToMem = lastBranch.paramsCopiedAtThisPoint; indexInTarget = FindClosingBranchIndexReadonly(lastBranch.openBranchSymbolIndex); var parentInMatch1 = targetIndexesToMatchIndexes[currentParentIndexInTarget]; var parentOfSearchState = indexInMatchDFSState.GetParentIndex(); if (parentInMatch1 != parentOfSearchState) { // if the parents dont match, that means that the algo will be stepping backwards to the last branch sybmol on the next update. paramsCopiedToMem = lastBranch.paramsCopiedAtThisPoint; } } } } return(false); }