|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- 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
- }
|