/// <summary> /// Goes through <paramref name="m"/> matches and fill <paramref name="matches"/> array with results /// according to Set Order. /// </summary> /// <param name="r"><see cref="Regex"/> that produced the match</param> /// <param name="m"><see cref="Match"/> to iterate through all matches by NextMatch() call.</param> /// <param name="matches">Array for storing results.</param> /// <param name="addOffsets">Whether or not add arrays with offsets instead of strings.</param> /// <returns>Number of full pattern matches.</returns> static int FillMatchesArrayAllSetOrder(PerlRegex.Regex r, PerlRegex.Match m, ref PhpArray matches, bool addOffsets) { // first index, increases at each match in set order int i = 0; while (m.Success) { var pa = new PhpArray(m.Groups.Count); // add all groups for (int j = 0; j < m.Groups.Count; j++) { var arr = NewArrayItem(m.Groups[j].Value, m.Groups[j].Index, addOffsets); AddGroupNameToResult(r, pa, j, (p, groupName) => { p[groupName] = arr; }); pa[j] = arr; } matches[i] = (PhpValue)pa; i++; m = m.NextMatch(); } return(i); }
/// <summary> /// Goes through <paramref name="m"/> matches and fill <paramref name="matches"/> array with results /// according to Pattern Order. /// </summary> /// <param name="r"><see cref="Regex"/> that produced the match</param> /// <param name="m"><see cref="Match"/> to iterate through all matches by NextMatch() call.</param> /// <param name="matches">Array for storing results.</param> /// <param name="addOffsets">Whether or not add arrays with offsets instead of strings.</param> /// <returns>Number of full pattern matches.</returns> static int FillMatchesArrayAllPatternOrder(PerlRegex.Regex r, PerlRegex.Match m, ref PhpArray matches, bool addOffsets) { // second index, increases at each match in pattern order int j = 0; while (m.Success) { // add all groups for (int i = 0; i < m.Groups.Count; i++) { var arr = NewArrayItem(m.Groups[i].Value, m.Groups[i].Index, addOffsets); AddGroupNameToResult(r, matches, i, (ms, groupName) => { if (j == 0) { ms[groupName] = (PhpValue) new PhpArray(); } ((PhpArray)ms[groupName])[j] = arr; }); if (j == 0) { matches[i] = (PhpValue) new PhpArray(); } ((PhpArray)matches[i])[j] = arr; } j++; m = m.NextMatch(); } return(j); }
/// <summary> /// Does a split. In the right-to-left case we reorder the /// array to be forwards. /// </summary> internal static string[] Split(Regex regex, string input, int count, int startat) { if (count < 0) { throw new ArgumentOutOfRangeException(nameof(count), SR.CountTooSmall); } if (startat < 0 || startat > input.Length) { throw new ArgumentOutOfRangeException(nameof(startat), SR.BeginIndexNotNegative); } string[] result; if (count == 1) { result = new string[1]; result[0] = input; return(result); } count -= 1; Match match = regex.Match(input, startat); if (!match.Success) { result = new string[1]; result[0] = input; return(result); } else { List <string> al = new List <string>(); if (!regex.RightToLeft) { int prevat = 0; for (; ;) { al.Add(input.Substring(prevat, match.Index - prevat)); prevat = match.Index + match.Length; // add all matched capture groups to the list. for (int i = 1; i < match.Groups.Count; i++) { if (match.IsMatched(i)) { al.Add(match.Groups[i].ToString()); } } if (--count == 0) { break; } match = match.NextMatch(); if (!match.Success) { break; } } al.Add(input.Substring(prevat, input.Length - prevat)); } else { int prevat = input.Length; for (; ;) { al.Add(input.Substring(match.Index + match.Length, prevat - match.Index - match.Length)); prevat = match.Index; // add all matched capture groups to the list. for (int i = 1; i < match.Groups.Count; i++) { if (match.IsMatched(i)) { al.Add(match.Groups[i].ToString()); } } if (--count == 0) { break; } match = match.NextMatch(); if (!match.Success) { break; } } al.Add(input.Substring(0, prevat)); al.Reverse(0, al.Count); } return(al.ToArray()); } }
/// <summary> /// Replaces all occurrences of the regex in the string with the /// replacement evaluator. /// /// Note that the special case of no matches is handled on its own: /// with no matches, the input string is returned unchanged. /// The right-to-left case is split out because StringBuilder /// doesn't handle right-to-left string building directly very well. /// </summary> internal static string Replace(MatchEvaluator evaluator, Regex regex, string input, int count, int startat, ref long replacements) { if (evaluator == null) { throw new ArgumentNullException(nameof(evaluator)); } if (count < -1) { throw new ArgumentOutOfRangeException(nameof(count), SR.CountTooSmall); } if (startat < 0 || startat > input.Length) { throw new ArgumentOutOfRangeException(nameof(startat), SR.BeginIndexNotNegative); } if (count == 0) { return(input); } Match match = regex.Match(input, startat); if (!match.Success) { return(input); } else { Span <char> charInitSpan = stackalloc char[ReplaceBufferSize]; var vsb = new ValueStringBuilder(charInitSpan); if (!regex.RightToLeft) { int prevat = 0; do { if (match.Index != prevat) { vsb.Append(input.AsSpan(prevat, match.Index - prevat)); } prevat = match.Index + match.Length; string result = evaluator(match); if (!string.IsNullOrEmpty(result)) { vsb.Append(evaluator(match)); } ++replacements; if (--count == 0) { break; } match = match.NextMatch(); } while (match.Success); if (prevat < input.Length) { vsb.Append(input.AsSpan(prevat, input.Length - prevat)); } } else { // In right to left mode append all the inputs in reversed order to avoid an extra dynamic data structure // and to be able to work with Spans. A final reverse of the transformed reversed input string generates // the desired output. Similar to Tower of Hanoi. int prevat = input.Length; do { if (match.Index + match.Length != prevat) { vsb.AppendReversed(input.AsSpan(match.Index + match.Length, prevat - match.Index - match.Length)); } prevat = match.Index; vsb.AppendReversed(evaluator(match).AsSpan()); ++replacements; if (--count == 0) { break; } match = match.NextMatch(); } while (match.Success); if (prevat > 0) { vsb.AppendReversed(input.AsSpan(0, prevat)); } vsb.Reverse(); } return(vsb.ToString()); } }