src.elv.sh@v0.21.0-dev.0.20240515223629-06979efb9a2a/pkg/eval/builtin_fn_io.go (about) 1 package eval 2 3 import ( 4 "bufio" 5 "encoding/json" 6 "fmt" 7 "io" 8 "math/big" 9 "strconv" 10 "strings" 11 12 "src.elv.sh/pkg/diag" 13 "src.elv.sh/pkg/eval/errs" 14 "src.elv.sh/pkg/eval/vals" 15 "src.elv.sh/pkg/parse" 16 "src.elv.sh/pkg/strutil" 17 ) 18 19 // Input and output. 20 21 func init() { 22 addBuiltinFns(map[string]any{ 23 // Value output 24 "put": put, 25 "repeat": repeat, 26 27 // Bytes input 28 "read-bytes": readBytes, 29 "read-upto": readUpto, 30 "read-line": readLine, 31 32 // Bytes output 33 "print": print, 34 "echo": echo, 35 "pprint": pprint, 36 "repr": repr, 37 "show": show, 38 "printf": printf, 39 40 // Only bytes or values 41 // 42 // These are now implemented as commands forwarding one part of input to 43 // output and discarding the other. A future optimization the evaler can 44 // do is to connect the relevant parts directly together without any 45 // kind of forwarding. 46 "only-bytes": onlyBytes, 47 "only-values": onlyValues, 48 49 // Bytes to value 50 "slurp": slurp, 51 "from-lines": fromLines, 52 "from-json": fromJSON, 53 "from-terminated": fromTerminated, 54 55 // Value to bytes 56 "to-lines": toLines, 57 "to-json": toJSON, 58 "to-terminated": toTerminated, 59 }) 60 } 61 62 func put(fm *Frame, args ...any) error { 63 out := fm.ValueOutput() 64 for _, a := range args { 65 err := out.Put(a) 66 if err != nil { 67 return err 68 } 69 } 70 return nil 71 } 72 73 func repeat(fm *Frame, n int, v any) error { 74 out := fm.ValueOutput() 75 for i := 0; i < n; i++ { 76 err := out.Put(v) 77 if err != nil { 78 return err 79 } 80 } 81 return nil 82 } 83 84 func readBytes(fm *Frame, max int) (string, error) { 85 in := fm.InputFile() 86 buf := make([]byte, max) 87 read := 0 88 for read < max { 89 n, err := in.Read(buf[read:]) 90 read += n 91 if err == io.EOF { 92 break 93 } else if err != nil { 94 return "", err 95 } 96 } 97 return string(buf[:read]), nil 98 } 99 100 func readUpto(fm *Frame, terminator string) (string, error) { 101 if err := checkTerminator(terminator); err != nil { 102 return "", err 103 } 104 in := fm.InputFile() 105 var buf []byte 106 for { 107 var b [1]byte 108 _, err := in.Read(b[:]) 109 if err != nil { 110 if err == io.EOF { 111 break 112 } 113 return "", err 114 } 115 buf = append(buf, b[0]) 116 if b[0] == terminator[0] { 117 break 118 } 119 } 120 return string(buf), nil 121 } 122 123 func checkTerminator(s string) error { 124 if len(s) != 1 || s[0] > 127 { 125 return errs.BadValue{What: "terminator", 126 Valid: "a single ASCII character", Actual: parse.Quote(s)} 127 } 128 return nil 129 } 130 131 func readLine(fm *Frame) (string, error) { 132 s, err := readUpto(fm, "\n") 133 if err != nil { 134 return "", err 135 } 136 return strutil.ChopLineEnding(s), nil 137 } 138 139 type printOpts struct{ Sep string } 140 141 func (o *printOpts) SetDefaultOptions() { o.Sep = " " } 142 143 func print(fm *Frame, opts printOpts, args ...any) error { 144 out := fm.ByteOutput() 145 for i, arg := range args { 146 if i > 0 { 147 _, err := out.WriteString(opts.Sep) 148 if err != nil { 149 return err 150 } 151 } 152 _, err := out.WriteString(vals.ToString(arg)) 153 if err != nil { 154 return err 155 } 156 } 157 return nil 158 } 159 160 func printf(fm *Frame, template string, args ...any) error { 161 wrappedArgs := make([]any, len(args)) 162 for i, arg := range args { 163 wrappedArgs[i] = formatter{arg} 164 } 165 166 _, err := fmt.Fprintf(fm.ByteOutput(), template, wrappedArgs...) 167 return err 168 } 169 170 type formatter struct { 171 wrapped any 172 } 173 174 func (f formatter) Format(state fmt.State, r rune) { 175 wrapped := f.wrapped 176 switch r { 177 case 's': 178 writeFmt(state, 's', vals.ToString(wrapped)) 179 case 'q': 180 // TODO: Support using the precision flag to specify indentation. 181 writeFmt(state, 's', vals.ReprPlain(wrapped)) 182 case 'v': 183 var s string 184 if state.Flag('#') { 185 s = vals.ReprPlain(wrapped) 186 } else { 187 s = vals.ToString(wrapped) 188 } 189 writeFmt(state, 's', s) 190 case 't': 191 writeFmt(state, 't', vals.Bool(wrapped)) 192 case 'b', 'c', 'd', 'o', 'O', 'x', 'X', 'U': 193 var i int 194 if err := vals.ScanToGo(wrapped, &i); err != nil { 195 fmt.Fprintf(state, "%%!%c(%s)", r, err.Error()) 196 return 197 } 198 writeFmt(state, r, i) 199 case 'e', 'E', 'f', 'F', 'g', 'G': 200 var f float64 201 if err := vals.ScanToGo(wrapped, &f); err != nil { 202 fmt.Fprintf(state, "%%!%c(%s)", r, err.Error()) 203 return 204 } 205 writeFmt(state, r, f) 206 default: 207 fmt.Fprintf(state, "%%!%c(unsupported formatting verb)", r) 208 } 209 } 210 211 // Writes to State using the flag it stores, but with a potentially different 212 // verb and value. 213 func writeFmt(state fmt.State, v rune, val any) { 214 // Reconstruct the verb string. 215 var sb strings.Builder 216 sb.WriteRune('%') 217 for _, f := range "+-# 0" { 218 if state.Flag(int(f)) { 219 sb.WriteRune(f) 220 } 221 } 222 if w, ok := state.Width(); ok { 223 sb.WriteString(strconv.Itoa(w)) 224 } 225 if p, ok := state.Precision(); ok { 226 sb.WriteRune('.') 227 sb.WriteString(strconv.Itoa(p)) 228 } 229 sb.WriteRune(v) 230 231 fmt.Fprintf(state, sb.String(), val) 232 } 233 234 func echo(fm *Frame, opts printOpts, args ...any) error { 235 err := print(fm, opts, args...) 236 if err != nil { 237 return err 238 } 239 _, err = fm.ByteOutput().WriteString("\n") 240 return err 241 } 242 243 func pprint(fm *Frame, args ...any) error { 244 out := fm.ByteOutput() 245 for _, arg := range args { 246 _, err := out.WriteString(vals.Repr(arg, 0)) 247 if err != nil { 248 return err 249 } 250 _, err = out.WriteString("\n") 251 if err != nil { 252 return err 253 } 254 } 255 return nil 256 } 257 258 func repr(fm *Frame, args ...any) error { 259 out := fm.ByteOutput() 260 for i, arg := range args { 261 if i > 0 { 262 _, err := out.WriteString(" ") 263 if err != nil { 264 return err 265 } 266 } 267 _, err := out.WriteString(vals.ReprPlain(arg)) 268 if err != nil { 269 return err 270 } 271 } 272 _, err := out.WriteString("\n") 273 return err 274 } 275 276 func show(fm *Frame, v diag.Shower) error { 277 out := fm.ByteOutput() 278 _, err := out.WriteString(v.Show("")) 279 if err != nil { 280 return err 281 } 282 _, err = out.WriteString("\n") 283 return err 284 } 285 286 func onlyBytes(fm *Frame) error { 287 // Discard values in a goroutine. 288 valuesDone := make(chan struct{}) 289 go func() { 290 for range fm.InputChan() { 291 } 292 close(valuesDone) 293 }() 294 // Make sure the goroutine has finished before returning. 295 defer func() { <-valuesDone }() 296 297 _, err := io.Copy(fm.ByteOutput(), fm.InputFile()) 298 return err 299 } 300 301 func onlyValues(fm *Frame) error { 302 // Discard bytes in a goroutine. 303 bytesDone := make(chan struct{}) 304 go func() { 305 // Ignore the error 306 _, _ = io.Copy(blackholeWriter{}, fm.InputFile()) 307 close(bytesDone) 308 }() 309 // Wait for the goroutine to finish before returning. 310 defer func() { <-bytesDone }() 311 312 // Forward values. 313 out := fm.ValueOutput() 314 for v := range fm.InputChan() { 315 err := out.Put(v) 316 if err != nil { 317 return err 318 } 319 } 320 return nil 321 } 322 323 type blackholeWriter struct{} 324 325 func (blackholeWriter) Write(p []byte) (int, error) { return len(p), nil } 326 327 func slurp(fm *Frame) (string, error) { 328 b, err := io.ReadAll(fm.InputFile()) 329 return string(b), err 330 } 331 332 func fromLines(fm *Frame) error { 333 filein := bufio.NewReader(fm.InputFile()) 334 out := fm.ValueOutput() 335 for { 336 line, err := filein.ReadString('\n') 337 if line != "" { 338 err := out.Put(strutil.ChopLineEnding(line)) 339 if err != nil { 340 return err 341 } 342 } 343 if err != nil { 344 if err != io.EOF { 345 return err 346 } 347 return nil 348 } 349 } 350 } 351 352 func fromJSON(fm *Frame) error { 353 in := fm.InputFile() 354 out := fm.ValueOutput() 355 356 dec := json.NewDecoder(in) 357 // See comments below about using json.Number. 358 dec.UseNumber() 359 for { 360 var v any 361 err := dec.Decode(&v) 362 if err != nil { 363 if err == io.EOF { 364 return nil 365 } 366 return err 367 } 368 converted, err := fromJSONInterface(v) 369 if err != nil { 370 return err 371 } 372 err = out.Put(converted) 373 if err != nil { 374 return err 375 } 376 } 377 } 378 379 // Converts a interface{} that results from json.Unmarshal to an Elvish value. 380 func fromJSONInterface(v any) (any, error) { 381 switch v := v.(type) { 382 case nil, bool, string: 383 return v, nil 384 case json.Number: 385 // The JSON syntax doesn't restrict the precision of numbers. Since 386 // we called json.Decoder.UseNumber, it preserves the full number 387 // literal, and we can try parsing it as a big int. 388 if z, ok := new(big.Int).SetString(v.String(), 0); ok { 389 // Also normalize to int if the value fits. 390 return vals.NormalizeBigInt(z), nil 391 } 392 // Parse as float64 instead. This can error if the number is not an 393 // integer and exceeds the range of float64. 394 return strconv.ParseFloat(v.String(), 64) 395 case float64: 396 return v, nil 397 case []any: 398 vec := vals.EmptyList 399 for _, elem := range v { 400 converted, err := fromJSONInterface(elem) 401 if err != nil { 402 return nil, err 403 } 404 vec = vec.Conj(converted) 405 } 406 return vec, nil 407 case map[string]any: 408 m := vals.EmptyMap 409 for key, val := range v { 410 convertedVal, err := fromJSONInterface(val) 411 if err != nil { 412 return nil, err 413 } 414 m = m.Assoc(key, convertedVal) 415 } 416 return m, nil 417 default: 418 return nil, fmt.Errorf("unexpected json type: %T", v) 419 } 420 } 421 422 func fromTerminated(fm *Frame, terminator string) error { 423 if err := checkTerminator(terminator); err != nil { 424 return err 425 } 426 427 filein := bufio.NewReader(fm.InputFile()) 428 out := fm.ValueOutput() 429 for { 430 line, err := filein.ReadString(terminator[0]) 431 if line != "" { 432 err := out.Put(strutil.ChopTerminator(line, terminator[0])) 433 if err != nil { 434 return err 435 } 436 } 437 if err != nil { 438 if err != io.EOF { 439 logger.Println("error on reading:", err) 440 return err 441 } 442 return nil 443 } 444 } 445 } 446 447 func toLines(fm *Frame, inputs Inputs) error { 448 out := fm.ByteOutput() 449 var errOut error 450 451 inputs(func(v any) { 452 if errOut != nil { 453 return 454 } 455 // TODO: Don't ignore the error. 456 _, errOut = fmt.Fprintln(out, vals.ToString(v)) 457 }) 458 return errOut 459 } 460 461 func toTerminated(fm *Frame, terminator string, inputs Inputs) error { 462 if err := checkTerminator(terminator); err != nil { 463 return err 464 } 465 466 out := fm.ByteOutput() 467 var errOut error 468 inputs(func(v any) { 469 if errOut != nil { 470 return 471 } 472 _, errOut = fmt.Fprint(out, vals.ToString(v), terminator) 473 }) 474 return errOut 475 } 476 477 func toJSON(fm *Frame, inputs Inputs) error { 478 encoder := json.NewEncoder(fm.ByteOutput()) 479 480 var errEncode error 481 inputs(func(v any) { 482 if errEncode != nil { 483 return 484 } 485 errEncode = encoder.Encode(v) 486 }) 487 return errEncode 488 }