github.com/mem/u-root@v2.0.1-0.20181004165302-9b18b4636a33+incompatible/cmds/elvish/eval/builtin_fn_str.go (about) 1 package eval 2 3 import ( 4 "bytes" 5 "errors" 6 "regexp" 7 "strconv" 8 "strings" 9 10 "github.com/u-root/u-root/cmds/elvish/eval/vals" 11 "github.com/u-root/u-root/cmds/elvish/util" 12 ) 13 14 // String operations. 15 16 var ErrInput = errors.New("input error") 17 18 func init() { 19 addBuiltinFns(map[string]interface{}{ 20 "<s": func(a, b string) bool { return a < b }, 21 "<=s": func(a, b string) bool { return a <= b }, 22 "==s": func(a, b string) bool { return a == b }, 23 "!=s": func(a, b string) bool { return a != b }, 24 ">s": func(a, b string) bool { return a > b }, 25 ">=s": func(a, b string) bool { return a >= b }, 26 27 "to-string": toString, 28 29 "ord": ord, 30 "base": base, 31 32 "wcswidth": util.Wcswidth, 33 "-override-wcwidth": util.OverrideWcwidth, 34 35 "has-prefix": strings.HasPrefix, 36 "has-suffix": strings.HasSuffix, 37 38 "joins": joins, 39 "splits": splits, 40 "replaces": replaces, 41 42 "eawk": eawk, 43 }) 44 } 45 46 // toString converts all arguments to strings. 47 func toString(fm *Frame, args ...interface{}) { 48 out := fm.OutputChan() 49 for _, a := range args { 50 out <- vals.ToString(a) 51 } 52 } 53 54 // joins joins all input strings with a delimiter. 55 func joins(sep string, inputs Inputs) string { 56 var buf bytes.Buffer 57 first := true 58 inputs(func(v interface{}) { 59 if s, ok := v.(string); ok { 60 if first { 61 first = false 62 } else { 63 buf.WriteString(sep) 64 } 65 buf.WriteString(s) 66 } else { 67 throwf("join wants string input, got %s", vals.Kind(v)) 68 } 69 }) 70 return buf.String() 71 } 72 73 // splits splits an argument strings by a delimiter and writes all pieces. 74 func splits(fm *Frame, rawOpts RawOptions, sep, s string) { 75 opts := struct{ Max int }{-1} 76 rawOpts.Scan(&opts) 77 78 out := fm.ports[1].Chan 79 parts := strings.SplitN(s, sep, opts.Max) 80 for _, p := range parts { 81 out <- p 82 } 83 } 84 85 func replaces(rawOpts RawOptions, old, repl, s string) string { 86 opts := struct{ Max int }{-1} 87 rawOpts.Scan(&opts) 88 return strings.Replace(s, old, repl, opts.Max) 89 } 90 91 func ord(fm *Frame, s string) { 92 out := fm.ports[1].Chan 93 for _, r := range s { 94 out <- "0x" + strconv.FormatInt(int64(r), 16) 95 } 96 } 97 98 // ErrBadBase is thrown by the "base" builtin if the base is smaller than 2 or 99 // greater than 36. 100 var ErrBadBase = errors.New("bad base") 101 102 func base(fm *Frame, b int, nums ...int) error { 103 if b < 2 || b > 36 { 104 return ErrBadBase 105 } 106 107 out := fm.ports[1].Chan 108 for _, num := range nums { 109 out <- strconv.FormatInt(int64(num), b) 110 } 111 return nil 112 } 113 114 var eawkWordSep = regexp.MustCompile("[ \t]+") 115 116 // eawk takes a function. For each line in the input stream, it calls the 117 // function with the line and the words in the line. The words are found by 118 // stripping the line and splitting the line by whitespaces. The function may 119 // call break and continue. Overall this provides a similar functionality to 120 // awk, hence the name. 121 func eawk(fm *Frame, f Callable, inputs Inputs) error { 122 broken := false 123 var err error 124 inputs(func(v interface{}) { 125 if broken { 126 return 127 } 128 line, ok := v.(string) 129 if !ok { 130 broken = true 131 err = ErrInput 132 return 133 } 134 args := []interface{}{line} 135 for _, field := range eawkWordSep.Split(strings.Trim(line, " \t"), -1) { 136 args = append(args, field) 137 } 138 139 newFm := fm.fork("fn of eawk") 140 // TODO: Close port 0 of newFm. 141 ex := newFm.Call(f, args, NoOpts) 142 newFm.Close() 143 144 if ex != nil { 145 switch ex.(*Exception).Cause { 146 case nil, Continue: 147 // nop 148 case Break: 149 broken = true 150 default: 151 broken = true 152 err = ex 153 } 154 } 155 }) 156 return err 157 }