github.com/mem/u-root@v2.0.1-0.20181004165302-9b18b4636a33+incompatible/cmds/elvish/eval/re/re.go (about)

     1  // Package re implements the re: module for using regular expressions.
     2  package re
     3  
     4  import (
     5  	"fmt"
     6  	"regexp"
     7  
     8  	"github.com/u-root/u-root/cmds/elvish/eval"
     9  	"github.com/u-root/u-root/cmds/elvish/eval/vals"
    10  	"github.com/u-root/u-root/cmds/elvish/util"
    11  	"github.com/xiaq/persistent/vector"
    12  )
    13  
    14  var Ns = eval.NewNs().AddBuiltinFns("re:", fns)
    15  
    16  var fns = map[string]interface{}{
    17  	"quote":   regexp.QuoteMeta,
    18  	"match":   match,
    19  	"find":    find,
    20  	"replace": replace,
    21  	"split":   split,
    22  }
    23  
    24  func match(rawOpts eval.RawOptions, argPattern, source string) bool {
    25  	opts := struct{ Posix bool }{}
    26  	rawOpts.Scan(&opts)
    27  
    28  	pattern := makePattern(argPattern, opts.Posix, false)
    29  	return pattern.MatchString(source)
    30  }
    31  
    32  func find(fm *eval.Frame, rawOpts eval.RawOptions, argPattern, source string) {
    33  	out := fm.OutputChan()
    34  	opts := struct {
    35  		Posix   bool
    36  		Longest bool
    37  		Max     int
    38  	}{Max: -1}
    39  	rawOpts.Scan(&opts)
    40  
    41  	pattern := makePattern(argPattern, opts.Posix, opts.Longest)
    42  	matches := pattern.FindAllSubmatchIndex([]byte(source), opts.Max)
    43  
    44  	for _, match := range matches {
    45  		start, end := match[0], match[1]
    46  		groups := vector.Empty
    47  		for i := 0; i < len(match); i += 2 {
    48  			start, end := match[i], match[i+1]
    49  			text := ""
    50  			// FindAllSubmatchIndex may return negative indicies to indicate
    51  			// that the pattern didn't appear in the text.
    52  			if start >= 0 && end >= 0 {
    53  				text = source[start:end]
    54  			}
    55  			groups = groups.Cons(newSubmatch(text, start, end))
    56  		}
    57  		out <- newMatch(source[start:end], start, end, groups)
    58  	}
    59  }
    60  
    61  func replace(fm *eval.Frame, rawOpts eval.RawOptions, argPattern string, argRepl interface{}, source string) (string, error) {
    62  
    63  	opts := struct {
    64  		Posix   bool
    65  		Longest bool
    66  		Literal bool
    67  	}{}
    68  	rawOpts.Scan(&opts)
    69  
    70  	pattern := makePattern(argPattern, opts.Posix, opts.Longest)
    71  
    72  	if opts.Literal {
    73  		repl, ok := argRepl.(string)
    74  		if !ok {
    75  			return "", fmt.Errorf(
    76  				"replacement must be string when literal is set, got %s",
    77  				vals.Kind(argRepl))
    78  		}
    79  		return pattern.ReplaceAllLiteralString(source, repl), nil
    80  	} else {
    81  		switch repl := argRepl.(type) {
    82  		case string:
    83  			return pattern.ReplaceAllString(source, repl), nil
    84  		case eval.Callable:
    85  			replFunc := func(s string) string {
    86  				values, err := fm.CaptureOutput(repl, []interface{}{s}, eval.NoOpts)
    87  				maybeThrow(err)
    88  				if len(values) != 1 {
    89  					throwf("replacement function must output exactly one value, got %d", len(values))
    90  				}
    91  				output, ok := values[0].(string)
    92  				if !ok {
    93  					throwf("replacement function must output one string, got %s",
    94  						vals.Kind(values[0]))
    95  				}
    96  				return output
    97  			}
    98  			return pattern.ReplaceAllStringFunc(source, replFunc), nil
    99  		default:
   100  			return "", fmt.Errorf(
   101  				"replacement must be string or function, got %s",
   102  				vals.Kind(argRepl))
   103  		}
   104  	}
   105  }
   106  
   107  func split(fm *eval.Frame, rawOpts eval.RawOptions, argPattern, source string) {
   108  	out := fm.OutputChan()
   109  	opts := struct {
   110  		Posix   bool
   111  		Longest bool
   112  		Max     int
   113  	}{Max: -1}
   114  	rawOpts.Scan(&opts)
   115  
   116  	pattern := makePattern(argPattern, opts.Posix, opts.Longest)
   117  
   118  	pieces := pattern.Split(source, opts.Max)
   119  	for _, piece := range pieces {
   120  		out <- piece
   121  	}
   122  }
   123  
   124  func makePattern(argPattern string, optPOSIX, optLongest bool) *regexp.Regexp {
   125  	var (
   126  		pattern *regexp.Regexp
   127  		err     error
   128  	)
   129  	if optPOSIX {
   130  		pattern, err = regexp.CompilePOSIX(string(argPattern))
   131  	} else {
   132  		pattern, err = regexp.Compile(string(argPattern))
   133  	}
   134  	maybeThrow(err)
   135  	if optLongest {
   136  		pattern.Longest()
   137  	}
   138  	return pattern
   139  }
   140  
   141  func throwf(format string, args ...interface{}) {
   142  	util.Throw(fmt.Errorf(format, args...))
   143  }
   144  
   145  func maybeThrow(err error) {
   146  	if err != nil {
   147  		util.Throw(err)
   148  	}
   149  }