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 }