github.com/kolbycrouch/elvish@v0.14.1-0.20210614162631-215b9ac1c423/pkg/eval/builtin_fn_str.go (about) 1 package eval 2 3 import ( 4 "errors" 5 "regexp" 6 "strconv" 7 "strings" 8 9 "src.elv.sh/pkg/eval/vals" 10 "src.elv.sh/pkg/wcwidth" 11 ) 12 13 // String operations. 14 15 // ErrInputOfEawkMustBeString is thrown when eawk gets a non-string input. 16 var ErrInputOfEawkMustBeString = errors.New("input of eawk must be string") 17 18 //elvdoc:fn <s <=s ==s !=s >s >=s {#str-cmp} 19 // 20 // ```elvish 21 // <s $string... # less 22 // <=s $string... # less or equal 23 // ==s $string... # equal 24 // !=s $string... # not equal 25 // >s $string... # greater 26 // >=s $string... # greater or equal 27 // ``` 28 // 29 // String comparisons. They behave similarly to their number counterparts when 30 // given multiple arguments. Examples: 31 // 32 // ```elvish-transcript 33 // ~> >s lorem ipsum 34 // ▶ $true 35 // ~> ==s 1 1.0 36 // ▶ $false 37 // ~> >s 8 12 38 // ▶ $true 39 // ``` 40 41 //elvdoc:fn wcswidth 42 // 43 // ```elvish 44 // wcswidth $string 45 // ``` 46 // 47 // Output the width of `$string` when displayed on the terminal. Examples: 48 // 49 // ```elvish-transcript 50 // ~> wcswidth a 51 // ▶ 1 52 // ~> wcswidth lorem 53 // ▶ 5 54 // ~> wcswidth 你好,世界 55 // ▶ 10 56 // ``` 57 58 // TODO(xiaq): Document -override-wcswidth. 59 60 func init() { 61 addBuiltinFns(map[string]interface{}{ 62 "<s": func(a, b string) bool { return a < b }, 63 "<=s": func(a, b string) bool { return a <= b }, 64 "==s": func(a, b string) bool { return a == b }, 65 "!=s": func(a, b string) bool { return a != b }, 66 ">s": func(a, b string) bool { return a > b }, 67 ">=s": func(a, b string) bool { return a >= b }, 68 69 "to-string": toString, 70 71 "base": base, 72 73 "wcswidth": wcwidth.Of, 74 "-override-wcwidth": wcwidth.Override, 75 76 "eawk": eawk, 77 }) 78 } 79 80 //elvdoc:fn to-string 81 // 82 // ```elvish 83 // to-string $value... 84 // ``` 85 // 86 // Convert arguments to string values. 87 // 88 // ```elvish-transcript 89 // ~> to-string foo [a] [&k=v] 90 // ▶ foo 91 // ▶ '[a]' 92 // ▶ '[&k=v]' 93 // ``` 94 95 func toString(fm *Frame, args ...interface{}) { 96 out := fm.OutputChan() 97 for _, a := range args { 98 out <- vals.ToString(a) 99 } 100 } 101 102 //elvdoc:fn base 103 // 104 // ```elvish 105 // base $base $number... 106 // ``` 107 // 108 // Outputs a string for each `$number` written in `$base`. The `$base` must be 109 // between 2 and 36, inclusive. Examples: 110 // 111 // ```elvish-transcript 112 // ~> base 2 1 3 4 16 255 113 // ▶ 1 114 // ▶ 11 115 // ▶ 100 116 // ▶ 10000 117 // ▶ 11111111 118 // ~> base 16 1 3 4 16 255 119 // ▶ 1 120 // ▶ 3 121 // ▶ 4 122 // ▶ 10 123 // ▶ ff 124 // ``` 125 126 // ErrBadBase is thrown by the "base" builtin if the base is smaller than 2 or 127 // greater than 36. 128 var ErrBadBase = errors.New("bad base") 129 130 func base(fm *Frame, b int, nums ...int) error { 131 if b < 2 || b > 36 { 132 return ErrBadBase 133 } 134 135 out := fm.OutputChan() 136 for _, num := range nums { 137 out <- strconv.FormatInt(int64(num), b) 138 } 139 return nil 140 } 141 142 var eawkWordSep = regexp.MustCompile("[ \t]+") 143 144 //elvdoc:fn eawk 145 // 146 // ```elvish 147 // eawk $f $input-list? 148 // ``` 149 // 150 // For each input, call `$f` with the input followed by all its fields. A 151 // [`break`](./builtin.html#break) command will cause `eawk` to stop processing inputs. A 152 // [`continue`](./builtin.html#continue) command will exit $f, but is ignored by `eawk`. 153 // 154 // It should behave the same as the following functions: 155 // 156 // ```elvish 157 // fn eawk [f @rest]{ 158 // each [line]{ 159 // @fields = (re:split '[ \t]+' 160 // (re:replace '^[ \t]+|[ \t]+$' '' $line)) 161 // $f $line $@fields 162 // } $@rest 163 // } 164 // ``` 165 // 166 // This command allows you to write code very similar to `awk` scripts using 167 // anonymous functions. Example: 168 // 169 // ```elvish-transcript 170 // ~> echo ' lorem ipsum 171 // 1 2' | awk '{ print $1 }' 172 // lorem 173 // 1 174 // ~> echo ' lorem ipsum 175 // 1 2' | eawk [line a b]{ put $a } 176 // ▶ lorem 177 // ▶ 1 178 // ``` 179 180 func eawk(fm *Frame, f Callable, inputs Inputs) error { 181 broken := false 182 var err error 183 inputs(func(v interface{}) { 184 if broken { 185 return 186 } 187 line, ok := v.(string) 188 if !ok { 189 broken = true 190 err = ErrInputOfEawkMustBeString 191 return 192 } 193 args := []interface{}{line} 194 for _, field := range eawkWordSep.Split(strings.Trim(line, " \t"), -1) { 195 args = append(args, field) 196 } 197 198 newFm := fm.fork("fn of eawk") 199 // TODO: Close port 0 of newFm. 200 ex := f.Call(newFm, args, NoOpts) 201 newFm.Close() 202 203 if ex != nil { 204 switch Reason(ex) { 205 case nil, Continue: 206 // nop 207 case Break: 208 broken = true 209 default: 210 broken = true 211 err = ex 212 } 213 } 214 }) 215 return err 216 }