github.com/markusbkk/elvish@v0.0.0-20231204143114-91dc52438621/pkg/eval/builtin_fn_str.go (about) 1 package eval 2 3 import ( 4 "errors" 5 "regexp" 6 "strconv" 7 "strings" 8 9 "github.com/markusbkk/elvish/pkg/eval/vals" 10 "github.com/markusbkk/elvish/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{}) error { 96 out := fm.ValueOutput() 97 for _, a := range args { 98 err := out.Put(vals.ToString(a)) 99 if err != nil { 100 return err 101 } 102 } 103 return nil 104 } 105 106 //elvdoc:fn base 107 // 108 // ```elvish 109 // base $base $number... 110 // ``` 111 // 112 // Outputs a string for each `$number` written in `$base`. The `$base` must be 113 // between 2 and 36, inclusive. Examples: 114 // 115 // ```elvish-transcript 116 // ~> base 2 1 3 4 16 255 117 // ▶ 1 118 // ▶ 11 119 // ▶ 100 120 // ▶ 10000 121 // ▶ 11111111 122 // ~> base 16 1 3 4 16 255 123 // ▶ 1 124 // ▶ 3 125 // ▶ 4 126 // ▶ 10 127 // ▶ ff 128 // ``` 129 130 // ErrBadBase is thrown by the "base" builtin if the base is smaller than 2 or 131 // greater than 36. 132 var ErrBadBase = errors.New("bad base") 133 134 func base(fm *Frame, b int, nums ...int) error { 135 if b < 2 || b > 36 { 136 return ErrBadBase 137 } 138 139 out := fm.ValueOutput() 140 for _, num := range nums { 141 err := out.Put(strconv.FormatInt(int64(num), b)) 142 if err != nil { 143 return err 144 } 145 } 146 return nil 147 } 148 149 var eawkWordSep = regexp.MustCompile("[ \t]+") 150 151 //elvdoc:fn eawk 152 // 153 // ```elvish 154 // eawk $f $inputs? 155 // ``` 156 // 157 // For each [value input](#value-inputs), calls `$f` with the input followed by 158 // all its fields. A [`break`](./builtin.html#break) command will cause `eawk` 159 // to stop processing inputs. A [`continue`](./builtin.html#continue) command 160 // will exit $f, but is ignored by `eawk`. 161 // 162 // It should behave the same as the following functions: 163 // 164 // ```elvish 165 // fn eawk {|f @rest| 166 // each {|line| 167 // var @fields = (re:split '[ \t]+' (str:trim $line " \t")) 168 // $f $line $@fields 169 // } $@rest 170 // } 171 // ``` 172 // 173 // This command allows you to write code very similar to `awk` scripts using 174 // anonymous functions. Example: 175 // 176 // ```elvish-transcript 177 // ~> echo " lorem ipsum\n1 2" | awk '{ print $1 }' 178 // lorem 179 // 1 180 // ~> echo " lorem ipsum\n1 2" | eawk {|line a b| put $a } 181 // ▶ lorem 182 // ▶ 1 183 // ``` 184 // 185 // **Note**: Since Elvish allows variable names consisting solely of digits, you 186 // can also do the following: 187 // 188 // ```elvish-transcript 189 // ~> echo " lorem ipsum\n1 2" | eawk {|0 1 2| put $1 } 190 // ▶ lorem 191 // ▶ 1 192 // ``` 193 194 func eawk(fm *Frame, f Callable, inputs Inputs) error { 195 broken := false 196 var err error 197 inputs(func(v interface{}) { 198 if broken { 199 return 200 } 201 line, ok := v.(string) 202 if !ok { 203 broken = true 204 err = ErrInputOfEawkMustBeString 205 return 206 } 207 args := []interface{}{line} 208 for _, field := range eawkWordSep.Split(strings.Trim(line, " \t"), -1) { 209 args = append(args, field) 210 } 211 212 newFm := fm.Fork("fn of eawk") 213 // TODO: Close port 0 of newFm. 214 ex := f.Call(newFm, args, NoOpts) 215 newFm.Close() 216 217 if ex != nil { 218 switch Reason(ex) { 219 case nil, Continue: 220 // nop 221 case Break: 222 broken = true 223 default: 224 broken = true 225 err = ex 226 } 227 } 228 }) 229 return err 230 }