Skip to content
Snippets Groups Projects
Select Git revision
  • fbefdfc50ca5698cfcd47d56ac5b49630d037696
  • master default protected
  • 0.5.9
  • 0.5.8
  • 0.5.7
  • 0.5.6
  • 0.5.5
  • 0.5.4
  • 0.5.3
  • 0.5.2
  • 0.5.1
  • 0.5.0
  • 0.4.17
  • 0.4.16
  • 0.4.15
  • 0.4.14
  • 0.4.13
  • 0.4.12
  • 0.4.11
  • 0.4.10
  • 0.4.9
  • 0.4.8
22 results

replace.go

Blame
  • replace.go 3.97 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
    }