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.

replace.go 4.0KB

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