public override string Execute(string input, TextWriter output) { Matches = new List <MatchContext>(); Separators = new List <MatchContext>(); // We need to restore this if InputAsRegex is used. var tempRegexSources = RegexSources.ToList(); if (Config.InputAsRegex) { // Swap input with first regex. There should be only one regex anyway. RegexSources[0] = input; input = tempRegexSources[0]; } switch (Config.Anchoring) { case Anchoring.String: RegexSources = RegexSources.ConvertAll(s => WrapRegex(@"\A", s, @"\z")); break; case Anchoring.Line: RegexSources = RegexSources.ConvertAll(s => WrapRegex(@"(?m:^)", s, @"(?m:$)")); break; } Regices = RegexSources.ConvertAll(source => new Regex(source, Config.RegexOptions)); FindAllMatches(input); LimitMatches(); if (Config.RegexOptions.HasFlag(RegexOptions.RightToLeft)) { Matches.Reverse(); } FillSeparators(input); if (Config.InvertMatches) { InvertMatches(input); } // Apply the corresponding substitution to each match. for (int i = 0; i < Matches.Count; ++i) { Matches[i].Replacement = Matches[i].Replacer.Process(input, Matches, Separators, i); } string result = Process(input, output); History.RegisterResult(HistoryIndex, result); // Restore the regex sources, in case we're using InputAsRegex RegexSources = tempRegexSources; return(result); }
// Returns a pair of match and the corresponding substitution string. private MatchContext GetMatch(string input, int startPos, int length = -1) { if (Regices.Count > 1 && Config.Greedy) { // Try all matches, pick the one with the earliest match position. List <Regex> patterns; if (length < 0) { patterns = Regices; } else { if (!Config.RegexOptions.HasFlag(RegexOptions.RightToLeft)) { patterns = RegexSources.ConvertAll(s => new Regex(WrapRegex( String.Format("\\G(?=(?s:.{{{0}}}(?<_suffix>.*)))", length), s, @"(?=\k<_suffix>(?<-_suffix>)\z)" ), Config.RegexOptions)); } else { patterns = RegexSources.ConvertAll(s => new Regex(WrapRegex( @"(?<=\A(?<-_suffix>)\k<_suffix>)", s, String.Format("(?<=(?s:(?<_suffix>.*).{{{0}}}))\\G", length) ), Config.RegexOptions)); } } var matches = new List <MatchContext>(); for (int i = 0; i < patterns.Count; ++i) { matches.Add(new MatchContext( patterns[i].Match(input, startPos), patterns[i], Replacers[i] )); } // For RTL matching, we have to pick the right-most match. if (!Config.RegexOptions.HasFlag(RegexOptions.RightToLeft)) { return(matches.Aggregate((curMin, m) => (!curMin.Match.Success || m.Match.Success && m.Match.Index < curMin.Match.Index) ? m : curMin )); } else { return(matches.Aggregate((curMin, m) => (!curMin.Match.Success || m.Match.Success && m.Match.Index + m.Match.Length > curMin.Match.Index + curMin.Match.Length) ? m : curMin )); } } else { // Cycle through patterns Replacer replacer = Replacers[PatternIndex]; if (length < 0) { // No length constraint, use regular pattern Regex pattern = Regices[PatternIndex]; Match m = pattern.Match(input, startPos); if (m.Success) { ++PatternIndex; PatternIndex %= Regices.Count; } return(new MatchContext(m, pattern, replacer)); } else { // Length is constrained, create length-specific pattern. // This also constrains the starting position of the match // exactly instead of giving a lower bound. string patternString = RegexSources[PatternIndex]; // TODO: Compile the pattern first to find a guaranteed unused group name. // However, this technique still messes with group numbers of explicitly // named groups. if (!Config.RegexOptions.HasFlag(RegexOptions.RightToLeft)) { patternString = WrapRegex( String.Format("\\G(?=(?s:.{{{0}}}(?<_suffix>.*)))", length), patternString, @"(?=\k<_suffix>(?<-_suffix>)\z)" ); } else { patternString = WrapRegex( @"(?<=\A(?<-_suffix>)\k<_suffix>)", patternString, String.Format("(?<=(?s:(?<_suffix>.*).{{{0}}}))\\G", length) ); } Regex pattern = new Regex(patternString, Config.RegexOptions); Match m = pattern.Match(input, startPos); if (m.Success) { ++PatternIndex; PatternIndex %= Regices.Count; } return(new MatchContext(m, pattern, replacer)); } } }