Платформа ЦРНП "Мирокод" для разработки проектов
https://git.mirocod.ru
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
177 lines
4.0 KiB
177 lines
4.0 KiB
package regexp2 |
|
|
|
import ( |
|
"bytes" |
|
"errors" |
|
|
|
"github.com/dlclark/regexp2/syntax" |
|
) |
|
|
|
const ( |
|
replaceSpecials = 4 |
|
replaceLeftPortion = -1 |
|
replaceRightPortion = -2 |
|
replaceLastGroup = -3 |
|
replaceWholeString = -4 |
|
) |
|
|
|
// MatchEvaluator is a function that takes a match and returns a replacement string to be used |
|
type MatchEvaluator func(Match) string |
|
|
|
// Three very similar algorithms appear below: replace (pattern), |
|
// replace (evaluator), and split. |
|
|
|
// Replace Replaces all occurrences of the regex in the string with the |
|
// replacement pattern. |
|
// |
|
// 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. |
|
func replace(regex *Regexp, data *syntax.ReplacerData, evaluator MatchEvaluator, input string, startAt, count int) (string, error) { |
|
if count < -1 { |
|
return "", errors.New("Count too small") |
|
} |
|
if count == 0 { |
|
return "", nil |
|
} |
|
|
|
m, err := regex.FindStringMatchStartingAt(input, startAt) |
|
|
|
if err != nil { |
|
return "", err |
|
} |
|
if m == nil { |
|
return input, nil |
|
} |
|
|
|
buf := &bytes.Buffer{} |
|
text := m.text |
|
|
|
if !regex.RightToLeft() { |
|
prevat := 0 |
|
for m != nil { |
|
if m.Index != prevat { |
|
buf.WriteString(string(text[prevat:m.Index])) |
|
} |
|
prevat = m.Index + m.Length |
|
if evaluator == nil { |
|
replacementImpl(data, buf, m) |
|
} else { |
|
buf.WriteString(evaluator(*m)) |
|
} |
|
|
|
count-- |
|
if count == 0 { |
|
break |
|
} |
|
m, err = regex.FindNextMatch(m) |
|
if err != nil { |
|
return "", nil |
|
} |
|
} |
|
|
|
if prevat < len(text) { |
|
buf.WriteString(string(text[prevat:])) |
|
} |
|
} else { |
|
prevat := len(text) |
|
var al []string |
|
|
|
for m != nil { |
|
if m.Index+m.Length != prevat { |
|
al = append(al, string(text[m.Index+m.Length:prevat])) |
|
} |
|
prevat = m.Index |
|
if evaluator == nil { |
|
replacementImplRTL(data, &al, m) |
|
} else { |
|
al = append(al, evaluator(*m)) |
|
} |
|
|
|
count-- |
|
if count == 0 { |
|
break |
|
} |
|
m, err = regex.FindNextMatch(m) |
|
if err != nil { |
|
return "", nil |
|
} |
|
} |
|
|
|
if prevat > 0 { |
|
buf.WriteString(string(text[:prevat])) |
|
} |
|
|
|
for i := len(al) - 1; i >= 0; i-- { |
|
buf.WriteString(al[i]) |
|
} |
|
} |
|
|
|
return buf.String(), nil |
|
} |
|
|
|
// Given a Match, emits into the StringBuilder the evaluated |
|
// substitution pattern. |
|
func replacementImpl(data *syntax.ReplacerData, buf *bytes.Buffer, m *Match) { |
|
for _, r := range data.Rules { |
|
|
|
if r >= 0 { // string lookup |
|
buf.WriteString(data.Strings[r]) |
|
} else if r < -replaceSpecials { // group lookup |
|
m.groupValueAppendToBuf(-replaceSpecials-1-r, buf) |
|
} else { |
|
switch -replaceSpecials - 1 - r { // special insertion patterns |
|
case replaceLeftPortion: |
|
for i := 0; i < m.Index; i++ { |
|
buf.WriteRune(m.text[i]) |
|
} |
|
case replaceRightPortion: |
|
for i := m.Index + m.Length; i < len(m.text); i++ { |
|
buf.WriteRune(m.text[i]) |
|
} |
|
case replaceLastGroup: |
|
m.groupValueAppendToBuf(m.GroupCount()-1, buf) |
|
case replaceWholeString: |
|
for i := 0; i < len(m.text); i++ { |
|
buf.WriteRune(m.text[i]) |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
func replacementImplRTL(data *syntax.ReplacerData, al *[]string, m *Match) { |
|
l := *al |
|
buf := &bytes.Buffer{} |
|
|
|
for _, r := range data.Rules { |
|
buf.Reset() |
|
if r >= 0 { // string lookup |
|
l = append(l, data.Strings[r]) |
|
} else if r < -replaceSpecials { // group lookup |
|
m.groupValueAppendToBuf(-replaceSpecials-1-r, buf) |
|
l = append(l, buf.String()) |
|
} else { |
|
switch -replaceSpecials - 1 - r { // special insertion patterns |
|
case replaceLeftPortion: |
|
for i := 0; i < m.Index; i++ { |
|
buf.WriteRune(m.text[i]) |
|
} |
|
case replaceRightPortion: |
|
for i := m.Index + m.Length; i < len(m.text); i++ { |
|
buf.WriteRune(m.text[i]) |
|
} |
|
case replaceLastGroup: |
|
m.groupValueAppendToBuf(m.GroupCount()-1, buf) |
|
case replaceWholeString: |
|
for i := 0; i < len(m.text); i++ { |
|
buf.WriteRune(m.text[i]) |
|
} |
|
} |
|
l = append(l, buf.String()) |
|
} |
|
} |
|
|
|
*al = l |
|
}
|
|
|