1package regexp2 2 3import ( 4 "bytes" 5 "errors" 6 7 "github.com/dlclark/regexp2/syntax" 8) 9 10const ( 11 replaceSpecials = 4 12 replaceLeftPortion = -1 13 replaceRightPortion = -2 14 replaceLastGroup = -3 15 replaceWholeString = -4 16) 17 18// MatchEvaluator is a function that takes a match and returns a replacement string to be used 19type MatchEvaluator func(Match) string 20 21// Three very similar algorithms appear below: replace (pattern), 22// replace (evaluator), and split. 23 24// Replace Replaces all occurrences of the regex in the string with the 25// replacement pattern. 26// 27// Note that the special case of no matches is handled on its own: 28// with no matches, the input string is returned unchanged. 29// The right-to-left case is split out because StringBuilder 30// doesn't handle right-to-left string building directly very well. 31func replace(regex *Regexp, data *syntax.ReplacerData, evaluator MatchEvaluator, input string, startAt, count int) (string, error) { 32 if count < -1 { 33 return "", errors.New("Count too small") 34 } 35 if count == 0 { 36 return "", nil 37 } 38 39 m, err := regex.FindStringMatchStartingAt(input, startAt) 40 41 if err != nil { 42 return "", err 43 } 44 if m == nil { 45 return input, nil 46 } 47 48 buf := &bytes.Buffer{} 49 text := m.text 50 51 if !regex.RightToLeft() { 52 prevat := 0 53 for m != nil { 54 if m.Index != prevat { 55 buf.WriteString(string(text[prevat:m.Index])) 56 } 57 prevat = m.Index + m.Length 58 if evaluator == nil { 59 replacementImpl(data, buf, m) 60 } else { 61 buf.WriteString(evaluator(*m)) 62 } 63 64 count-- 65 if count == 0 { 66 break 67 } 68 m, err = regex.FindNextMatch(m) 69 if err != nil { 70 return "", nil 71 } 72 } 73 74 if prevat < len(text) { 75 buf.WriteString(string(text[prevat:])) 76 } 77 } else { 78 prevat := len(text) 79 var al []string 80 81 for m != nil { 82 if m.Index+m.Length != prevat { 83 al = append(al, string(text[m.Index+m.Length:prevat])) 84 } 85 prevat = m.Index 86 if evaluator == nil { 87 replacementImplRTL(data, &al, m) 88 } else { 89 al = append(al, evaluator(*m)) 90 } 91 92 count-- 93 if count == 0 { 94 break 95 } 96 m, err = regex.FindNextMatch(m) 97 if err != nil { 98 return "", nil 99 } 100 } 101 102 if prevat > 0 { 103 buf.WriteString(string(text[:prevat])) 104 } 105 106 for i := len(al) - 1; i >= 0; i-- { 107 buf.WriteString(al[i]) 108 } 109 } 110 111 return buf.String(), nil 112} 113 114// Given a Match, emits into the StringBuilder the evaluated 115// substitution pattern. 116func replacementImpl(data *syntax.ReplacerData, buf *bytes.Buffer, m *Match) { 117 for _, r := range data.Rules { 118 119 if r >= 0 { // string lookup 120 buf.WriteString(data.Strings[r]) 121 } else if r < -replaceSpecials { // group lookup 122 m.groupValueAppendToBuf(-replaceSpecials-1-r, buf) 123 } else { 124 switch -replaceSpecials - 1 - r { // special insertion patterns 125 case replaceLeftPortion: 126 for i := 0; i < m.Index; i++ { 127 buf.WriteRune(m.text[i]) 128 } 129 case replaceRightPortion: 130 for i := m.Index + m.Length; i < len(m.text); i++ { 131 buf.WriteRune(m.text[i]) 132 } 133 case replaceLastGroup: 134 m.groupValueAppendToBuf(m.GroupCount()-1, buf) 135 case replaceWholeString: 136 for i := 0; i < len(m.text); i++ { 137 buf.WriteRune(m.text[i]) 138 } 139 } 140 } 141 } 142} 143 144func replacementImplRTL(data *syntax.ReplacerData, al *[]string, m *Match) { 145 l := *al 146 buf := &bytes.Buffer{} 147 148 for _, r := range data.Rules { 149 buf.Reset() 150 if r >= 0 { // string lookup 151 l = append(l, data.Strings[r]) 152 } else if r < -replaceSpecials { // group lookup 153 m.groupValueAppendToBuf(-replaceSpecials-1-r, buf) 154 l = append(l, buf.String()) 155 } else { 156 switch -replaceSpecials - 1 - r { // special insertion patterns 157 case replaceLeftPortion: 158 for i := 0; i < m.Index; i++ { 159 buf.WriteRune(m.text[i]) 160 } 161 case replaceRightPortion: 162 for i := m.Index + m.Length; i < len(m.text); i++ { 163 buf.WriteRune(m.text[i]) 164 } 165 case replaceLastGroup: 166 m.groupValueAppendToBuf(m.GroupCount()-1, buf) 167 case replaceWholeString: 168 for i := 0; i < len(m.text); i++ { 169 buf.WriteRune(m.text[i]) 170 } 171 } 172 l = append(l, buf.String()) 173 } 174 } 175 176 *al = l 177} 178