...

Source file src/github.com/dlclark/regexp2/replace.go

Documentation: github.com/dlclark/regexp2

     1  package regexp2
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  
     7  	"github.com/dlclark/regexp2/syntax"
     8  )
     9  
    10  const (
    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
    19  type 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.
    31  func 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.
   116  func 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  
   144  func 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  

View as plain text