github.com/ladydascalie/elvish@v0.0.0-20170703214355-2964dd3ece7f/eval/re/re.go (about) 1 package re 2 3 import ( 4 "fmt" 5 "regexp" 6 "strconv" 7 8 "github.com/elves/elvish/eval" 9 "github.com/elves/elvish/util" 10 "github.com/xiaq/persistent/vector" 11 ) 12 13 func Namespace() eval.Namespace { 14 ns := eval.Namespace{} 15 eval.AddBuiltinFns(ns, fns...) 16 return ns 17 } 18 19 var fns = []*eval.BuiltinFn{ 20 {"quote", eval.WrapStringToString(regexp.QuoteMeta)}, 21 {"match", match}, 22 {"find", find}, 23 {"replace", replace}, 24 {"split", split}, 25 } 26 27 func match(ec *eval.EvalCtx, args []eval.Value, opts map[string]eval.Value) { 28 out := ec.OutputChan() 29 var ( 30 argPattern eval.String 31 argSource eval.String 32 optPOSIX eval.Bool 33 ) 34 eval.ScanArgs(args, &argPattern, &argSource) 35 eval.ScanOpts(opts, eval.Opt{"posix", &optPOSIX, eval.Bool(false)}) 36 37 pattern := makePattern(argPattern, optPOSIX, eval.Bool(false)) 38 matched := pattern.MatchString(string(argSource)) 39 out <- eval.Bool(matched) 40 } 41 42 var ( 43 matchFields = []string{"text", "start", "end", "groups"} 44 submatchFields = []string{"text", "start", "end"} 45 ) 46 47 func find(ec *eval.EvalCtx, args []eval.Value, opts map[string]eval.Value) { 48 out := ec.OutputChan() 49 var ( 50 argPattern eval.String 51 argSource eval.String 52 optPOSIX eval.Bool 53 optLongest eval.Bool 54 optMax int 55 ) 56 eval.ScanArgs(args, &argPattern, &argSource) 57 eval.ScanOpts(opts, 58 eval.Opt{"posix", &optPOSIX, eval.Bool(false)}, 59 eval.Opt{"longest", &optLongest, eval.Bool(false)}, 60 eval.Opt{"max", &optMax, eval.String("-1")}) 61 62 pattern := makePattern(argPattern, optPOSIX, optLongest) 63 64 matches := pattern.FindAllSubmatchIndex([]byte(argSource), optMax) 65 for _, match := range matches { 66 start, end := match[0], match[1] 67 groups := vector.Empty 68 for i := 0; i < len(match); i += 2 { 69 start, end := match[i], match[i+1] 70 groups = groups.Cons(&eval.Struct{submatchFields, []eval.Variable{ 71 eval.NewRoVariable(argSource[start:end]), 72 eval.NewRoVariable(eval.String(strconv.Itoa(start))), 73 eval.NewRoVariable(eval.String(strconv.Itoa(end))), 74 }}) 75 } 76 out <- &eval.Struct{matchFields, []eval.Variable{ 77 eval.NewRoVariable(argSource[start:end]), 78 eval.NewRoVariable(eval.String(strconv.Itoa(start))), 79 eval.NewRoVariable(eval.String(strconv.Itoa(end))), 80 eval.NewRoVariable(eval.NewListFromVector(groups)), 81 }} 82 } 83 } 84 85 func replace(ec *eval.EvalCtx, args []eval.Value, opts map[string]eval.Value) { 86 out := ec.OutputChan() 87 var ( 88 argPattern eval.String 89 argRepl eval.Value 90 argSource eval.String 91 optPOSIX eval.Bool 92 optLongest eval.Bool 93 optLiteral eval.Bool 94 ) 95 eval.ScanArgs(args, &argPattern, &argRepl, &argSource) 96 eval.ScanOpts(opts, 97 eval.Opt{"posix", &optPOSIX, eval.Bool(false)}, 98 eval.Opt{"longest", &optLongest, eval.Bool(false)}, 99 eval.Opt{"literal", &optLiteral, eval.Bool(false)}) 100 101 pattern := makePattern(argPattern, optPOSIX, optLongest) 102 103 var result string 104 if optLiteral { 105 repl, ok := argRepl.(eval.String) 106 if !ok { 107 throwf("replacement must be string when literal is set, got %s", 108 argRepl.Kind()) 109 } 110 result = pattern.ReplaceAllLiteralString(string(argSource), string(repl)) 111 } else { 112 switch repl := argRepl.(type) { 113 case eval.String: 114 result = pattern.ReplaceAllString(string(argSource), string(repl)) 115 case eval.CallableValue: 116 replFunc := func(s string) string { 117 values, err := ec.PCaptureOutput(repl, 118 []eval.Value{eval.String(s)}, eval.NoOpts) 119 maybeThrow(err) 120 if len(values) != 1 { 121 throwf("replacement function must output exactly one value, got %d", len(values)) 122 } 123 output, ok := values[0].(eval.String) 124 if !ok { 125 throwf("replacement function must output one string, got %s", values[0].Kind()) 126 } 127 return string(output) 128 } 129 result = pattern.ReplaceAllStringFunc(string(argSource), replFunc) 130 default: 131 throwf("replacement must be string or function, got %s", 132 argRepl.Kind()) 133 } 134 } 135 out <- eval.String(result) 136 } 137 138 func split(ec *eval.EvalCtx, args []eval.Value, opts map[string]eval.Value) { 139 out := ec.OutputChan() 140 var ( 141 argPattern eval.String 142 argSource eval.String 143 optPOSIX eval.Bool 144 optLongest eval.Bool 145 optMax int 146 ) 147 eval.ScanArgs(args, &argPattern, &argSource) 148 eval.ScanOpts(opts, 149 eval.Opt{"posix", &optPOSIX, eval.Bool(false)}, 150 eval.Opt{"longest", &optLongest, eval.Bool(false)}, 151 eval.Opt{"max", &optMax, eval.String("-1")}) 152 153 pattern := makePattern(argPattern, optPOSIX, optLongest) 154 155 pieces := pattern.Split(string(argSource), optMax) 156 for _, piece := range pieces { 157 out <- eval.String(piece) 158 } 159 } 160 161 func makePattern(argPattern eval.String, 162 optPOSIX, optLongest eval.Bool) *regexp.Regexp { 163 164 var ( 165 pattern *regexp.Regexp 166 err error 167 ) 168 if optPOSIX { 169 pattern, err = regexp.CompilePOSIX(string(argPattern)) 170 } else { 171 pattern, err = regexp.Compile(string(argPattern)) 172 } 173 maybeThrow(err) 174 if optLongest { 175 pattern.Longest() 176 } 177 return pattern 178 } 179 180 func throwf(format string, args ...interface{}) { 181 util.Throw(fmt.Errorf(format, args...)) 182 } 183 184 func maybeThrow(err error) { 185 if err != nil { 186 util.Throw(err) 187 } 188 }