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  }