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  }