protected override bool CompareAndAdvanceImp( string str, int firstIndex, int depth, RunState runState, out int index) { if (EndComparison == null && !MaxLength.HasValue) { Parser.ThrowParseError(string.Format("Invalid combination of parameters. " + "Both the end comparison and max length values have not been specified. " + "How can we tell when the pattern match has completed?")); } index = -1; // If the minimum value is less than the length of the input string then it can never match int numCharsLeftInStr = str.Length - firstIndex; if (MinLength.HasValue && MinLength.Value > numCharsLeftInStr) { return(false); } // Sort the ranges if (!m_SortedRanges) { // A speedup by only doing it once m_SortedRanges = true; m_ComparisonRange.Sort(tComparisonRange.CompareByRange); } //// Iterate the string // The relevant range tComparisonRange range = null; // The index of the relevant range int rangeIndex = 0; // The index into the input string int i; int depthPlusOne = depth + 1; bool matched = true; for (i = firstIndex; matched && i < str.Length;) { // The current length int thisIndex1Based = i - firstIndex + 1; // Check the maximum length if (EndComparison != null) { // If the end comparison has been specified but we've exceeded the maximum length then this is not a match if (MaxLength.HasValue && thisIndex1Based > MaxLength.Value) { matched = false; } } else { // If there is no end comparison and we've reached the maximum length then this is a successful match // and stop trying to match any further if (thisIndex1Based > MaxLength.Value) { break; } } if (matched) { // Check the current range is still relevant if (range != null) { if (!range.Range.IsInRange(thisIndex1Based)) { rangeIndex += 1; range = null; } } // Find/update the appropriate range if (range == null) { for (int iRange = rangeIndex; iRange < m_ComparisonRange.Count; ++iRange) { // Is it in range? tComparisonRange iterRange = m_ComparisonRange[iRange]; if (iterRange.Range.IsInRange(thisIndex1Based)) { // Update the range in use range = iterRange; rangeIndex = iRange; break; } } if (range == null) { string text = string.Format("No range specified for character at index {0}.", i); Parser.ThrowParseError(text); } } // Do the comparison // If the comparison is not specified then simply advance by 1 if we have not reached the end. // Can be used to specify match on everything bool endMatches; if (EndComparison != null) { endMatches = EndComparison.Compare(str, i, depthPlusOne, runState); } else { endMatches = false; } if (range.Comparison != null) { int afterMatch; matched = range.Comparison.CompareAndAdvance(str, i, depthPlusOne, runState, out afterMatch); // This is a key nuance with the way this works. DO NOT STOP if both the end comparisons and range // comparisons succeed. This is a design decision to make it behave in this manner if (endMatches && !matched) { matched = true; break; } // Advance to after the successful match i = afterMatch; } else { if (endMatches) { // Stop if we found the end break; } ++i; } } } if (matched) { index = i; // Check the length int length = i - firstIndex; bool isMinimumLength = (!MinLength.HasValue || (MinLength.HasValue && length >= MinLength.Value)); matched = (isMinimumLength); } return(matched); }
abstract protected bool CompareImp( string input, int pos, int depth, RunState runState);
private string Process( string input, string replaceWith, IComparisonWithAdvance mainComparison, Dictionary <string, Capture> capturing, List <State> stateList, bool replace, out int numMatches, Func <RunState, string, int, string> ReplaceFunc = null, Action <RunState> InitRunState = null) { // The replaced string StringBuilder replaced = new StringBuilder(); var runState = new RunState(); if (InitRunState != null) { InitRunState(runState); } numMatches = 0; // Iterate the input string character by character for (int outerIndex = 0; outerIndex < input.Length;) { runState.Clear(); runState.BeginPos = outerIndex; #if DEBUG int dbgInitialOuterIndex = outerIndex; if (dbgInitialOuterIndex == 1374) { Misc.Break(); } string dbgInputPortion = input.Substring(dbgInitialOuterIndex, Math.Min(20, input.Length - dbgInitialOuterIndex)); #endif //// All states enabled? int afterState = outerIndex; bool stateEnabled; if (stateList != null) { for (; ;) { // If the state becomes enabled, check that directly afterwards we don't encounter a close state // Imagine we have a state which opens on '<%' and closes on '%>' and the input string is '<%%> ...' // We enable then instantly disable again. We only stop trying to update the state when updating the // state doesn't advance the position int curIndex = afterState; stateEnabled = UpdateState(stateList, input, afterState, runState, out afterState); if (curIndex == afterState) { // Updating the state did not advance the position - continue with the replace break; } } } else { stateEnabled = true; } // Copy the state string (if any) if (afterState != outerIndex) { string stateString = input.Substring(outerIndex, afterState - outerIndex); replaced.Append(stateString); } // Update the main index outerIndex = afterState; if (outerIndex < input.Length) { bool matched = stateEnabled; if (stateEnabled) { // Run the statements beginning from 'afterState' int innerIndex = outerIndex; int afterMatch; matched = mainComparison.CompareAndAdvance(input, innerIndex, 0, runState, out afterMatch); if (matched) { // The begin of the matching string runState.BeginPos = outerIndex; ++numMatches; // This is a match! :) // Do the replace string iterReplaced = ReplaceMatch(replaceWith, ReplaceFunc, capturing, runState, input, afterMatch); replaced.Append(iterReplaced); LogMatchReplace( input, innerIndex, afterMatch, iterReplaced, capturing); // Update the outer index and continue iterating from there outerIndex = afterMatch; } } if (!matched) { if (replace) { // Match failed: copy this character to the output string and move on to the next one (if this is replace mode) replaced.Append(input[outerIndex]); } ++outerIndex; } } } return(replaced.ToString()); }
abstract protected void PerformImp( string input, int pos, int depth, RunState runState, out int index);